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 }