helper.go (5139B)
1 // This file is part of taldir, the Taler Directory implementation. 2 // Copyright (C) 2022 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 "crypto/sha512" 23 "crypto/rand" 24 "time" 25 "errors" 26 "strings" 27 28 talerutil "github.com/schanzen/taler-go/pkg/util" 29 ) 30 31 // Generates a solution from a challenge and pubkey 32 func GenerateSolution(targetUriEncoded string, challenge string) string { 33 h := sha512.New() 34 h.Write([]byte(challenge)) 35 h.Write([]byte(targetUriEncoded)) 36 return Base32CrockfordEncode(h.Sum(nil)) 37 } 38 39 // Generates random reference token used in the validation flow. 40 func GenerateChallenge(bytes int) string { 41 randBytes := make([]byte, bytes) 42 _, err := rand.Read(randBytes) 43 if err != nil { 44 panic(err) 45 } 46 return Base32CrockfordEncode(randBytes) 47 } 48 49 // Check if this is a non-zero, positive amount 50 func CalculateCost(sliceCostAmount string, fixedCostAmount string, howLong time.Duration, sliceDuration time.Duration) (*talerutil.Amount, error) { 51 sliceCount := int(float64(howLong.Microseconds()) / float64(sliceDuration.Microseconds())) 52 sliceCost, err := talerutil.ParseAmount(sliceCostAmount) 53 if nil != err { 54 return nil, err 55 } 56 fixedCost, err := talerutil.ParseAmount(fixedCostAmount) 57 if nil != err { 58 return nil, err 59 } 60 sum := &talerutil.Amount{ 61 Currency: sliceCost.Currency, 62 Value: 0, 63 Fraction: 0, 64 } 65 for i := 0; i < sliceCount; i++ { 66 sum, err = sum.Add(*sliceCost) 67 if nil != err { 68 return nil, err 69 } 70 } 71 sum, err = sum.Add(*fixedCost) 72 if nil != err { 73 return nil, err 74 } 75 return sum, nil 76 } 77 78 //------------------------------------------------------------------------ 79 // Base32 conversion between binary data and string representation 80 //------------------------------------------------------------------------ 81 // 82 // A binary array of size m is viewed as a consecutive stream of bits 83 // from left to right. Bytes are ordered with ascending address, while 84 // bits (in a byte) are ordered MSB to LSB. 85 86 // For encoding the stream is partitioned into 5-bit chunks; the last chunk 87 // is right-padded with 0's if 8*m is not divisible by 5. Each chunk (value 88 // between 0 and 31) is encoded into a character; the mapping for encoding 89 // is the same as in [https://www.crockford.com/wrmg/base32.html]. 90 // 91 // For decoding each character is converted to a 5-bit chunk based on the 92 // encoder mapping (with one addition: the character 'U' maps to the value 93 // 27). The chunks are concatenated to produce the bit stream to be stored 94 // in the output array. 95 96 // character set used for encoding/decoding 97 const xlate = "0123456789ABCDEFGHJKMNPQRSTVWXYZ" 98 99 var ( 100 // ErrInvalidEncoding signals an invalid encoding 101 ErrInvalidEncoding = errors.New("invalid encoding") 102 // ErrBufferTooSmall signalsa too small buffer for decoding 103 ErrBufferTooSmall = errors.New("buffer to small") 104 ) 105 106 // EncodeBinaryToString encodes a byte array into a string. 107 func Base32CrockfordEncode(data []byte) string { 108 size, pos, bits, n := len(data), 0, 0, 0 109 out := "" 110 for { 111 if n < 5 { 112 if pos < size { 113 bits = (bits << 8) | (int(data[pos]) & 0xFF) 114 pos++ 115 n += 8 116 } else if n > 0 { 117 bits <<= uint(5 - n) 118 n = 5 119 } else { 120 break 121 } 122 } 123 out += string(xlate[(bits>>uint(n-5))&0x1F]) 124 n -= 5 125 } 126 return out 127 } 128 129 // Base32CrockfordDecode decodes a string into a byte array. 130 // The function expects the size of the output buffer to be specified as an 131 // argument ('num'); the function returns an error if the buffer is overrun 132 // or if an invalid character is found in the encoded string. If the decoded 133 // bit stream is smaller than the output buffer, it is padded with 0's. 134 func Base32CrockfordDecode(s string, num int) ([]byte, error) { 135 size := len(s) 136 out := make([]byte, num) 137 rpos, wpos, n, bits := 0, 0, 0, 0 138 for { 139 if n < 8 { 140 if rpos < size { 141 c := rune(s[rpos]) 142 rpos++ 143 v := strings.IndexRune(xlate, c) 144 if v == -1 { 145 switch c { 146 case 'O': 147 v = 0 148 case 'I', 'L': 149 v = 1 150 case 'U': 151 v = 27 152 default: 153 return nil, ErrInvalidEncoding 154 } 155 } 156 bits = (bits << 5) | (v & 0x1F) 157 n += 5 158 } else { 159 if wpos < num { 160 out[wpos] = byte(bits & ((1 << uint(n+1)) - 1)) 161 wpos++ 162 for i := wpos; i < num; i++ { 163 out[i] = 0 164 } 165 } 166 break 167 } 168 } else { 169 if wpos < num { 170 out[wpos] = byte((bits >> uint(n-8)) & 0xFF) 171 wpos++ 172 n -= 8 173 } else { 174 return nil, ErrBufferTooSmall 175 } 176 } 177 } 178 return out, nil 179 }