signature.rs (3248B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2026 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Affero General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 17 use aws_lc_rs::{ 18 digest::{Context, SHA512}, 19 signature::{ED25519, Ed25519KeyPair, UnparsedPublicKey}, 20 }; 21 22 use crate::{ 23 api::{EddsaPublicKey, EddsaSignature}, 24 types::{amount::Amount, payto::PaytoURI, timestamp::TalerTimestamp}, 25 }; 26 27 pub trait Signature<const N: usize> { 28 const PURPOSE: u32; 29 30 fn signature_bytes(&self, buf: Buf<N>) -> Buf<N>; 31 32 fn sign(&self, pair: &Ed25519KeyPair) -> EddsaSignature { 33 let msg = self 34 .signature_bytes(Buf::new().put_u32(N as u32).put_u32(Self::PURPOSE)) 35 .finish(); 36 let signature = pair.sign(&msg); 37 EddsaSignature::try_from(signature.as_ref()).unwrap() 38 } 39 40 fn verify(&self, key: &EddsaPublicKey, sig: &EddsaSignature) -> bool { 41 let msg = self 42 .signature_bytes(Buf::new().put_u32(N as u32).put_u32(Self::PURPOSE)) 43 .finish(); 44 UnparsedPublicKey::new(&ED25519, key.as_ref()) 45 .verify(&msg, sig.as_ref()) 46 .is_ok() 47 } 48 } 49 50 /** Buffer for signature bytes */ 51 pub struct Buf<const N: usize> { 52 buf: [u8; N], 53 pos: usize, 54 } 55 56 impl<const N: usize> Buf<N> { 57 pub fn new() -> Self { 58 Self { 59 buf: [0; N], 60 pos: 0, 61 } 62 } 63 64 pub fn put_u16(self, nb: u16) -> Self { 65 // Numbers are network order encoded 66 self.put(&nb.to_be_bytes()) 67 } 68 69 pub fn put_u32(self, nb: u32) -> Self { 70 // Numbers are network order encoded 71 self.put(&nb.to_be_bytes()) 72 } 73 74 pub fn put_u64(self, nb: u64) -> Self { 75 // Numbers are network order encoded 76 self.put(&nb.to_be_bytes()) 77 } 78 79 pub fn put_amount(self, amount: Amount) -> Self { 80 self.put(&amount.signature_bytes()) 81 } 82 83 pub fn put_timestamp(self, timestamp: TalerTimestamp) -> Self { 84 self.put(×tamp.signature_bytes()) 85 } 86 87 pub fn put_payto(self, payto: &PaytoURI) -> Self { 88 // Payto are encoded into a c string, hashed using sha-512 and then truncated to 32 bytes 89 let mut ctx = Context::new(&SHA512); 90 ctx.update(payto.as_ref().as_str().as_bytes()); 91 ctx.update(&[0]); 92 self.put(&ctx.finish().as_ref()[..32]) 93 } 94 95 pub fn put(mut self, bytes: &[u8]) -> Self { 96 let end = self.pos + bytes.len(); 97 self.buf[self.pos..end].copy_from_slice(bytes); 98 self.pos = end; 99 self 100 } 101 102 pub fn finish(self) -> [u8; N] { 103 self.buf 104 } 105 } 106 107 impl<const N: usize> Default for Buf<N> { 108 fn default() -> Self { 109 Self::new() 110 } 111 }