cashless2ecash

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

utils.go (3398B)


      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 	"encoding/base64"
     23 	"errors"
     24 	"strings"
     25 
     26 	"golang.org/x/crypto/argon2"
     27 )
     28 
     29 // https://docs.taler.net/core/api-common.html#hash-codes
     30 type WithdrawalIdentifier string
     31 
     32 // https://docs.taler.net/core/api-common.html#cryptographic-primitives
     33 type EddsaPublicKey string
     34 
     35 // https://docs.taler.net/core/api-common.html#hash-codes
     36 type HashCode string
     37 
     38 // https://docs.taler.net/core/api-common.html#hash-codes
     39 type ShortHashCode string
     40 
     41 // https://docs.taler.net/core/api-common.html#timestamps
     42 type Timestamp struct {
     43 	Ts int `json:"t_s"`
     44 }
     45 
     46 // https://docs.taler.net/core/api-common.html#wadid
     47 type WadId [6]uint32
     48 
     49 // according to https://docs.taler.net/core/api-bank-integration.html#tsref-type-BankWithdrawalOperationStatus
     50 type WithdrawalOperationStatus string
     51 
     52 const (
     53 	PENDING   WithdrawalOperationStatus = "pending"
     54 	SELECTED  WithdrawalOperationStatus = "selected"
     55 	ABORTED   WithdrawalOperationStatus = "aborted"
     56 	CONFIRMED WithdrawalOperationStatus = "confirmed"
     57 )
     58 
     59 func ToWithdrawalOperationStatus(s string) (WithdrawalOperationStatus, error) {
     60 	switch strings.ToLower(s) {
     61 	case string(PENDING):
     62 		return PENDING, nil
     63 	case string(SELECTED):
     64 		return SELECTED, nil
     65 	case string(ABORTED):
     66 		return ABORTED, nil
     67 	case string(CONFIRMED):
     68 		return CONFIRMED, nil
     69 	default:
     70 		return "", errors.New("invalid withdrawal operation status '" + s + "'")
     71 	}
     72 }
     73 
     74 func RemoveNulls[T any](l []*T) []*T {
     75 
     76 	withoutNulls := make([]*T, 0)
     77 	for _, e := range l {
     78 		if e != nil {
     79 			withoutNulls = append(withoutNulls, e)
     80 		}
     81 	}
     82 	return withoutNulls
     83 }
     84 
     85 // takes a password and a base64 encoded password hash, including salt and checks
     86 // the password supplied against it.
     87 // the format of the password hash is expected to be the following:
     88 //
     89 //	[32 BYTES HASH][16 BYTES SALT] = Bytes array with length of 48 bytes.
     90 //
     91 // returns true if password matches the password hash. Otherwise false.
     92 func ValidPassword(pw string, base64EncodedHashAndSalt string) bool {
     93 
     94 	hashedBytes := make([]byte, 48)
     95 	decodedLen, err := base64.StdEncoding.Decode(hashedBytes, []byte(base64EncodedHashAndSalt))
     96 	if err != nil {
     97 		return false
     98 	}
     99 
    100 	if decodedLen != 48 {
    101 		// malformed credentials
    102 		return false
    103 	}
    104 
    105 	salt := hashedBytes[32:48]
    106 	rfcTime := 3
    107 	rfcMemory := 32 * 1024
    108 	key := argon2.Key([]byte(pw), salt, uint32(rfcTime), uint32(rfcMemory), 4, 32)
    109 
    110 	if len(key) != 32 {
    111 		// length mismatch
    112 		return false
    113 	}
    114 
    115 	for i := range key {
    116 		if key[i] != hashedBytes[i] {
    117 			// wrong password (application user key)
    118 			return false
    119 		}
    120 	}
    121 
    122 	// password correct.
    123 	return true
    124 }