From dcaf080ef2277961409351f2c4cc69cc0d5d5477 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 1 Sep 2025 23:30:40 +0100 Subject: [PATCH] mgm: Support AES management keys (#589) --- Cargo.lock | 13 ++++++++++ Cargo.toml | 1 + src/mgm.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 77 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e49dd9a..aa34396 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-rc.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd4838e4ad37bb032dea137f441d5f71c16c26c068af512e64c5bc13a88cdfc7" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", + "zeroize", +] + [[package]] name = "aho-corasick" version = "1.1.2" @@ -1077,6 +1089,7 @@ dependencies = [ name = "yubikey" version = "0.8.0" dependencies = [ + "aes", "base16ct", "bitflags 2.5.0", "cipher", diff --git a/Cargo.toml b/Cargo.toml index 65182f2..22e9b66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ sha2 = "0.11.0-rc.0" x509-cert = { version = "0.3.0-rc.1", features = ["builder", "hazmat"] } [dependencies] +aes = { version = "0.9.0-rc.0", features = ["zeroize"] } bitflags = "2.5.0" cipher = { version = "0.5.0-rc.0", features = ["rand_core"] } der = "0.8.0-rc.7" diff --git a/src/mgm.rs b/src/mgm.rs index 0de6303..6d47838 100644 --- a/src/mgm.rs +++ b/src/mgm.rs @@ -107,6 +107,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 { @@ -115,6 +121,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), } } @@ -124,6 +133,9 @@ impl From for u8 { fn from(id: MgmAlgorithmId) -> u8 { match id { MgmAlgorithmId::ThreeDes => 0x03, + MgmAlgorithmId::Aes128 => 0x08, + MgmAlgorithmId::Aes192 => 0x0a, + MgmAlgorithmId::Aes256 => 0x0c, } } } @@ -153,13 +165,16 @@ impl MgmAlgorithmId { /// 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(Key), + Aes128(Key), + Aes192(Key), + Aes256(Key), } impl MgmKey { @@ -169,6 +184,15 @@ impl MgmKey { MgmAlgorithmId::ThreeDes => { des::TdesEde3::try_generate_key_with_rng(rng).map(MgmKeyKind::Tdes) } + MgmAlgorithmId::Aes128 => { + aes::Aes128::try_generate_key_with_rng(rng).map(MgmKeyKind::Aes128) + } + MgmAlgorithmId::Aes192 => { + aes::Aes192::try_generate_key_with_rng(rng).map(MgmKeyKind::Aes192) + } + MgmAlgorithmId::Aes256 => { + aes::Aes256::try_generate_key_with_rng(rng).map(MgmKeyKind::Aes256) + } } .map_err(|e| { error!("RNG failure: {}", e); @@ -194,7 +218,7 @@ impl MgmKey { minor: 7.., .. } - | Version { major: 6.., .. } => Err(Error::NotSupported), + | Version { major: 6.., .. } => Self::generate(MgmAlgorithmId::Aes192, rng), } } @@ -232,7 +256,7 @@ impl MgmKey { minor: 7.., .. } - | Version { major: 6.., .. } => Err(Error::NotSupported), + | Version { major: 6.., .. } => Ok(Self(MgmKeyKind::Aes192(DEFAULT_MGM_KEY.into()))), } } @@ -284,11 +308,7 @@ impl MgmKey { pub 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))?; @@ -440,6 +460,9 @@ impl MgmKey { pub(crate) fn algorithm_id(&self) -> MgmAlgorithmId { match &self.0 { MgmKeyKind::Tdes(_) => MgmAlgorithmId::ThreeDes, + MgmKeyKind::Aes128(_) => MgmAlgorithmId::Aes128, + MgmKeyKind::Aes192(_) => MgmAlgorithmId::Aes192, + MgmKeyKind::Aes256(_) => MgmAlgorithmId::Aes256, } } @@ -447,6 +470,9 @@ impl MgmKey { pub(crate) fn key_size(&self) -> u8 { match &self.0 { MgmKeyKind::Tdes(_) => ::KeySize::U8, + MgmKeyKind::Aes128(_) => ::KeySize::U8, + MgmKeyKind::Aes192(_) => ::KeySize::U8, + MgmKeyKind::Aes256(_) => ::KeySize::U8, } } @@ -462,6 +488,15 @@ impl MgmKey { des::TdesEde3::weak_key_test(&key).map_err(|_| Error::KeyError)?; Ok(MgmKeyKind::Tdes(key)) } + MgmAlgorithmId::Aes128 => Key::::try_from(bytes.as_ref()) + .map_err(|_| Error::SizeError) + .map(MgmKeyKind::Aes128), + MgmAlgorithmId::Aes192 => Key::::try_from(bytes.as_ref()) + .map_err(|_| Error::SizeError) + .map(MgmKeyKind::Aes192), + MgmAlgorithmId::Aes256 => Key::::try_from(bytes.as_ref()) + .map_err(|_| Error::SizeError) + .map(MgmKeyKind::Aes256), } .map(Self) } @@ -474,6 +509,15 @@ impl MgmKey { MgmKeyKind::Tdes(k) => { des::TdesEde3::new(k).encrypt_block(block.try_into().map_err(|_| Error::SizeError)?) } + MgmKeyKind::Aes128(k) => { + aes::Aes128::new(k).encrypt_block(block.try_into().map_err(|_| Error::SizeError)?) + } + MgmKeyKind::Aes192(k) => { + aes::Aes192::new(k).encrypt_block(block.try_into().map_err(|_| Error::SizeError)?) + } + MgmKeyKind::Aes256(k) => { + aes::Aes256::new(k).encrypt_block(block.try_into().map_err(|_| Error::SizeError)?) + } } Ok(()) } @@ -486,6 +530,15 @@ impl MgmKey { MgmKeyKind::Tdes(k) => { des::TdesEde3::new(k).decrypt_block(block.try_into().map_err(|_| Error::SizeError)?) } + MgmKeyKind::Aes128(k) => { + aes::Aes128::new(k).decrypt_block(block.try_into().map_err(|_| Error::SizeError)?) + } + MgmKeyKind::Aes192(k) => { + aes::Aes192::new(k).decrypt_block(block.try_into().map_err(|_| Error::SizeError)?) + } + MgmKeyKind::Aes256(k) => { + aes::Aes256::new(k).decrypt_block(block.try_into().map_err(|_| Error::SizeError)?) + } } Ok(()) } @@ -516,6 +569,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(), } } }