From 1a3e15ce32236c484dd29dfdf6d8027608af9e3a Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 4 Jan 2025 17:54:19 +0000 Subject: [PATCH] mgm: Support AES management keys --- Cargo.lock | 13 ++++++++++++ Cargo.toml | 1 + src/lib.rs | 5 ++++- src/mgm.rs | 54 +++++++++++++++++++++++++++++++++++++++++++------- src/mgm/aes.rs | 22 ++++++++++++++++++++ 5 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 src/mgm/aes.rs diff --git a/Cargo.lock b/Cargo.lock index 84e4450..59e8d30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aes" +version = "0.9.0-pre.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7856582c758ade85d71daf27ec6bcea6c1c73913692b07b8dffea2dc03531c9" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", + "zeroize", +] + [[package]] name = "aho-corasick" version = "1.1.2" @@ -1135,6 +1147,7 @@ dependencies = [ name = "yubikey" version = "0.8.0" dependencies = [ + "aes", "base16ct", "cipher", "der", diff --git a/Cargo.toml b/Cargo.toml index 5eef663..154ee36 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ sha2 = "=0.11.0-pre.4" x509-cert = { version = "=0.3.0-pre.0", features = [ "builder", "hazmat" ] } [dependencies] +aes = { version = "=0.9.0-pre.2", features = ["zeroize"] } cipher = { version = "=0.5.0-pre.7", features = ["rand_core"] } der = "=0.8.0-rc.1" des = "=0.9.0-pre.2" diff --git a/src/lib.rs b/src/lib.rs index ee859a8..00613ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,7 +71,10 @@ pub use crate::{ chuid::ChuId, config::Config, error::{Error, Result}, - mgm::{MgmAlgorithmId, MgmKey, MgmKey3Des, MgmKeyAlgorithm, MgmKeyOps, MgmType}, + mgm::{ + MgmAlgorithmId, MgmKey, MgmKey3Des, MgmKeyAes128, MgmKeyAes192, MgmKeyAes256, + MgmKeyAlgorithm, MgmKeyOps, MgmType, + }, piv::Key, policy::{PinPolicy, TouchPolicy}, reader::Context, diff --git a/src/mgm.rs b/src/mgm.rs index b352eeb..968a9ca 100644 --- a/src/mgm.rs +++ b/src/mgm.rs @@ -59,6 +59,9 @@ pub(crate) const APPLET_NAME: &str = "YubiKey MGMT"; #[cfg(feature = "untested")] pub(crate) const APPLET_ID: &[u8] = &[0xa0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17]; +mod aes; +pub use aes::{MgmKeyAes128, MgmKeyAes192, MgmKeyAes256}; + mod tdes; pub use tdes::MgmKey3Des; @@ -95,6 +98,12 @@ pub enum MgmType { pub enum MgmAlgorithmId { /// Triple DES (3DES) in EDE mode ThreeDes, + /// AES-128 + Aes128, + /// AES-192 + Aes192, + /// AES-256 + Aes256, } impl TryFrom for MgmAlgorithmId { @@ -103,6 +112,9 @@ impl TryFrom for MgmAlgorithmId { fn try_from(value: u8) -> Result { match value { 0x03 => Ok(MgmAlgorithmId::ThreeDes), + 0x08 => Ok(MgmAlgorithmId::Aes128), + 0x0a => Ok(MgmAlgorithmId::Aes192), + 0x0c => Ok(MgmAlgorithmId::Aes256), _ => Err(Error::AlgorithmError), } } @@ -112,6 +124,9 @@ impl From for u8 { fn from(id: MgmAlgorithmId) -> u8 { match id { MgmAlgorithmId::ThreeDes => 0x03, + MgmAlgorithmId::Aes128 => 0x08, + MgmAlgorithmId::Aes192 => 0x0a, + MgmAlgorithmId::Aes256 => 0x0c, } } } @@ -157,13 +172,16 @@ pub trait MgmKeyAlgorithm: /// This key is used to authenticate to the management applet running on /// a YubiKey in order to perform administrative functions. /// -/// The only supported algorithm for MGM keys is 3DES. +/// The only supported algorithm for MGM keys are 3DES and AES. #[derive(Clone)] pub struct MgmKey(MgmKeyKind); #[derive(Clone)] enum MgmKeyKind { Tdes(MgmKey3Des), + Aes128(MgmKeyAes128), + Aes192(MgmKeyAes192), + Aes256(MgmKeyAes256), } impl MgmKey { @@ -171,6 +189,9 @@ impl MgmKey { pub fn generate(rng: &mut impl CryptoRngCore) -> Result { match C::ALGORITHM_ID { MgmAlgorithmId::ThreeDes => MgmKey3Des::generate(rng).map(MgmKeyKind::Tdes), + MgmAlgorithmId::Aes128 => MgmKeyAes128::generate(rng).map(MgmKeyKind::Aes128), + MgmAlgorithmId::Aes192 => MgmKeyAes192::generate(rng).map(MgmKeyKind::Aes192), + MgmAlgorithmId::Aes256 => MgmKeyAes256::generate(rng).map(MgmKeyKind::Aes256), } .map(Self) } @@ -192,7 +213,7 @@ impl MgmKey { minor: 7.., .. } - | Version { major: 6.., .. } => Err(Error::NotSupported), + | Version { major: 6.., .. } => MgmKeyAes192::generate(rng).map(MgmKeyKind::Aes192), } .map(Self) } @@ -230,7 +251,9 @@ impl MgmKey { minor: 7.., .. } - | Version { major: 6.., .. } => Err(Error::NotSupported), + | Version { major: 6.., .. } => Ok(Self(MgmKeyKind::Aes192( + MgmKeyAes192::new(DEFAULT_MGM_KEY.into()).expect("valid"), + ))), } } @@ -349,11 +372,7 @@ pub trait MgmKeyOps: AsRef<[u8]> + private::MgmKeyOpsInternal { fn get_protected(yubikey: &mut YubiKey) -> Result { let txn = yubikey.begin_transaction()?; - // Check the key algorithm. let alg = MgmAlgorithmId::query(&txn)?; - if alg != MgmAlgorithmId::ThreeDes { - return Err(Error::NotSupported); - } let protected_data = ProtectedData::read(&txn) .inspect_err(|e| error!("could not read protected data (err: {:?})", e))?; @@ -537,18 +556,27 @@ impl private::MgmKeyOpsInternal for MgmKey { fn algorithm_id(&self) -> MgmAlgorithmId { match &self.0 { MgmKeyKind::Tdes(k) => k.algorithm_id(), + MgmKeyKind::Aes128(k) => k.algorithm_id(), + MgmKeyKind::Aes192(k) => k.algorithm_id(), + MgmKeyKind::Aes256(k) => k.algorithm_id(), } } fn key_size(&self) -> u8 { match &self.0 { MgmKeyKind::Tdes(k) => k.key_size(), + MgmKeyKind::Aes128(k) => k.key_size(), + MgmKeyKind::Aes192(k) => k.key_size(), + MgmKeyKind::Aes256(k) => k.key_size(), } } fn parse_key(alg: MgmAlgorithmId, bytes: impl AsRef<[u8]>) -> Result { match alg { MgmAlgorithmId::ThreeDes => MgmKey3Des::from_bytes(bytes).map(MgmKeyKind::Tdes), + MgmAlgorithmId::Aes128 => MgmKeyAes128::from_bytes(bytes).map(MgmKeyKind::Aes128), + MgmAlgorithmId::Aes192 => MgmKeyAes192::from_bytes(bytes).map(MgmKeyKind::Aes192), + MgmAlgorithmId::Aes256 => MgmKeyAes256::from_bytes(bytes).map(MgmKeyKind::Aes256), } .map(Self) } @@ -556,12 +584,18 @@ impl private::MgmKeyOpsInternal for MgmKey { fn encrypt_block(&self, block: &mut [u8]) -> Result<()> { match &self.0 { MgmKeyKind::Tdes(k) => k.encrypt_block(block), + MgmKeyKind::Aes128(k) => k.encrypt_block(block), + MgmKeyKind::Aes192(k) => k.encrypt_block(block), + MgmKeyKind::Aes256(k) => k.encrypt_block(block), } } fn decrypt_block(&self, block: &mut [u8]) -> Result<()> { match &self.0 { MgmKeyKind::Tdes(k) => k.decrypt_block(block), + MgmKeyKind::Aes128(k) => k.decrypt_block(block), + MgmKeyKind::Aes192(k) => k.decrypt_block(block), + MgmKeyKind::Aes256(k) => k.decrypt_block(block), } } } @@ -580,6 +614,9 @@ impl AsRef<[u8]> for MgmKey { fn as_ref(&self) -> &[u8] { match &self.0 { MgmKeyKind::Tdes(k) => k.as_ref(), + MgmKeyKind::Aes128(k) => k.as_ref(), + MgmKeyKind::Aes192(k) => k.as_ref(), + MgmKeyKind::Aes256(k) => k.as_ref(), } } } @@ -605,6 +642,9 @@ mod private { pub trait Seal {} impl Seal for des::TdesEde3 {} + impl Seal for aes::Aes128 {} + impl Seal for aes::Aes192 {} + impl Seal for aes::Aes256 {} pub trait MgmKeyOpsInternal: Sized { /// Parses an MGM key from the given byte slice. diff --git a/src/mgm/aes.rs b/src/mgm/aes.rs new file mode 100644 index 0000000..1e6f18d --- /dev/null +++ b/src/mgm/aes.rs @@ -0,0 +1,22 @@ +use super::{MgmAlgorithmId, MgmKeyAlgorithm, SpecificMgmKey}; + +impl MgmKeyAlgorithm for aes::Aes128 { + const ALGORITHM_ID: MgmAlgorithmId = MgmAlgorithmId::Aes128; +} + +impl MgmKeyAlgorithm for aes::Aes192 { + const ALGORITHM_ID: MgmAlgorithmId = MgmAlgorithmId::Aes192; +} + +impl MgmKeyAlgorithm for aes::Aes256 { + const ALGORITHM_ID: MgmAlgorithmId = MgmAlgorithmId::Aes256; +} + +/// A Management Key (MGM) using AES-128 +pub type MgmKeyAes128 = SpecificMgmKey; + +/// A Management Key (MGM) using AES-192 +pub type MgmKeyAes192 = SpecificMgmKey; + +/// A Management Key (MGM) using AES-256 +pub type MgmKeyAes256 = SpecificMgmKey;