taldir

Directory service to resolve wallet mailboxes by messenger addresses
Log | Files | Refs | Submodules | README | LICENSE

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 }