Skip to content

Commit

Permalink
mgm: Support AES management keys
Browse files Browse the repository at this point in the history
  • Loading branch information
str4d committed Jan 4, 2025
1 parent 622d989 commit 1d87b5f
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 8 deletions.
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
5 changes: 4 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
54 changes: 47 additions & 7 deletions src/mgm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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<u8> for MgmAlgorithmId {
Expand All @@ -103,6 +112,9 @@ impl TryFrom<u8> for MgmAlgorithmId {
fn try_from(value: u8) -> Result<Self> {
match value {
0x03 => Ok(MgmAlgorithmId::ThreeDes),
0x08 => Ok(MgmAlgorithmId::Aes128),
0x0a => Ok(MgmAlgorithmId::Aes192),
0x0c => Ok(MgmAlgorithmId::Aes256),
_ => Err(Error::AlgorithmError),
}
}
Expand All @@ -112,6 +124,9 @@ impl From<MgmAlgorithmId> for u8 {
fn from(id: MgmAlgorithmId) -> u8 {
match id {
MgmAlgorithmId::ThreeDes => 0x03,
MgmAlgorithmId::Aes128 => 0x08,
MgmAlgorithmId::Aes192 => 0x0a,
MgmAlgorithmId::Aes256 => 0x0c,
}
}
}
Expand Down Expand Up @@ -157,20 +172,26 @@ 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 {
/// Generates a random MGM key for the given algorithm.
pub fn generate<C: MgmKeyAlgorithm>(rng: &mut impl CryptoRngCore) -> Result<Self> {
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)
}
Expand All @@ -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)
}
Expand Down Expand Up @@ -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"),
))),
}
}

Expand Down Expand Up @@ -349,11 +372,7 @@ pub trait MgmKeyOps: AsRef<[u8]> + private::MgmKeyOpsInternal {
fn get_protected(yubikey: &mut YubiKey) -> Result<Self> {
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))?;
Expand Down Expand Up @@ -537,31 +556,46 @@ 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<Self> {
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)
}

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),
}
}
}
Expand All @@ -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(),
}
}
}
Expand All @@ -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.
Expand Down
22 changes: 22 additions & 0 deletions src/mgm/aes.rs
Original file line number Diff line number Diff line change
@@ -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<aes::Aes128>;

/// A Management Key (MGM) using AES-192
pub type MgmKeyAes192 = SpecificMgmKey<aes::Aes192>;

/// A Management Key (MGM) using AES-256
pub type MgmKeyAes256 = SpecificMgmKey<aes::Aes256>;

0 comments on commit 1d87b5f

Please sign in to comment.