package authenticator import ( "fmt" "regexp" "strings" ) // base32Alphabet is the RFC 4648 / 3548 base32 alphabet (no padding). const base32Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" var ( base32EncodeTable [32]byte base32DecodeTable [256]int8 base32CleanRE = regexp.MustCompile(`[\s-]+`) base32PadRE = regexp.MustCompile(`=+$`) ) func init() { for i, c := range base32Alphabet { base32EncodeTable[i] = byte(c) } for i := range base32DecodeTable { base32DecodeTable[i] = -1 } for i, c := range base32Alphabet { base32DecodeTable[c] = int8(i) } } // Base32Decode decodes a base32 string into bytes. Whitespace and dashes // are stripped; trailing '=' padding is ignored; input is uppercased before // decoding. This mirrors the lenient behavior of the original C# Base32 class. func Base32Decode(encoded string) ([]byte, error) { encoded = base32CleanRE.ReplaceAllString(encoded, "") encoded = base32PadRE.ReplaceAllString(encoded, "") encoded = strings.ToUpper(encoded) if encoded == "" { return []byte{}, nil } const shift = 5 const mask = 0x1F outLen := len(encoded) * shift / 8 out := make([]byte, outLen) var buffer int var bitsLeft int var next int for _, c := range encoded { if c >= 256 || base32DecodeTable[c] < 0 { return nil, fmt.Errorf("base32: illegal character %q", c) } buffer <<= shift buffer |= int(base32DecodeTable[c]) & mask bitsLeft += shift if bitsLeft >= 8 { out[next] = byte(buffer >> (bitsLeft - 8)) next++ bitsLeft -= 8 } } return out, nil } // Base32Encode encodes raw bytes as base32 with no padding. func Base32Encode(data []byte) string { if len(data) == 0 { return "" } const shift = 5 const mask = 0x1F var sb strings.Builder buffer := int(data[0]) next := 1 bitsLeft := 8 for bitsLeft > 0 || next < len(data) { if bitsLeft < shift { if next < len(data) { buffer <<= 8 buffer |= int(data[next]) & 0xFF next++ bitsLeft += 8 } else { pad := shift - bitsLeft buffer <<= pad bitsLeft += pad } } index := mask & (buffer >> (bitsLeft - shift)) bitsLeft -= shift sb.WriteByte(base32EncodeTable[index]) } return sb.String() }