helper.go (3769B)
1 // This file is part of taldir, the Taler Directory implementation. 2 // Copyright (C) 2025 Martin Schanzenbach 3 // 4 // Taldir is free software: you can redistribute it and/or modify it 5 // under the terms of the GNU Affero General Public License as published 6 // by the Free Software Foundation, either version 3 of the License, 7 // or (at your option) any later version. 8 // 9 // Taldir is distributed in the hope that it will be useful, but 10 // WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 // Affero General Public License for more details. 13 // 14 // You should have received a copy of the GNU Affero General Public License 15 // along with this program. If not, see <http://www.gnu.org/licenses/>. 16 // 17 // SPDX-License-Identifier: AGPL3.0-or-later 18 19 package util 20 21 import ( 22 "errors" 23 "strings" 24 ) 25 26 //------------------------------------------------------------------------ 27 // Base32 conversion between binary data and string representation 28 //------------------------------------------------------------------------ 29 // 30 // A binary array of size m is viewed as a consecutive stream of bits 31 // from left to right. Bytes are ordered with ascending address, while 32 // bits (in a byte) are ordered MSB to LSB. 33 34 // For encoding the stream is partitioned into 5-bit chunks; the last chunk 35 // is right-padded with 0's if 8*m is not divisible by 5. Each chunk (value 36 // between 0 and 31) is encoded into a character; the mapping for encoding 37 // is the same as in [https://www.crockford.com/wrmg/base32.html]. 38 // 39 // For decoding each character is converted to a 5-bit chunk based on the 40 // encoder mapping (with one addition: the character 'U' maps to the value 41 // 27). The chunks are concatenated to produce the bit stream to be stored 42 // in the output array. 43 44 // character set used for encoding/decoding 45 const xlate = "0123456789ABCDEFGHJKMNPQRSTVWXYZ" 46 47 var ( 48 // ErrInvalidEncoding signals an invalid encoding 49 ErrInvalidEncoding = errors.New("invalid encoding") 50 // ErrBufferTooSmall signalsa too small buffer for decoding 51 ErrBufferTooSmall = errors.New("buffer to small") 52 ) 53 54 // EncodeBinaryToString encodes a byte array into a string. 55 func Base32CrockfordEncode(data []byte) string { 56 size, pos, bits, n := len(data), 0, 0, 0 57 out := "" 58 for { 59 if n < 5 { 60 if pos < size { 61 bits = (bits << 8) | (int(data[pos]) & 0xFF) 62 pos++ 63 n += 8 64 } else if n > 0 { 65 bits <<= uint(5 - n) 66 n = 5 67 } else { 68 break 69 } 70 } 71 out += string(xlate[(bits>>uint(n-5))&0x1F]) 72 n -= 5 73 } 74 return out 75 } 76 77 // Base32CrockfordDecode decodes a string into a byte array. 78 // The function expects the size of the output buffer to be specified as an 79 // argument ('num'); the function returns an error if the buffer is overrun 80 // or if an invalid character is found in the encoded string. If the decoded 81 // bit stream is smaller than the output buffer, it is padded with 0's. 82 func Base32CrockfordDecode(s string, num int) ([]byte, error) { 83 size := len(s) 84 out := make([]byte, num) 85 rpos, wpos, n, bits := 0, 0, 0, 0 86 for { 87 if n < 8 { 88 if rpos < size { 89 c := rune(s[rpos]) 90 rpos++ 91 v := strings.IndexRune(xlate, c) 92 if v == -1 { 93 switch c { 94 case 'O': 95 v = 0 96 case 'I', 'L': 97 v = 1 98 case 'U': 99 v = 27 100 default: 101 return nil, ErrInvalidEncoding 102 } 103 } 104 bits = (bits << 5) | (v & 0x1F) 105 n += 5 106 } else { 107 if wpos < num { 108 out[wpos] = byte(bits & ((1 << uint(n+1)) - 1)) 109 wpos++ 110 for i := wpos; i < num; i++ { 111 out[i] = 0 112 } 113 } 114 break 115 } 116 } else { 117 if wpos < num { 118 out[wpos] = byte((bits >> uint(n-8)) & 0xFF) 119 wpos++ 120 n -= 8 121 } else { 122 return nil, ErrBufferTooSmall 123 } 124 } 125 } 126 return out, nil 127 }