From 9b5b073cc4764696fb8942366f702350520c1ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Wed, 28 Sep 2022 11:54:49 +0200 Subject: [PATCH 1/8] Add KeyRefs to runtime state --- src/command.rs | 34 ++++++++++++++++++++++++++++++++-- src/state.rs | 23 +++++++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/command.rs b/src/command.rs index 5e28be4c..504c57e7 100644 --- a/src/command.rs +++ b/src/command.rs @@ -6,12 +6,13 @@ mod gen; mod private_key_template; mod pso; +use hex_literal::hex; use iso7816::Status; use crate::card::{Context, LoadedContext, RID}; use crate::error::Error; use crate::state::{ - LifeCycle, State, MAX_GENERIC_LENGTH, MAX_PIN_LENGTH, MIN_LENGTH_ADMIN_PIN, + KeyRef, LifeCycle, State, MAX_GENERIC_LENGTH, MAX_PIN_LENGTH, MIN_LENGTH_ADMIN_PIN, MIN_LENGTH_RESET_CODE, MIN_LENGTH_USER_PIN, }; use crate::tlv; @@ -82,6 +83,7 @@ impl Command { Self::SelectData(occurrence) => select_data(context, *occurrence), Self::GetChallenge(length) => get_challenge(context, *length), Self::ResetRetryCounter(mode) => reset_retry_conter(context.load_state()?, *mode), + Self::ManageSecurityEnvironment(mode) => manage_security_environment(context, *mode), _ => { error!("Command not yet implemented: {:x?}", self); Err(Status::FunctionNotSupported) @@ -299,7 +301,7 @@ impl TryFrom for GenerateAsymmetricKeyPairMode { } } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum ManageSecurityEnvironmentMode { Authentication, Dec, @@ -321,6 +323,7 @@ impl TryFrom for ManageSecurityEnvironmentMode { fn select(context: Context<'_, R, T>) -> Result<(), Status> { if context.data.starts_with(&RID) { context.state.runtime.cur_do = None; + context.state.runtime.keyrefs = Default::default(); Ok(()) } else { info!("Selected application {:x?} not found", context.data); @@ -630,3 +633,30 @@ fn get_challenge( Ok(()) } + +// § 7.2.18 +fn manage_security_environment( + ctx: Context<'_, R, T>, + mode: ManageSecurityEnvironmentMode, +) -> Result<(), Status> { + let key_ref = match ctx.data { + hex!("83 01 02") => KeyRef::Dec, + hex!("83 01 03") => KeyRef::Aut, + _ => { + warn!( + "Manage Security Environment called with invalid reference: {:x?}", + ctx.data + ); + return Err(Status::IncorrectDataParameter); + } + }; + + match mode { + ManageSecurityEnvironmentMode::Dec => ctx.state.runtime.keyrefs.pso_decipher = key_ref, + ManageSecurityEnvironmentMode::Authentication => { + ctx.state.runtime.keyrefs.internal_aut = key_ref + } + } + + todo!() +} diff --git a/src/state.rs b/src/state.rs index 6e855aec..bf720744 100644 --- a/src/state.rs +++ b/src/state.rs @@ -709,12 +709,35 @@ impl Internal { } } +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum KeyRef { + Dec, + Aut, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct KeyRefs { + // We can't use `KeyType` because the Signing key cannot be reassigned + pub pso_decipher: KeyRef, + pub internal_aut: KeyRef, +} + +impl Default for KeyRefs { + fn default() -> KeyRefs { + KeyRefs { + pso_decipher: KeyRef::Dec, + internal_aut: KeyRef::Aut, + } + } +} + #[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct Runtime { pub sign_verified: bool, pub other_verified: bool, pub admin_verified: bool, pub cur_do: Option<(Tag, Occurrence)>, + pub keyrefs: KeyRefs, } /// DOs that can store arbitrary data from the user From 53b58c3cf9de45d2a951af56b8b2d5d3a760f1c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Wed, 28 Sep 2022 12:04:39 +0200 Subject: [PATCH 2/8] Remove outdated #[allow(unused)] --- src/types.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/types.rs b/src/types.rs index 1b28d8b5..3db0532c 100644 --- a/src/types.rs +++ b/src/types.rs @@ -221,7 +221,6 @@ impl TryFrom<&[u8]> for AuthenticationAlgorithm { } #[derive(Clone, Debug, Copy)] -#[allow(unused)] pub enum KeyType { Sign, Dec, From 2746f195d4fa41fdf142ea7e8e9b37257f518375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Wed, 28 Sep 2022 14:14:40 +0200 Subject: [PATCH 3/8] Fix typo --- src/command/pso.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/command/pso.rs b/src/command/pso.rs index 8d525530..faaa8438 100644 --- a/src/command/pso.rs +++ b/src/command/pso.rs @@ -113,7 +113,7 @@ pub fn decipher( ctx: LoadedContext<'_, R, T>, ) -> Result<(), Status> { let key_id = ctx.state.internal.key_id(KeyType::Dec).ok_or_else(|| { - warn!("Attempt to authenticat without a key set"); + warn!("Attempt to decrypt without a key set"); Status::KeyReferenceNotFound })?; if !ctx.state.runtime.other_verified { From d07c4bc53f62d12cf3ba460fcb06b59144479f24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Wed, 28 Sep 2022 15:15:32 +0200 Subject: [PATCH 4/8] Use keyrefs in PSO:DECIPHER --- src/command/pso.rs | 97 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 67 insertions(+), 30 deletions(-) diff --git a/src/command/pso.rs b/src/command/pso.rs index faaa8438..d83f0b94 100644 --- a/src/command/pso.rs +++ b/src/command/pso.rs @@ -7,6 +7,7 @@ use trussed::try_syscall; use trussed::types::*; use crate::card::LoadedContext; +use crate::state::KeyRef; use crate::tlv::get_do; use crate::types::*; @@ -14,18 +15,29 @@ fn check_uif( ctx: LoadedContext<'_, R, T>, key: KeyType, ) -> Result<(), Status> { - if ctx.state.internal.uif(key).is_enabled() - && !ctx - .backend - .confirm_user_present() - .map_err(|_| Status::UnspecifiedNonpersistentExecutionError)? - { + if ctx.state.internal.uif(key).is_enabled() { + prompt_uif(ctx) + } else { + Ok(()) + } +} + +fn prompt_uif( + ctx: LoadedContext<'_, R, T>, +) -> Result<(), Status> { + let success = ctx + .backend + .confirm_user_present() + .map_err(|_| Status::UnspecifiedNonpersistentExecutionError)?; + if !success { warn!("User presence confirmation timed out"); // FIXME SecurityRelatedIssues (0x6600 is not available?) - return Err(Status::SecurityStatusNotSatisfied); + Err(Status::SecurityStatusNotSatisfied) + } else { + Ok(()) } - Ok(()) } + // § 7.2.10 pub fn sign( mut ctx: LoadedContext<'_, R, T>, @@ -108,38 +120,63 @@ pub fn internal_authenticate( } } +pub fn decipher_key_mecha_uif( + ctx: LoadedContext<'_, R, T>, +) -> Result<(KeyId, Mechanism, bool), Status> { + let (key_type, mechanism) = match ctx.state.runtime.keyrefs.pso_decipher { + KeyRef::Dec => ( + KeyType::Dec, + match ctx.state.internal.dec_alg() { + DecryptionAlgorithm::X255 => Mechanism::X255, + DecryptionAlgorithm::EcDhP256 => Mechanism::P256, + DecryptionAlgorithm::Rsa2k | DecryptionAlgorithm::Rsa4k => { + error!("RSA is not implemented"); + return Err(Status::ConditionsOfUseNotSatisfied); + } + }, + ), + KeyRef::Aut => ( + KeyType::Aut, + match ctx.state.internal.aut_alg() { + AuthenticationAlgorithm::EcDsaP256 => Mechanism::P256, + AuthenticationAlgorithm::Ed255 => { + warn!("Attempt to decipher with Ed255 key"); + return Err(Status::ConditionsOfUseNotSatisfied); + } + + AuthenticationAlgorithm::Rsa2k | AuthenticationAlgorithm::Rsa4k => { + error!("RSA is not implemented"); + return Err(Status::ConditionsOfUseNotSatisfied); + } + }, + ), + }; + + Ok(( + ctx.state.internal.key_id(key_type).ok_or_else(|| { + warn!("Attempt to decrypt without a key set"); + Status::KeyReferenceNotFound + })?, + mechanism, + ctx.state.internal.uif(key_type).is_enabled(), + )) +} + // § 7.2.11 pub fn decipher( - ctx: LoadedContext<'_, R, T>, + mut ctx: LoadedContext<'_, R, T>, ) -> Result<(), Status> { - let key_id = ctx.state.internal.key_id(KeyType::Dec).ok_or_else(|| { - warn!("Attempt to decrypt without a key set"); - Status::KeyReferenceNotFound - })?; if !ctx.state.runtime.other_verified { warn!("Attempt to sign without PW1 verified"); return Err(Status::SecurityStatusNotSatisfied); } - if ctx.state.internal.uif(KeyType::Dec).is_enabled() - && !ctx - .backend - .confirm_user_present() - .map_err(|_| Status::UnspecifiedNonpersistentExecutionError)? - { - warn!("User presence confirmation timed out"); - // FIXME SecurityRelatedIssues (0x6600 is not available?) - return Err(Status::SecurityStatusNotSatisfied); + let (key_id, mechanism, uif) = decipher_key_mecha_uif(ctx.lend())?; + if uif { + prompt_uif(ctx.lend())?; } - match ctx.state.internal.dec_alg() { - DecryptionAlgorithm::X255 => decrypt_ec(ctx, key_id, Mechanism::X255), - DecryptionAlgorithm::EcDhP256 => decrypt_ec(ctx, key_id, Mechanism::P256), - _ => { - error!("Unimplemented operation"); - Err(Status::ConditionsOfUseNotSatisfied) - } - } + decrypt_ec(ctx, key_id, mechanism) } fn decrypt_ec( From 6df2811febc1b068acc38af5a53a24c620411355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Wed, 28 Sep 2022 15:35:38 +0200 Subject: [PATCH 5/8] Use KeyRefs in Internal Authenticate --- src/command/pso.rs | 72 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 18 deletions(-) diff --git a/src/command/pso.rs b/src/command/pso.rs index d83f0b94..0dd7207d 100644 --- a/src/command/pso.rs +++ b/src/command/pso.rs @@ -90,34 +90,70 @@ fn sign_ec( ctx.reply.expand(&signature) } +pub fn int_aut_key_mecha_uif( + ctx: LoadedContext<'_, R, T>, +) -> Result<(KeyId, Mechanism, bool), Status> { + let (key_type, mechanism) = match ctx.state.runtime.keyrefs.internal_aut { + KeyRef::Aut => ( + KeyType::Aut, + match ctx.state.internal.aut_alg() { + AuthenticationAlgorithm::EcDsaP256 => Mechanism::P256Prehashed, + AuthenticationAlgorithm::Ed255 => Mechanism::Ed255, + + AuthenticationAlgorithm::Rsa2k | AuthenticationAlgorithm::Rsa4k => { + error!("RSA is not implemented"); + return Err(Status::ConditionsOfUseNotSatisfied); + } + }, + ), + KeyRef::Dec => ( + KeyType::Dec, + match ctx.state.internal.dec_alg() { + DecryptionAlgorithm::X255 => { + warn!("Attempt to authenticate with X25519 key"); + return Err(Status::ConditionsOfUseNotSatisfied); + } + DecryptionAlgorithm::EcDhP256 => Mechanism::P256Prehashed, + DecryptionAlgorithm::Rsa2k | DecryptionAlgorithm::Rsa4k => { + error!("RSA is not implemented"); + return Err(Status::ConditionsOfUseNotSatisfied); + } + }, + ), + }; + + if mechanism == Mechanism::P256Prehashed && ctx.data.len() != 32 { + warn!( + "Attempt to sign with P256 with data length != 32: {}", + ctx.data.len() + ); + return Err(Status::ConditionsOfUseNotSatisfied); + } + + Ok(( + ctx.state.internal.key_id(key_type).ok_or_else(|| { + warn!("Attempt to INTERNAL AUTHENTICATE without a key set"); + Status::KeyReferenceNotFound + })?, + mechanism, + ctx.state.internal.uif(key_type).is_enabled(), + )) +} // § 7.2.13 pub fn internal_authenticate( mut ctx: LoadedContext<'_, R, T>, ) -> Result<(), Status> { - let key_id = ctx.state.internal.key_id(KeyType::Aut).ok_or_else(|| { - warn!("Attempt to authenticate without a key set"); - Status::KeyReferenceNotFound - })?; if !ctx.state.runtime.other_verified { warn!("Attempt to sign without PW1 verified"); return Err(Status::SecurityStatusNotSatisfied); } - check_uif(ctx.lend(), KeyType::Aut)?; - - match ctx.state.internal.aut_alg() { - AuthenticationAlgorithm::Ed255 => sign_ec(ctx, key_id, Mechanism::Ed255), - AuthenticationAlgorithm::EcDsaP256 => { - if ctx.data.len() != 32 { - return Err(Status::ConditionsOfUseNotSatisfied); - } - sign_ec(ctx, key_id, Mechanism::P256Prehashed) - } - _ => { - error!("Unimplemented operation"); - Err(Status::ConditionsOfUseNotSatisfied) - } + let (key_id, mechanism, uif) = int_aut_key_mecha_uif(ctx.lend())?; + if uif { + prompt_uif(ctx.lend())?; } + + sign_ec(ctx, key_id, mechanism) } pub fn decipher_key_mecha_uif( From 3cbef5dec11ffcdf80891c5494565d31c73d2764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Wed, 28 Sep 2022 16:52:52 +0200 Subject: [PATCH 6/8] Remove todo --- src/command.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/command.rs b/src/command.rs index 504c57e7..a5400191 100644 --- a/src/command.rs +++ b/src/command.rs @@ -650,6 +650,7 @@ fn manage_security_environment( return Err(Status::IncorrectDataParameter); } }; + info!("MANAGE SECURITY ENVIRONMENT: mode = {mode:?}, ref = {key_ref:?}"); match mode { ManageSecurityEnvironmentMode::Dec => ctx.state.runtime.keyrefs.pso_decipher = key_ref, @@ -657,6 +658,5 @@ fn manage_security_environment( ctx.state.runtime.keyrefs.internal_aut = key_ref } } - - todo!() + Ok(()) } From 0c31c6b6b75d75ddad8e0c4cbfddfc42b96865b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Thu, 29 Sep 2022 15:20:50 +0200 Subject: [PATCH 7/8] Add tests for MANAGE SECURITY ENVIRONMENT --- tests/crypto-sequoia.rs | 54 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/tests/crypto-sequoia.rs b/tests/crypto-sequoia.rs index 2be84f38..fb5643ab 100644 --- a/tests/crypto-sequoia.rs +++ b/tests/crypto-sequoia.rs @@ -30,6 +30,9 @@ fn sequoia_gen_key() { let dec_pubk = public_key_material_to_key(&material, KeyType::Decryption, &gendate, None, None) .unwrap(); + let dec_pubk_aut = + public_key_material_to_key(&material, KeyType::Authentication, &gendate, None, None) + .unwrap(); let (material, gendate) = admin .generate_key_simple(KeyType::Authentication, Some(AlgoSimple::NIST256)) @@ -37,6 +40,9 @@ fn sequoia_gen_key() { let aut_pubk = public_key_material_to_key(&material, KeyType::Authentication, &gendate, None, None) .unwrap(); + let aut_pubk_dec = + public_key_material_to_key(&material, KeyType::Decryption, &gendate, None, None) + .unwrap(); let (material, gendate) = admin .generate_key_simple(KeyType::Signing, Some(AlgoSimple::NIST256)) @@ -58,6 +64,9 @@ fn sequoia_gen_key() { let mut authenticator = user_card.authenticator_from_public(aut_pubk.clone(), &|| {}); let data = [2; 32]; let signature = authenticator.sign(HashAlgorithm::SHA256, &data).unwrap(); + assert!(dec_pubk_aut + .verify(&signature, HashAlgorithm::SHA256, &data) + .is_err()); assert!(aut_pubk .verify(&signature, HashAlgorithm::SHA256, &data) .is_ok()); @@ -67,6 +76,28 @@ fn sequoia_gen_key() { let ciphertext = dec_pubk.encrypt(&session).unwrap(); let mut decryptor = user_card.decryptor_from_public(dec_pubk, &|| {}); assert_eq!(session, decryptor.decrypt(&ciphertext, Some(32)).unwrap()); + + open.manage_security_environment(KeyType::Authentication, KeyType::Decryption) + .unwrap(); + let mut user_card = open.user_card().unwrap(); + let mut authenticator = user_card.authenticator_from_public(aut_pubk.clone(), &|| {}); + let data = [3; 32]; + let signature = authenticator.sign(HashAlgorithm::SHA256, &data).unwrap(); + assert!(aut_pubk + .verify(&signature, HashAlgorithm::SHA256, &data) + .is_err()); + dec_pubk_aut + .verify(&signature, HashAlgorithm::SHA256, &data) + .unwrap(); + + open.manage_security_environment(KeyType::Decryption, KeyType::Authentication) + .unwrap(); + let mut user_card = open.user_card().unwrap(); + let mut session = SessionKey::new(19); + session[0] = 7; + let ciphertext = aut_pubk_dec.encrypt(&session).unwrap(); + let mut decryptor = user_card.decryptor_from_public(aut_pubk_dec, &|| {}); + assert_eq!(session, decryptor.decrypt(&ciphertext, Some(32)).unwrap()); }); virt::with_vsc(|| { @@ -117,7 +148,28 @@ fn sequoia_gen_key() { 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, &|| {}); + let mut decryptor = user_card.decryptor_from_public(dec_pubk.clone(), &|| {}); assert_eq!(session, decryptor.decrypt(&ciphertext, None).unwrap()); + + open.manage_security_environment(KeyType::Authentication, KeyType::Decryption) + .unwrap(); + let mut user_card = open.user_card().unwrap(); + let mut authenticator = user_card.authenticator_from_public(aut_pubk, &|| {}); + let data = [3; 32]; + // Signature with X25519 key should fail + let _ = authenticator + .sign(HashAlgorithm::SHA256, &data) + .unwrap_err(); + + open.manage_security_environment(KeyType::Decryption, KeyType::Authentication) + .unwrap(); + let mut user_card = open.user_card().unwrap(); + 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, &|| {}); + + // X25519 with and EdDSA key should fail + decryptor.decrypt(&ciphertext, None).unwrap_err(); }); } From 57a1ced51b4d583199a37277e1efec0034671b5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Mon, 17 Oct 2022 16:03:12 +0200 Subject: [PATCH 8/8] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbd2170f..d273f0f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,12 +9,14 @@ SPDX-License-Identifier: CC0-1.0 ### Features +- Support using authentication keys for decryption and vice-versa with MANAGE SECURITY ENVIRONMENT ([#60][]) - Support PIN resets using a resetting code ([#63][]) ### Bugfixes - Fix the length of the Digital signature counter DO 0x93 ([#76][]) +[#60]: https://github.com/Nitrokey/opcard-rs/pull/60 [#63]: https://github.com/Nitrokey/opcard-rs/pull/63 [#76]: https://github.com/Nitrokey/opcard-rs/pull/76