encoding.go (3324B)
1 // This file is part of taler-cashless2ecash. 2 // Copyright (C) 2024 Joel Häberli 3 // 4 // taler-cashless2ecash 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 // taler-cashless2ecash 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 internal_utils 20 21 import ( 22 "errors" 23 "math" 24 "strings" 25 ) 26 27 func TalerBinaryEncode(byts []byte) string { 28 29 return encodeCrock(byts) 30 } 31 32 func TalerBinaryDecode(str string) ([]byte, error) { 33 34 return decodeCrock(str) 35 } 36 37 func ParseWopid(wopid string) ([]byte, error) { 38 39 wopidBytes, err := TalerBinaryDecode(wopid) 40 if err != nil { 41 return nil, err 42 } 43 44 if len(wopidBytes) != 32 { 45 err = errors.New("invalid wopid") 46 LogError("encoding", err) 47 return nil, err 48 } 49 50 return wopidBytes, nil 51 } 52 53 func FormatWopid(wopid []byte) string { 54 55 return TalerBinaryEncode(wopid) 56 } 57 58 func ParseEddsaPubKey(key EddsaPublicKey) ([]byte, error) { 59 60 return TalerBinaryDecode(string(key)) 61 } 62 63 func FormatEddsaPubKey(key []byte) string { 64 65 return TalerBinaryEncode(key) 66 } 67 68 func decodeCrock(e string) ([]byte, error) { 69 size := len(e) 70 bitpos := 0 71 bitbuf := 0 72 readPosition := 0 73 outLen := int(math.Floor((float64(size) * 5.0) / 8.0)) 74 out := make([]byte, outLen) 75 outPos := 0 76 77 getValue := func(c byte) (int, error) { 78 alphabet := "0123456789ABCDEFGHJKMNPQRSTVWXYZ" 79 switch c { 80 case 'o', 'O': 81 return 0, nil 82 case 'i', 'I', 'l', 'L': 83 return 1, nil 84 case 'u', 'U': 85 return 27, nil 86 } 87 88 i := strings.IndexRune(alphabet, rune(c)) 89 if i > -1 && i < 32 { 90 return i, nil 91 } 92 93 return -1, errors.New("crockford decoding error") 94 } 95 96 for readPosition < size || bitpos > 0 { 97 if readPosition < size { 98 v, err := getValue(e[readPosition]) 99 if err != nil { 100 return nil, err 101 } 102 readPosition++ 103 bitbuf = bitbuf<<5 | v 104 bitpos += 5 105 } 106 for bitpos >= 8 { 107 d := byte(bitbuf >> (bitpos - 8) & 0xff) 108 out[outPos] = d 109 outPos++ 110 bitpos -= 8 111 } 112 if readPosition == size && bitpos > 0 { 113 bitbuf = bitbuf << (8 - bitpos) & 0xff 114 if bitbuf == 0 { 115 bitpos = 0 116 } else { 117 bitpos = 8 118 } 119 } 120 } 121 return out, nil 122 } 123 124 func encodeCrock(data []byte) string { 125 out := "" 126 bitbuf := 0 127 bitpos := 0 128 129 encodeValue := func(value int) byte { 130 alphabet := "ABCDEFGHJKMNPQRSTVWXYZ" 131 switch { 132 case value >= 0 && value <= 9: 133 return byte('0' + value) 134 case value >= 10 && value <= 31: 135 return alphabet[value-10] 136 default: 137 panic("Invalid value for encoding") 138 } 139 } 140 141 for _, b := range data { 142 bitbuf = bitbuf<<8 | int(b&0xff) 143 bitpos += 8 144 for bitpos >= 5 { 145 value := bitbuf >> (bitpos - 5) & 0x1f 146 out += string(encodeValue(value)) 147 bitpos -= 5 148 } 149 } 150 if bitpos > 0 { 151 bitbuf = bitbuf << (5 - bitpos) 152 value := bitbuf & 0x1f 153 out += string(encodeValue(value)) 154 } 155 return out 156 }