Skip to content

Commit

Permalink
mgm: Check management key algorithm when fetching from Yubikey
Browse files Browse the repository at this point in the history
  • Loading branch information
str4d committed Dec 29, 2024
1 parent c50e37a commit 5e99c58
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 15 deletions.
6 changes: 6 additions & 0 deletions src/apdu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ impl Apdu {
self
}

/// Set this APDU's second parameter only
pub(crate) fn p2(&mut self, value: u8) -> &mut Self {
self.p2 = value;
self
}

/// Set both parameters for this APDU
pub fn params(&mut self, p1: u8, p2: u8) -> &mut Self {
self.p1 = p1;
Expand Down
35 changes: 35 additions & 0 deletions src/mgm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ use zeroize::Zeroize;
use crate::{
consts::{TAG_ADMIN_FLAGS_1, TAG_ADMIN_SALT, TAG_PROTECTED_MGM},
metadata::{AdminData, ProtectedData},
piv::{ManagementSlotId, SlotAlgorithmId},
transaction::Transaction,
yubikey::YubiKey,
};
use des::{
Expand Down Expand Up @@ -110,6 +112,27 @@ impl From<MgmAlgorithmId> for u8 {
}
}

impl MgmAlgorithmId {
/// Looks up the algorithm for the given Yubikey's current management key.
#[cfg(feature = "untested")]
fn query(txn: &Transaction<'_>) -> Result<Self> {
match txn.get_metadata(crate::piv::SlotId::Management(ManagementSlotId::Management)) {
Ok(metadata) => match metadata.algorithm {
SlotAlgorithmId::Management(alg) => Ok(alg),
// We specifically queried the management key slot; getting a known
// non-management algorithm back from the Yubikey is invalid.
_ => Err(Error::InvalidObject),
},
// Firmware versions without `GET METADATA` only support 3DES.
Err(Error::NotSupported) => Ok(MgmAlgorithmId::ThreeDes),
// `Error::AlgorithmError` only occurs when a new algorithm is encountered.
Err(Error::AlgorithmError) => Err(Error::NotSupported),
// Raise other errors as-is.
Err(e) => Err(e),
}
}
}

/// Management Key (MGM).
///
/// This key is used to authenticate to the management applet running on
Expand Down Expand Up @@ -155,6 +178,12 @@ impl MgmKey {
pub fn get_derived(yubikey: &mut YubiKey, pin: &[u8]) -> Result<Self> {
let txn = yubikey.begin_transaction()?;

// Check the key algorithm.
let alg = MgmAlgorithmId::query(&txn)?;
if alg != MgmAlgorithmId::ThreeDes {
return Err(Error::NotSupported);
}

// recover management key
let admin_data = AdminData::read(&txn)?;
let salt = admin_data.get_item(TAG_ADMIN_SALT)?;
Expand All @@ -179,6 +208,12 @@ impl MgmKey {
pub 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
19 changes: 4 additions & 15 deletions src/piv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
use crate::{
apdu::{Ins, StatusWords},
certificate::{self, Certificate},
consts::CB_OBJ_MAX,
error::{Error, Result},
mgm::MgmAlgorithmId,
policy::{PinPolicy, TouchPolicy},
Expand Down Expand Up @@ -75,6 +74,9 @@ use {
#[cfg(feature = "untested")]
use zeroize::Zeroizing;

#[cfg(feature = "untested")]
use crate::consts::CB_OBJ_MAX;

/// PIV Applet Name
pub(crate) const APPLET_NAME: &str = "PIV";

Expand Down Expand Up @@ -925,21 +927,8 @@ pub fn decrypt_data(
/// Read metadata
pub fn metadata(yubikey: &mut YubiKey, slot: SlotId) -> Result<SlotMetadata> {
let txn = yubikey.begin_transaction()?;
let templ = [0, Ins::GetMetadata.code(), 0, slot.into()];

let response = txn.transfer_data(&templ, &[], CB_OBJ_MAX)?;

if !response.is_success() {
if response.status_words() == StatusWords::NotSupportedError {
return Err(Error::NotSupported); // Requires firmware 5.2.3
} else {
return Err(Error::GenericError);
}
}

let buf = Buffer::new(response.data().into());

SlotMetadata::try_from(buf)
txn.get_metadata(slot)
}

/// Metadata from a slot
Expand Down
19 changes: 19 additions & 0 deletions src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,25 @@ impl<'tx> Transaction<'tx> {
}
}

/// Read metadata
pub(crate) fn get_metadata(&self, slot: SlotId) -> Result<piv::SlotMetadata> {
let response = Apdu::new(Ins::GetMetadata)
.p2(slot.into())
.transmit(self, CB_OBJ_MAX)?;

if !response.is_success() {
if response.status_words() == StatusWords::NotSupportedError {
return Err(Error::NotSupported); // Requires firmware 5.2.3
} else {
return Err(Error::GenericError);
}
}

let buf = Buffer::new(response.data().into());

piv::SlotMetadata::try_from(buf)
}

/// Verify device PIN.
pub fn verify_pin(&self, pin: &[u8]) -> Result<()> {
if pin.len() > CB_PIN_MAX {
Expand Down

0 comments on commit 5e99c58

Please sign in to comment.