cashless2ecash

cashless2ecash: pay with cards for digital cash (experimental)
Log | Files | Refs | README

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 }