├── .gitignore ├── fuzz.go ├── example_test.go ├── test-against-python ├── test-fuzz ├── flag.go ├── cmd ├── zbase32-encode │ └── encode.go └── zbase32-decode │ └── decode.go ├── LICENSE ├── README.md ├── flag_test.go ├── bench_test.go ├── zbase32_test.go ├── quick_test.go └── zbase32.go /.gitignore: -------------------------------------------------------------------------------- 1 | /zbase32-encode 2 | /zbase32-decode 3 | *.test 4 | /virtualenv/ 5 | /fuzz/ 6 | /zbase32-fuzz.zip 7 | -------------------------------------------------------------------------------- /fuzz.go: -------------------------------------------------------------------------------- 1 | // +build gofuzz 2 | 3 | package zbase32 4 | 5 | func Fuzz(data []byte) int { 6 | if _, err := DecodeString(string(data)); err != nil { 7 | return 0 8 | } 9 | return 1 10 | } 11 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | package zbase32_test 2 | 3 | import "fmt" 4 | import "github.com/tv42/zbase32" 5 | 6 | func Example() { 7 | s := zbase32.EncodeToString([]byte{240, 191, 199}) 8 | fmt.Println(s) 9 | // Output: 10 | // 6n9hq 11 | } 12 | -------------------------------------------------------------------------------- /test-against-python: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | if [ ! -e virtualenv/ok ]; then 5 | python -m virtualenv virtualenv 6 | ./virtualenv/bin/pip install zbase32 7 | touch virtualenv/ok 8 | fi 9 | 10 | PATH="$PWD/virtualenv/bin:$PATH" go test -python -test.run=Python "$@" 11 | -------------------------------------------------------------------------------- /test-fuzz: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | go-fuzz-build github.com/tv42/zbase32 5 | install -d fuzz 6 | # kludge to extract corpus from tests 7 | perl -ne 'print "$1\n" if /"([ybndrfg8ejkmcpqxot1uwisza345h769]+)"/' zbase32_test.go \ 8 | | while read f; do printf '%s\n' "$f" >"fuzz/$f.input"; done 9 | exec go-fuzz -bin=zbase32-fuzz.zip -workdir=fuzz 10 | -------------------------------------------------------------------------------- /flag.go: -------------------------------------------------------------------------------- 1 | package zbase32 2 | 3 | import "flag" 4 | 5 | // Value implements flag parsing for zbase32 values. 6 | type Value []byte 7 | 8 | var _ flag.Value = (*Value)(nil) 9 | 10 | // String returns the z-base-32 encoding of the value. 11 | func (v *Value) String() string { 12 | return EncodeToString(*v) 13 | } 14 | 15 | // Set the value to data encoded by string. 16 | func (v *Value) Set(s string) error { 17 | b, err := DecodeString(s) 18 | if err != nil { 19 | return err 20 | } 21 | *v = b 22 | return nil 23 | } 24 | 25 | var _ flag.Getter = (*Value)(nil) 26 | 27 | // Get the data stored in the value. Returns a value of type []byte. 28 | func (v *Value) Get() interface{} { 29 | return []byte(*v) 30 | } 31 | -------------------------------------------------------------------------------- /cmd/zbase32-encode/encode.go: -------------------------------------------------------------------------------- 1 | // Command zbase32-encode encodes its standard input as zbase32. 2 | // 3 | // Usage: 4 | // 5 | // zbase32-encode 0 { 39 | for _, input := range flag.Args() { 40 | decoded, err := zbase32.DecodeString(input) 41 | if err != nil { 42 | log.Fatalf("decoding input: %q: %v", input, err) 43 | } 44 | if _, err := os.Stdout.Write(decoded); err != nil { 45 | log.Fatal(err) 46 | } 47 | } 48 | } else { 49 | scanner := bufio.NewScanner(os.Stdin) 50 | for scanner.Scan() { 51 | input := scanner.Text() 52 | decoded, err := zbase32.DecodeString(input) 53 | if err != nil { 54 | log.Fatalf("decoding input: %q: %v", input, err) 55 | } 56 | if _, err = os.Stdout.Write(decoded); err != nil { 57 | log.Fatal(err) 58 | } 59 | } 60 | if err := scanner.Err(); err != nil { 61 | log.Fatalf("reading standard input:", err) 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /bench_test.go: -------------------------------------------------------------------------------- 1 | package zbase32_test 2 | 3 | import ( 4 | "encoding/base32" 5 | "encoding/hex" 6 | "testing" 7 | 8 | "github.com/tv42/zbase32" 9 | ) 10 | 11 | func BenchmarkEncodeBytes(b *testing.B) { 12 | decoded := []byte{ 13 | 0xc0, 0x73, 0x62, 0x4a, 0xaf, 0x39, 0x78, 0x51, 14 | 0x4e, 0xf8, 0x44, 0x3b, 0xb2, 0xa8, 0x59, 0xc7, 15 | 0x5f, 0xc3, 0xcc, 0x6a, 0xf2, 0x6d, 0x5a, 0xaa, 16 | } 17 | dst := make([]byte, zbase32.EncodedLen(len(decoded))) 18 | b.ResetTimer() 19 | 20 | for i := 0; i < b.N; i++ { 21 | n := zbase32.Encode(dst, decoded) 22 | _ = dst[:n] 23 | } 24 | } 25 | 26 | func BenchmarkEncodeBase32(b *testing.B) { 27 | decoded := []byte{ 28 | 0xc0, 0x73, 0x62, 0x4a, 0xaf, 0x39, 0x78, 0x51, 29 | 0x4e, 0xf8, 0x44, 0x3b, 0xb2, 0xa8, 0x59, 0xc7, 30 | 0x5f, 0xc3, 0xcc, 0x6a, 0xf2, 0x6d, 0x5a, 0xaa, 31 | } 32 | dst := make([]byte, base32.StdEncoding.EncodedLen(len(decoded))) 33 | b.ResetTimer() 34 | 35 | for i := 0; i < b.N; i++ { 36 | base32.StdEncoding.Encode(dst, decoded) 37 | _ = dst 38 | } 39 | } 40 | 41 | func BenchmarkEncodeHex(b *testing.B) { 42 | decoded := []byte{ 43 | 0xc0, 0x73, 0x62, 0x4a, 0xaf, 0x39, 0x78, 0x51, 44 | 0x4e, 0xf8, 0x44, 0x3b, 0xb2, 0xa8, 0x59, 0xc7, 45 | 0x5f, 0xc3, 0xcc, 0x6a, 0xf2, 0x6d, 0x5a, 0xaa, 46 | } 47 | dst := make([]byte, hex.EncodedLen(len(decoded))) 48 | b.ResetTimer() 49 | 50 | for i := 0; i < b.N; i++ { 51 | n := hex.Encode(dst, decoded) 52 | _ = dst[:n] 53 | } 54 | } 55 | 56 | func BenchmarkDecodeBytes(b *testing.B) { 57 | encoded := []byte("ab3sr1ix8fhfnuzaeo75fkn3a7xh8udk6jsiiko") 58 | dst := make([]byte, zbase32.DecodedLen(len(encoded))) 59 | b.ResetTimer() 60 | 61 | for i := 0; i < b.N; i++ { 62 | n, err := zbase32.Decode(dst, encoded) 63 | if err != nil { 64 | b.Fatalf("decode error: %v", err) 65 | } 66 | _ = dst[:n] 67 | } 68 | } 69 | 70 | func BenchmarkDecodeBase32(b *testing.B) { 71 | encoded := []byte("YBZWESVPHF4FCTXYIQ53FKCZY5P4HTDK6JWVVKQ=") 72 | dst := make([]byte, base32.StdEncoding.DecodedLen(len(encoded))) 73 | b.ResetTimer() 74 | 75 | for i := 0; i < b.N; i++ { 76 | n, err := base32.StdEncoding.Decode(dst, encoded) 77 | if err != nil { 78 | b.Fatalf("decode error: %v", err) 79 | } 80 | _ = dst[:n] 81 | } 82 | } 83 | 84 | func BenchmarkDecodeHex(b *testing.B) { 85 | encoded := []byte("c073624aaf3978514ef8443bb2a859c75fc3cc6af26d5aaa") 86 | dst := make([]byte, hex.DecodedLen(len(encoded))) 87 | b.ResetTimer() 88 | 89 | for i := 0; i < b.N; i++ { 90 | n, err := hex.Decode(dst, encoded) 91 | if err != nil { 92 | b.Fatalf("decode error: %v", err) 93 | } 94 | _ = dst[:n] 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /zbase32_test.go: -------------------------------------------------------------------------------- 1 | package zbase32_test 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/tv42/zbase32" 8 | ) 9 | 10 | type bitTestCase struct { 11 | bits int 12 | decoded []byte 13 | encoded string 14 | } 15 | 16 | var bitTests = []bitTestCase{ 17 | // Test cases from the spec 18 | {0, []byte{}, ""}, 19 | {1, []byte{0}, "y"}, 20 | {1, []byte{128}, "o"}, 21 | {2, []byte{64}, "e"}, 22 | {2, []byte{192}, "a"}, 23 | {10, []byte{0, 0}, "yy"}, 24 | {10, []byte{128, 128}, "on"}, 25 | {20, []byte{139, 136, 128}, "tqre"}, 26 | {24, []byte{240, 191, 199}, "6n9hq"}, 27 | {24, []byte{212, 122, 4}, "4t7ye"}, 28 | // Note: this test varies from what's in the spec by one character! 29 | {30, []byte{245, 87, 189, 12}, "6im54d"}, 30 | 31 | // Edge cases we stumbled on, that are not covered above. 32 | {8, []byte{0xff}, "9h"}, 33 | {11, []byte{0xff, 0xE0}, "99o"}, 34 | {40, []byte{0xff, 0xff, 0xff, 0xff, 0xff}, "99999999"}, 35 | {48, []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, "999999999h"}, 36 | {192, []byte{ 37 | 0xc0, 0x73, 0x62, 0x4a, 0xaf, 0x39, 0x78, 0x51, 38 | 0x4e, 0xf8, 0x44, 0x3b, 0xb2, 0xa8, 0x59, 0xc7, 39 | 0x5f, 0xc3, 0xcc, 0x6a, 0xf2, 0x6d, 0x5a, 0xaa, 40 | }, "ab3sr1ix8fhfnuzaeo75fkn3a7xh8udk6jsiiko"}, 41 | 42 | // Used in the docs. 43 | {20, []byte{0x10, 0x11, 0x10}, "nyet"}, 44 | {24, []byte{0x10, 0x11, 0x10}, "nyety"}, 45 | } 46 | 47 | type byteTestCase struct { 48 | decoded []byte 49 | encoded string 50 | } 51 | 52 | var byteTests = []byteTestCase{ 53 | // Byte-aligned test cases from the spec 54 | {[]byte{240, 191, 199}, "6n9hq"}, 55 | {[]byte{212, 122, 4}, "4t7ye"}, 56 | 57 | // Edge cases we stumbled on, that are not covered above. 58 | {[]byte{0xff}, "9h"}, 59 | {[]byte{0xb5}, "sw"}, 60 | {[]byte{0x34, 0x5a}, "gtpy"}, 61 | {[]byte{0xff, 0xff, 0xff, 0xff, 0xff}, "99999999"}, 62 | {[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, "999999999h"}, 63 | {[]byte{ 64 | 0xc0, 0x73, 0x62, 0x4a, 0xaf, 0x39, 0x78, 0x51, 65 | 0x4e, 0xf8, 0x44, 0x3b, 0xb2, 0xa8, 0x59, 0xc7, 66 | 0x5f, 0xc3, 0xcc, 0x6a, 0xf2, 0x6d, 0x5a, 0xaa, 67 | }, "ab3sr1ix8fhfnuzaeo75fkn3a7xh8udk6jsiiko"}, 68 | } 69 | 70 | func TestEncodeBits(t *testing.T) { 71 | for _, tc := range bitTests { 72 | dst := make([]byte, zbase32.EncodedLen(len(tc.decoded))) 73 | n := zbase32.EncodeBits(dst, tc.decoded, tc.bits) 74 | dst = dst[:n] 75 | if g, e := string(dst), tc.encoded; g != e { 76 | t.Errorf("EncodeBits %d bits of %x wrong result: %q != %q", tc.bits, tc.decoded, g, e) 77 | continue 78 | } 79 | } 80 | } 81 | 82 | func TestEncodeBitsString(t *testing.T) { 83 | for _, tc := range bitTests { 84 | s := zbase32.EncodeBitsToString(tc.decoded, tc.bits) 85 | if g, e := s, tc.encoded; g != e { 86 | t.Errorf("EncodeBitsToString %d bits of %x wrong result: %q != %q", tc.bits, tc.decoded, g, e) 87 | continue 88 | } 89 | } 90 | } 91 | 92 | func TestEncodeBytes(t *testing.T) { 93 | for _, tc := range byteTests { 94 | dst := make([]byte, zbase32.EncodedLen(len(tc.decoded))) 95 | n := zbase32.Encode(dst, tc.decoded) 96 | dst = dst[:n] 97 | 98 | if g, e := string(dst), tc.encoded; g != e { 99 | t.Errorf("Encode %x wrong result: %q != %q", tc.decoded, g, e) 100 | continue 101 | } 102 | } 103 | } 104 | 105 | func TestEncodeBitsMasksExcess(t *testing.T) { 106 | for _, tc := range []bitTestCase{ 107 | {0, []byte{255, 255}, ""}, 108 | {1, []byte{255, 255}, "o"}, 109 | {2, []byte{255, 255}, "a"}, 110 | {3, []byte{255, 255}, "h"}, 111 | {4, []byte{255, 255}, "6"}, 112 | {5, []byte{255, 255}, "9"}, 113 | {6, []byte{255, 255}, "9o"}, 114 | {7, []byte{255, 255}, "9a"}, 115 | {8, []byte{255, 255}, "9h"}, 116 | {9, []byte{255, 255}, "96"}, 117 | {10, []byte{255, 255}, "99"}, 118 | {11, []byte{255, 255}, "99o"}, 119 | {12, []byte{255, 255}, "99a"}, 120 | {13, []byte{255, 255}, "99h"}, 121 | {14, []byte{255, 255}, "996"}, 122 | {15, []byte{255, 255}, "999"}, 123 | {16, []byte{255, 255}, "999o"}, 124 | } { 125 | dst := make([]byte, zbase32.EncodedLen(len(tc.decoded))) 126 | n := zbase32.EncodeBits(dst, tc.decoded, tc.bits) 127 | dst = dst[:n] 128 | if g, e := string(dst), tc.encoded; g != e { 129 | t.Errorf("EncodeBits %d bits of %x wrong result: %q != %q", tc.bits, tc.decoded, g, e) 130 | } 131 | } 132 | } 133 | 134 | func TestDecodeBits(t *testing.T) { 135 | for _, tc := range bitTests { 136 | dst := make([]byte, zbase32.DecodedLen(len(tc.encoded))) 137 | n, err := zbase32.DecodeBits(dst, []byte(tc.encoded), tc.bits) 138 | dst = dst[:n] 139 | if err != nil { 140 | t.Errorf("DecodeBits %d bits from %q: error: %v", tc.bits, tc.encoded, err) 141 | continue 142 | } 143 | if g, e := dst, tc.decoded; !bytes.Equal(g, e) { 144 | t.Errorf("DecodeBits %d bits from %q, %x != %x", tc.bits, tc.encoded, g, e) 145 | } 146 | } 147 | } 148 | 149 | func TestDecodeBitsString(t *testing.T) { 150 | for _, tc := range bitTests { 151 | dec, err := zbase32.DecodeBitsString(tc.encoded, tc.bits) 152 | if err != nil { 153 | t.Errorf("DecodeBits %d bits from %q: error: %v", tc.bits, tc.encoded, err) 154 | continue 155 | } 156 | if g, e := dec, tc.decoded; !bytes.Equal(g, e) { 157 | t.Errorf("DecodeBits %d bits from %q, %x != %x", tc.bits, tc.encoded, g, e) 158 | } 159 | } 160 | } 161 | 162 | func TestDecodeBytes(t *testing.T) { 163 | for _, tc := range byteTests { 164 | dst := make([]byte, zbase32.DecodedLen(len(tc.encoded))) 165 | n, err := zbase32.Decode(dst, []byte(tc.encoded)) 166 | dst = dst[:n] 167 | if err != nil { 168 | t.Errorf("Decode %q: error: %v", tc.encoded, err) 169 | continue 170 | } 171 | if g, e := dst, tc.decoded; !bytes.Equal(g, e) { 172 | t.Errorf("Decode %q, %x != %x", tc.encoded, g, e) 173 | } 174 | } 175 | } 176 | 177 | func TestDecodeBad(t *testing.T) { 178 | input := `foo!bar` 179 | _, err := zbase32.DecodeString(input) 180 | switch err := err.(type) { 181 | case nil: 182 | t.Fatalf("expected error from bad decode") 183 | case zbase32.CorruptInputError: 184 | if g, e := err.Error(), `illegal z-base-32 data at input byte 3`; g != e { 185 | t.Fatalf("wrong error: %q != %q", g, e) 186 | } 187 | default: 188 | t.Fatalf("wrong error from bad decode: %T: %v", err, err) 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /quick_test.go: -------------------------------------------------------------------------------- 1 | package zbase32_test 2 | 3 | import ( 4 | "bytes" 5 | "flag" 6 | "fmt" 7 | "os" 8 | "os/exec" 9 | "strconv" 10 | "strings" 11 | "testing" 12 | "testing/quick" 13 | 14 | "github.com/tv42/zbase32" 15 | ) 16 | 17 | var python = flag.Bool("python", false, "run comparison tests against Python zbase32 library") 18 | 19 | func calcBits(orig []byte, partial uint) int { 20 | bits := 8 * uint(len(orig)) 21 | partial &= 7 22 | if bits > partial { 23 | bits -= partial 24 | mask := ^(byte(1< 0); i += 5 { 65 | b0 := src[0] 66 | b1 := byte(0) 67 | 68 | if len(src) > 1 { 69 | b1 = src[1] 70 | } 71 | 72 | char := byte(0) 73 | offset := uint(i % 8) 74 | 75 | if offset < 4 { 76 | char = b0 & (31 << (3 - offset)) >> (3 - offset) 77 | } else { 78 | char = b0 & (31 >> (offset - 3)) << (offset - 3) 79 | char |= b1 & (255 << (11 - offset)) >> (11 - offset) 80 | } 81 | 82 | // If src is longer than necessary, mask trailing bits to zero 83 | if bits >= 0 && i+5 > bits { 84 | char &= 255 << uint((i+5)-bits) 85 | } 86 | 87 | dst[off] = alphabet[char] 88 | off++ 89 | 90 | if offset > 2 { 91 | src = src[1:] 92 | } 93 | } 94 | return off 95 | } 96 | 97 | // EncodeBits encodes the specified number of bits of src. It writes at 98 | // most EncodedLen(len(src)) bytes to dst and returns the number of 99 | // bytes written. 100 | // 101 | // EncodeBits is not appropriate for use on individual blocks of a 102 | // large data stream. 103 | func EncodeBits(dst, src []byte, bits int) int { 104 | if bits < 0 { 105 | return 0 106 | } 107 | return encode(dst, src, bits) 108 | } 109 | 110 | // Encode encodes src. It writes at most EncodedLen(len(src)) bytes to 111 | // dst and returns the number of bytes written. 112 | // 113 | // Encode is not appropriate for use on individual blocks of a large 114 | // data stream. 115 | func Encode(dst, src []byte) int { 116 | return encode(dst, src, -1) 117 | } 118 | 119 | // EncodeToString returns the z-base-32 encoding of src. 120 | func EncodeToString(src []byte) string { 121 | dst := make([]byte, EncodedLen(len(src))) 122 | n := Encode(dst, src) 123 | return string(dst[:n]) 124 | } 125 | 126 | // EncodeBitsToString returns the z-base-32 encoding of the specified 127 | // number of bits of src. 128 | func EncodeBitsToString(src []byte, bits int) string { 129 | dst := make([]byte, EncodedLen(len(src))) 130 | n := EncodeBits(dst, src, bits) 131 | return string(dst[:n]) 132 | } 133 | 134 | func decode(dst, src []byte, bits int) (int, error) { 135 | olen := len(src) 136 | off := 0 137 | for len(src) > 0 { 138 | // Decode quantum using the z-base-32 alphabet 139 | var dbuf [8]byte 140 | 141 | j := 0 142 | for ; j < 8; j++ { 143 | if len(src) == 0 { 144 | break 145 | } 146 | in := src[0] 147 | src = src[1:] 148 | dbuf[j] = decodeMap[in] 149 | if dbuf[j] == 0xFF { 150 | return off, CorruptInputError(olen - len(src) - 1) 151 | } 152 | } 153 | 154 | // 8x 5-bit source blocks, 5 byte destination quantum 155 | dst[off+0] = dbuf[0]<<3 | dbuf[1]>>2 156 | dst[off+1] = dbuf[1]<<6 | dbuf[2]<<1 | dbuf[3]>>4 157 | dst[off+2] = dbuf[3]<<4 | dbuf[4]>>1 158 | dst[off+3] = dbuf[4]<<7 | dbuf[5]<<2 | dbuf[6]>>3 159 | dst[off+4] = dbuf[6]<<5 | dbuf[7] 160 | 161 | // bits < 0 means as many bits as there are in src 162 | if bits < 0 { 163 | var lookup = []int{0, 1, 1, 2, 2, 3, 4, 4, 5} 164 | off += lookup[j] 165 | continue 166 | } 167 | bitsInBlock := bits 168 | if bitsInBlock > 40 { 169 | bitsInBlock = 40 170 | } 171 | off += (bitsInBlock + 7) / 8 172 | bits -= 40 173 | } 174 | return off, nil 175 | } 176 | 177 | // DecodeBits decodes the specified number of bits of z-base-32 178 | // encoded data from src. It writes at most DecodedLen(len(src)) bytes 179 | // to dst and returns the number of bytes written. 180 | // 181 | // If src contains invalid z-base-32 data, it will return the number 182 | // of bytes successfully written and CorruptInputError. 183 | func DecodeBits(dst, src []byte, bits int) (int, error) { 184 | if bits < 0 { 185 | return 0, errors.New("cannot decode a negative bit count") 186 | } 187 | return decode(dst, src, bits) 188 | } 189 | 190 | // Decode decodes z-base-32 encoded data from src. It writes at most 191 | // DecodedLen(len(src)) bytes to dst and returns the number of bytes 192 | // written. 193 | // 194 | // If src contains invalid z-base-32 data, it will return the number 195 | // of bytes successfully written and CorruptInputError. 196 | func Decode(dst, src []byte) (int, error) { 197 | return decode(dst, src, -1) 198 | } 199 | 200 | func decodeString(s string, bits int) ([]byte, error) { 201 | dst := make([]byte, DecodedLen(len(s))) 202 | n, err := decode(dst, []byte(s), bits) 203 | if err != nil { 204 | return nil, err 205 | } 206 | return dst[:n], nil 207 | } 208 | 209 | // DecodeBitsString returns the bytes represented by the z-base-32 210 | // string s containing the specified number of bits. 211 | func DecodeBitsString(s string, bits int) ([]byte, error) { 212 | if bits < 0 { 213 | return nil, errors.New("cannot decode a negative bit count") 214 | } 215 | return decodeString(s, bits) 216 | } 217 | 218 | // DecodeString returns the bytes represented by the z-base-32 string 219 | // s. 220 | func DecodeString(s string) ([]byte, error) { 221 | return decodeString(s, -1) 222 | } 223 | --------------------------------------------------------------------------------