mgm: Generalize TDES logic to enable other algorithms (#625)

Co-authored-by: Jack Grigg <thestr4d@gmail.com>
Co-authored-by: Greg Bowyer <gbowyer@fastmail.co.uk>
This commit is contained in:
Tony Arcieri (iqlusion)
2025-08-22 09:37:41 -06:00
committed by GitHub
parent 7eb7a31a28
commit 1e1fe34734
7 changed files with 275 additions and 145 deletions
+21
View File
@@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `yubikey::certificate::SelfSigned` - `yubikey::certificate::SelfSigned`
- `yubikey::Error::CertificateBuilder` - `yubikey::Error::CertificateBuilder`
- `yubikey::MgmAlgorithmId` - `yubikey::MgmAlgorithmId`
- `yubikey::mgm`:
- `MgmKey::generate_for`
- `MgmKey::get_default`
- `impl AsRef<[u8]> for MgmKey`
### Changed ### Changed
- MSRV is now 1.81. - MSRV is now 1.81.
@@ -20,12 +24,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `rsa 0.10.0-pre.3` - `rsa 0.10.0-pre.3`
- `sha2 0.11.0-pre.4` - `sha2 0.11.0-pre.4`
- `x509-cert 0.3.0-pre.0` - `x509-cert 0.3.0-pre.0`
- `yubikey::mgm`:
- `MgmKey::generate` now takes a `rand::TryCryptoRng` argument.
- `MgmKey::generate` now requires the caller to specify the key algorithm via
an `MgmAlgorithmId` parameter.
- Use `MgmKey::generate_for` if you want to generate a key using the
preferred algorithm for a given Yubikey's firmware version.
- `MgmKey::from_bytes` now takes an `Option<MgmAlgorithmId>` argument, to
disambiguate algorithms with the same key length.
- `yubikey::piv`: - `yubikey::piv`:
- `ManagementAlgorithmId` has been renamed to `SlotAlgorithmId`, and its - `ManagementAlgorithmId` has been renamed to `SlotAlgorithmId`, and its
`ThreeDes` variant has been replaced by `SlotAlgorithmId::Management` `ThreeDes` variant has been replaced by `SlotAlgorithmId::Management`
containing a `yubikey::MgmAlgorithmId`. containing a `yubikey::MgmAlgorithmId`.
- Metadata command returns `Error:NotFound` instead of `Error::GenericError` when the object doesn't exist ([#558]). - Metadata command returns `Error:NotFound` instead of `Error::GenericError` when the object doesn't exist ([#558]).
### Removed
- `yubikey::mgm`:
- `MgmKey::new` (use `MgmKey::from_bytes(_, Some(MgmAlgorithmId::ThreeDes))`
instead).
- `impl AsRef<[u8; DES_LEN_3DES]> for MgmKey` (use
`impl AsRef<[u8]> for MgmKey` instead).
- `impl Default for MgmKey` (use `MgmKey::get_default` instead).
- `impl TryFrom<&[u8]> for MgmKey` (use `MgmKey::from_bytes` instead).
## 0.8.0 (2023-08-15) ## 0.8.0 (2023-08-15)
### Added ### Added
- `impl Debug for {Context, YubiKey}` ([#457]) - `impl Debug for {Context, YubiKey}` ([#457])
Generated
+2
View File
@@ -197,6 +197,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a23fa214dea9efd4dacee5a5614646b30216ae0f05d4bb51bafb50e9da1c5be" checksum = "8a23fa214dea9efd4dacee5a5614646b30216ae0f05d4bb51bafb50e9da1c5be"
dependencies = [ dependencies = [
"hybrid-array", "hybrid-array",
"rand_core",
] ]
[[package]] [[package]]
@@ -1078,6 +1079,7 @@ version = "0.8.0"
dependencies = [ dependencies = [
"base16ct", "base16ct",
"bitflags 2.5.0", "bitflags 2.5.0",
"cipher",
"der", "der",
"des", "des",
"ecdsa", "ecdsa",
+1
View File
@@ -25,6 +25,7 @@ x509-cert = { version = "0.3.0-rc.1", features = ["builder", "hazmat"] }
[dependencies] [dependencies]
bitflags = "2.5.0" bitflags = "2.5.0"
cipher = { version = "0.5.0-rc.0", features = ["rand_core"] }
der = "0.8.0-rc.7" der = "0.8.0-rc.7"
des = "0.9.0-rc.0" des = "0.9.0-rc.0"
elliptic-curve = "0.14.0-rc.7" elliptic-curve = "0.14.0-rc.7"
+195 -99
View File
@@ -35,17 +35,14 @@ use crate::{
metadata::{AdminData, ProtectedData}, metadata::{AdminData, ProtectedData},
piv::{ManagementSlotId, SlotAlgorithmId}, piv::{ManagementSlotId, SlotAlgorithmId},
transaction::Transaction, transaction::Transaction,
Error, Result, YubiKey, Error, Result, Version, YubiKey,
}; };
use bitflags::bitflags; use bitflags::bitflags;
use log::error; use cipher::{
use rand_core::{OsRng, RngCore, TryRngCore}; typenum::Unsigned, BlockCipherDecrypt, BlockCipherEncrypt, Key, KeyInit, KeySizeUser,
use zeroize::Zeroize;
use des::{
cipher::{BlockCipherDecrypt, BlockCipherEncrypt, KeyInit},
TdesEde3,
}; };
use log::error;
use rand::TryCryptoRng;
#[cfg(feature = "untested")] #[cfg(feature = "untested")]
use { use {
@@ -56,7 +53,7 @@ use {
TAG_SERIAL, TAG_UNLOCK, TAG_USB_ENABLED, TAG_USB_SUPPORTED, TAG_VERSION, TAG_SERIAL, TAG_UNLOCK, TAG_USB_ENABLED, TAG_USB_SUPPORTED, TAG_VERSION,
}, },
serialization::Tlv, serialization::Tlv,
Serial, Version, Serial,
}, },
pbkdf2::pbkdf2_hmac, pbkdf2::pbkdf2_hmac,
sha1::Sha1, sha1::Sha1,
@@ -72,17 +69,22 @@ pub(crate) const APPLET_NAME: &str = "YubiKey MGMT";
#[cfg(feature = "untested")] #[cfg(feature = "untested")]
pub(crate) const APPLET_ID: &[u8] = &[0xa0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17]; pub(crate) const APPLET_ID: &[u8] = &[0xa0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17];
pub(crate) const ADMIN_FLAGS_1_PROTECTED_MGM: u8 = 0x02;
/// Size of a DES key /// Size of a DES key
pub(super) const DES_LEN_DES: usize = 8; const DES_LEN_DES: usize = 8;
/// Size of a 3DES key /// Size of a 3DES key
pub(crate) const DES_LEN_3DES: usize = DES_LEN_DES * 3; pub(super) const DES_LEN_3DES: usize = DES_LEN_DES * 3;
pub(crate) const ADMIN_FLAGS_1_PROTECTED_MGM: u8 = 0x02;
#[cfg(feature = "untested")] #[cfg(feature = "untested")]
const CB_ADMIN_SALT: usize = 16; const CB_ADMIN_SALT: usize = 16;
/// The default MGM key loaded for both Triple-DES and AES keys
const DEFAULT_MGM_KEY: [u8; 24] = [
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
];
/// Number of PBKDF2 iterations to use when deriving from a password /// Number of PBKDF2 iterations to use when deriving from a password
#[cfg(feature = "untested")] #[cfg(feature = "untested")]
const ITER_MGM_PBKDF2: u32 = 10000; const ITER_MGM_PBKDF2: u32 = 10000;
@@ -153,41 +155,101 @@ impl MgmAlgorithmId {
/// ///
/// The only supported algorithm for MGM keys is 3DES. /// The only supported algorithm for MGM keys is 3DES.
#[derive(Clone)] #[derive(Clone)]
pub struct MgmKey([u8; DES_LEN_3DES]); pub struct MgmKey(MgmKeyKind);
#[derive(Clone)]
enum MgmKeyKind {
Tdes(Key<des::TdesEde3>),
}
impl MgmKey { impl MgmKey {
/// Generate a random MGM key /// Generates a random MGM key for the given algorithm.
pub fn generate() -> Self { pub fn generate(alg: MgmAlgorithmId, rng: &mut impl TryCryptoRng) -> Result<Self> {
let mut key_bytes = [0u8; DES_LEN_3DES]; match alg {
let mut rng = OsRng.unwrap_err(); MgmAlgorithmId::ThreeDes => {
rng.fill_bytes(&mut key_bytes); des::TdesEde3::try_generate_key_with_rng(rng).map(MgmKeyKind::Tdes)
Self(key_bytes) }
}
/// Create an MGM key from byte slice.
///
/// Returns an error if the slice is the wrong size or the key is weak.
pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self> {
bytes.as_ref().try_into()
}
/// Create an MGM key from the given byte array.
///
/// Returns an error if the key is weak.
pub fn new(key_bytes: [u8; DES_LEN_3DES]) -> Result<Self> {
if TdesEde3::weak_key_test(key_bytes.as_ref()).is_err() {
error!(
"blacklisting key '{:?}' since it's weak (with odd parity)",
&key_bytes
);
return Err(Error::KeyError);
} }
.map_err(|e| {
Ok(Self(key_bytes)) error!("RNG failure: {}", e);
Error::KeyError
})
.map(Self)
} }
/// Get derived management key (MGM) /// Generates a random MGM key using the preferred algorithm for the given Yubikey's
/// firmware version.
pub fn generate_for(yubikey: &YubiKey, rng: &mut impl TryCryptoRng) -> Result<Self> {
match yubikey.version() {
// Initial firmware versions default to 3DES.
Version { major: ..=4, .. }
| Version {
major: 5,
minor: ..=6,
..
} => Self::generate(MgmAlgorithmId::ThreeDes, rng),
// Firmware 5.7.0 and above default to AES-192.
Version {
major: 5,
minor: 7..,
..
}
| Version { major: 6.., .. } => Err(Error::NotSupported),
}
}
/// Parses an MGM key from the given byte slice.
///
/// Returns an error if the slice is an invalid size or the key is weak.
///
/// If `alg` is `None`, the algorithm will be selected based on the length of the
/// slice, returning an error if there is not a unique match.
pub fn from_bytes(bytes: impl AsRef<[u8]>, alg: Option<MgmAlgorithmId>) -> Result<Self> {
match alg {
Some(alg) => Self::parse_key(alg, bytes),
None => match bytes.as_ref().len() {
DES_LEN_3DES => Self::parse_key(MgmAlgorithmId::ThreeDes, bytes),
_ => Err(Error::ParseError),
},
}
}
/// Gets the default management key for the given Yubikey's firmware version.
///
/// Returns an error if the Yubikey's default algorithm is unsupported.
pub fn get_default(yubikey: &YubiKey) -> Result<Self> {
match yubikey.version() {
// Initial firmware versions default to 3DES.
Version { major: ..=4, .. }
| Version {
major: 5,
minor: ..=6,
..
} => Ok(Self(MgmKeyKind::Tdes(DEFAULT_MGM_KEY.into()))),
// Firmware 5.7.0 and above default to AES-192.
Version {
major: 5,
minor: 7..,
..
}
| Version { major: 6.., .. } => Err(Error::NotSupported),
}
}
/// Resets the management key for the given YubiKey to the default value for that
/// Yubikey's firmware version.
///
/// This will wipe any metadata related to derived and PIN-protected management keys.
pub fn set_default(yubikey: &mut YubiKey) -> Result<()> {
Self::get_default(yubikey)?.set_manual(yubikey, false)
}
/// Derives a 3DES management key (MGM) from a stored salt.
///
/// # Security
///
/// Warning: PIN-derived mode is not secure. You should not use this technique. It is
/// offered only for backwards compatibility.
#[cfg(feature = "untested")] #[cfg(feature = "untested")]
pub fn get_derived(yubikey: &mut YubiKey, pin: &[u8]) -> Result<Self> { pub fn get_derived(yubikey: &mut YubiKey, pin: &[u8]) -> Result<Self> {
let txn = yubikey.begin_transaction()?; let txn = yubikey.begin_transaction()?;
@@ -212,9 +274,10 @@ impl MgmKey {
return Err(Error::GenericError); return Err(Error::GenericError);
} }
let mut mgm = [0u8; DES_LEN_3DES]; let mut mgm = Key::<des::TdesEde3>::default();
pbkdf2_hmac::<Sha1>(pin, salt, ITER_MGM_PBKDF2, &mut mgm); pbkdf2_hmac::<Sha1>(pin, salt, ITER_MGM_PBKDF2, &mut mgm);
MgmKey::from_bytes(mgm) des::TdesEde3::weak_key_test(&mgm).map_err(|_| Error::KeyError)?;
Ok(Self(MgmKeyKind::Tdes(mgm)))
} }
/// Get protected management key (MGM) /// Get protected management key (MGM)
@@ -234,24 +297,17 @@ impl MgmKey {
.get_item(TAG_PROTECTED_MGM) .get_item(TAG_PROTECTED_MGM)
.inspect_err(|e| error!("could not read protected MGM from metadata (err: {:?})", e))?; .inspect_err(|e| error!("could not read protected MGM from metadata (err: {:?})", e))?;
if item.len() != DES_LEN_3DES { Self::parse_key(alg, item).map_err(|e| match e {
error!( Error::SizeError => {
"protected data contains MGM, but is the wrong size: {} (expected {})", error!(
item.len(), "protected data contains MGM, but is the wrong size: {} (expected {:?})",
DES_LEN_3DES item.len(),
); alg,
);
return Err(Error::AuthenticationError); Error::AuthenticationError
} }
_ => e,
MgmKey::from_bytes(item) })
}
/// Resets the management key for the given YubiKey to the default value.
///
/// This will wipe any metadata related to derived and PIN-protected management keys.
pub fn set_default(yubikey: &mut YubiKey) -> Result<()> {
MgmKey::default().set_manual(yubikey, false)
} }
/// Configures the given YubiKey to use this management key. /// Configures the given YubiKey to use this management key.
@@ -380,47 +436,87 @@ impl MgmKey {
Ok(()) Ok(())
} }
/// Encrypt with 3DES key /// Returns the ID used to identify the key algorithm with APDU packets.
pub(crate) fn encrypt(&self, input: &[u8; DES_LEN_DES]) -> [u8; DES_LEN_DES] { pub(crate) fn algorithm_id(&self) -> MgmAlgorithmId {
let mut output = input.to_owned(); match &self.0 {
TdesEde3::new(&self.0.into()).encrypt_block((&mut output).into()); MgmKeyKind::Tdes(_) => MgmAlgorithmId::ThreeDes,
output }
} }
/// Decrypt with 3DES key /// Returns the key size in bytes.
pub(crate) fn decrypt(&self, input: &[u8; DES_LEN_DES]) -> [u8; DES_LEN_DES] { pub(crate) fn key_size(&self) -> u8 {
let mut output = input.to_owned(); match &self.0 {
TdesEde3::new(&self.0.into()).decrypt_block((&mut output).into()); MgmKeyKind::Tdes(_) => <des::TdesEde3 as KeySizeUser>::KeySize::U8,
output }
}
/// Parses an MGM key from the given byte slice.
///
/// Returns an error if the algorithm is unsupported, or the slice is the wrong size,
/// or the key is weak.
fn parse_key(alg: MgmAlgorithmId, bytes: impl AsRef<[u8]>) -> Result<Self> {
match alg {
MgmAlgorithmId::ThreeDes => {
let key =
Key::<des::TdesEde3>::try_from(bytes.as_ref()).map_err(|_| Error::SizeError)?;
des::TdesEde3::weak_key_test(&key).map_err(|_| Error::KeyError)?;
Ok(MgmKeyKind::Tdes(key))
}
}
.map(Self)
}
/// Encrypts a block with this key.
///
/// Returns an error if the block is the wrong size.
fn encrypt_block(&self, block: &mut [u8]) -> Result<()> {
match &self.0 {
MgmKeyKind::Tdes(k) => {
des::TdesEde3::new(k).encrypt_block(block.try_into().map_err(|_| Error::SizeError)?)
}
}
Ok(())
}
/// Decrypts a block with this key.
///
/// Returns an error if the block is the wrong size.
fn decrypt_block(&self, block: &mut [u8]) -> Result<()> {
match &self.0 {
MgmKeyKind::Tdes(k) => {
des::TdesEde3::new(k).decrypt_block(block.try_into().map_err(|_| Error::SizeError)?)
}
}
Ok(())
}
/// Given a challenge from a card, decrypts it and return the value
pub(crate) fn card_challenge(&self, challenge: &[u8]) -> Result<Vec<u8>> {
let mut output = challenge.to_owned();
self.decrypt_block(output.as_mut_slice())?;
Ok(output)
}
/// Checks the authentication matches the challenge and auth data
pub(crate) fn check_challenge(&self, challenge: &[u8], auth_data: &[u8]) -> Result<()> {
let mut response = challenge.to_owned();
self.encrypt_block(response.as_mut_slice())?;
use subtle::ConstantTimeEq;
if response.ct_eq(auth_data).unwrap_u8() != 1 {
return Err(Error::AuthenticationError);
}
Ok(())
} }
} }
impl AsRef<[u8; DES_LEN_3DES]> for MgmKey { impl AsRef<[u8]> for MgmKey {
fn as_ref(&self) -> &[u8; DES_LEN_3DES] { fn as_ref(&self) -> &[u8] {
&self.0 match &self.0 {
} MgmKeyKind::Tdes(k) => k.as_ref(),
} }
/// Default MGM key configured on all YubiKeys
impl Default for MgmKey {
fn default() -> Self {
MgmKey([
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
])
}
}
impl Drop for MgmKey {
fn drop(&mut self) {
self.0.zeroize();
}
}
impl<'a> TryFrom<&'a [u8]> for MgmKey {
type Error = Error;
fn try_from(key_bytes: &'a [u8]) -> Result<Self> {
Self::new(key_bytes.try_into().map_err(|_| Error::SizeError)?)
} }
} }
+6 -6
View File
@@ -5,7 +5,7 @@ use crate::{
apdu::{Apdu, Ins, StatusWords}, apdu::{Apdu, Ins, StatusWords},
consts::{CB_BUF_MAX, CB_OBJ_MAX}, consts::{CB_BUF_MAX, CB_OBJ_MAX},
error::{Error, Result}, error::{Error, Result},
mgm::{MgmKey, DES_LEN_3DES}, mgm::MgmKey,
otp, otp,
piv::{self, AlgorithmId, SlotId}, piv::{self, AlgorithmId, SlotId},
serialization::*, serialization::*,
@@ -251,11 +251,11 @@ impl<'tx> Transaction<'tx> {
pub fn set_mgm_key(&self, new_key: &MgmKey, require_touch: bool) -> Result<()> { pub fn set_mgm_key(&self, new_key: &MgmKey, require_touch: bool) -> Result<()> {
let p2 = if require_touch { 0xfe } else { 0xff }; let p2 = if require_touch { 0xfe } else { 0xff };
let mut data = [0u8; DES_LEN_3DES + 3]; let mut data = Vec::with_capacity(usize::from(new_key.key_size()) + 3);
data[0] = ALGO_3DES; data.push(new_key.algorithm_id().into());
data[1] = KEY_CARDMGM; data.push(KEY_CARDMGM);
data[2] = DES_LEN_3DES as u8; data.push(new_key.key_size());
data[3..3 + DES_LEN_3DES].copy_from_slice(new_key.as_ref()); data.extend_from_slice(new_key.as_ref());
let status_words = Apdu::new(Ins::SetMgmKey) let status_words = Apdu::new(Ins::SetMgmKey)
.params(0xff, p2) .params(0xff, p2)
+26 -25
View File
@@ -68,6 +68,7 @@ use {
pub(crate) const ADMIN_FLAGS_1_PUK_BLOCKED: u8 = 0x01; pub(crate) const ADMIN_FLAGS_1_PUK_BLOCKED: u8 = 0x01;
/// 3DES authentication /// 3DES authentication
#[cfg(feature = "untested")]
pub(crate) const ALGO_3DES: u8 = 0x03; pub(crate) const ALGO_3DES: u8 = 0x03;
/// Card management key /// Card management key
@@ -410,38 +411,45 @@ impl YubiKey {
} }
/// Authenticate to the card using the provided management key (MGM). /// Authenticate to the card using the provided management key (MGM).
pub fn authenticate(&mut self, mgm_key: MgmKey) -> Result<()> { pub fn authenticate(&mut self, mgm_key: &MgmKey) -> Result<()> {
let txn = self.begin_transaction()?; let txn = self.begin_transaction()?;
// get a challenge from the card // get a challenge from the card
let challenge = Apdu::new(Ins::Authenticate) let card_response = Apdu::new(Ins::Authenticate)
.params(ALGO_3DES, KEY_CARDMGM) .params(mgm_key.algorithm_id().into(), KEY_CARDMGM)
.data([TAG_DYN_AUTH, 0x02, 0x80, 0x00]) .data([TAG_DYN_AUTH, 0x02, 0x80, 0x00])
.transmit(&txn, 261)?; .transmit(&txn, 261)?;
if !challenge.is_success() || challenge.data().len() < 12 { if !card_response.is_success() || card_response.data().len() < 5 {
return Err(Error::AuthenticationError); return Err(Error::AuthenticationError);
} }
// send a response to the cards challenge and a challenge of our own. // send a response to the cards challenge and a challenge of our own.
let response = mgm_key.decrypt(challenge.data()[4..12].try_into()?); let card_challenge = mgm_key.card_challenge(&card_response.data()[4..])?;
let challenge_len = card_challenge.len();
let mut data = [0u8; 22]; // If this exceeds a `u8` then the card is giving us unexpected data.
data[0] = TAG_DYN_AUTH; let auth_len = (2 + challenge_len + 2 + challenge_len)
data[1] = 20; // 2 + 8 + 2 +8 .try_into()
data[2] = 0x80; .map_err(|_| Error::AuthenticationError)?;
data[3] = 8;
data[4..12].copy_from_slice(&response); let mut data = Vec::with_capacity(4 + challenge_len + 2 + challenge_len);
data[12] = 0x81; data.push(TAG_DYN_AUTH);
data[13] = 8; data.push(auth_len);
data.push(0x80);
data.push(challenge_len as u8);
data.extend_from_slice(&card_challenge);
data.push(0x81);
data.push(challenge_len as u8);
let mut host_challenge = vec![0u8; challenge_len];
let mut rng = OsRng.unwrap_err(); let mut rng = OsRng.unwrap_err();
rng.fill_bytes(&mut data[14..22]); rng.fill_bytes(&mut host_challenge);
let mut challenge = [0u8; 8]; data.extend_from_slice(&host_challenge);
challenge.copy_from_slice(&data[14..22]);
let authentication = Apdu::new(Ins::Authenticate) let authentication = Apdu::new(Ins::Authenticate)
.params(ALGO_3DES, KEY_CARDMGM) .params(mgm_key.algorithm_id().into(), KEY_CARDMGM)
.data(data) .data(data)
.transmit(&txn, 261)?; .transmit(&txn, 261)?;
@@ -450,14 +458,7 @@ impl YubiKey {
} }
// compare the response from the card with our challenge // compare the response from the card with our challenge
let response = mgm_key.encrypt(&challenge); mgm_key.check_challenge(&host_challenge, &authentication.data()[4..])
use subtle::ConstantTimeEq;
if response.ct_eq(&authentication.data()[4..12]).unwrap_u8() != 1 {
return Err(Error::AuthenticationError);
}
Ok(())
} }
/// Get the PIV keys contained in this YubiKey. /// Get the PIV keys contained in this YubiKey.
+24 -15
View File
@@ -114,32 +114,37 @@ fn test_verify_pin() {
#[test] #[test]
#[ignore] #[ignore]
fn test_set_mgmkey() { fn test_set_mgmkey() {
let mut rng = OsRng;
let mut yubikey = YUBIKEY.lock().unwrap(); let mut yubikey = YUBIKEY.lock().unwrap();
let default_key = MgmKey::get_default(&yubikey).unwrap();
assert!(yubikey.verify_pin(b"123456").is_ok()); assert!(yubikey.verify_pin(b"123456").is_ok());
assert!(MgmKey::get_protected(&mut yubikey).is_err()); assert!(MgmKey::get_protected(&mut yubikey).is_err());
assert!(yubikey.authenticate(MgmKey::default()).is_ok()); assert!(yubikey.authenticate(&default_key).is_ok());
// Set a protected management key. // Set a protected management key.
assert!(MgmKey::generate().set_protected(&mut yubikey).is_ok()); assert!(MgmKey::generate_for(&yubikey, &mut rng)
.unwrap()
.set_protected(&mut yubikey)
.is_ok());
let protected = MgmKey::get_protected(&mut yubikey).unwrap(); let protected = MgmKey::get_protected(&mut yubikey).unwrap();
assert!(yubikey.authenticate(MgmKey::default()).is_err()); assert!(yubikey.authenticate(&default_key).is_err());
assert!(yubikey.authenticate(protected.clone()).is_ok()); assert!(yubikey.authenticate(&protected).is_ok());
// Set a manual management key. // Set a manual management key.
let manual = MgmKey::generate(); let manual = MgmKey::generate_for(&yubikey, &mut rng).unwrap();
assert!(manual.set_manual(&mut yubikey, false).is_ok()); assert!(manual.set_manual(&mut yubikey, false).is_ok());
assert!(MgmKey::get_protected(&mut yubikey).is_err()); assert!(MgmKey::get_protected(&mut yubikey).is_err());
assert!(yubikey.authenticate(MgmKey::default()).is_err()); assert!(yubikey.authenticate(&default_key).is_err());
assert!(yubikey.authenticate(protected.clone()).is_err()); assert!(yubikey.authenticate(&protected).is_err());
assert!(yubikey.authenticate(manual.clone()).is_ok()); assert!(yubikey.authenticate(&manual).is_ok());
// Set back to the default management key. // Set back to the default management key.
assert!(MgmKey::set_default(&mut yubikey).is_ok()); assert!(MgmKey::set_default(&mut yubikey).is_ok());
assert!(MgmKey::get_protected(&mut yubikey).is_err()); assert!(MgmKey::get_protected(&mut yubikey).is_err());
assert!(yubikey.authenticate(protected).is_err()); assert!(yubikey.authenticate(&protected).is_err());
assert!(yubikey.authenticate(manual).is_err()); assert!(yubikey.authenticate(&manual).is_err());
assert!(yubikey.authenticate(MgmKey::default()).is_ok()); assert!(yubikey.authenticate(&default_key).is_ok());
} }
// //
@@ -148,9 +153,10 @@ fn test_set_mgmkey() {
fn generate_self_signed_cert<KT: yubikey_signer::KeyType>() -> Certificate { fn generate_self_signed_cert<KT: yubikey_signer::KeyType>() -> Certificate {
let mut yubikey = YUBIKEY.lock().unwrap(); let mut yubikey = YUBIKEY.lock().unwrap();
let default_key = MgmKey::get_default(&yubikey).unwrap();
assert!(yubikey.verify_pin(b"123456").is_ok()); assert!(yubikey.verify_pin(b"123456").is_ok());
assert!(yubikey.authenticate(MgmKey::default()).is_ok()); assert!(yubikey.authenticate(&default_key).is_ok());
let slot = SlotId::Retired(RetiredSlotId::R1); let slot = SlotId::Retired(RetiredSlotId::R1);
@@ -215,8 +221,9 @@ fn generate_self_signed_rsa_cert() {
fn generate_rsa3072() { fn generate_rsa3072() {
let mut yubikey = YUBIKEY.lock().unwrap(); let mut yubikey = YUBIKEY.lock().unwrap();
let version = yubikey.version(); let version = yubikey.version();
let default_key = MgmKey::get_default(&yubikey).unwrap();
assert!(yubikey.authenticate(MgmKey::default()).is_ok()); assert!(yubikey.authenticate(&default_key).is_ok());
let slot = SlotId::Retired(RetiredSlotId::R1); let slot = SlotId::Retired(RetiredSlotId::R1);
@@ -314,9 +321,10 @@ fn test_slot_id_display() {
#[ignore] #[ignore]
fn test_read_metadata() { fn test_read_metadata() {
let mut yubikey = YUBIKEY.lock().unwrap(); let mut yubikey = YUBIKEY.lock().unwrap();
let default_key = MgmKey::get_default(&yubikey).unwrap();
assert!(yubikey.verify_pin(b"123456").is_ok()); assert!(yubikey.verify_pin(b"123456").is_ok());
assert!(yubikey.authenticate(MgmKey::default()).is_ok()); assert!(yubikey.authenticate(&default_key).is_ok());
let slot = SlotId::Retired(RetiredSlotId::R1); let slot = SlotId::Retired(RetiredSlotId::R1);
@@ -344,9 +352,10 @@ fn test_read_metadata() {
#[ignore] #[ignore]
fn test_read_metadata_missing_key() { fn test_read_metadata_missing_key() {
let mut yubikey = YUBIKEY.lock().unwrap(); let mut yubikey = YUBIKEY.lock().unwrap();
let default_key = MgmKey::get_default(&yubikey).unwrap();
assert!(yubikey.verify_pin(b"123456").is_ok()); assert!(yubikey.verify_pin(b"123456").is_ok());
assert!(yubikey.authenticate(MgmKey::default()).is_ok()); assert!(yubikey.authenticate(&default_key).is_ok());
// we assume that at least one of these slots is empty // we assume that at least one of these slots is empty
let slots_to_check = [ let slots_to_check = [