From be24d9c3002243491b5a14d92e035879b97d50d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Thu, 6 Oct 2022 17:42:44 +0200 Subject: [PATCH 01/19] Add rsa implementation as dependency --- Cargo.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c9948573..34fc24d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,9 +60,11 @@ log-warn = [] log-error = [] [patch.crates-io] -trussed = { git = "https://github.com/trussed-dev/trussed" , rev = "6de826f3bcbef247e55fd890d80d9ed6ce9f0abc" } +# trussed = { git = "https://github.com/trussed-dev/trussed" , rev = "6de826f3bcbef247e55fd890d80d9ed6ce9f0abc" } +trussed = { git = "https://github.com/sosthene-nitrokey/trussed" , branch = "rsa-rebased" } littlefs2-sys = { git = "https://github.com/sosthene-nitrokey/littlefs2-sys.git", branch = "bindgen-runtime-feature" } interchange = { git = "https://github.com/trussed-dev/interchange.git", rev = "fe5633466640e1e9a8c06d9b5dd1d0af08c272af" } +p256-cortex-m4 = { git = "https://github.com/sosthene-nitrokey/p256-cortex-m4.git", branch = "upgrade" } [package.metadata.docs.rs] all-features = true From 8ab346ffb3abc1b52b42679e36c06f0d16f6ff05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Fri, 7 Oct 2022 17:01:09 +0200 Subject: [PATCH 02/19] Add RSA key generation --- src/command/gen.rs | 89 ++++++++++++++++++++++++++++++++++++++++- tests/crypto-sequoia.rs | 51 +++++++++++++++++++++++ 2 files changed, 139 insertions(+), 1 deletion(-) diff --git a/src/command/gen.rs b/src/command/gen.rs index 3a8707e9..7e6c1ccc 100644 --- a/src/command/gen.rs +++ b/src/command/gen.rs @@ -3,7 +3,7 @@ use hex_literal::hex; use iso7816::Status; -use trussed::types::{KeyId, KeySerialization, Location, StorageAttributes}; +use trussed::types::{KeyId, KeySerialization, Location, Mechanism, StorageAttributes}; use trussed::{syscall, try_syscall}; use crate::card::LoadedContext; @@ -34,6 +34,7 @@ pub fn sign( SignatureAlgorithm::EcDsaP256 => { gen_ec_key(ctx.lend(), KeyType::Sign, CurveAlgo::EcDsaP256) } + SignatureAlgorithm::Rsa2k => gen_rsa_key(ctx.lend(), KeyType::Sign, Mechanism::Rsa2kPkcs), _ => { error!("Unimplemented operation"); Err(Status::ConditionsOfUseNotSatisfied) @@ -49,6 +50,7 @@ pub fn dec( match algo { DecryptionAlgorithm::X255 => gen_ec_key(ctx.lend(), KeyType::Dec, CurveAlgo::X255), DecryptionAlgorithm::EcDhP256 => gen_ec_key(ctx.lend(), KeyType::Dec, CurveAlgo::EcDhP256), + DecryptionAlgorithm::Rsa2k => gen_rsa_key(ctx.lend(), KeyType::Dec, Mechanism::Rsa2kPkcs), _ => { error!("Unimplemented operation"); Err(Status::ConditionsOfUseNotSatisfied) @@ -66,6 +68,9 @@ pub fn aut( AuthenticationAlgorithm::EcDsaP256 => { gen_ec_key(ctx.lend(), KeyType::Aut, CurveAlgo::EcDsaP256) } + AuthenticationAlgorithm::Rsa2k => { + gen_rsa_key(ctx.lend(), KeyType::Aut, Mechanism::Rsa2kPkcs) + } _ => { error!("Unimplemented operation"); Err(Status::ConditionsOfUseNotSatisfied) @@ -73,6 +78,38 @@ pub fn aut( } } +fn gen_rsa_key( + ctx: LoadedContext<'_, R, T>, + key: KeyType, + mechanism: Mechanism, +) -> Result<(), Status> { + let client = ctx.backend.client_mut(); + let key_id = try_syscall!(client.generate_key( + mechanism, + StorageAttributes::new().set_persistence(Location::Internal) + )) + .map_err(|_err| { + error!("Failed to generate key: {_err:?}"); + Status::UnspecifiedNonpersistentExecutionError + })? + .key; + + if let Some((old_key, _)) = ctx + .state + .internal + .set_key_id(key, Some((key_id, KeyOrigin::Generated)), client) + .map_err(|_| Status::UnspecifiedNonpersistentExecutionError)? + { + // Deletion is not a fatal error + try_syscall!(client.delete(old_key)) + .inspect_err_stable(|_err| { + error!("Failed to delete old key: {_err:?}"); + }) + .ok(); + } + read_rsa_key(ctx, key_id, mechanism) +} + fn gen_ec_key( ctx: LoadedContext<'_, R, T>, key: KeyType, @@ -117,6 +154,7 @@ pub fn read_sign( match algo { SignatureAlgorithm::Ed255 => read_ec_key(ctx.lend(), key_id, CurveAlgo::Ed255), SignatureAlgorithm::EcDsaP256 => read_ec_key(ctx.lend(), key_id, CurveAlgo::EcDsaP256), + SignatureAlgorithm::Rsa2k => read_rsa_key(ctx.lend(), key_id, Mechanism::Rsa2kPkcs), _ => { error!("Unimplemented operation"); Err(Status::ConditionsOfUseNotSatisfied) @@ -137,6 +175,7 @@ pub fn read_dec( match algo { DecryptionAlgorithm::X255 => read_ec_key(ctx.lend(), key_id, CurveAlgo::X255), DecryptionAlgorithm::EcDhP256 => read_ec_key(ctx.lend(), key_id, CurveAlgo::EcDhP256), + DecryptionAlgorithm::Rsa2k => read_rsa_key(ctx.lend(), key_id, Mechanism::Rsa2kPkcs), _ => { error!("Unimplemented operation"); Err(Status::ConditionsOfUseNotSatisfied) @@ -157,6 +196,7 @@ pub fn read_aut( match algo { AuthenticationAlgorithm::Ed255 => read_ec_key(ctx.lend(), key_id, CurveAlgo::Ed255), AuthenticationAlgorithm::EcDsaP256 => read_ec_key(ctx.lend(), key_id, CurveAlgo::EcDsaP256), + AuthenticationAlgorithm::Rsa2k => read_rsa_key(ctx.lend(), key_id, Mechanism::Rsa2kPkcs), _ => { error!("Unimplemented operation"); Err(Status::ConditionsOfUseNotSatisfied) @@ -210,3 +250,50 @@ fn read_ec_key( serialize_pub(curve, ctx.lend(), &serialized)?; ctx.reply.prepend_len(offset) } + +fn read_rsa_key( + mut ctx: LoadedContext<'_, R, T>, + key_id: KeyId, + mechanism: Mechanism, +) -> Result<(), Status> { + let client = ctx.backend.client_mut(); + let public_key = syscall!(client.derive_key( + mechanism, + key_id, + None, + StorageAttributes::new().set_persistence(Location::Volatile) + )) + .key; + ctx.reply.expand(KEYGEN_DO_TAG)?; + let offset = ctx.reply.len(); + + let serialized_n = + try_syscall!(client.serialize_key(mechanism, public_key, KeySerialization::RsaN)) + .map_err(|_err| { + error!("Failed to serialize public key N: {_err:?}"); + syscall!(client.delete(public_key)); + Status::UnspecifiedNonpersistentExecutionError + })? + .serialized_key; + ctx.reply.expand(&[0x81])?; + ctx.reply.append_len(serialized_n.len())?; + ctx.reply.expand(&serialized_n)?; + drop(serialized_n); + + let serialized_e = + try_syscall!(client.serialize_key(mechanism, public_key, KeySerialization::RsaE)) + .map_err(|_err| { + error!("Failed to serialize public key E: {_err:?}"); + syscall!(client.delete(public_key)); + Status::UnspecifiedNonpersistentExecutionError + })? + .serialized_key; + ctx.reply.expand(&[0x82])?; + ctx.reply.append_len(serialized_e.len())?; + ctx.reply.expand(&serialized_e)?; + + ctx.reply.prepend_len(offset)?; + + syscall!(client.delete(public_key)); + Ok(()) +} diff --git a/tests/crypto-sequoia.rs b/tests/crypto-sequoia.rs index 2be84f38..c977173a 100644 --- a/tests/crypto-sequoia.rs +++ b/tests/crypto-sequoia.rs @@ -18,6 +18,57 @@ use test_log::test; #[test] fn sequoia_gen_key() { + virt::with_vsc(|| { + let mut cards = PcscBackend::cards(None).unwrap(); + let mut pgp = OpenPgp::new(cards.pop().unwrap()); + let mut open = Open::new(pgp.transaction().unwrap()).unwrap(); + open.verify_admin(b"12345678").unwrap(); + let mut admin = open.admin_card().unwrap(); + + let (material, gendate) = admin + .generate_key_simple(KeyType::Decryption, Some(AlgoSimple::RSA2k)) + .unwrap(); + let dec_pubk = + public_key_material_to_key(&material, KeyType::Decryption, &gendate, None, None) + .unwrap(); + + let (material, gendate) = admin + .generate_key_simple(KeyType::Authentication, Some(AlgoSimple::RSA2k)) + .unwrap(); + let aut_pubk = + public_key_material_to_key(&material, KeyType::Authentication, &gendate, None, None) + .unwrap(); + + let (material, gendate) = admin + .generate_key_simple(KeyType::Signing, Some(AlgoSimple::RSA2k)) + .unwrap(); + let pubk = + public_key_material_to_key(&material, KeyType::Signing, &gendate, None, None).unwrap(); + + open.verify_user_for_signing(b"123456").unwrap(); + let mut sign_card = open.signing_card().unwrap(); + let mut signer = sign_card.signer_from_public(pubk.clone(), &|| {}); + let data = [1; 32]; + let signature = signer.sign(HashAlgorithm::SHA256, &data).unwrap(); + assert!(pubk + .verify(&signature, HashAlgorithm::SHA256, &data) + .is_ok()); + + open.verify_user(b"123456").unwrap(); + let mut user_card = open.user_card().unwrap(); + let mut authenticator = user_card.authenticator_from_public(aut_pubk.clone(), &|| {}); + let data = [2; 32]; + let signature = authenticator.sign(HashAlgorithm::SHA256, &data).unwrap(); + assert!(aut_pubk + .verify(&signature, HashAlgorithm::SHA256, &data) + .is_ok()); + + let mut session = SessionKey::new(19); + session[0] = 7; + let ciphertext = dec_pubk.encrypt(&session).unwrap(); + let mut decryptor = user_card.decryptor_from_public(dec_pubk, &|| {}); + assert_eq!(session, decryptor.decrypt(&ciphertext, None).unwrap()); + }); virt::with_vsc(|| { let mut cards = PcscBackend::cards(None).unwrap(); let mut pgp = OpenPgp::new(cards.pop().unwrap()); From 768c2d36b5d0a22d8ae33cb825b619d45ac11c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Fri, 7 Oct 2022 17:36:15 +0200 Subject: [PATCH 03/19] Add RSA sign support --- src/command/pso.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/command/pso.rs b/src/command/pso.rs index 8d525530..02f61ffc 100644 --- a/src/command/pso.rs +++ b/src/command/pso.rs @@ -10,6 +10,14 @@ use crate::card::LoadedContext; use crate::tlv::get_do; use crate::types::*; +fn rsa_hash_len(mechanism: Mechanism) -> usize { + match mechanism { + // SHA256 + Mechanism::Rsa2kPkcs => 32, + _ => unreachable!(), + } +} + fn check_uif( ctx: LoadedContext<'_, R, T>, key: KeyType, @@ -26,6 +34,7 @@ fn check_uif( } Ok(()) } + // § 7.2.10 pub fn sign( mut ctx: LoadedContext<'_, R, T>, @@ -52,6 +61,7 @@ pub fn sign( } sign_ec(ctx, key_id, Mechanism::P256Prehashed) } + SignatureAlgorithm::Rsa2k => sign_rsa(ctx, key_id, Mechanism::Rsa2kPkcs), _ => { error!("Unimplemented operation"); Err(Status::ConditionsOfUseNotSatisfied) @@ -78,6 +88,31 @@ fn sign_ec( ctx.reply.expand(&signature) } +fn sign_rsa( + mut ctx: LoadedContext<'_, R, T>, + key_id: KeyId, + mechanism: Mechanism, +) -> Result<(), Status> { + let hash = get_do(&[0x30, 0x04], ctx.data).ok_or_else(|| Status::IncorrectDataParameter)?; + if hash.len() != rsa_hash_len(mechanism) { + warn!("RSA invalid hash length: {}", hash.len()); + return Err(Status::IncorrectDataParameter); + } + + let signature = try_syscall!(ctx.backend.client_mut().sign( + mechanism, + key_id, + hash, + SignatureSerialization::Raw + )) + .map_err(|_err| { + error!("Failed to sign data: {_err:?}"); + Status::UnspecifiedNonpersistentExecutionError + })? + .signature; + ctx.reply.expand(&signature) +} + // § 7.2.13 pub fn internal_authenticate( mut ctx: LoadedContext<'_, R, T>, @@ -101,6 +136,7 @@ pub fn internal_authenticate( } sign_ec(ctx, key_id, Mechanism::P256Prehashed) } + AuthenticationAlgorithm::Rsa2k => sign_rsa(ctx, key_id, Mechanism::Rsa2kPkcs), _ => { error!("Unimplemented operation"); Err(Status::ConditionsOfUseNotSatisfied) From 72b375c05797327ab5b0575191900b933cdb1601 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Fri, 7 Oct 2022 17:57:45 +0200 Subject: [PATCH 04/19] Add RSA decryption support This needs more work from the trussed side --- src/command/pso.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/command/pso.rs b/src/command/pso.rs index 02f61ffc..b42fd83f 100644 --- a/src/command/pso.rs +++ b/src/command/pso.rs @@ -171,6 +171,7 @@ pub fn decipher( match ctx.state.internal.dec_alg() { DecryptionAlgorithm::X255 => decrypt_ec(ctx, key_id, Mechanism::X255), DecryptionAlgorithm::EcDhP256 => decrypt_ec(ctx, key_id, Mechanism::P256), + DecryptionAlgorithm::Rsa2k => decrypt_rsa(ctx, key_id, Mechanism::Rsa2kPkcs), _ => { error!("Unimplemented operation"); Err(Status::ConditionsOfUseNotSatisfied) @@ -178,6 +179,34 @@ pub fn decipher( } } +fn decrypt_rsa( + mut ctx: LoadedContext<'_, R, T>, + private_key: KeyId, + mechanism: Mechanism, +) -> Result<(), Status> { + if ctx.data.is_empty() { + return Err(Status::IncorrectDataParameter); + } + let plaintext = try_syscall!(ctx.backend.client_mut().decrypt( + mechanism, + private_key, + &ctx.data[1..], + &[], + &[], + &[] + )) + .map_err(|_err| { + error!("Failed to decrypt data: {_err:?}"); + Status::IncorrectDataParameter + })? + .plaintext + .ok_or_else(|| { + warn!("No plaintext"); + return Status::IncorrectDataParameter; + })?; + ctx.reply.expand(&plaintext) +} + fn decrypt_ec( mut ctx: LoadedContext<'_, R, T>, private_key: KeyId, From 10451dc4724e4e4decbd8231bc95a3e717708757 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Fri, 7 Oct 2022 18:02:48 +0200 Subject: [PATCH 05/19] Fix clippy warnings --- src/command/pso.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/command/pso.rs b/src/command/pso.rs index b42fd83f..d4ef9923 100644 --- a/src/command/pso.rs +++ b/src/command/pso.rs @@ -93,7 +93,7 @@ fn sign_rsa( key_id: KeyId, mechanism: Mechanism, ) -> Result<(), Status> { - let hash = get_do(&[0x30, 0x04], ctx.data).ok_or_else(|| Status::IncorrectDataParameter)?; + let hash = get_do(&[0x30, 0x04], ctx.data).ok_or(Status::IncorrectDataParameter)?; if hash.len() != rsa_hash_len(mechanism) { warn!("RSA invalid hash length: {}", hash.len()); return Err(Status::IncorrectDataParameter); @@ -202,7 +202,7 @@ fn decrypt_rsa( .plaintext .ok_or_else(|| { warn!("No plaintext"); - return Status::IncorrectDataParameter; + Status::IncorrectDataParameter })?; ctx.reply.expand(&plaintext) } From 48022055ab470eb3ab48478053c77450177b727d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Mon, 10 Oct 2022 12:17:55 +0200 Subject: [PATCH 06/19] RSA: Add gpg tests --- tests/crypto-gpg.rs | 222 ++++++++++++++++++++++++++++++++++++++++++++ tests/virt/mod.rs | 9 ++ 2 files changed, 231 insertions(+) diff --git a/tests/crypto-gpg.rs b/tests/crypto-gpg.rs index 1d470f13..1626a504 100644 --- a/tests/crypto-gpg.rs +++ b/tests/crypto-gpg.rs @@ -510,8 +510,230 @@ fn gpg_p256() { }); } +fn gpg_rsa() { + with_vsc(|| { + let file_number: u32 = rand::rngs::OsRng.gen(); + let tmp = format!("/tmp/opcard-tests-{file_number}.gpg"); + let encrypted_file = &tmp; + let tmp = format!("/tmp/opcard-tests-{file_number}-sig.gpg"); + let sign_file = &tmp; + let tmp = format!("/tmp/opcard-tests-{file_number}.toml"); + let decrypted_file = &tmp; + let _dropper = FileDropper { + temp_file_name: encrypted_file, + }; + let _dropper = FileDropper { + temp_file_name: sign_file, + }; + let _dropper = FileDropper { + temp_file_name: decrypted_file, + }; + + let tmp = format!("test name{file_number}"); + let temp_name = &tmp; + + let tmp = format!("test{file_number}@email.com"); + let temp_email = &tmp; + + let custom_match = format!( + r"uid:u::::\d{{10}}::[0-9A-F]{{40}}::{temp_name} \(no comment\) <{temp_email}>::::::::::0:" + ); + + gnupg_test( + &[ + "admin", + "generate", + "n", + DEFAULT_PW3, + DEFAULT_PW1, + "0", + temp_name, + temp_email, + "no comment", + DEFAULT_PW1, + "quit", + ], + &[ + vec![r"\[GNUPG:\] CARDCTRL \d D2760001240103040000000000000000"], + virt::gpg_status(virt::KeyType::RsaNone,), + vec![ + r"\[GNUPG:\] GET_LINE cardedit.prompt", + r"\[GNUPG:\] GET_LINE cardedit.prompt", + r"\[GNUPG:\] GET_LINE cardedit.genkeys.backup_enc", + ], + virt::gpg_inquire_pin(), + virt::gpg_inquire_pin(), + vec![ + r"\[GNUPG:\] GET_LINE keygen.valid", + r"\[GNUPG:\] GET_LINE keygen.name", + r"\[GNUPG:\] GET_LINE keygen.email", + r"\[GNUPG:\] GET_LINE keygen.comment", + r"\[GNUPG:\] USERID_HINT [0-9A-F]{16} \[\?\]", + r"\[GNUPG:\] NEED_PASSPHRASE [0-9A-F]{16} [0-9A-F]{16} 1 \d", + ], + virt::gpg_inquire_pin(), + vec![ + r"pub:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}:::u:::scESCA:::D2760001240103040000000000000000:::23::0:", + r"fpr:::::::::[0-9A-F]{40}:", + r"grp:::::::::[0-9A-F]{40}:", + &custom_match, + r"sub:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}::::::a:::D2760001240103040000000000000000:::23:", + r"fpr:::::::::[0-9A-F]{40}:", + r"grp:::::::::[0-9A-F]{40}:", + r"sub:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}::::::e:::D2760001240103040000000000000000:::23:", + r"fpr:::::::::[0-9A-F]{40}:", + r"grp:::::::::[0-9A-F]{40}:", + r"\[GNUPG:\] KEY_CREATED B [0-9A-F]{40}", + r"\[GNUPG:\] GET_LINE cardedit.prompt", + ] + ].into_iter().flatten().collect::>(), + &[ + r"gpg: revocation certificate stored as '.*\.rev'", + r"gpg: checking the trustdb", + r"gpg: marginals needed: \d completes needed: \d trust model: pgp", + r"gpg: depth:[ 0-9]*valid:[ 0-9]*signed:[ 0-9]*trust: \d*-, \d*q, \d*n, \d*m, \d*f, \d*u", + ], + EditCard, + ); + + println!("================ FINISHED GENERATING 25519 KEYS ================"); + + gnupg_test( + &[], + &[ + r"\[GNUPG:\] BEGIN_ENCRYPTION \d \d", + r"\[GNUPG:\] END_ENCRYPTION", + ], + &[], + Encrypt { + i: "Cargo.toml", + o: encrypted_file, + r: temp_email, + }, + ); + + println!("================ FINISHED ENCRYPTION ================"); + + let custom1 = format!( + r"\[GNUPG:\] USERID_HINT [a-fA-F0-9]{{16}} {temp_name} \(no comment\) <{temp_email}>" + ); + let custom2 = format!(r"{temp_name} \(no comment\) <{temp_email}>"); + gnupg_test( + &[DEFAULT_PW1], + &[ + vec![ + r"\[GNUPG:\] ENC_TO [a-fA-F0-9]{16} \d* \d*", + &custom1, + r"\[GNUPG:\] NEED_PASSPHRASE [a-fA-F0-9]{16} [a-fA-F0-9]{16} 1 0", + ], + virt::gpg_inquire_pin(), + vec![ + r"\[GNUPG:\] DECRYPTION_KEY [a-fA-F0-9]{40} [a-fA-F0-9]{40} u", + r"\[GNUPG:\] BEGIN_DECRYPTION", + r"\[GNUPG:\] DECRYPTION_INFO \d \d \d", + r"\[GNUPG:\] PLAINTEXT \d* \d* Cargo.toml", + r"\[GNUPG:\] PLAINTEXT_LENGTH \d*", + r"\[GNUPG:\] DECRYPTION_OKAY", + r"\[GNUPG:\] GOODMDC", + r"\[GNUPG:\] END_DECRYPTION", + ], + ] + .into_iter() + .flatten() + .collect::>(), + &[ + r"gpg: encrypted with \d*-bit RSA key, ID [a-fA-F0-9]{16}, created \d{4}-\d\d-\d\d", + &custom2, + ], + Decrypt { + i: encrypted_file, + o: decrypted_file, + }, + ); + + println!("================ FINISHED DECRYPTION ================"); + + gnupg_test( + &[DEFAULT_PW1], + &[ + vec![ + r"\[GNUPG:\] CARDCTRL 3 D2760001240103040000000000000000", + r"\[GNUPG:\] BEGIN_SIGNING H8", + &custom1, + r"\[GNUPG:\] NEED_PASSPHRASE [a-fA-F0-9]{16} [a-fA-F0-9]{16} 1 0", + ], + virt::gpg_inquire_pin(), + vec![r"\[GNUPG:\] SIG_CREATED S 1 8 00 [a-fA-F0-9]{10} [a-fA-F0-9]{40}"], + ] + .into_iter() + .flatten() + .collect::>(), + &[r#"gpg: using "test\d*@email.com" as default secret key for signing"#], + Sign { + i: "Cargo.toml", + o: sign_file, + s: temp_email, + }, + ); + + println!("================ FINISHED SIGNATURE ================"); + + gnupg_test( + &[], + &[ + r"\[GNUPG:\] NEWSIG test\d*@email.com", + r"\[GNUPG:\] SIG_ID [^ ]* \d{4}-\d\d-\d\d [a-fA-F0-9]{10}", + r"\[GNUPG:\] GOODSIG [a-fA-F0-9]{16} test name\d* \(no comment\) ", + r"\[GNUPG:\] VALIDSIG [a-fA-F0-9]{40} \d{4}-\d\d-\d\d [a-fA-F0-9]{10} \d \d \d 1 8 00 [a-fA-F0-9]{40}", + r"\[GNUPG:\] TRUST_ULTIMATE 0 pgp", + ], + &[ + r"gpg: Signature made .*", + r"gpg: using RSA key [a-fA-F0-9]{40}", + r#"gpg: issuer "test\d*@email.com""#, + r#"pg: Good signature from "test name\d* \(no comment\) "#, + ], + Verify { i: sign_file }, + ); + gnupg_test( + &[ + "admin", + "factory-reset", + "y", + "yes", + "verify", + DEFAULT_PW1, + "quit", + ], + &[ + vec![r"\[GNUPG:\] CARDCTRL \d D2760001240103040000000000000000"], + virt::gpg_status(virt::KeyType::Rsa), + vec![ + r"\[GNUPG:\] GET_LINE cardedit.prompt", + r"\[GNUPG:\] GET_LINE cardedit.prompt", + r"\[GNUPG:\] GET_BOOL cardedit.factory-reset.proceed", + r"\[GNUPG:\] GET_LINE cardedit.factory-reset.really", + r"\[GNUPG:\] GET_LINE cardedit.prompt", + ], + virt::gpg_inquire_pin(), + virt::gpg_status(virt::KeyType::RsaNone), + vec![r"\[GNUPG:\] GET_LINE cardedit.prompt"], + ] + .into_iter() + .flatten() + .collect::>(), + &[ + r"gpg: OpenPGP card no. [0-9A-F]{32} detected", + r"gpg: Note: This command destroys all keys stored on the card!", + ], + EditCard, + ); + }); +} + #[test] fn gpg_crypto() { + gpg_rsa(); gpg_255(); gpg_p256(); } diff --git a/tests/virt/mod.rs b/tests/virt/mod.rs index 6cf78d64..50b9ff5e 100644 --- a/tests/virt/mod.rs +++ b/tests/virt/mod.rs @@ -18,6 +18,7 @@ use stoppable_thread::spawn; const STDOUT_FILTER: &[&str] = &[ r"\[GNUPG:\] KEY_CONSIDERED [0-9A-F]{40} \d", + r"\[GNUPG:\] ENCRYPTION_COMPLIANCE_MODE \d*", r"\[GNUPG:\] GOT_IT", ]; @@ -72,6 +73,7 @@ pub fn with_vsc R, R>(f: F) -> R { #[allow(unused)] pub enum KeyType { RsaNone, + Rsa, Cv25519, Cv25519NoAut, P256, @@ -109,6 +111,13 @@ pub fn gpg_status(key: KeyType) -> Vec<&'static str> { "fpr:[0-9a-zA-Z]{40}:[0-9a-zA-Z]{40}::", "grp:[0-9a-zA-Z]{40}:[0-9a-zA-Z]{40}:[0]{40}:", ), + KeyType::Rsa => ( + r"keyattr:1:1:2048:", + r"keyattr:2:1:2048:", + r"keyattr:3:1:2048:", + "fpr:[0-9a-zA-Z]{40}:[0-9a-zA-Z]{40}:[0-9a-zA-Z]{40}:", + "grp:[0-9a-zA-Z]{40}:[0-9a-zA-Z]{40}:[0-9a-zA-Z]{40}:", + ), KeyType::RsaNone => ( r"keyattr:1:1:2048:", r"keyattr:2:1:2048:", From 62007a3bd6f68da13394725f19df5a68d4e5ad1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Mon, 10 Oct 2022 15:17:02 +0200 Subject: [PATCH 07/19] Do not de-pad then re-pad the hash digest This also allows supporting other hashes than SHA256. Depends on 23f726894376887f59b2e3dd496180ac79289937 --- src/command/pso.rs | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/command/pso.rs b/src/command/pso.rs index d4ef9923..90d37128 100644 --- a/src/command/pso.rs +++ b/src/command/pso.rs @@ -10,14 +10,6 @@ use crate::card::LoadedContext; use crate::tlv::get_do; use crate::types::*; -fn rsa_hash_len(mechanism: Mechanism) -> usize { - match mechanism { - // SHA256 - Mechanism::Rsa2kPkcs => 32, - _ => unreachable!(), - } -} - fn check_uif( ctx: LoadedContext<'_, R, T>, key: KeyType, @@ -93,16 +85,10 @@ fn sign_rsa( key_id: KeyId, mechanism: Mechanism, ) -> Result<(), Status> { - let hash = get_do(&[0x30, 0x04], ctx.data).ok_or(Status::IncorrectDataParameter)?; - if hash.len() != rsa_hash_len(mechanism) { - warn!("RSA invalid hash length: {}", hash.len()); - return Err(Status::IncorrectDataParameter); - } - let signature = try_syscall!(ctx.backend.client_mut().sign( mechanism, key_id, - hash, + ctx.data, SignatureSerialization::Raw )) .map_err(|_err| { From 07b3c58813e767edce29eec33ed3ca4eb22d4018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Mon, 10 Oct 2022 15:43:39 +0200 Subject: [PATCH 08/19] Fix missing filter --- tests/virt/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/virt/mod.rs b/tests/virt/mod.rs index 50b9ff5e..d31f23d0 100644 --- a/tests/virt/mod.rs +++ b/tests/virt/mod.rs @@ -19,6 +19,7 @@ use stoppable_thread::spawn; const STDOUT_FILTER: &[&str] = &[ r"\[GNUPG:\] KEY_CONSIDERED [0-9A-F]{40} \d", r"\[GNUPG:\] ENCRYPTION_COMPLIANCE_MODE \d*", + r"\[GNUPG:\] DECRYPTION_COMPLIANCE_MODE \d*", r"\[GNUPG:\] GOT_IT", ]; From 1f1c8fb853a4d0b811823a45da633f95f12430fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Mon, 10 Oct 2022 16:10:57 +0200 Subject: [PATCH 09/19] Relax gpg test output regex --- tests/crypto-gpg.rs | 10 +++++----- tests/virt/mod.rs | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/crypto-gpg.rs b/tests/crypto-gpg.rs index 1626a504..af684bfc 100644 --- a/tests/crypto-gpg.rs +++ b/tests/crypto-gpg.rs @@ -200,7 +200,7 @@ fn gpg_255() { &[ vec![ r"\[GNUPG:\] CARDCTRL 3 D2760001240103040000000000000000", - r"\[GNUPG:\] BEGIN_SIGNING H8", + r"\[GNUPG:\] BEGIN_SIGNING H\d*", &custom1, r"\[GNUPG:\] NEED_PASSPHRASE [a-fA-F0-9]{16} [a-fA-F0-9]{16} 22 0", ], @@ -436,7 +436,7 @@ fn gpg_p256() { &[ vec![ r"\[GNUPG:\] CARDCTRL 3 D2760001240103040000000000000000", - r"\[GNUPG:\] BEGIN_SIGNING H8", + r"\[GNUPG:\] BEGIN_SIGNING H\d*", &custom1, r"\[GNUPG:\] NEED_PASSPHRASE [a-fA-F0-9]{16} [a-fA-F0-9]{16} 19 0", ], @@ -658,12 +658,12 @@ fn gpg_rsa() { &[ vec![ r"\[GNUPG:\] CARDCTRL 3 D2760001240103040000000000000000", - r"\[GNUPG:\] BEGIN_SIGNING H8", + r"\[GNUPG:\] BEGIN_SIGNING H\d*", &custom1, r"\[GNUPG:\] NEED_PASSPHRASE [a-fA-F0-9]{16} [a-fA-F0-9]{16} 1 0", ], virt::gpg_inquire_pin(), - vec![r"\[GNUPG:\] SIG_CREATED S 1 8 00 [a-fA-F0-9]{10} [a-fA-F0-9]{40}"], + vec![r"\[GNUPG:\] SIG_CREATED S 1 \d* 00 [a-fA-F0-9]{10} [a-fA-F0-9]{40}"], ] .into_iter() .flatten() @@ -684,7 +684,7 @@ fn gpg_rsa() { r"\[GNUPG:\] NEWSIG test\d*@email.com", r"\[GNUPG:\] SIG_ID [^ ]* \d{4}-\d\d-\d\d [a-fA-F0-9]{10}", r"\[GNUPG:\] GOODSIG [a-fA-F0-9]{16} test name\d* \(no comment\) ", - r"\[GNUPG:\] VALIDSIG [a-fA-F0-9]{40} \d{4}-\d\d-\d\d [a-fA-F0-9]{10} \d \d \d 1 8 00 [a-fA-F0-9]{40}", + r"\[GNUPG:\] VALIDSIG [a-fA-F0-9]{40} \d{4}-\d\d-\d\d [a-fA-F0-9]{10} \d \d \d 1 \d* 00 [a-fA-F0-9]{40}", r"\[GNUPG:\] TRUST_ULTIMATE 0 pgp", ], &[ diff --git a/tests/virt/mod.rs b/tests/virt/mod.rs index d31f23d0..1d8f43d9 100644 --- a/tests/virt/mod.rs +++ b/tests/virt/mod.rs @@ -20,6 +20,7 @@ const STDOUT_FILTER: &[&str] = &[ r"\[GNUPG:\] KEY_CONSIDERED [0-9A-F]{40} \d", r"\[GNUPG:\] ENCRYPTION_COMPLIANCE_MODE \d*", r"\[GNUPG:\] DECRYPTION_COMPLIANCE_MODE \d*", + r"\[GNUPG:\] VERIFICATION_COMPLIANCE_MODE \d*", r"\[GNUPG:\] GOT_IT", ]; From 6723ce400986c1a5328c1681336526cc52d0a819 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Fri, 14 Oct 2022 10:36:16 +0200 Subject: [PATCH 10/19] Rename rsa with full bit size rsa2k -> rsa2048 rsa3k -> rsa3072 rsa4k -> rsa4096 --- Cargo.toml | 2 +- src/command/data.rs | 6 ++--- src/command/gen.rs | 20 +++++++++----- src/command/private_key_template.rs | 6 ++--- src/command/pso.rs | 6 ++--- src/types.rs | 42 ++++++++++++++--------------- 6 files changed, 44 insertions(+), 38 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 34fc24d3..09951d29 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,7 @@ log-error = [] [patch.crates-io] # trussed = { git = "https://github.com/trussed-dev/trussed" , rev = "6de826f3bcbef247e55fd890d80d9ed6ce9f0abc" } -trussed = { git = "https://github.com/sosthene-nitrokey/trussed" , branch = "rsa-rebased" } +trussed = { git = "https://github.com/nitrokey/trussed" , branch = "rsa-rebased" } littlefs2-sys = { git = "https://github.com/sosthene-nitrokey/littlefs2-sys.git", branch = "bindgen-runtime-feature" } interchange = { git = "https://github.com/trussed-dev/interchange.git", rev = "fe5633466640e1e9a8c06d9b5dd1d0af08c272af" } p256-cortex-m4 = { git = "https://github.com/sosthene-nitrokey/p256-cortex-m4.git", branch = "upgrade" } diff --git a/src/command/data.rs b/src/command/data.rs index 1dc6ee0d..1f90a86c 100644 --- a/src/command/data.rs +++ b/src/command/data.rs @@ -1257,15 +1257,15 @@ mod tests { (DataObject::ExtendedCapabilities, &EXTENDED_CAPABILITIES), ( DataObject::AlgorithmAttributesSignature, - SignatureAlgorithm::Rsa2k.attributes(), + SignatureAlgorithm::Rsa2048.attributes(), ), ( DataObject::AlgorithmAttributesDecryption, - DecryptionAlgorithm::Rsa2k.attributes(), + DecryptionAlgorithm::Rsa2048.attributes(), ), ( DataObject::AlgorithmAttributesAuthentication, - AuthenticationAlgorithm::Rsa2k.attributes(), + AuthenticationAlgorithm::Rsa2048.attributes(), ), ( DataObject::PwStatusBytes, diff --git a/src/command/gen.rs b/src/command/gen.rs index 7e6c1ccc..d031aceb 100644 --- a/src/command/gen.rs +++ b/src/command/gen.rs @@ -34,7 +34,9 @@ pub fn sign( SignatureAlgorithm::EcDsaP256 => { gen_ec_key(ctx.lend(), KeyType::Sign, CurveAlgo::EcDsaP256) } - SignatureAlgorithm::Rsa2k => gen_rsa_key(ctx.lend(), KeyType::Sign, Mechanism::Rsa2kPkcs), + SignatureAlgorithm::Rsa2048 => { + gen_rsa_key(ctx.lend(), KeyType::Sign, Mechanism::Rsa2048Pkcs) + } _ => { error!("Unimplemented operation"); Err(Status::ConditionsOfUseNotSatisfied) @@ -50,7 +52,9 @@ pub fn dec( match algo { DecryptionAlgorithm::X255 => gen_ec_key(ctx.lend(), KeyType::Dec, CurveAlgo::X255), DecryptionAlgorithm::EcDhP256 => gen_ec_key(ctx.lend(), KeyType::Dec, CurveAlgo::EcDhP256), - DecryptionAlgorithm::Rsa2k => gen_rsa_key(ctx.lend(), KeyType::Dec, Mechanism::Rsa2kPkcs), + DecryptionAlgorithm::Rsa2048 => { + gen_rsa_key(ctx.lend(), KeyType::Dec, Mechanism::Rsa2048Pkcs) + } _ => { error!("Unimplemented operation"); Err(Status::ConditionsOfUseNotSatisfied) @@ -68,8 +72,8 @@ pub fn aut( AuthenticationAlgorithm::EcDsaP256 => { gen_ec_key(ctx.lend(), KeyType::Aut, CurveAlgo::EcDsaP256) } - AuthenticationAlgorithm::Rsa2k => { - gen_rsa_key(ctx.lend(), KeyType::Aut, Mechanism::Rsa2kPkcs) + AuthenticationAlgorithm::Rsa2048 => { + gen_rsa_key(ctx.lend(), KeyType::Aut, Mechanism::Rsa2048Pkcs) } _ => { error!("Unimplemented operation"); @@ -154,7 +158,7 @@ pub fn read_sign( match algo { SignatureAlgorithm::Ed255 => read_ec_key(ctx.lend(), key_id, CurveAlgo::Ed255), SignatureAlgorithm::EcDsaP256 => read_ec_key(ctx.lend(), key_id, CurveAlgo::EcDsaP256), - SignatureAlgorithm::Rsa2k => read_rsa_key(ctx.lend(), key_id, Mechanism::Rsa2kPkcs), + SignatureAlgorithm::Rsa2048 => read_rsa_key(ctx.lend(), key_id, Mechanism::Rsa2048Pkcs), _ => { error!("Unimplemented operation"); Err(Status::ConditionsOfUseNotSatisfied) @@ -175,7 +179,7 @@ pub fn read_dec( match algo { DecryptionAlgorithm::X255 => read_ec_key(ctx.lend(), key_id, CurveAlgo::X255), DecryptionAlgorithm::EcDhP256 => read_ec_key(ctx.lend(), key_id, CurveAlgo::EcDhP256), - DecryptionAlgorithm::Rsa2k => read_rsa_key(ctx.lend(), key_id, Mechanism::Rsa2kPkcs), + DecryptionAlgorithm::Rsa2048 => read_rsa_key(ctx.lend(), key_id, Mechanism::Rsa2048Pkcs), _ => { error!("Unimplemented operation"); Err(Status::ConditionsOfUseNotSatisfied) @@ -196,7 +200,9 @@ pub fn read_aut( match algo { AuthenticationAlgorithm::Ed255 => read_ec_key(ctx.lend(), key_id, CurveAlgo::Ed255), AuthenticationAlgorithm::EcDsaP256 => read_ec_key(ctx.lend(), key_id, CurveAlgo::EcDsaP256), - AuthenticationAlgorithm::Rsa2k => read_rsa_key(ctx.lend(), key_id, Mechanism::Rsa2kPkcs), + AuthenticationAlgorithm::Rsa2048 => { + read_rsa_key(ctx.lend(), key_id, Mechanism::Rsa2048Pkcs) + } _ => { error!("Unimplemented operation"); Err(Status::ConditionsOfUseNotSatisfied) diff --git a/src/command/private_key_template.rs b/src/command/private_key_template.rs index c6f5593f..c6763fee 100644 --- a/src/command/private_key_template.rs +++ b/src/command/private_key_template.rs @@ -49,7 +49,7 @@ pub fn put_sign( let key_id = match attr { SignatureAlgorithm::EcDsaP256 => put_ec(ctx.lend(), CurveAlgo::EcDsaP256)?, SignatureAlgorithm::Ed255 => put_ec(ctx.lend(), CurveAlgo::Ed255)?, - SignatureAlgorithm::Rsa2k | SignatureAlgorithm::Rsa4k => { + SignatureAlgorithm::Rsa2048 | SignatureAlgorithm::Rsa4096 => { warn!("Key import for RSA not supported"); return Err(Status::FunctionNotSupported); } @@ -76,7 +76,7 @@ pub fn put_dec( let key_id = match attr { DecryptionAlgorithm::EcDhP256 => put_ec(ctx.lend(), CurveAlgo::EcDhP256)?, DecryptionAlgorithm::X255 => put_ec(ctx.lend(), CurveAlgo::X255)?, - DecryptionAlgorithm::Rsa2k | DecryptionAlgorithm::Rsa4k => { + DecryptionAlgorithm::Rsa2048 | DecryptionAlgorithm::Rsa4096 => { warn!("Key import for RSA not supported"); return Err(Status::FunctionNotSupported); } @@ -103,7 +103,7 @@ pub fn put_aut( let key_id = match attr { AuthenticationAlgorithm::EcDsaP256 => put_ec(ctx.lend(), CurveAlgo::EcDsaP256)?, AuthenticationAlgorithm::Ed255 => put_ec(ctx.lend(), CurveAlgo::Ed255)?, - AuthenticationAlgorithm::Rsa2k | AuthenticationAlgorithm::Rsa4k => { + AuthenticationAlgorithm::Rsa2048 | AuthenticationAlgorithm::Rsa4096 => { warn!("Key import for RSA not supported"); return Err(Status::FunctionNotSupported); } diff --git a/src/command/pso.rs b/src/command/pso.rs index 90d37128..f22512c6 100644 --- a/src/command/pso.rs +++ b/src/command/pso.rs @@ -53,7 +53,7 @@ pub fn sign( } sign_ec(ctx, key_id, Mechanism::P256Prehashed) } - SignatureAlgorithm::Rsa2k => sign_rsa(ctx, key_id, Mechanism::Rsa2kPkcs), + SignatureAlgorithm::Rsa2048 => sign_rsa(ctx, key_id, Mechanism::Rsa2048Pkcs), _ => { error!("Unimplemented operation"); Err(Status::ConditionsOfUseNotSatisfied) @@ -122,7 +122,7 @@ pub fn internal_authenticate( } sign_ec(ctx, key_id, Mechanism::P256Prehashed) } - AuthenticationAlgorithm::Rsa2k => sign_rsa(ctx, key_id, Mechanism::Rsa2kPkcs), + AuthenticationAlgorithm::Rsa2048 => sign_rsa(ctx, key_id, Mechanism::Rsa2048Pkcs), _ => { error!("Unimplemented operation"); Err(Status::ConditionsOfUseNotSatisfied) @@ -157,7 +157,7 @@ pub fn decipher( match ctx.state.internal.dec_alg() { DecryptionAlgorithm::X255 => decrypt_ec(ctx, key_id, Mechanism::X255), DecryptionAlgorithm::EcDhP256 => decrypt_ec(ctx, key_id, Mechanism::P256), - DecryptionAlgorithm::Rsa2k => decrypt_rsa(ctx, key_id, Mechanism::Rsa2kPkcs), + DecryptionAlgorithm::Rsa2048 => decrypt_rsa(ctx, key_id, Mechanism::Rsa2048Pkcs), _ => { error!("Unimplemented operation"); Err(Status::ConditionsOfUseNotSatisfied) diff --git a/src/types.rs b/src/types.rs index 1b28d8b5..baa06647 100644 --- a/src/types.rs +++ b/src/types.rs @@ -68,14 +68,14 @@ iterable_enum! { // Part of draft https://datatracker.ietf.org/doc/draft-ietf-openpgp-crypto-refresh/ Ed255, EcDsaP256, - Rsa2k, - Rsa4k, + Rsa2048, + Rsa4096, } } impl Default for SignatureAlgorithm { fn default() -> Self { - Self::Rsa2k + Self::Rsa2048 } } @@ -89,8 +89,8 @@ impl SignatureAlgorithm { match self { Self::Ed255 => ED255_ATTRIBUTES, Self::EcDsaP256 => ECDSA_P256_ATTRIBUTES, - Self::Rsa2k => RSA_2K_ATTRIBUTES, - Self::Rsa4k => RSA_4K_ATTRIBUTES, + Self::Rsa2048 => RSA_2K_ATTRIBUTES, + Self::Rsa4096 => RSA_4K_ATTRIBUTES, } } @@ -107,8 +107,8 @@ impl TryFrom<&[u8]> for SignatureAlgorithm { match v { ED255_ATTRIBUTES => Ok(Self::Ed255), ECDSA_P256_ATTRIBUTES => Ok(Self::EcDsaP256), - RSA_2K_ATTRIBUTES => Ok(Self::Rsa2k), - RSA_4K_ATTRIBUTES => Ok(Self::Rsa4k), + RSA_2K_ATTRIBUTES => Ok(Self::Rsa2048), + RSA_4K_ATTRIBUTES => Ok(Self::Rsa4096), _ => Err(AlgorithmFromAttributesError), } } @@ -121,14 +121,14 @@ iterable_enum! { // Part of draft https://datatracker.ietf.org/doc/draft-ietf-openpgp-crypto-refresh/ X255, EcDhP256, - Rsa2k, - Rsa4k, + Rsa2048, + Rsa4096, } } impl Default for DecryptionAlgorithm { fn default() -> Self { - Self::Rsa2k + Self::Rsa2048 } } @@ -142,8 +142,8 @@ impl DecryptionAlgorithm { match self { Self::X255 => X255_ATTRIBUTES, Self::EcDhP256 => ECDH_P256_ATTRIBUTES, - Self::Rsa2k => RSA_2K_ATTRIBUTES, - Self::Rsa4k => RSA_4K_ATTRIBUTES, + Self::Rsa2048 => RSA_2K_ATTRIBUTES, + Self::Rsa4096 => RSA_4K_ATTRIBUTES, } } @@ -160,8 +160,8 @@ impl TryFrom<&[u8]> for DecryptionAlgorithm { match v { X255_ATTRIBUTES => Ok(Self::X255), ECDH_P256_ATTRIBUTES => Ok(Self::EcDhP256), - RSA_2K_ATTRIBUTES => Ok(Self::Rsa2k), - RSA_4K_ATTRIBUTES => Ok(Self::Rsa4k), + RSA_2K_ATTRIBUTES => Ok(Self::Rsa2048), + RSA_4K_ATTRIBUTES => Ok(Self::Rsa4096), _ => Err(AlgorithmFromAttributesError), } } @@ -174,14 +174,14 @@ iterable_enum! { // Part of draft https://datatracker.ietf.org/doc/draft-ietf-openpgp-crypto-refresh/ Ed255, EcDsaP256, - Rsa2k, - Rsa4k, + Rsa2048, + Rsa4096, } } impl Default for AuthenticationAlgorithm { fn default() -> Self { - Self::Rsa2k + Self::Rsa2048 } } @@ -195,8 +195,8 @@ impl AuthenticationAlgorithm { match self { Self::Ed255 => ED255_ATTRIBUTES, Self::EcDsaP256 => ECDSA_P256_ATTRIBUTES, - Self::Rsa2k => RSA_2K_ATTRIBUTES, - Self::Rsa4k => RSA_4K_ATTRIBUTES, + Self::Rsa2048 => RSA_2K_ATTRIBUTES, + Self::Rsa4096 => RSA_4K_ATTRIBUTES, } } @@ -213,8 +213,8 @@ impl TryFrom<&[u8]> for AuthenticationAlgorithm { match v { ED255_ATTRIBUTES => Ok(Self::Ed255), ECDSA_P256_ATTRIBUTES => Ok(Self::EcDsaP256), - RSA_2K_ATTRIBUTES => Ok(Self::Rsa2k), - RSA_4K_ATTRIBUTES => Ok(Self::Rsa4k), + RSA_2K_ATTRIBUTES => Ok(Self::Rsa2048), + RSA_4K_ATTRIBUTES => Ok(Self::Rsa4096), _ => Err(AlgorithmFromAttributesError), } } From fcf441cade60db4636fbe4344b58fe8f35587a91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Fri, 14 Oct 2022 09:25:10 +0200 Subject: [PATCH 11/19] Add rsa key import --- Cargo.toml | 2 +- src/command/private_key_template.rs | 70 ++++++- src/tlv.rs | 7 +- src/types.rs | 4 +- tests/crypto-gpg-import.rs | 290 ++++++++++++++++++++++++++++ tests/virt/mod.rs | 8 + 6 files changed, 370 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e6a0c26d..5333b290 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,7 +64,7 @@ log-error = [] [patch.crates-io] # trussed = { git = "https://github.com/trussed-dev/trussed" , rev = "6de826f3bcbef247e55fd890d80d9ed6ce9f0abc" } -trussed = { git = "https://github.com/nitrokey/trussed" , branch = "rsa-rebased" } +trussed = { git = "https://github.com/nitrokey/trussed" , branch = "rsa-import" } littlefs2-sys = { git = "https://github.com/sosthene-nitrokey/littlefs2-sys.git", branch = "bindgen-runtime-feature" } interchange = { git = "https://github.com/trussed-dev/interchange.git", rev = "fe5633466640e1e9a8c06d9b5dd1d0af08c272af" } p256-cortex-m4 = { git = "https://github.com/sosthene-nitrokey/p256-cortex-m4.git", branch = "upgrade" } diff --git a/src/command/private_key_template.rs b/src/command/private_key_template.rs index 5d9d20d1..c3186314 100644 --- a/src/command/private_key_template.rs +++ b/src/command/private_key_template.rs @@ -2,16 +2,17 @@ // SPDX-License-Identifier: LGPL-3.0-only use iso7816::Status; -use trussed::types::{KeyId, KeySerialization, Location}; -use trussed::{syscall, try_syscall}; +use trussed::types::{KeyId, KeySerialization, Location, Mechanism, Message, RsaCrtImportFormat}; +use trussed::{postcard_serialize_bytes, syscall, try_syscall}; use crate::card::LoadedContext; use crate::state::KeyOrigin; -use crate::tlv::get_do; +use crate::tlv::{get_do, take_len}; use crate::types::*; const PRIVATE_KEY_TEMPLATE_DO: u16 = 0x4D; const CONCATENATION_KEY_DATA_DO: u16 = 0x5F48; +const TEMPLATE_DO: u16 = 0x7F48; // § 4.4.3.12 pub fn put_private_key_template( @@ -40,7 +41,8 @@ pub fn put_sign( let key_id = match attr { SignatureAlgorithm::EcDsaP256 => put_ec(ctx.lend(), CurveAlgo::EcDsaP256)?, SignatureAlgorithm::Ed255 => put_ec(ctx.lend(), CurveAlgo::Ed255)?, - SignatureAlgorithm::Rsa2048 | SignatureAlgorithm::Rsa4096 => { + SignatureAlgorithm::Rsa2048 => put_rsa(ctx.lend(), Mechanism::Rsa2048Pkcs)?, + SignatureAlgorithm::Rsa4096 => { warn!("Key import for RSA not supported"); return Err(Status::FunctionNotSupported); } @@ -67,7 +69,8 @@ pub fn put_dec( let key_id = match attr { DecryptionAlgorithm::EcDhP256 => put_ec(ctx.lend(), CurveAlgo::EcDhP256)?, DecryptionAlgorithm::X255 => put_ec(ctx.lend(), CurveAlgo::X255)?, - DecryptionAlgorithm::Rsa2048 | DecryptionAlgorithm::Rsa4096 => { + DecryptionAlgorithm::Rsa2048 => put_rsa(ctx.lend(), Mechanism::Rsa2048Pkcs)?, + DecryptionAlgorithm::Rsa4096 => { warn!("Key import for RSA not supported"); return Err(Status::FunctionNotSupported); } @@ -94,7 +97,8 @@ pub fn put_aut( let key_id = match attr { AuthenticationAlgorithm::EcDsaP256 => put_ec(ctx.lend(), CurveAlgo::EcDsaP256)?, AuthenticationAlgorithm::Ed255 => put_ec(ctx.lend(), CurveAlgo::Ed255)?, - AuthenticationAlgorithm::Rsa2048 | AuthenticationAlgorithm::Rsa4096 => { + AuthenticationAlgorithm::Rsa2048 => put_rsa(ctx.lend(), Mechanism::Rsa2048Pkcs)?, + AuthenticationAlgorithm::Rsa4096 => { warn!("Key import for RSA not supported"); return Err(Status::FunctionNotSupported); } @@ -141,3 +145,57 @@ fn put_ec( .key; Ok(Some(key)) } + +fn parse_rsa_template(data: &[u8]) -> Option> { + let mut template = get_do(&[PRIVATE_KEY_TEMPLATE_DO, TEMPLATE_DO], data)?; + let mut res = [(0, 0); 6]; + let mut acc = 0; + for i in 0..6 { + if *template.first()? != i + 0x91 { + warn!("Unexpected template data: {}", template.first()?); + return None; + } + + let (size, d) = take_len(&template[1..])?; + res[i as usize] = (acc, acc + size); + acc += size; + template = d; + } + + let key_data = get_do(&[PRIVATE_KEY_TEMPLATE_DO, CONCATENATION_KEY_DATA_DO], data)?; + Some(RsaCrtImportFormat { + e: key_data.get(res[0].0..res[0].1)?, + p: key_data.get(res[1].0..res[1].1)?, + q: key_data.get(res[2].0..res[2].1)?, + qinv: key_data.get(res[3].0..res[3].1)?, + dp: key_data.get(res[4].0..res[4].1)?, + dq: key_data.get(res[5].0..res[5].1)?, + }) +} + +fn put_rsa( + ctx: LoadedContext<'_, R, T>, + mechanism: Mechanism, +) -> Result, Status> { + let key_data = parse_rsa_template(ctx.data).ok_or_else(|| { + warn!("Unable to parse rsa key"); + Status::IncorrectDataParameter + })?; + + let key_message: Message = postcard_serialize_bytes(&key_data).map_err(|_err| { + error!("Failed to serialize rsa key: {_err:?}"); + Status::UnspecifiedNonpersistentExecutionError + })?; + let key = try_syscall!(ctx.backend.client_mut().unsafe_inject_key( + mechanism, + &key_message, + Location::Internal, + KeySerialization::RsaCrt + )) + .map_err(|_err| { + warn!("Failed to store key: {_err:?}"); + Status::UnspecifiedNonpersistentExecutionError + })? + .key; + Ok(Some(key)) +} diff --git a/src/tlv.rs b/src/tlv.rs index ad6ff762..372216c4 100644 --- a/src/tlv.rs +++ b/src/tlv.rs @@ -52,7 +52,7 @@ fn take_tag(data: &[u8]) -> Option<(u16, &[u8])> { } } -fn take_len(data: &[u8]) -> Option<(usize, &[u8])> { +pub fn take_len(data: &[u8]) -> Option<(usize, &[u8])> { let l1 = *data.first()?; if l1 <= 0x7F { Some((l1 as usize, &data[1..])) @@ -60,7 +60,10 @@ fn take_len(data: &[u8]) -> Option<(usize, &[u8])> { Some((*data.get(1)? as usize, &data[2..])) } else { if l1 != 0x82 { - warn!("Got an unexpected length tag: {l1:x}"); + warn!( + "Got an unexpected length tag: {l1:x}, data: {:x?}", + &data[..3] + ); return None; } let l2 = *data.get(1)?; diff --git a/src/types.rs b/src/types.rs index 002b0659..f0d800fa 100644 --- a/src/types.rs +++ b/src/types.rs @@ -46,14 +46,14 @@ const RSA_2K_ATTRIBUTES: &[u8] = hex!(" 01 0800 // Length modulus (in bit): 2048 0020 // Length exponent (in bit): 32 - 00 // 0: Acceptable format is: P and Q + 02 // import in CRT Format ").as_slice(); const RSA_4K_ATTRIBUTES: &[u8] = hex!( " 01 1000 // Length modulus (in bit): 4096 0020 // Length exponent (in bit): 32 - 00 // 0: Acceptable format is: P and Q + 02 // import in CRT Format " ) .as_slice(); diff --git a/tests/crypto-gpg-import.rs b/tests/crypto-gpg-import.rs index 0f664157..e9ee3099 100644 --- a/tests/crypto-gpg-import.rs +++ b/tests/crypto-gpg-import.rs @@ -570,8 +570,298 @@ fn gpg_p256() { }); } +fn gpg_rsa() { + with_vsc(|| { + let file_number: u32 = rand::rngs::OsRng.gen(); + let tmp = format!("/tmp/opcard-tests-{file_number}.gpg"); + let encrypted_file = &tmp; + let tmp = format!("/tmp/opcard-tests-{file_number}-sig.gpg"); + let sign_file = &tmp; + let tmp = format!("/tmp/opcard-tests-{file_number}.toml"); + let decrypted_file = &tmp; + let _dropper = FileDropper { + temp_file_name: encrypted_file, + }; + let _dropper = FileDropper { + temp_file_name: sign_file, + }; + let _dropper = FileDropper { + temp_file_name: decrypted_file, + }; + + let tmp = format!("test name{file_number}"); + let temp_name = &tmp; + + let tmp = format!("test{file_number}@email.com"); + let temp_email = &tmp; + + let custom_match = format!( + r"uid:u::::\d{{10}}::[0-9A-F]{{40}}::{temp_name} \(no comment\) <{temp_email}>::::::::::0:" + ); + + let custom_match2 = format!( + r"uid:u::::::::{temp_name} \(no comment\) <{temp_email}>:::.*,mdc,no-ks-modify:1,p::" + ); + + gnupg_test( + &[ + "1", + "2048", + "2048", + "0", + temp_name, + temp_email, + "no comment", + "", + "", + ], + &[ + vec![ + r"\[GNUPG:\] GET_LINE keygen.algo", + r"\[GNUPG:\] GET_LINE keygen.size", + r"\[GNUPG:\] GET_LINE keygen.size", + r"\[GNUPG:\] GET_LINE keygen.valid", + r"\[GNUPG:\] GET_LINE keygen.name", + r"\[GNUPG:\] GET_LINE keygen.email", + r"\[GNUPG:\] GET_LINE keygen.comment", + ], + virt::gpg_inquire_pin(), + virt::gpg_inquire_pin(), + vec![ + r"pub:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}:::u:::scESC:::\+:::23::0:", + r"fpr:::::::::[0-9A-F]{40}:", + r"grp:::::::::[0-9A-F]{40}:", + &custom_match, + r"sub:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}::::::e:::\+:::23:", + r"fpr:::::::::[0-9A-F]{40}:", + r"grp:::::::::[0-9A-F]{40}:", + r"\[GNUPG:\] KEY_CREATED B [A-F0-9]{40}", + ], + ] + .into_iter() + .flatten() + .collect::>(), + &[ + r"gpg: revocation certificate stored as '.*\.rev'", + r"gpg: checking the trustdb", + r"gpg: marginals needed: \d completes needed: \d trust model: pgp", + r"gpg: depth:[ 0-9]*valid:[ 0-9]*signed:[ 0-9]*trust: \d*-, \d*q, \d*n, \d*m, \d*f, \d*u", + ], + Generate, + ); + + println!("================ FINISHED GENERATING Rsa2048 KEYS ================"); + + gnupg_test( + &["key *", "keytocard", "2", DEFAULT_PW3, "save"], + &[ + vec![ + r"sec:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}:0::u:::sc", + r"fpr:::::::::[0-9A-F]{40}:", + r"ssb:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}:0:::::e", + r"fpr:::::::::[0-9A-F]{40}:", + &custom_match2, + r"\[GNUPG:\] GET_LINE keyedit.prompt", + r"sec:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}:0::u:::sc", + r"fpr:::::::::[0-9A-F]{40}:", + r"ssb:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}:0:::::e", + r"fpr:::::::::[0-9A-F]{40}:", + &custom_match2, + r"\[GNUPG:\] GET_LINE keyedit.prompt", + r"\[GNUPG:\] CARDCTRL 3 D2760001240103040000000000000000", + r"\[GNUPG:\] GET_LINE cardedit.genkeys.storekeytype", + ], + virt::gpg_inquire_pin(), + // virt::gpg_inquire_pin(), + vec![ + r"sec:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}:0::u:::sc", + r"fpr:::::::::[0-9A-F]{40}:", + r"ssb:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}:0:::::e", + r"fpr:::::::::[0-9A-F]{40}:", + &custom_match2, + r"\[GNUPG:\] GET_LINE keyedit.prompt", + ], + ] + .into_iter() + .flatten() + .collect::>(), + &[], + EditKey { o: temp_email }, + ); + + println!("================ FINISHED IMPORTING DECRYPTION KEY ================"); + + gnupg_test( + &["keytocard", "y", "1", DEFAULT_PW3, "save"], + &[ + vec![ + r"sec:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}:0::u:::sc", + r"fpr:::::::::[0-9A-F]{40}:", + r"ssb:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}:0:::::e", + r"fpr:::::::::[0-9A-F]{40}:", + &custom_match2, + r"\[GNUPG:\] GET_LINE keyedit.prompt", + r"\[GNUPG:\] GET_BOOL keyedit.keytocard.use_primary", + r"\[GNUPG:\] CARDCTRL 3 D2760001240103040000000000000000", + r"\[GNUPG:\] GET_LINE cardedit.genkeys.storekeytype", + ], + vec![ + r"sec:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}:0::u:::sc", + r"fpr:::::::::[0-9A-F]{40}:", + r"ssb:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}:0:::::e", + r"fpr:::::::::[0-9A-F]{40}:", + &custom_match2, + r"\[GNUPG:\] GET_LINE keyedit.prompt", + r"\[GNUPG:\] GET_LINE keyedit.prompt", + ], + ] + .into_iter() + .flatten() + .collect::>(), + &[], + EditKey { o: temp_email }, + ); + + println!("================ FINISHED IMPORTING Rsa2048 KEYS ================"); + + gnupg_test( + &[], + &[ + r"\[GNUPG:\] BEGIN_ENCRYPTION \d \d", + r"\[GNUPG:\] END_ENCRYPTION", + ], + &[], + Encrypt { + i: "Cargo.toml", + o: encrypted_file, + r: temp_email, + }, + ); + + println!("================ FINISHED ENCRYPTION ================"); + + let custom1 = format!( + r"\[GNUPG:\] USERID_HINT [a-fA-F0-9]{{16}} {temp_name} \(no comment\) <{temp_email}>" + ); + let custom2 = format!(r"{temp_name} \(no comment\) <{temp_email}>"); + + gnupg_test( + &[DEFAULT_PW1], + &[ + vec![ + r"\[GNUPG:\] ENC_TO [a-fA-F0-9]{16} \d* \d*", + &custom1, + r"\[GNUPG:\] NEED_PASSPHRASE [a-fA-F0-9]{16} [a-fA-F0-9]{16} 1 0", + ], + virt::gpg_inquire_pin(), + vec![ + r"\[GNUPG:\] DECRYPTION_KEY [a-fA-F0-9]{40} [a-fA-F0-9]{40} u", + r"\[GNUPG:\] BEGIN_DECRYPTION", + r"\[GNUPG:\] DECRYPTION_INFO \d \d \d", + r"\[GNUPG:\] PLAINTEXT \d* \d* Cargo.toml", + r"\[GNUPG:\] PLAINTEXT_LENGTH \d*", + r"\[GNUPG:\] DECRYPTION_OKAY", + r"\[GNUPG:\] GOODMDC", + r"\[GNUPG:\] END_DECRYPTION", + ], + ] + .into_iter() + .flatten() + .collect::>(), + &[ + r"gpg: encrypted with \d*-bit RSA key, ID [a-fA-F0-9]{16}, created \d{4}-\d\d-\d\d", + &custom2, + ], + Decrypt { + i: encrypted_file, + o: decrypted_file, + }, + ); + + println!("================ FINISHED DECRYPTION ================"); + + gnupg_test( + &[DEFAULT_PW1], + &[ + vec![ + r"\[GNUPG:\] CARDCTRL 3 D2760001240103040000000000000000", + r"\[GNUPG:\] BEGIN_SIGNING H8", + &custom1, + r"\[GNUPG:\] NEED_PASSPHRASE [a-fA-F0-9]{16} [a-fA-F0-9]{16} 1 0", + ], + virt::gpg_inquire_pin(), + vec![r"\[GNUPG:\] SIG_CREATED S 1 8 00 [a-fA-F0-9]{10} [a-fA-F0-9]{40}"], + ] + .into_iter() + .flatten() + .collect::>(), + &[r#"gpg: using "test\d*@email.com" as default secret key for signing"#], + Sign { + i: "Cargo.toml", + o: sign_file, + s: temp_email, + }, + ); + + println!("================ FINISHED SIGNATURE ================"); + + gnupg_test( + &[], + &[ + r"\[GNUPG:\] NEWSIG test\d*@email.com", + r"\[GNUPG:\] SIG_ID [^ ]* \d{4}-\d\d-\d\d [a-fA-F0-9]{10}", + r"\[GNUPG:\] GOODSIG [a-fA-F0-9]{16} test name\d* \(no comment\) ", + r"\[GNUPG:\] VALIDSIG [a-fA-F0-9]{40} \d{4}-\d\d-\d\d [a-fA-F0-9]{10} \d \d \d 1 8 00 [a-fA-F0-9]{40}", + r"\[GNUPG:\] TRUST_ULTIMATE 0 pgp", + ], + &[ + r"gpg: Signature made .*", + r"gpg: using RSA key [a-fA-F0-9]{40}", + r#"gpg: issuer "test\d*@email.com""#, + r#"pg: Good signature from "test name\d* \(no comment\) "#, + ], + Verify { i: sign_file }, + ); + + gnupg_test( + &[ + "admin", + "factory-reset", + "y", + "yes", + "verify", + DEFAULT_PW1, + "quit", + ], + &[ + vec![r"\[GNUPG:\] CARDCTRL \d D2760001240103040000000000000000"], + virt::gpg_status(virt::KeyType::RsaNoAut), + vec![ + r"\[GNUPG:\] GET_LINE cardedit.prompt", + r"\[GNUPG:\] GET_LINE cardedit.prompt", + r"\[GNUPG:\] GET_BOOL cardedit.factory-reset.proceed", + r"\[GNUPG:\] GET_LINE cardedit.factory-reset.really", + r"\[GNUPG:\] GET_LINE cardedit.prompt", + ], + virt::gpg_inquire_pin(), + virt::gpg_status(virt::KeyType::RsaNone), + vec![r"\[GNUPG:\] GET_LINE cardedit.prompt"], + ] + .into_iter() + .flatten() + .collect::>(), + &[ + r"gpg: OpenPGP card no. [0-9A-F]{32} detected", + r"gpg: Note: This command destroys all keys stored on the card!", + ], + EditCard, + ); + }); +} + #[test] fn gpg_crypto_import() { + gpg_rsa(); gpg_255(); gpg_p256(); } diff --git a/tests/virt/mod.rs b/tests/virt/mod.rs index 92a127e0..a3f5ca53 100644 --- a/tests/virt/mod.rs +++ b/tests/virt/mod.rs @@ -71,6 +71,7 @@ pub fn with_vsc R, R>(f: F) -> R { pub enum KeyType { RsaNone, Rsa, + RsaNoAut, Cv25519, Cv25519NoAut, P256, @@ -115,6 +116,13 @@ pub fn gpg_status(key: KeyType, sign_count: usize) -> Vec<&'static str> { "fpr:[0-9a-zA-Z]{40}:[0-9a-zA-Z]{40}:[0-9a-zA-Z]{40}:", "grp:[0-9a-zA-Z]{40}:[0-9a-zA-Z]{40}:[0-9a-zA-Z]{40}:", ), + KeyType::RsaNoAut => ( + r"keyattr:1:1:2048:", + r"keyattr:2:1:2048:", + r"keyattr:3:1:2048:", + "fpr:[0-9a-zA-Z]{40}:[0-9a-zA-Z]{40}::", + "grp:[0-9a-zA-Z]{40}:[0-9a-zA-Z]{40}:[0]{40}:", + ), KeyType::RsaNone => ( r"keyattr:1:1:2048:", r"keyattr:2:1:2048:", From 823d49d4ee79ee40f597f56ba6915273d0382c22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Fri, 14 Oct 2022 09:38:37 +0200 Subject: [PATCH 12/19] Relax regex matching in key import tests --- tests/crypto-gpg-import.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/crypto-gpg-import.rs b/tests/crypto-gpg-import.rs index e9ee3099..3afd09d1 100644 --- a/tests/crypto-gpg-import.rs +++ b/tests/crypto-gpg-import.rs @@ -218,7 +218,7 @@ fn gpg_255() { &[ vec![ r"\[GNUPG:\] CARDCTRL 3 D2760001240103040000000000000000", - r"\[GNUPG:\] BEGIN_SIGNING H8", + r"\[GNUPG:\] BEGIN_SIGNING H\d*", &custom1, r"\[GNUPG:\] NEED_PASSPHRASE [a-fA-F0-9]{16} [a-fA-F0-9]{16} 22 0", ], @@ -496,7 +496,7 @@ fn gpg_p256() { &[ vec![ r"\[GNUPG:\] CARDCTRL 3 D2760001240103040000000000000000", - r"\[GNUPG:\] BEGIN_SIGNING H8", + r"\[GNUPG:\] BEGIN_SIGNING H\d*", &custom1, r"\[GNUPG:\] NEED_PASSPHRASE [a-fA-F0-9]{16} [a-fA-F0-9]{16} 19 0", ], @@ -785,12 +785,12 @@ fn gpg_rsa() { &[ vec![ r"\[GNUPG:\] CARDCTRL 3 D2760001240103040000000000000000", - r"\[GNUPG:\] BEGIN_SIGNING H8", + r"\[GNUPG:\] BEGIN_SIGNING H\d*", &custom1, r"\[GNUPG:\] NEED_PASSPHRASE [a-fA-F0-9]{16} [a-fA-F0-9]{16} 1 0", ], virt::gpg_inquire_pin(), - vec![r"\[GNUPG:\] SIG_CREATED S 1 8 00 [a-fA-F0-9]{10} [a-fA-F0-9]{40}"], + vec![r"\[GNUPG:\] SIG_CREATED S 1 \d* 00 [a-fA-F0-9]{10} [a-fA-F0-9]{40}"], ] .into_iter() .flatten() @@ -811,7 +811,7 @@ fn gpg_rsa() { r"\[GNUPG:\] NEWSIG test\d*@email.com", r"\[GNUPG:\] SIG_ID [^ ]* \d{4}-\d\d-\d\d [a-fA-F0-9]{10}", r"\[GNUPG:\] GOODSIG [a-fA-F0-9]{16} test name\d* \(no comment\) ", - r"\[GNUPG:\] VALIDSIG [a-fA-F0-9]{40} \d{4}-\d\d-\d\d [a-fA-F0-9]{10} \d \d \d 1 8 00 [a-fA-F0-9]{40}", + r"\[GNUPG:\] VALIDSIG [a-fA-F0-9]{40} \d{4}-\d\d-\d\d [a-fA-F0-9]{10} \d \d \d 1 \d* 00 [a-fA-F0-9]{40}", r"\[GNUPG:\] TRUST_ULTIMATE 0 pgp", ], &[ From 871ef918ea5d07c9ae08ae2d7ab0a79de1a2479a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Fri, 14 Oct 2022 09:45:08 +0200 Subject: [PATCH 13/19] Clear log warning for RSA4k import --- src/command/private_key_template.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/command/private_key_template.rs b/src/command/private_key_template.rs index c3186314..33b44a3d 100644 --- a/src/command/private_key_template.rs +++ b/src/command/private_key_template.rs @@ -43,7 +43,7 @@ pub fn put_sign( SignatureAlgorithm::Ed255 => put_ec(ctx.lend(), CurveAlgo::Ed255)?, SignatureAlgorithm::Rsa2048 => put_rsa(ctx.lend(), Mechanism::Rsa2048Pkcs)?, SignatureAlgorithm::Rsa4096 => { - warn!("Key import for RSA not supported"); + warn!("Key import for RSA4096 not supported"); return Err(Status::FunctionNotSupported); } } @@ -71,7 +71,7 @@ pub fn put_dec( DecryptionAlgorithm::X255 => put_ec(ctx.lend(), CurveAlgo::X255)?, DecryptionAlgorithm::Rsa2048 => put_rsa(ctx.lend(), Mechanism::Rsa2048Pkcs)?, DecryptionAlgorithm::Rsa4096 => { - warn!("Key import for RSA not supported"); + warn!("Key import for RSA4096 not supported"); return Err(Status::FunctionNotSupported); } } @@ -99,7 +99,7 @@ pub fn put_aut( AuthenticationAlgorithm::Ed255 => put_ec(ctx.lend(), CurveAlgo::Ed255)?, AuthenticationAlgorithm::Rsa2048 => put_rsa(ctx.lend(), Mechanism::Rsa2048Pkcs)?, AuthenticationAlgorithm::Rsa4096 => { - warn!("Key import for RSA not supported"); + warn!("Key import for RSA4096 not supported"); return Err(Status::FunctionNotSupported); } } @@ -178,12 +178,12 @@ fn put_rsa( mechanism: Mechanism, ) -> Result, Status> { let key_data = parse_rsa_template(ctx.data).ok_or_else(|| { - warn!("Unable to parse rsa key"); + warn!("Unable to parse RSA key"); Status::IncorrectDataParameter })?; let key_message: Message = postcard_serialize_bytes(&key_data).map_err(|_err| { - error!("Failed to serialize rsa key: {_err:?}"); + error!("Failed to serialize RSA key: {_err:?}"); Status::UnspecifiedNonpersistentExecutionError })?; let key = try_syscall!(ctx.backend.client_mut().unsafe_inject_key( From 82eb16c7e63f83741f24fa7563d891f9d64c95f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Mon, 17 Oct 2022 18:49:41 +0200 Subject: [PATCH 14/19] Fix tests with latest main --- tests/crypto-gpg-import.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/crypto-gpg-import.rs b/tests/crypto-gpg-import.rs index 3afd09d1..f4a6942a 100644 --- a/tests/crypto-gpg-import.rs +++ b/tests/crypto-gpg-import.rs @@ -835,7 +835,7 @@ fn gpg_rsa() { ], &[ vec![r"\[GNUPG:\] CARDCTRL \d D2760001240103040000000000000000"], - virt::gpg_status(virt::KeyType::RsaNoAut), + virt::gpg_status(virt::KeyType::RsaNoAut, 1), vec![ r"\[GNUPG:\] GET_LINE cardedit.prompt", r"\[GNUPG:\] GET_LINE cardedit.prompt", @@ -844,7 +844,7 @@ fn gpg_rsa() { r"\[GNUPG:\] GET_LINE cardedit.prompt", ], virt::gpg_inquire_pin(), - virt::gpg_status(virt::KeyType::RsaNone), + virt::gpg_status(virt::KeyType::RsaNone, 0), vec![r"\[GNUPG:\] GET_LINE cardedit.prompt"], ] .into_iter() From d73011d2199f1c6c73f107e5f4a7569988847c83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Wed, 16 Nov 2022 10:54:13 +0100 Subject: [PATCH 15/19] Make RSA functionnality optionnal --- Cargo.toml | 1 + Makefile | 4 ++-- tests/crypto-gpg-import.rs | 2 ++ tests/crypto-gpg.rs | 2 ++ tests/crypto-sequoia.rs | 1 + 5 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5333b290..04df55f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,7 @@ hex = { version = "0.4", features = ["serde"] } [features] std = [] virtual = ["std", "vpicc"] +rsa2048 = ["trussed/rsa2048"] # used for delog log-all = [] diff --git a/Makefile b/Makefile index 399d63f3..8afa0b75 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ FUZZ_DURATION?="0" .PHONY: check check: cargo check --all-features --all-targets --workspace - cargo check --no-default-features + cargo check --no-default-features --all-targets cargo clippy --all-features --all-targets -- --deny warnings cargo fmt -- --check RUSTDOCFLAGS='-Dwarnings' cargo doc --all-features --package opcard @@ -24,7 +24,7 @@ fix: .PHONY: test test: - cargo test --features virtual + cargo test --features virtual,rsa2048 .PHONY: fuzz fuzz: fuzz-corpus diff --git a/tests/crypto-gpg-import.rs b/tests/crypto-gpg-import.rs index f4a6942a..12b9b6e1 100644 --- a/tests/crypto-gpg-import.rs +++ b/tests/crypto-gpg-import.rs @@ -570,6 +570,7 @@ fn gpg_p256() { }); } +#[cfg(feature = "rsa2048")] fn gpg_rsa() { with_vsc(|| { let file_number: u32 = rand::rngs::OsRng.gen(); @@ -861,6 +862,7 @@ fn gpg_rsa() { #[test] fn gpg_crypto_import() { + #[cfg(feature = "rsa2048")] gpg_rsa(); gpg_255(); gpg_p256(); diff --git a/tests/crypto-gpg.rs b/tests/crypto-gpg.rs index fdf2ed9d..ce6d508e 100644 --- a/tests/crypto-gpg.rs +++ b/tests/crypto-gpg.rs @@ -510,6 +510,7 @@ fn gpg_p256() { }); } +#[cfg(feature = "rsa2048")] fn gpg_rsa() { with_vsc(|| { let file_number: u32 = rand::rngs::OsRng.gen(); @@ -733,6 +734,7 @@ fn gpg_rsa() { #[test] fn gpg_crypto() { + #[cfg(feature = "rsa2048")] gpg_rsa(); gpg_255(); gpg_p256(); diff --git a/tests/crypto-sequoia.rs b/tests/crypto-sequoia.rs index 22c5ea2a..cfe982ef 100644 --- a/tests/crypto-sequoia.rs +++ b/tests/crypto-sequoia.rs @@ -18,6 +18,7 @@ use test_log::test; #[test] fn sequoia_gen_key() { + #[cfg(feature = "rsa2048")] virt::with_vsc(|| { let mut cards = PcscBackend::cards(None).unwrap(); let mut pgp = OpenPgp::new(cards.pop().unwrap()); From 849cd48ba08750aa66f9af54f5d4627182bfd65b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Wed, 16 Nov 2022 11:28:16 +0100 Subject: [PATCH 16/19] Return "FuntionNotSupported" for RSA operations when Rsa is disabled --- src/command/gen.rs | 20 ++++++++++++++++++++ src/command/private_key_template.rs | 26 ++++++++++++++++++++------ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/command/gen.rs b/src/command/gen.rs index d031aceb..e68b97bf 100644 --- a/src/command/gen.rs +++ b/src/command/gen.rs @@ -82,6 +82,7 @@ pub fn aut( } } +#[cfg(feature = "rsa2048")] fn gen_rsa_key( ctx: LoadedContext<'_, R, T>, key: KeyType, @@ -257,6 +258,7 @@ fn read_ec_key( ctx.reply.prepend_len(offset) } +#[cfg(feature = "rsa2048")] fn read_rsa_key( mut ctx: LoadedContext<'_, R, T>, key_id: KeyId, @@ -303,3 +305,21 @@ fn read_rsa_key( syscall!(client.delete(public_key)); Ok(()) } + +#[cfg(not(feature = "rsa2048"))] +fn gen_rsa_key( + _ctx: LoadedContext<'_, R, T>, + _key: KeyType, + _mechanism: Mechanism, +) -> Result<(), Status> { + Err(Status::FunctionNotSupported) +} + +#[cfg(not(feature = "rsa2048"))] +fn read_rsa_key( + _ctx: LoadedContext<'_, R, T>, + _key_id: KeyId, + _mechanism: Mechanism, +) -> Result<(), Status> { + Err(Status::FunctionNotSupported) +} diff --git a/src/command/private_key_template.rs b/src/command/private_key_template.rs index 33b44a3d..046b809e 100644 --- a/src/command/private_key_template.rs +++ b/src/command/private_key_template.rs @@ -2,17 +2,16 @@ // SPDX-License-Identifier: LGPL-3.0-only use iso7816::Status; -use trussed::types::{KeyId, KeySerialization, Location, Mechanism, Message, RsaCrtImportFormat}; -use trussed::{postcard_serialize_bytes, syscall, try_syscall}; +use trussed::types::{KeyId, KeySerialization, Location, Mechanism}; +use trussed::{syscall, try_syscall}; use crate::card::LoadedContext; use crate::state::KeyOrigin; -use crate::tlv::{get_do, take_len}; +use crate::tlv::get_do; use crate::types::*; const PRIVATE_KEY_TEMPLATE_DO: u16 = 0x4D; const CONCATENATION_KEY_DATA_DO: u16 = 0x5F48; -const TEMPLATE_DO: u16 = 0x7F48; // § 4.4.3.12 pub fn put_private_key_template( @@ -146,7 +145,11 @@ fn put_ec( Ok(Some(key)) } -fn parse_rsa_template(data: &[u8]) -> Option> { +#[cfg(feature = "rsa2048")] +fn parse_rsa_template(data: &[u8]) -> Option> { + use crate::tlv::take_len; + const TEMPLATE_DO: u16 = 0x7F48; + let mut template = get_do(&[PRIVATE_KEY_TEMPLATE_DO, TEMPLATE_DO], data)?; let mut res = [(0, 0); 6]; let mut acc = 0; @@ -163,7 +166,7 @@ fn parse_rsa_template(data: &[u8]) -> Option> { } let key_data = get_do(&[PRIVATE_KEY_TEMPLATE_DO, CONCATENATION_KEY_DATA_DO], data)?; - Some(RsaCrtImportFormat { + Some(trussed::types::RsaCrtImportFormat { e: key_data.get(res[0].0..res[0].1)?, p: key_data.get(res[1].0..res[1].1)?, q: key_data.get(res[2].0..res[2].1)?, @@ -173,10 +176,13 @@ fn parse_rsa_template(data: &[u8]) -> Option> { }) } +#[cfg(feature = "rsa2048")] fn put_rsa( ctx: LoadedContext<'_, R, T>, mechanism: Mechanism, ) -> Result, Status> { + use trussed::{postcard_serialize_bytes, types::Message}; + let key_data = parse_rsa_template(ctx.data).ok_or_else(|| { warn!("Unable to parse RSA key"); Status::IncorrectDataParameter @@ -199,3 +205,11 @@ fn put_rsa( .key; Ok(Some(key)) } + +#[cfg(not(feature = "rsa2048"))] +fn put_rsa( + _ctx: LoadedContext<'_, R, T>, + _mechanism: Mechanism, +) -> Result, Status> { + Err(Status::FunctionNotSupported) +} From d2a669651bfa325ad2004e79cb4887e693ef8457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Thu, 17 Nov 2022 17:09:48 +0100 Subject: [PATCH 17/19] Add support for Rsa4096 --- Cargo.toml | 7 + Makefile | 2 +- src/command/gen.rs | 30 +-- src/command/private_key_template.rs | 19 +- src/command/pso.rs | 25 +-- tests/crypto-gpg-import.rs | 298 +++++++++++++++++++++++++++- tests/crypto-gpg.rs | 256 +++++++++++++++++++++++- tests/crypto-sequoia.rs | 53 +++++ tests/virt/mod.rs | 16 ++ 9 files changed, 646 insertions(+), 60 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 04df55f9..eb6c4d8a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,7 @@ hex = { version = "0.4", features = ["serde"] } std = [] virtual = ["std", "vpicc"] rsa2048 = ["trussed/rsa2048"] +rsa4096 = ["rsa2048", "trussed/rsa4096"] # used for delog log-all = [] @@ -72,3 +73,9 @@ p256-cortex-m4 = { git = "https://github.com/sosthene-nitrokey/p256-cortex-m4.gi [package.metadata.docs.rs] all-features = true + +[profile.dev.package.rsa] +opt-level = 2 + +[profile.dev.package.num-bigint-dig] +opt-level = 2 diff --git a/Makefile b/Makefile index 8afa0b75..41eb1446 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ fix: .PHONY: test test: - cargo test --features virtual,rsa2048 + cargo test --features virtual,rsa2048,rsa4096 .PHONY: fuzz fuzz: fuzz-corpus diff --git a/src/command/gen.rs b/src/command/gen.rs index e68b97bf..9260eed2 100644 --- a/src/command/gen.rs +++ b/src/command/gen.rs @@ -37,9 +37,8 @@ pub fn sign( SignatureAlgorithm::Rsa2048 => { gen_rsa_key(ctx.lend(), KeyType::Sign, Mechanism::Rsa2048Pkcs) } - _ => { - error!("Unimplemented operation"); - Err(Status::ConditionsOfUseNotSatisfied) + SignatureAlgorithm::Rsa4096 => { + gen_rsa_key(ctx.lend(), KeyType::Sign, Mechanism::Rsa4096Pkcs) } } } @@ -55,9 +54,8 @@ pub fn dec( DecryptionAlgorithm::Rsa2048 => { gen_rsa_key(ctx.lend(), KeyType::Dec, Mechanism::Rsa2048Pkcs) } - _ => { - error!("Unimplemented operation"); - Err(Status::ConditionsOfUseNotSatisfied) + DecryptionAlgorithm::Rsa4096 => { + gen_rsa_key(ctx.lend(), KeyType::Dec, Mechanism::Rsa4096Pkcs) } } } @@ -75,9 +73,8 @@ pub fn aut( AuthenticationAlgorithm::Rsa2048 => { gen_rsa_key(ctx.lend(), KeyType::Aut, Mechanism::Rsa2048Pkcs) } - _ => { - error!("Unimplemented operation"); - Err(Status::ConditionsOfUseNotSatisfied) + AuthenticationAlgorithm::Rsa4096 => { + gen_rsa_key(ctx.lend(), KeyType::Aut, Mechanism::Rsa4096Pkcs) } } } @@ -160,10 +157,7 @@ pub fn read_sign( SignatureAlgorithm::Ed255 => read_ec_key(ctx.lend(), key_id, CurveAlgo::Ed255), SignatureAlgorithm::EcDsaP256 => read_ec_key(ctx.lend(), key_id, CurveAlgo::EcDsaP256), SignatureAlgorithm::Rsa2048 => read_rsa_key(ctx.lend(), key_id, Mechanism::Rsa2048Pkcs), - _ => { - error!("Unimplemented operation"); - Err(Status::ConditionsOfUseNotSatisfied) - } + SignatureAlgorithm::Rsa4096 => read_rsa_key(ctx.lend(), key_id, Mechanism::Rsa4096Pkcs), } } @@ -181,10 +175,7 @@ pub fn read_dec( DecryptionAlgorithm::X255 => read_ec_key(ctx.lend(), key_id, CurveAlgo::X255), DecryptionAlgorithm::EcDhP256 => read_ec_key(ctx.lend(), key_id, CurveAlgo::EcDhP256), DecryptionAlgorithm::Rsa2048 => read_rsa_key(ctx.lend(), key_id, Mechanism::Rsa2048Pkcs), - _ => { - error!("Unimplemented operation"); - Err(Status::ConditionsOfUseNotSatisfied) - } + DecryptionAlgorithm::Rsa4096 => read_rsa_key(ctx.lend(), key_id, Mechanism::Rsa4096Pkcs), } } @@ -204,9 +195,8 @@ pub fn read_aut( AuthenticationAlgorithm::Rsa2048 => { read_rsa_key(ctx.lend(), key_id, Mechanism::Rsa2048Pkcs) } - _ => { - error!("Unimplemented operation"); - Err(Status::ConditionsOfUseNotSatisfied) + AuthenticationAlgorithm::Rsa4096 => { + read_rsa_key(ctx.lend(), key_id, Mechanism::Rsa4096Pkcs) } } } diff --git a/src/command/private_key_template.rs b/src/command/private_key_template.rs index 046b809e..c068772d 100644 --- a/src/command/private_key_template.rs +++ b/src/command/private_key_template.rs @@ -41,10 +41,7 @@ pub fn put_sign( SignatureAlgorithm::EcDsaP256 => put_ec(ctx.lend(), CurveAlgo::EcDsaP256)?, SignatureAlgorithm::Ed255 => put_ec(ctx.lend(), CurveAlgo::Ed255)?, SignatureAlgorithm::Rsa2048 => put_rsa(ctx.lend(), Mechanism::Rsa2048Pkcs)?, - SignatureAlgorithm::Rsa4096 => { - warn!("Key import for RSA4096 not supported"); - return Err(Status::FunctionNotSupported); - } + SignatureAlgorithm::Rsa4096 => put_rsa(ctx.lend(), Mechanism::Rsa4096Pkcs)?, } .map(|key_id| (key_id, KeyOrigin::Imported)); let old_key_id = ctx @@ -69,10 +66,7 @@ pub fn put_dec( DecryptionAlgorithm::EcDhP256 => put_ec(ctx.lend(), CurveAlgo::EcDhP256)?, DecryptionAlgorithm::X255 => put_ec(ctx.lend(), CurveAlgo::X255)?, DecryptionAlgorithm::Rsa2048 => put_rsa(ctx.lend(), Mechanism::Rsa2048Pkcs)?, - DecryptionAlgorithm::Rsa4096 => { - warn!("Key import for RSA4096 not supported"); - return Err(Status::FunctionNotSupported); - } + DecryptionAlgorithm::Rsa4096 => put_rsa(ctx.lend(), Mechanism::Rsa4096Pkcs)?, } .map(|key_id| (key_id, KeyOrigin::Imported)); let old_key_id = ctx @@ -97,10 +91,7 @@ pub fn put_aut( AuthenticationAlgorithm::EcDsaP256 => put_ec(ctx.lend(), CurveAlgo::EcDsaP256)?, AuthenticationAlgorithm::Ed255 => put_ec(ctx.lend(), CurveAlgo::Ed255)?, AuthenticationAlgorithm::Rsa2048 => put_rsa(ctx.lend(), Mechanism::Rsa2048Pkcs)?, - AuthenticationAlgorithm::Rsa4096 => { - warn!("Key import for RSA4096 not supported"); - return Err(Status::FunctionNotSupported); - } + AuthenticationAlgorithm::Rsa4096 => put_rsa(ctx.lend(), Mechanism::Rsa4096Pkcs)?, } .map(|key_id| (key_id, KeyOrigin::Imported)); let old_key_id = ctx @@ -181,14 +172,14 @@ fn put_rsa( ctx: LoadedContext<'_, R, T>, mechanism: Mechanism, ) -> Result, Status> { - use trussed::{postcard_serialize_bytes, types::Message}; + use trussed::{postcard_serialize_bytes, types::SerializedKey}; let key_data = parse_rsa_template(ctx.data).ok_or_else(|| { warn!("Unable to parse RSA key"); Status::IncorrectDataParameter })?; - let key_message: Message = postcard_serialize_bytes(&key_data).map_err(|_err| { + let key_message: SerializedKey = postcard_serialize_bytes(&key_data).map_err(|_err| { error!("Failed to serialize RSA key: {_err:?}"); Status::UnspecifiedNonpersistentExecutionError })?; diff --git a/src/command/pso.rs b/src/command/pso.rs index 9192df2d..3fdea4d5 100644 --- a/src/command/pso.rs +++ b/src/command/pso.rs @@ -72,10 +72,7 @@ pub fn sign( sign_ec(ctx, key_id, Mechanism::P256Prehashed) } SignatureAlgorithm::Rsa2048 => sign_rsa(ctx, key_id, Mechanism::Rsa2048Pkcs), - _ => { - error!("Unimplemented operation"); - Err(Status::ConditionsOfUseNotSatisfied) - } + SignatureAlgorithm::Rsa4096 => sign_rsa(ctx, key_id, Mechanism::Rsa4096Pkcs), } } @@ -133,10 +130,7 @@ fn int_aut_key_mecha_uif( AuthenticationAlgorithm::Ed255 => (Mechanism::Ed255, RsaOrEcc::Ecc), AuthenticationAlgorithm::Rsa2048 => (Mechanism::Rsa2048Pkcs, RsaOrEcc::Rsa), - AuthenticationAlgorithm::Rsa4096 => { - error!("RSA is not implemented"); - return Err(Status::ConditionsOfUseNotSatisfied); - } + AuthenticationAlgorithm::Rsa4096 => (Mechanism::Rsa4096Pkcs, RsaOrEcc::Rsa), }, ), KeyRef::Dec => ( @@ -148,10 +142,7 @@ fn int_aut_key_mecha_uif( } DecryptionAlgorithm::EcDhP256 => (Mechanism::P256Prehashed, RsaOrEcc::Ecc), DecryptionAlgorithm::Rsa2048 => (Mechanism::Rsa2048Pkcs, RsaOrEcc::Rsa), - DecryptionAlgorithm::Rsa4096 => { - error!("RSA is not implemented"); - return Err(Status::ConditionsOfUseNotSatisfied); - } + DecryptionAlgorithm::Rsa4096 => (Mechanism::Rsa4096Pkcs, RsaOrEcc::Rsa), }, ), }; @@ -205,10 +196,7 @@ fn decipher_key_mecha_uif( DecryptionAlgorithm::X255 => (Mechanism::X255, RsaOrEcc::Ecc), DecryptionAlgorithm::EcDhP256 => (Mechanism::P256, RsaOrEcc::Ecc), DecryptionAlgorithm::Rsa2048 => (Mechanism::Rsa2048Pkcs, RsaOrEcc::Rsa), - DecryptionAlgorithm::Rsa4096 => { - error!("RSA is not implemented"); - return Err(Status::ConditionsOfUseNotSatisfied); - } + DecryptionAlgorithm::Rsa4096 => (Mechanism::Rsa4096Pkcs, RsaOrEcc::Rsa), }, ), KeyRef::Aut => ( @@ -221,10 +209,7 @@ fn decipher_key_mecha_uif( } AuthenticationAlgorithm::Rsa2048 => (Mechanism::Rsa2048Pkcs, RsaOrEcc::Rsa), - AuthenticationAlgorithm::Rsa4096 => { - error!("RSA is not implemented"); - return Err(Status::ConditionsOfUseNotSatisfied); - } + AuthenticationAlgorithm::Rsa4096 => (Mechanism::Rsa4096Pkcs, RsaOrEcc::Rsa), }, ), }; diff --git a/tests/crypto-gpg-import.rs b/tests/crypto-gpg-import.rs index 12b9b6e1..feea7dd9 100644 --- a/tests/crypto-gpg-import.rs +++ b/tests/crypto-gpg-import.rs @@ -361,7 +361,7 @@ fn gpg_p256() { Generate, ); - println!("================ FINISHED GENERATING 25519 KEYS ================"); + println!("================ FINISHED GENERATING P256 KEYS ================"); gnupg_test( &["key *", "keytocard", "2", DEFAULT_PW3, DEFAULT_PW3, "save"], @@ -571,7 +571,7 @@ fn gpg_p256() { } #[cfg(feature = "rsa2048")] -fn gpg_rsa() { +fn gpg_rsa_2048() { with_vsc(|| { let file_number: u32 = rand::rngs::OsRng.gen(); let tmp = format!("/tmp/opcard-tests-{file_number}.gpg"); @@ -860,10 +860,302 @@ fn gpg_rsa() { }); } +#[cfg(feature = "rsa4096")] +fn gpg_rsa_4096() { + with_vsc(|| { + let file_number: u32 = rand::rngs::OsRng.gen(); + let tmp = format!("/tmp/opcard-tests-{file_number}.gpg"); + let encrypted_file = &tmp; + let tmp = format!("/tmp/opcard-tests-{file_number}-sig.gpg"); + let sign_file = &tmp; + let tmp = format!("/tmp/opcard-tests-{file_number}.toml"); + let decrypted_file = &tmp; + let _dropper = FileDropper { + temp_file_name: encrypted_file, + }; + let _dropper = FileDropper { + temp_file_name: sign_file, + }; + let _dropper = FileDropper { + temp_file_name: decrypted_file, + }; + + let tmp = format!("test name{file_number}"); + let temp_name = &tmp; + + let tmp = format!("test{file_number}@email.com"); + let temp_email = &tmp; + + let custom_match = format!( + r"uid:u::::\d{{10}}::[0-9A-F]{{40}}::{temp_name} \(no comment\) <{temp_email}>::::::::::0:" + ); + + let custom_match2 = format!( + r"uid:u::::::::{temp_name} \(no comment\) <{temp_email}>:::.*,mdc,no-ks-modify:1,p::" + ); + + gnupg_test( + &[ + "1", + "4096", + "4096", + "0", + temp_name, + temp_email, + "no comment", + "", + "", + ], + &[ + vec![ + r"\[GNUPG:\] GET_LINE keygen.algo", + r"\[GNUPG:\] GET_LINE keygen.size", + r"\[GNUPG:\] GET_LINE keygen.size", + r"\[GNUPG:\] GET_LINE keygen.valid", + r"\[GNUPG:\] GET_LINE keygen.name", + r"\[GNUPG:\] GET_LINE keygen.email", + r"\[GNUPG:\] GET_LINE keygen.comment", + ], + virt::gpg_inquire_pin(), + virt::gpg_inquire_pin(), + vec![ + r"pub:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}:::u:::scESC:::\+:::23::0:", + r"fpr:::::::::[0-9A-F]{40}:", + r"grp:::::::::[0-9A-F]{40}:", + &custom_match, + r"sub:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}::::::e:::\+:::23:", + r"fpr:::::::::[0-9A-F]{40}:", + r"grp:::::::::[0-9A-F]{40}:", + r"\[GNUPG:\] KEY_CREATED B [A-F0-9]{40}", + ], + ] + .into_iter() + .flatten() + .collect::>(), + &[ + r"gpg: revocation certificate stored as '.*\.rev'", + r"gpg: checking the trustdb", + r"gpg: marginals needed: \d completes needed: \d trust model: pgp", + r"gpg: depth:[ 0-9]*valid:[ 0-9]*signed:[ 0-9]*trust: \d*-, \d*q, \d*n, \d*m, \d*f, \d*u", + ], + Generate, + ); + + println!("================ FINISHED GENERATING Rsa4096 KEYS ================"); + + gnupg_test( + &["key *", "keytocard", "2", DEFAULT_PW3, DEFAULT_PW3, "save"], + &[ + vec![ + r"sec:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}:0::u:::sc", + r"fpr:::::::::[0-9A-F]{40}:", + r"ssb:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}:0:::::e", + r"fpr:::::::::[0-9A-F]{40}:", + &custom_match2, + r"\[GNUPG:\] GET_LINE keyedit.prompt", + r"sec:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}:0::u:::sc", + r"fpr:::::::::[0-9A-F]{40}:", + r"ssb:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}:0:::::e", + r"fpr:::::::::[0-9A-F]{40}:", + &custom_match2, + r"\[GNUPG:\] GET_LINE keyedit.prompt", + r"\[GNUPG:\] CARDCTRL 3 D2760001240103040000000000000000", + r"\[GNUPG:\] GET_LINE cardedit.genkeys.storekeytype", + ], + virt::gpg_inquire_pin(), + virt::gpg_inquire_pin(), + vec![ + r"sec:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}:0::u:::sc", + r"fpr:::::::::[0-9A-F]{40}:", + r"ssb:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}:0:::::e", + r"fpr:::::::::[0-9A-F]{40}:", + &custom_match2, + r"\[GNUPG:\] GET_LINE keyedit.prompt", + ], + ] + .into_iter() + .flatten() + .collect::>(), + &[], + EditKey { o: temp_email }, + ); + + println!("================ FINISHED IMPORTING DECRYPTION KEY ================"); + + gnupg_test( + &["keytocard", "y", "1", DEFAULT_PW3, "save"], + &[ + vec![ + r"sec:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}:0::u:::sc", + r"fpr:::::::::[0-9A-F]{40}:", + r"ssb:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}:0:::::e", + r"fpr:::::::::[0-9A-F]{40}:", + &custom_match2, + r"\[GNUPG:\] GET_LINE keyedit.prompt", + r"\[GNUPG:\] GET_BOOL keyedit.keytocard.use_primary", + r"\[GNUPG:\] CARDCTRL 3 D2760001240103040000000000000000", + r"\[GNUPG:\] GET_LINE cardedit.genkeys.storekeytype", + ], + virt::gpg_inquire_pin(), + vec![ + r"sec:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}:0::u:::sc", + r"fpr:::::::::[0-9A-F]{40}:", + r"ssb:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}:0:::::e", + r"fpr:::::::::[0-9A-F]{40}:", + &custom_match2, + r"\[GNUPG:\] GET_LINE keyedit.prompt", + ], + ] + .into_iter() + .flatten() + .collect::>(), + &[], + EditKey { o: temp_email }, + ); + + println!("================ FINISHED IMPORTING Rsa4096 KEYS ================"); + + gnupg_test( + &[], + &[ + r"\[GNUPG:\] BEGIN_ENCRYPTION \d \d", + r"\[GNUPG:\] END_ENCRYPTION", + ], + &[], + Encrypt { + i: "Cargo.toml", + o: encrypted_file, + r: temp_email, + }, + ); + + println!("================ FINISHED ENCRYPTION ================"); + + let custom1 = format!( + r"\[GNUPG:\] USERID_HINT [a-fA-F0-9]{{16}} {temp_name} \(no comment\) <{temp_email}>" + ); + let custom2 = format!(r"{temp_name} \(no comment\) <{temp_email}>"); + + gnupg_test( + &[DEFAULT_PW1], + &[ + vec![ + r"\[GNUPG:\] ENC_TO [a-fA-F0-9]{16} \d* \d*", + &custom1, + r"\[GNUPG:\] NEED_PASSPHRASE [a-fA-F0-9]{16} [a-fA-F0-9]{16} 1 0", + ], + virt::gpg_inquire_pin(), + vec![ + r"\[GNUPG:\] DECRYPTION_KEY [a-fA-F0-9]{40} [a-fA-F0-9]{40} u", + r"\[GNUPG:\] BEGIN_DECRYPTION", + r"\[GNUPG:\] DECRYPTION_INFO \d \d \d", + r"\[GNUPG:\] PLAINTEXT \d* \d* Cargo.toml", + r"\[GNUPG:\] PLAINTEXT_LENGTH \d*", + r"\[GNUPG:\] DECRYPTION_OKAY", + r"\[GNUPG:\] GOODMDC", + r"\[GNUPG:\] END_DECRYPTION", + ], + ] + .into_iter() + .flatten() + .collect::>(), + &[ + r"gpg: encrypted with \d*-bit RSA key, ID [a-fA-F0-9]{16}, created \d{4}-\d\d-\d\d", + &custom2, + ], + Decrypt { + i: encrypted_file, + o: decrypted_file, + }, + ); + + println!("================ FINISHED DECRYPTION ================"); + + gnupg_test( + &[DEFAULT_PW1], + &[ + vec![ + r"\[GNUPG:\] CARDCTRL 3 D2760001240103040000000000000000", + r"\[GNUPG:\] BEGIN_SIGNING H\d*", + &custom1, + r"\[GNUPG:\] NEED_PASSPHRASE [a-fA-F0-9]{16} [a-fA-F0-9]{16} 1 0", + ], + virt::gpg_inquire_pin(), + vec![r"\[GNUPG:\] SIG_CREATED S 1 \d* 00 [a-fA-F0-9]{10} [a-fA-F0-9]{40}"], + ] + .into_iter() + .flatten() + .collect::>(), + &[r#"gpg: using "test\d*@email.com" as default secret key for signing"#], + Sign { + i: "Cargo.toml", + o: sign_file, + s: temp_email, + }, + ); + + println!("================ FINISHED SIGNATURE ================"); + + gnupg_test( + &[], + &[ + r"\[GNUPG:\] NEWSIG test\d*@email.com", + r"\[GNUPG:\] SIG_ID [^ ]* \d{4}-\d\d-\d\d [a-fA-F0-9]{10}", + r"\[GNUPG:\] GOODSIG [a-fA-F0-9]{16} test name\d* \(no comment\) ", + r"\[GNUPG:\] VALIDSIG [a-fA-F0-9]{40} \d{4}-\d\d-\d\d [a-fA-F0-9]{10} \d \d \d 1 \d* 00 [a-fA-F0-9]{40}", + r"\[GNUPG:\] TRUST_ULTIMATE 0 pgp", + ], + &[ + r"gpg: Signature made .*", + r"gpg: using RSA key [a-fA-F0-9]{40}", + r#"gpg: issuer "test\d*@email.com""#, + r#"pg: Good signature from "test name\d* \(no comment\) "#, + ], + Verify { i: sign_file }, + ); + + gnupg_test( + &[ + "admin", + "factory-reset", + "y", + "yes", + "verify", + DEFAULT_PW1, + "quit", + ], + &[ + vec![r"\[GNUPG:\] CARDCTRL \d D2760001240103040000000000000000"], + virt::gpg_status(virt::KeyType::Rsa4096NoAut, 1), + vec![ + r"\[GNUPG:\] GET_LINE cardedit.prompt", + r"\[GNUPG:\] GET_LINE cardedit.prompt", + r"\[GNUPG:\] GET_BOOL cardedit.factory-reset.proceed", + r"\[GNUPG:\] GET_LINE cardedit.factory-reset.really", + r"\[GNUPG:\] GET_LINE cardedit.prompt", + ], + virt::gpg_inquire_pin(), + virt::gpg_status(virt::KeyType::RsaNone, 0), + vec![r"\[GNUPG:\] GET_LINE cardedit.prompt"], + ] + .into_iter() + .flatten() + .collect::>(), + &[ + r"gpg: OpenPGP card no. [0-9A-F]{32} detected", + r"gpg: Note: This command destroys all keys stored on the card!", + ], + EditCard, + ); + }); +} + #[test] fn gpg_crypto_import() { #[cfg(feature = "rsa2048")] - gpg_rsa(); + gpg_rsa_2048(); + #[cfg(feature = "rsa2048")] + gpg_rsa_4096(); gpg_255(); gpg_p256(); } diff --git a/tests/crypto-gpg.rs b/tests/crypto-gpg.rs index ce6d508e..8637a191 100644 --- a/tests/crypto-gpg.rs +++ b/tests/crypto-gpg.rs @@ -26,6 +26,20 @@ fn attr_ec_ask() -> Vec<&'static str> { .collect() } +fn attr_rsa_ask() -> Vec<&'static str> { + iter::repeat( + [ + r"\[GNUPG:\] GET_LINE cardedit.genkeys.algo", + r"\[GNUPG:\] GET_LINE cardedit.genkeys.size", + ] + .into_iter() + .chain(virt::gpg_inquire_pin()), + ) + .take(3) + .flatten() + .collect() +} + const DEFAULT_PW3: &str = "12345678"; const DEFAULT_PW1: &str = "123456"; @@ -511,7 +525,7 @@ fn gpg_p256() { } #[cfg(feature = "rsa2048")] -fn gpg_rsa() { +fn gpg_rsa_2048() { with_vsc(|| { let file_number: u32 = rand::rngs::OsRng.gen(); let tmp = format!("/tmp/opcard-tests-{file_number}.gpg"); @@ -732,10 +746,248 @@ fn gpg_rsa() { }); } +#[cfg(feature = "rsa4096")] +fn gpg_rsa_4096() { + with_vsc(|| { + let file_number: u32 = rand::rngs::OsRng.gen(); + let tmp = format!("/tmp/opcard-tests-{file_number}.gpg"); + let encrypted_file = &tmp; + let tmp = format!("/tmp/opcard-tests-{file_number}-sig.gpg"); + let sign_file = &tmp; + let tmp = format!("/tmp/opcard-tests-{file_number}.toml"); + let decrypted_file = &tmp; + let _dropper = FileDropper { + temp_file_name: encrypted_file, + }; + let _dropper = FileDropper { + temp_file_name: sign_file, + }; + let _dropper = FileDropper { + temp_file_name: decrypted_file, + }; + + let tmp = format!("test name{file_number}"); + let temp_name = &tmp; + + let tmp = format!("test{file_number}@email.com"); + let temp_email = &tmp; + + let custom_match = format!( + r"uid:u::::\d{{10}}::[0-9A-F]{{40}}::{temp_name} \(no comment\) <{temp_email}>::::::::::0:" + ); + + gnupg_test( + &[ + "admin", + "key-attr", + "1", + "4096", + DEFAULT_PW3, + "1", + "4096", + DEFAULT_PW3, + "1", + "4096", + DEFAULT_PW3, + "generate", + "n", + DEFAULT_PW3, + DEFAULT_PW1, + "0", + temp_name, + temp_email, + "no comment", + DEFAULT_PW1, + "quit", + ], + &[ + vec![r"\[GNUPG:\] CARDCTRL \d D2760001240103040000000000000000"], + virt::gpg_status(virt::KeyType::RsaNone,0), + vec![ + r"\[GNUPG:\] GET_LINE cardedit.prompt", + r"\[GNUPG:\] GET_LINE cardedit.prompt", + ], + attr_rsa_ask(), + vec![ + r"\[GNUPG:\] GET_LINE cardedit.prompt", + r"\[GNUPG:\] GET_LINE cardedit.genkeys.backup_enc", + ], + virt::gpg_inquire_pin(), + virt::gpg_inquire_pin(), + vec![ + r"\[GNUPG:\] GET_LINE keygen.valid", + r"\[GNUPG:\] GET_LINE keygen.name", + r"\[GNUPG:\] GET_LINE keygen.email", + r"\[GNUPG:\] GET_LINE keygen.comment", + r"\[GNUPG:\] USERID_HINT [0-9A-F]{16} \[\?\]", + r"\[GNUPG:\] NEED_PASSPHRASE [0-9A-F]{16} [0-9A-F]{16} 1 \d", + ], + virt::gpg_inquire_pin(), + vec![ + r"pub:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}:::u:::scESCA:::D2760001240103040000000000000000:::23::0:", + r"fpr:::::::::[0-9A-F]{40}:", + r"grp:::::::::[0-9A-F]{40}:", + &custom_match, + r"sub:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}::::::a:::D2760001240103040000000000000000:::23:", + r"fpr:::::::::[0-9A-F]{40}:", + r"grp:::::::::[0-9A-F]{40}:", + r"sub:u:\d*:1:[0-9A-F]{16}:[0-9A-F]{10}::::::e:::D2760001240103040000000000000000:::23:", + r"fpr:::::::::[0-9A-F]{40}:", + r"grp:::::::::[0-9A-F]{40}:", + r"\[GNUPG:\] KEY_CREATED B [0-9A-F]{40}", + r"\[GNUPG:\] GET_LINE cardedit.prompt", + ] + ].into_iter().flatten().collect::>(), + &[ + r"gpg: revocation certificate stored as '.*\.rev'", + r"gpg: checking the trustdb", + r"gpg: marginals needed: \d completes needed: \d trust model: pgp", + r"gpg: depth:[ 0-9]*valid:[ 0-9]*signed:[ 0-9]*trust: \d*-, \d*q, \d*n, \d*m, \d*f, \d*u", + ], + EditCard, + ); + + println!("================ FINISHED GENERATING 25519 KEYS ================"); + + gnupg_test( + &[], + &[ + r"\[GNUPG:\] BEGIN_ENCRYPTION \d \d", + r"\[GNUPG:\] END_ENCRYPTION", + ], + &[], + Encrypt { + i: "Cargo.toml", + o: encrypted_file, + r: temp_email, + }, + ); + + println!("================ FINISHED ENCRYPTION ================"); + + let custom1 = format!( + r"\[GNUPG:\] USERID_HINT [a-fA-F0-9]{{16}} {temp_name} \(no comment\) <{temp_email}>" + ); + let custom2 = format!(r"{temp_name} \(no comment\) <{temp_email}>"); + gnupg_test( + &[DEFAULT_PW1], + &[ + vec![ + r"\[GNUPG:\] ENC_TO [a-fA-F0-9]{16} \d* \d*", + &custom1, + r"\[GNUPG:\] NEED_PASSPHRASE [a-fA-F0-9]{16} [a-fA-F0-9]{16} 1 0", + ], + virt::gpg_inquire_pin(), + vec![ + r"\[GNUPG:\] DECRYPTION_KEY [a-fA-F0-9]{40} [a-fA-F0-9]{40} u", + r"\[GNUPG:\] BEGIN_DECRYPTION", + r"\[GNUPG:\] DECRYPTION_INFO \d \d \d", + r"\[GNUPG:\] PLAINTEXT \d* \d* Cargo.toml", + r"\[GNUPG:\] PLAINTEXT_LENGTH \d*", + r"\[GNUPG:\] DECRYPTION_OKAY", + r"\[GNUPG:\] GOODMDC", + r"\[GNUPG:\] END_DECRYPTION", + ], + ] + .into_iter() + .flatten() + .collect::>(), + &[ + r"gpg: encrypted with \d*-bit RSA key, ID [a-fA-F0-9]{16}, created \d{4}-\d\d-\d\d", + &custom2, + ], + Decrypt { + i: encrypted_file, + o: decrypted_file, + }, + ); + + println!("================ FINISHED DECRYPTION ================"); + + gnupg_test( + &[DEFAULT_PW1], + &[ + vec![ + r"\[GNUPG:\] CARDCTRL 3 D2760001240103040000000000000000", + r"\[GNUPG:\] BEGIN_SIGNING H\d*", + &custom1, + r"\[GNUPG:\] NEED_PASSPHRASE [a-fA-F0-9]{16} [a-fA-F0-9]{16} 1 0", + ], + virt::gpg_inquire_pin(), + vec![r"\[GNUPG:\] SIG_CREATED S 1 \d* 00 [a-fA-F0-9]{10} [a-fA-F0-9]{40}"], + ] + .into_iter() + .flatten() + .collect::>(), + &[r#"gpg: using "test\d*@email.com" as default secret key for signing"#], + Sign { + i: "Cargo.toml", + o: sign_file, + s: temp_email, + }, + ); + + println!("================ FINISHED SIGNATURE ================"); + + gnupg_test( + &[], + &[ + r"\[GNUPG:\] NEWSIG test\d*@email.com", + r"\[GNUPG:\] SIG_ID [^ ]* \d{4}-\d\d-\d\d [a-fA-F0-9]{10}", + r"\[GNUPG:\] GOODSIG [a-fA-F0-9]{16} test name\d* \(no comment\) ", + r"\[GNUPG:\] VALIDSIG [a-fA-F0-9]{40} \d{4}-\d\d-\d\d [a-fA-F0-9]{10} \d \d \d 1 \d* 00 [a-fA-F0-9]{40}", + r"\[GNUPG:\] TRUST_ULTIMATE 0 pgp", + ], + &[ + r"gpg: Signature made .*", + r"gpg: using RSA key [a-fA-F0-9]{40}", + r#"gpg: issuer "test\d*@email.com""#, + r#"pg: Good signature from "test name\d* \(no comment\) "#, + ], + Verify { i: sign_file }, + ); + gnupg_test( + &[ + "admin", + "factory-reset", + "y", + "yes", + "verify", + DEFAULT_PW1, + "quit", + ], + &[ + vec![r"\[GNUPG:\] CARDCTRL \d D2760001240103040000000000000000"], + virt::gpg_status(virt::KeyType::Rsa4096, 5), + vec![ + r"\[GNUPG:\] GET_LINE cardedit.prompt", + r"\[GNUPG:\] GET_LINE cardedit.prompt", + r"\[GNUPG:\] GET_BOOL cardedit.factory-reset.proceed", + r"\[GNUPG:\] GET_LINE cardedit.factory-reset.really", + r"\[GNUPG:\] GET_LINE cardedit.prompt", + ], + virt::gpg_inquire_pin(), + virt::gpg_status(virt::KeyType::RsaNone, 0), + vec![r"\[GNUPG:\] GET_LINE cardedit.prompt"], + ] + .into_iter() + .flatten() + .collect::>(), + &[ + r"gpg: OpenPGP card no. [0-9A-F]{32} detected", + r"gpg: Note: This command destroys all keys stored on the card!", + ], + EditCard, + ); + }); +} + #[test] fn gpg_crypto() { #[cfg(feature = "rsa2048")] - gpg_rsa(); + gpg_rsa_2048(); + #[cfg(feature = "rsa4096")] + gpg_rsa_4096(); gpg_255(); gpg_p256(); } diff --git a/tests/crypto-sequoia.rs b/tests/crypto-sequoia.rs index cfe982ef..d841f606 100644 --- a/tests/crypto-sequoia.rs +++ b/tests/crypto-sequoia.rs @@ -70,6 +70,59 @@ fn sequoia_gen_key() { let mut decryptor = user_card.decryptor_from_public(dec_pubk, &|| {}); assert_eq!(session, decryptor.decrypt(&ciphertext, None).unwrap()); }); + + #[cfg(feature = "rsa4096")] + virt::with_vsc(|| { + let mut cards = PcscBackend::cards(None).unwrap(); + let mut pgp = OpenPgp::new(cards.pop().unwrap()); + let mut open = Open::new(pgp.transaction().unwrap()).unwrap(); + open.verify_admin(b"12345678").unwrap(); + let mut admin = open.admin_card().unwrap(); + + let (material, gendate) = admin + .generate_key_simple(KeyType::Decryption, Some(AlgoSimple::RSA4k)) + .unwrap(); + let dec_pubk = + public_key_material_to_key(&material, KeyType::Decryption, &gendate, None, None) + .unwrap(); + + let (material, gendate) = admin + .generate_key_simple(KeyType::Authentication, Some(AlgoSimple::RSA4k)) + .unwrap(); + let aut_pubk = + public_key_material_to_key(&material, KeyType::Authentication, &gendate, None, None) + .unwrap(); + + let (material, gendate) = admin + .generate_key_simple(KeyType::Signing, Some(AlgoSimple::RSA4k)) + .unwrap(); + let pubk = + public_key_material_to_key(&material, KeyType::Signing, &gendate, None, None).unwrap(); + + open.verify_user_for_signing(b"123456").unwrap(); + let mut sign_card = open.signing_card().unwrap(); + let mut signer = sign_card.signer_from_public(pubk.clone(), &|| {}); + let data = [1; 64]; + let signature = signer.sign(HashAlgorithm::SHA512, &data).unwrap(); + assert!(pubk + .verify(&signature, HashAlgorithm::SHA512, &data) + .is_ok()); + open.verify_user(b"123456").unwrap(); + let mut user_card = open.user_card().unwrap(); + let mut authenticator = user_card.authenticator_from_public(aut_pubk.clone(), &|| {}); + let data = [2; 64]; + let signature = authenticator.sign(HashAlgorithm::SHA512, &data).unwrap(); + assert!(aut_pubk + .verify(&signature, HashAlgorithm::SHA512, &data) + .is_ok()); + + let mut session = SessionKey::new(19); + session[0] = 7; + let ciphertext = dec_pubk.encrypt(&session).unwrap(); + let mut decryptor = user_card.decryptor_from_public(dec_pubk, &|| {}); + assert_eq!(session, decryptor.decrypt(&ciphertext, None).unwrap()); + }); + virt::with_vsc(|| { let mut cards = PcscBackend::cards(None).unwrap(); let mut pgp = OpenPgp::new(cards.pop().unwrap()); diff --git a/tests/virt/mod.rs b/tests/virt/mod.rs index a3f5ca53..4fe8a579 100644 --- a/tests/virt/mod.rs +++ b/tests/virt/mod.rs @@ -71,7 +71,9 @@ pub fn with_vsc R, R>(f: F) -> R { pub enum KeyType { RsaNone, Rsa, + Rsa4096, RsaNoAut, + Rsa4096NoAut, Cv25519, Cv25519NoAut, P256, @@ -116,6 +118,13 @@ pub fn gpg_status(key: KeyType, sign_count: usize) -> Vec<&'static str> { "fpr:[0-9a-zA-Z]{40}:[0-9a-zA-Z]{40}:[0-9a-zA-Z]{40}:", "grp:[0-9a-zA-Z]{40}:[0-9a-zA-Z]{40}:[0-9a-zA-Z]{40}:", ), + KeyType::Rsa4096 => ( + r"keyattr:1:1:4096:", + r"keyattr:2:1:4096:", + r"keyattr:3:1:4096:", + "fpr:[0-9a-zA-Z]{40}:[0-9a-zA-Z]{40}:[0-9a-zA-Z]{40}:", + "grp:[0-9a-zA-Z]{40}:[0-9a-zA-Z]{40}:[0-9a-zA-Z]{40}:", + ), KeyType::RsaNoAut => ( r"keyattr:1:1:2048:", r"keyattr:2:1:2048:", @@ -123,6 +132,13 @@ pub fn gpg_status(key: KeyType, sign_count: usize) -> Vec<&'static str> { "fpr:[0-9a-zA-Z]{40}:[0-9a-zA-Z]{40}::", "grp:[0-9a-zA-Z]{40}:[0-9a-zA-Z]{40}:[0]{40}:", ), + KeyType::Rsa4096NoAut => ( + r"keyattr:1:1:4096:", + r"keyattr:2:1:4096:", + r"keyattr:3:1:2048:", + "fpr:[0-9a-zA-Z]{40}:[0-9a-zA-Z]{40}::", + "grp:[0-9a-zA-Z]{40}:[0-9a-zA-Z]{40}:[0]{40}:", + ), KeyType::RsaNone => ( r"keyattr:1:1:2048:", r"keyattr:2:1:2048:", From 937ee57183a470859ee5d8d3f9c95c2d17241984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Fri, 18 Nov 2022 14:26:13 +0100 Subject: [PATCH 18/19] Put Rsa4096 keygen behind a flag --- Cargo.toml | 1 + Makefile | 1 + src/command/gen.rs | 15 ++++++++++++--- tests/crypto-gpg.rs | 5 +++-- tests/crypto-sequoia.rs | 2 +- 5 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index eb6c4d8a..755c855e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,7 @@ std = [] virtual = ["std", "vpicc"] rsa2048 = ["trussed/rsa2048"] rsa4096 = ["rsa2048", "trussed/rsa4096"] +rsa4096-gen = ["rsa4096"] # used for delog log-all = [] diff --git a/Makefile b/Makefile index 41eb1446..a752d3bb 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,7 @@ fix: .PHONY: test test: + cargo test --features virtual,rsa2048,rsa4096-gen gpg_crypto cargo test --features virtual,rsa2048,rsa4096 .PHONY: fuzz diff --git a/src/command/gen.rs b/src/command/gen.rs index 9260eed2..5896ffa2 100644 --- a/src/command/gen.rs +++ b/src/command/gen.rs @@ -38,7 +38,10 @@ pub fn sign( gen_rsa_key(ctx.lend(), KeyType::Sign, Mechanism::Rsa2048Pkcs) } SignatureAlgorithm::Rsa4096 => { - gen_rsa_key(ctx.lend(), KeyType::Sign, Mechanism::Rsa4096Pkcs) + #[cfg(feature = "rsa4096-gen")] + return gen_rsa_key(ctx.lend(), KeyType::Sign, Mechanism::Rsa4096Pkcs); + #[cfg(not(feature = "rsa4096-gen"))] + return Err(Status::FunctionNotSupported); } } } @@ -55,7 +58,10 @@ pub fn dec( gen_rsa_key(ctx.lend(), KeyType::Dec, Mechanism::Rsa2048Pkcs) } DecryptionAlgorithm::Rsa4096 => { - gen_rsa_key(ctx.lend(), KeyType::Dec, Mechanism::Rsa4096Pkcs) + #[cfg(feature = "rsa4096-gen")] + return gen_rsa_key(ctx.lend(), KeyType::Dec, Mechanism::Rsa4096Pkcs); + #[cfg(not(feature = "rsa4096-gen"))] + return Err(Status::FunctionNotSupported); } } } @@ -74,7 +80,10 @@ pub fn aut( gen_rsa_key(ctx.lend(), KeyType::Aut, Mechanism::Rsa2048Pkcs) } AuthenticationAlgorithm::Rsa4096 => { - gen_rsa_key(ctx.lend(), KeyType::Aut, Mechanism::Rsa4096Pkcs) + #[cfg(feature = "rsa4096-gen")] + return gen_rsa_key(ctx.lend(), KeyType::Aut, Mechanism::Rsa4096Pkcs); + #[cfg(not(feature = "rsa4096-gen"))] + return Err(Status::FunctionNotSupported); } } } diff --git a/tests/crypto-gpg.rs b/tests/crypto-gpg.rs index 8637a191..abcfe1ef 100644 --- a/tests/crypto-gpg.rs +++ b/tests/crypto-gpg.rs @@ -26,6 +26,7 @@ fn attr_ec_ask() -> Vec<&'static str> { .collect() } +#[cfg(feature = "rsa4096-gen")] fn attr_rsa_ask() -> Vec<&'static str> { iter::repeat( [ @@ -746,7 +747,7 @@ fn gpg_rsa_2048() { }); } -#[cfg(feature = "rsa4096")] +#[cfg(feature = "rsa4096-gen")] fn gpg_rsa_4096() { with_vsc(|| { let file_number: u32 = rand::rngs::OsRng.gen(); @@ -986,7 +987,7 @@ fn gpg_rsa_4096() { fn gpg_crypto() { #[cfg(feature = "rsa2048")] gpg_rsa_2048(); - #[cfg(feature = "rsa4096")] + #[cfg(feature = "rsa4096-gen")] gpg_rsa_4096(); gpg_255(); gpg_p256(); diff --git a/tests/crypto-sequoia.rs b/tests/crypto-sequoia.rs index d841f606..7d85e488 100644 --- a/tests/crypto-sequoia.rs +++ b/tests/crypto-sequoia.rs @@ -71,7 +71,7 @@ fn sequoia_gen_key() { assert_eq!(session, decryptor.decrypt(&ciphertext, None).unwrap()); }); - #[cfg(feature = "rsa4096")] + #[cfg(feature = "rsa4096-gen")] virt::with_vsc(|| { let mut cards = PcscBackend::cards(None).unwrap(); let mut pgp = OpenPgp::new(cards.pop().unwrap()); From f5485a221bd7b2ce80255b7e4c0fab3c1269496f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Fri, 18 Nov 2022 14:55:36 +0100 Subject: [PATCH 19/19] Also test sequoia rsa 4096 keygen --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index a752d3bb..8d681364 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ fix: .PHONY: test test: - cargo test --features virtual,rsa2048,rsa4096-gen gpg_crypto + cargo test --features virtual,rsa2048,rsa4096-gen gpg_crypto,sequoia_gen_key cargo test --features virtual,rsa2048,rsa4096 .PHONY: fuzz @@ -46,7 +46,7 @@ fuzz-cov: .PHONY: tarpaulin tarpaulin: - cargo tarpaulin --features virtual -o Html -o Xml + cargo tarpaulin --features virtual,rsa4096-gen -o Html -o Xml .PHONY: ci ci: check tarpaulin