libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

crypto.rs (10027B)


      1 /*
      2 * This file is part of LibEuFin.
      3 * Copyright (C) 2026 Taler Systems S.A.
      4 
      5 * LibEuFin is free software; you can redistribute it and/or modify
      6 * it under the terms of the GNU Affero General Public License as
      7 * published by the Free Software Foundation; either version 3, or
      8 * (at your option) any later version.
      9 
     10 * LibEuFin is distributed in the hope that it will be useful, but
     11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     12 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General
     13 * Public License for more details.
     14 
     15 * You should have received a copy of the GNU Affero General Public
     16 * License along with LibEuFin; see the file COPYING.  If not, see
     17 * <http://www.gnu.org/licenses/>
     18 */
     19 
     20 use aws_lc_rs::{
     21     cipher::{
     22         AES_128, DecryptingKey, DecryptionContext, EncryptingKey, EncryptionContext,
     23         UnboundCipherKey,
     24     },
     25     digest::{Context, Digest, SHA256},
     26     encoding::AsDer,
     27     iv::FixedLength,
     28     rand::SystemRandom,
     29     rsa::{
     30         KeyPair, Pkcs1PrivateDecryptingKey, Pkcs1PublicEncryptingKey, PrivateDecryptingKey,
     31         PublicEncryptingKey, PublicKey,
     32     },
     33     signature::{RSA_PSS_2048_8192_SHA256, RSA_PSS_SHA256, UnparsedPublicKey},
     34 };
     35 use jiff::{Timestamp, Zoned, tz::TimeZone};
     36 use rand::{TryRng, rngs::SysRng};
     37 use rcgen::{BasicConstraints, CertificateParams, DnType, IsCa, KeyUsagePurpose};
     38 use taler_common::encoding::{base64, hex};
     39 use x509_parser::prelude::{FromDer as _, X509Certificate};
     40 
     41 /// Generate a self-signed X.509 certificate from an RSA private key (PEM or DER)
     42 pub fn x509_certificate_from_rsa_private(
     43     pem: &str,
     44     name: &str,
     45 ) -> Result<rcgen::Certificate, rcgen::Error> {
     46     let keys = rcgen::KeyPair::from_pem(pem).unwrap();
     47     let mut params = CertificateParams::new(vec![])?;
     48 
     49     // Set subject/issuer CN
     50     params.distinguished_name.push(DnType::CommonName, name);
     51 
     52     let now = Zoned::new(Timestamp::now(), TimeZone::UTC).date();
     53 
     54     // 1000-year validity
     55     params.not_before = rcgen::date_time_ymd(now.year() as i32, now.month() as u8, now.day() as u8);
     56     params.not_after =
     57         rcgen::date_time_ymd(now.year() as i32 + 1000, now.month() as u8, now.day() as u8);
     58 
     59     // CA: true (basicConstraints)
     60     params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
     61 
     62     // Key usage flags
     63     params.key_usages = vec![
     64         KeyUsagePurpose::DigitalSignature,
     65         KeyUsagePurpose::ContentCommitment, // NonRepudiation
     66         KeyUsagePurpose::KeyEncipherment,
     67         KeyUsagePurpose::DataEncipherment,
     68         KeyUsagePurpose::KeyAgreement,
     69         KeyUsagePurpose::KeyCertSign,
     70         KeyUsagePurpose::CrlSign,
     71         KeyUsagePurpose::EncipherOnly,
     72         KeyUsagePurpose::DecipherOnly,
     73     ];
     74 
     75     let cert = params.self_signed(&keys)?;
     76     Ok(cert)
     77 }
     78 
     79 /** Create an RSA public key from its components: [modulus] and [exponent] */
     80 pub fn rsa_pub_from_component(modulus: &[u8], exponent: &[u8]) -> anyhow::Result<PublicKey> {
     81     let key: PublicEncryptingKey = aws_lc_rs::rsa::PublicKeyComponents {
     82         n: modulus,
     83         e: exponent,
     84     }
     85     .try_into()?;
     86     Ok(PublicKey::from_der(key.as_der()?.as_ref())?)
     87 }
     88 
     89 /// Extract an RSA public key from a X.509 certificate
     90 pub fn rsa_private_from_b64_x509_certificate(encoded: &str) -> anyhow::Result<PublicKey> {
     91     let der = base64::decode(encoded)?;
     92     let (_, cert) = X509Certificate::from_der(&der)?;
     93     let issuer_public_key = cert.public_key();
     94     Ok(PublicKey::from_der(issuer_public_key.raw)?)
     95 }
     96 
     97 /// Hash an RSA public key according to the EBICS standard (EBICS 2.5: 4.4.1.2.3).
     98 pub fn ebics_pub_key_hash(public_key: &PublicKey) -> Digest {
     99     let mut ctx = Context::new(&SHA256);
    100     let hex_encoded = |input: &[u8], ctx: &mut Context| {
    101         let encoded = hex::encode(input);
    102         if encoded.starts_with('0') {
    103             ctx.update(&encoded.as_bytes()[1..]);
    104         } else {
    105             ctx.update(encoded.as_bytes());
    106         }
    107     };
    108 
    109     hex_encoded(
    110         public_key.exponent().big_endian_without_leading_zero(),
    111         &mut ctx,
    112     );
    113     ctx.update(b" ");
    114     hex_encoded(
    115         public_key.modulus().big_endian_without_leading_zero(),
    116         &mut ctx,
    117     );
    118     ctx.finish()
    119 }
    120 
    121 pub fn gen_ebics_e002_key(pub_key: &PublicKey) -> ([u8; 16], Vec<u8>) {
    122     let mut transaction_key = [0u8; 16];
    123     SysRng.try_fill_bytes(&mut transaction_key).unwrap();
    124 
    125     let key = Pkcs1PublicEncryptingKey::new(
    126         PublicEncryptingKey::from_der(pub_key.as_der().unwrap().as_ref()).unwrap(),
    127     )
    128     .unwrap();
    129     let mut encrypted_key = vec![0; key.ciphertext_size()];
    130     key.encrypt(&transaction_key, &mut encrypted_key).unwrap();
    131 
    132     (transaction_key, encrypted_key)
    133 }
    134 
    135 pub fn encrypt_ebics_e002(transaction_key: &[u8; 16], mut data: Vec<u8>) -> Vec<u8> {
    136     let block_size = 16;
    137     let padding_len = block_size - (data.len() % block_size);
    138 
    139     // Add padding
    140     for i in 0..padding_len {
    141         if i == padding_len - 1 {
    142             data.push(padding_len as u8);
    143         } else {
    144             data.push(0);
    145         }
    146     }
    147 
    148     let iv = FixedLength::from([0u8; 16]);
    149     let enc_key =
    150         EncryptingKey::cbc(UnboundCipherKey::new(&AES_128, transaction_key).unwrap()).unwrap();
    151     enc_key
    152         .less_safe_encrypt(&mut data, EncryptionContext::Iv128(iv))
    153         .unwrap();
    154 
    155     data
    156 }
    157 
    158 pub fn decrypt_ebics_e002(
    159     transaction_key: &DecryptingKey,
    160     mut encrypted_data: Vec<u8>,
    161 ) -> Result<Vec<u8>, ()> {
    162     let iv = FixedLength::from([0u8; 16]);
    163 
    164     let plaintext = transaction_key.decrypt(&mut encrypted_data, DecryptionContext::Iv128(iv))?;
    165 
    166     // Strip X9.23 / ANSI X9.23 padding:
    167     // The last byte holds the number of padding bytes to remove.
    168     let pad_len = *plaintext.last().ok_or(())? as usize;
    169     if pad_len == 0 || pad_len > 16 || pad_len > plaintext.len() {
    170         return Err(());
    171     }
    172     let decoded = plaintext.len() - pad_len;
    173     encrypted_data.truncate(decoded);
    174     Ok(encrypted_data)
    175 }
    176 
    177 pub fn decrypt_ebics_e002_key(
    178     private_key: PrivateDecryptingKey,
    179     encrypted_transaction_key: &[u8],
    180 ) -> DecryptingKey {
    181     let private_key = Pkcs1PrivateDecryptingKey::new(private_key).unwrap();
    182     let mut plaintext = vec![0u8; private_key.min_output_size()];
    183     let cipher = private_key
    184         .decrypt(encrypted_transaction_key, &mut plaintext)
    185         .unwrap();
    186     let cipher_key = UnboundCipherKey::new(&AES_128, cipher).unwrap();
    187     DecryptingKey::cbc(cipher_key).unwrap()
    188 }
    189 
    190 pub fn digest_ebics_order_a006(order_data: &[u8]) -> Digest {
    191     let mut digest = Context::new(&SHA256);
    192     for chunk in order_data.split(|b| matches!(b, b'\r' | b'\n' | b'\x1a')) {
    193         digest.update(chunk);
    194     }
    195     digest.finish()
    196 }
    197 
    198 pub fn sign_ebics_a006(data: &[u8], key_pair: &KeyPair) -> Vec<u8> {
    199     let mut sig = vec![0; key_pair.public_modulus_len()];
    200     key_pair
    201         .sign(&RSA_PSS_SHA256, &SystemRandom::new(), data, &mut sig)
    202         .unwrap();
    203     sig
    204 }
    205 
    206 pub fn verify_ebics_a006(sig: &[u8], data: &[u8], public_key_der: &PublicKey) -> bool {
    207     UnparsedPublicKey::new(&RSA_PSS_2048_8192_SHA256, public_key_der.as_ref())
    208         .verify(data, sig)
    209         .is_ok()
    210 }
    211 
    212 #[cfg(test)]
    213 mod test {
    214     use aws_lc_rs::{
    215         encoding::AsDer,
    216         rsa::{KeyPair, KeySize, PrivateDecryptingKey, PublicKey},
    217         signature::KeyPair as _,
    218     };
    219     use taler_common::encoding::hex;
    220 
    221     use crate::crypto::{
    222         decrypt_ebics_e002, decrypt_ebics_e002_key, ebics_pub_key_hash, encrypt_ebics_e002,
    223         gen_ebics_e002_key, rsa_pub_from_component, sign_ebics_a006, verify_ebics_a006,
    224     };
    225 
    226     #[test]
    227     fn e002() {
    228         let data = b"Hello, World!";
    229         let key = PrivateDecryptingKey::generate(KeySize::Rsa2048).unwrap();
    230 
    231         let (tx_key, encrypted_key) = gen_ebics_e002_key(
    232             &PublicKey::from_der(key.public_key().as_der().unwrap().as_ref()).unwrap(),
    233         );
    234         let enc = encrypt_ebics_e002(&tx_key, data.to_vec());
    235         let key = decrypt_ebics_e002_key(key, &encrypted_key);
    236         let dec = decrypt_ebics_e002(&key, enc).unwrap();
    237         assert_eq!(&data, &dec.as_slice());
    238     }
    239 
    240     #[test]
    241     fn a006() {
    242         let data = b"Hello, World!";
    243         let key_pair = KeyPair::generate(KeySize::Rsa2048).unwrap();
    244         let sig = sign_ebics_a006(data, &key_pair);
    245         assert!(verify_ebics_a006(&sig, data, key_pair.public_key()));
    246     }
    247 
    248     #[test]
    249     fn public_key_hash() {
    250         let exponent = "01 00 01".replace(|it: char| it.is_whitespace(), "");
    251         let modulus = "
    252             EB BD B8 E3 73 45 60 06 44 A1 AD 6A 25 33 65 F5
    253             9C EB E5 93 E0 51 72 77 90 6B F0 58 A8 89 EB 00
    254             C6 0B 37 38 F3 3C 55 F2 4D 83 D0 33 C3 A8 F0 3C
    255             82 4E AF 78 51 D6 F4 71 6A CC 9C 10 2A 58 C9 5F
    256             3D 30 B4 31 D7 1B 79 6D 43 AA F9 75 B5 7E 0B 4A
    257             55 52 1D 7C AC 8F 92 B0 AE 9F CF 5F 16 5C 6A D1
    258             88 DB E2 48 E7 78 43 F9 18 63 29 45 ED 6C 08 6C
    259             16 1C DE F3 02 01 23 8A 58 35 43 2B 2E C5 3F 6F
    260             33 B7 A3 46 E1 75 BD 98 7C 6D 55 DE 71 11 56 3D
    261             7A 2C 85 42 98 42 DF 94 BF E8 8B 76 84 13 3E CA
    262             0E 8D 12 57 D6 8A CF 82 DE B7 D7 BB BC 45 AE 25
    263             95 76 00 19 08 AA D2 C8 A7 D8 10 37 88 96 B9 98
    264             14 B4 B0 65 F3 36 CE 93 F7 46 12 58 9F E7 79 33
    265             D5 BE 0D 0E F8 E7 E0 A9 C3 10 51 A1 3E A4 4F 67
    266             5E 75 8C 9D E6 FE 27 B6 3C CF 61 9B 31 D4 D0 22
    267             B9 2E 4C AF 5F D6 4B 1F F0 4D 06 5F 68 EB 0B 71
    268         "
    269         .replace(|it: char| it.is_whitespace(), "");
    270         let expected = "
    271             72 71 D5 83 B4 24 A6 DA 0B 7B 22 24 3B E2 B8 8C
    272             6E A6 0F 9F 76 11 FD 18 BE 2C E8 8B 21 03 A9 41
    273         "
    274         .replace(|it: char| it.is_whitespace(), "");
    275         let key = rsa_pub_from_component(
    276             &hex::decode(modulus).unwrap(),
    277             &hex::decode(exponent).unwrap(),
    278         )
    279         .unwrap();
    280         let hash = ebics_pub_key_hash(&key);
    281         assert_eq!(&hex::decode(expected).unwrap(), hash.as_ref());
    282     }
    283 }