package crypto import ( "bytes" "crypto/sha256" "fmt" "math/big" "github.com/pkg/errors" ) const prefix rune = '1' var decodeMap = map[rune]int64{ '1': 0, '2': 1, '3': 2, '4': 3, '5': 4, '6': 5, '7': 6, '8': 7, '9': 8, 'A': 9, 'B': 10, 'C': 11, 'D': 12, 'E': 13, 'F': 14, 'G': 15, 'H': 16, 'J': 17, 'K': 18, 'L': 19, 'M': 20, 'N': 21, 'P': 22, 'Q': 23, 'R': 24, 'S': 25, 'T': 26, 'U': 27, 'V': 28, 'W': 29, 'X': 30, 'Y': 31, 'Z': 32, 'a': 33, 'b': 34, 'c': 35, 'd': 36, 'e': 37, 'f': 38, 'g': 39, 'h': 40, 'i': 41, 'j': 42, 'k': 43, 'm': 44, 'n': 45, 'o': 46, 'p': 47, 'q': 48, 'r': 49, 's': 50, 't': 51, 'u': 52, 'v': 53, 'w': 54, 'x': 55, 'y': 56, 'z': 57, } // Base58Decode decodes the base58 encoded string. func Base58Decode(s string) ([]byte, error) { var ( startIndex = 0 zero = 0 ) for i, c := range s { if c == prefix { zero++ } else { startIndex = i break } } var ( n = big.NewInt(0) div = big.NewInt(58) ) for _, c := range s[startIndex:] { charIndex, ok := decodeMap[c] if !ok { return nil, fmt.Errorf( "invalid character '%c' when decoding this base58 string: '%s'", c, s, ) } n.Add(n.Mul(n, div), big.NewInt(charIndex)) } out := n.Bytes() buf := make([]byte, zero+len(out)) copy(buf[zero:], out) return buf, nil } // Base58Encode encodes a byte slice to be a base58 encoded string. func Base58Encode(bytes []byte) string { var ( lookupTable = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" x = new(big.Int).SetBytes(bytes) r = new(big.Int) m = big.NewInt(58) zero = big.NewInt(0) encoded string ) for x.Cmp(zero) > 0 { x.QuoRem(x, m, r) encoded = string(lookupTable[r.Int64()]) + encoded } return encoded } // Base58CheckDecode decodes the given string. func Base58CheckDecode(s string) (b []byte, err error) { b, err = Base58Decode(s) if err != nil { return nil, err } for i := 0; i < len(s); i++ { if s[i] != '1' { break } b = append([]byte{0x00}, b...) } if len(b) < 5 { return nil, errors.New("invalid base-58 check string: missing checksum.") } sha := sha256.New() if _, err = sha.Write(b[:len(b)-4]); err != nil { return nil, err } hash := sha.Sum(nil) sha.Reset() if _, err = sha.Write(hash); err != nil { return nil, err } hash = sha.Sum(nil) if !bytes.Equal(hash[0:4], b[len(b)-4:]) { return nil, errors.New("invalid base-58 check string: invalid checksum.") } // Strip the 4 byte long hash. b = b[:len(b)-4] return b, nil } // Base58checkEncode encodes b into a base-58 check encoded string. func Base58CheckEncode(b []byte) string { sha := sha256.New() sha.Write(b) hash := sha.Sum(nil) sha.Reset() sha.Write(hash) hash = sha.Sum(nil) b = append(b, hash[0:4]...) return Base58Encode(b) }