github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libkb/basex.go (about) 1 // Copyright 2020 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package libkb 5 6 import ( 7 "bytes" 8 "fmt" 9 "math/big" 10 "strconv" 11 ) 12 13 const invalidBaseIndex = 0xFF 14 15 var Base30 = NewBaseX("abcdefghjkmnpqrsuvwxyz23456789") 16 var Base58 = NewBaseX("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") 17 18 type BaseXEncoder struct { 19 size int 20 base *big.Int 21 alphabet string 22 alphabetMap [256]byte 23 } 24 25 func NewBaseX(alphabet string) *BaseXEncoder { 26 enc := &BaseXEncoder{ 27 size: len(alphabet), 28 base: big.NewInt(int64(len(alphabet))), 29 alphabet: alphabet, 30 } 31 if enc.size > 255 { 32 panic("unsupported BaseX alphabet size, got " + strconv.Itoa(enc.size)) 33 } 34 for i := range enc.alphabetMap { 35 enc.alphabetMap[i] = invalidBaseIndex 36 } 37 for i, c := range alphabet { 38 enc.alphabetMap[c] = uint8(i) 39 } 40 return enc 41 } 42 43 func reverseBuf(buf []byte) { 44 tot := len(buf) 45 mid := tot / 2 46 for i := 0; i < mid; i++ { 47 buf[i], buf[tot-i-1] = buf[tot-i-1], buf[i] 48 } 49 } 50 51 func (b *BaseXEncoder) EncodeToString(input []byte) string { 52 num := new(big.Int).SetBytes(input) 53 buf := make([]byte, 0, len(input)) 54 rem := new(big.Int) 55 quo := new(big.Int) 56 57 for num.Sign() != 0 { 58 num, rem = quo.QuoRem(num, b.base, rem) 59 c := b.alphabet[rem.Uint64()] 60 buf = append(buf, c) 61 } 62 63 // Pad leading zeros... 64 for _, c := range input { 65 if c == 0x0 { 66 buf = append(buf, b.alphabet[0]) 67 } else { 68 // Stop adding padding after the first nonzero byte. 69 break 70 } 71 } 72 reverseBuf(buf) 73 74 return string(buf) 75 } 76 77 func (b *BaseXEncoder) DecodeString(inp string) (outp []byte, err error) { 78 place := big.NewInt(1) 79 buf := []byte(inp) 80 padlen := 0 81 82 // Advance to first non-pad byte 83 for ; padlen < len(buf); padlen++ { 84 if buf[padlen] != b.alphabet[0] { 85 break 86 } 87 } 88 buf = buf[padlen:] 89 reverseBuf(buf) 90 91 tmp := new(big.Int) 92 res := big.NewInt(0) 93 94 for i, c := range buf { 95 charIndex := b.alphabetMap[c] 96 if charIndex == invalidBaseIndex { 97 err = fmt.Errorf("Bad character '%c' found at pos %d", c, i) 98 return 99 } 100 101 tmp.Mul(place, big.NewInt(int64(charIndex))) 102 res.Add(res, tmp) 103 104 if i != len(buf)-1 { 105 place.Mul(place, b.base) 106 } 107 } 108 buf = res.Bytes() 109 pad := bytes.Repeat([]byte{0}, padlen) 110 outp = make([]byte, len(pad)+len(buf)) 111 copy(outp, pad) 112 copy(outp[len(pad):], buf) 113 114 return 115 }