mgm: Support AES management keys (#589)

This commit is contained in:
Jack Grigg
2025-09-01 23:30:40 +01:00
committed by GitHub
parent 0072b174b4
commit dcaf080ef2
3 changed files with 77 additions and 7 deletions
Generated
+13
View File
@@ -2,6 +2,18 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 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]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "1.1.2" version = "1.1.2"
@@ -1077,6 +1089,7 @@ dependencies = [
name = "yubikey" name = "yubikey"
version = "0.8.0" version = "0.8.0"
dependencies = [ dependencies = [
"aes",
"base16ct", "base16ct",
"bitflags 2.5.0", "bitflags 2.5.0",
"cipher", "cipher",
+1
View File
@@ -24,6 +24,7 @@ sha2 = "0.11.0-rc.0"
x509-cert = { version = "0.3.0-rc.1", features = ["builder", "hazmat"] } x509-cert = { version = "0.3.0-rc.1", features = ["builder", "hazmat"] }
[dependencies] [dependencies]
aes = { version = "0.9.0-rc.0", features = ["zeroize"] }
bitflags = "2.5.0" bitflags = "2.5.0"
cipher = { version = "0.5.0-rc.0", features = ["rand_core"] } cipher = { version = "0.5.0-rc.0", features = ["rand_core"] }
der = "0.8.0-rc.7" der = "0.8.0-rc.7"
+63 -7
View File
@@ -107,6 +107,12 @@ pub enum MgmType {
pub enum MgmAlgorithmId { pub enum MgmAlgorithmId {
/// Triple DES (3DES) in EDE mode /// Triple DES (3DES) in EDE mode
ThreeDes, ThreeDes,
/// AES-128
Aes128,
/// AES-192
Aes192,
/// AES-256
Aes256,
} }
impl TryFrom<u8> for MgmAlgorithmId { impl TryFrom<u8> for MgmAlgorithmId {
@@ -115,6 +121,9 @@ impl TryFrom<u8> for MgmAlgorithmId {
fn try_from(value: u8) -> Result<Self> { fn try_from(value: u8) -> Result<Self> {
match value { match value {
0x03 => Ok(MgmAlgorithmId::ThreeDes), 0x03 => Ok(MgmAlgorithmId::ThreeDes),
0x08 => Ok(MgmAlgorithmId::Aes128),
0x0a => Ok(MgmAlgorithmId::Aes192),
0x0c => Ok(MgmAlgorithmId::Aes256),
_ => Err(Error::AlgorithmError), _ => Err(Error::AlgorithmError),
} }
} }
@@ -124,6 +133,9 @@ impl From<MgmAlgorithmId> for u8 {
fn from(id: MgmAlgorithmId) -> u8 { fn from(id: MgmAlgorithmId) -> u8 {
match id { match id {
MgmAlgorithmId::ThreeDes => 0x03, 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 /// This key is used to authenticate to the management applet running on
/// a YubiKey in order to perform administrative functions. /// 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)] #[derive(Clone)]
pub struct MgmKey(MgmKeyKind); pub struct MgmKey(MgmKeyKind);
#[derive(Clone)] #[derive(Clone)]
enum MgmKeyKind { enum MgmKeyKind {
Tdes(Key<des::TdesEde3>), Tdes(Key<des::TdesEde3>),
Aes128(Key<aes::Aes128>),
Aes192(Key<aes::Aes192>),
Aes256(Key<aes::Aes256>),
} }
impl MgmKey { impl MgmKey {
@@ -169,6 +184,15 @@ impl MgmKey {
MgmAlgorithmId::ThreeDes => { MgmAlgorithmId::ThreeDes => {
des::TdesEde3::try_generate_key_with_rng(rng).map(MgmKeyKind::Tdes) 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| { .map_err(|e| {
error!("RNG failure: {}", e); error!("RNG failure: {}", e);
@@ -194,7 +218,7 @@ impl MgmKey {
minor: 7.., minor: 7..,
.. ..
} }
| Version { major: 6.., .. } => Err(Error::NotSupported), | Version { major: 6.., .. } => Self::generate(MgmAlgorithmId::Aes192, rng),
} }
} }
@@ -232,7 +256,7 @@ impl MgmKey {
minor: 7.., 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<Self> { pub fn get_protected(yubikey: &mut YubiKey) -> Result<Self> {
let txn = yubikey.begin_transaction()?; let txn = yubikey.begin_transaction()?;
// Check the key algorithm.
let alg = MgmAlgorithmId::query(&txn)?; let alg = MgmAlgorithmId::query(&txn)?;
if alg != MgmAlgorithmId::ThreeDes {
return Err(Error::NotSupported);
}
let protected_data = ProtectedData::read(&txn) let protected_data = ProtectedData::read(&txn)
.inspect_err(|e| error!("could not read protected data (err: {:?})", e))?; .inspect_err(|e| error!("could not read protected data (err: {:?})", e))?;
@@ -440,6 +460,9 @@ impl MgmKey {
pub(crate) fn algorithm_id(&self) -> MgmAlgorithmId { pub(crate) fn algorithm_id(&self) -> MgmAlgorithmId {
match &self.0 { match &self.0 {
MgmKeyKind::Tdes(_) => MgmAlgorithmId::ThreeDes, 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 { pub(crate) fn key_size(&self) -> u8 {
match &self.0 { match &self.0 {
MgmKeyKind::Tdes(_) => <des::TdesEde3 as KeySizeUser>::KeySize::U8, MgmKeyKind::Tdes(_) => <des::TdesEde3 as KeySizeUser>::KeySize::U8,
MgmKeyKind::Aes128(_) => <aes::Aes128 as KeySizeUser>::KeySize::U8,
MgmKeyKind::Aes192(_) => <aes::Aes192 as KeySizeUser>::KeySize::U8,
MgmKeyKind::Aes256(_) => <aes::Aes256 as KeySizeUser>::KeySize::U8,
} }
} }
@@ -462,6 +488,15 @@ impl MgmKey {
des::TdesEde3::weak_key_test(&key).map_err(|_| Error::KeyError)?; des::TdesEde3::weak_key_test(&key).map_err(|_| Error::KeyError)?;
Ok(MgmKeyKind::Tdes(key)) Ok(MgmKeyKind::Tdes(key))
} }
MgmAlgorithmId::Aes128 => Key::<aes::Aes128>::try_from(bytes.as_ref())
.map_err(|_| Error::SizeError)
.map(MgmKeyKind::Aes128),
MgmAlgorithmId::Aes192 => Key::<aes::Aes192>::try_from(bytes.as_ref())
.map_err(|_| Error::SizeError)
.map(MgmKeyKind::Aes192),
MgmAlgorithmId::Aes256 => Key::<aes::Aes256>::try_from(bytes.as_ref())
.map_err(|_| Error::SizeError)
.map(MgmKeyKind::Aes256),
} }
.map(Self) .map(Self)
} }
@@ -474,6 +509,15 @@ impl MgmKey {
MgmKeyKind::Tdes(k) => { MgmKeyKind::Tdes(k) => {
des::TdesEde3::new(k).encrypt_block(block.try_into().map_err(|_| Error::SizeError)?) 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(()) Ok(())
} }
@@ -486,6 +530,15 @@ impl MgmKey {
MgmKeyKind::Tdes(k) => { MgmKeyKind::Tdes(k) => {
des::TdesEde3::new(k).decrypt_block(block.try_into().map_err(|_| Error::SizeError)?) 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(()) Ok(())
} }
@@ -516,6 +569,9 @@ impl AsRef<[u8]> for MgmKey {
fn as_ref(&self) -> &[u8] { fn as_ref(&self) -> &[u8] {
match &self.0 { match &self.0 {
MgmKeyKind::Tdes(k) => k.as_ref(), MgmKeyKind::Tdes(k) => k.as_ref(),
MgmKeyKind::Aes128(k) => k.as_ref(),
MgmKeyKind::Aes192(k) => k.as_ref(),
MgmKeyKind::Aes256(k) => k.as_ref(),
} }
} }
} }