From 1fc807fdcbaa5c4e34f22a7e485c158cf5337811 Mon Sep 17 00:00:00 2001 From: Nazar Serhiichuk <43041209+G1gg1L3s@users.noreply.github.com> Date: Thu, 14 Aug 2025 16:37:57 +0300 Subject: [PATCH] Handle reference data not found in metadata command (#558) --- CHANGELOG.md | 1 + src/apdu.rs | 5 +++++ src/transaction.rs | 17 ++++++++-------- tests/integration.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f41d73e..1509cf0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `ManagementAlgorithmId` has been renamed to `SlotAlgorithmId`, and its `ThreeDes` variant has been replaced by `SlotAlgorithmId::Management` containing a `yubikey::MgmAlgorithmId`. +- Metadata command returns `Error:NotFound` instead of `Error::GenericError` when the object doesn't exist ([#558]). ## 0.8.0 (2023-08-15) ### Added diff --git a/src/apdu.rs b/src/apdu.rs index 28267e8..3d23530 100644 --- a/src/apdu.rs +++ b/src/apdu.rs @@ -421,6 +421,9 @@ pub(crate) enum StatusWords { /// Not enough memory NoSpaceError, + /// Referenced data or reference data not found + ReferenceDataNotFoundError, + // // Custom Yubico Status Word extensions // @@ -454,6 +457,7 @@ impl StatusWords { StatusWords::IncorrectParamError => 0x6a80, StatusWords::NotFoundError => 0x6a82, StatusWords::NoSpaceError => 0x6a84, + StatusWords::ReferenceDataNotFoundError => 0x6a88, StatusWords::IncorrectSlotError => 0x6b00, StatusWords::NotSupportedError => 0x6d00, StatusWords::CommandAbortedError => 0x6f00, @@ -488,6 +492,7 @@ impl From for StatusWords { 0x6a80 => StatusWords::IncorrectParamError, 0x6a82 => StatusWords::NotFoundError, 0x6a84 => StatusWords::NoSpaceError, + 0x6a88 => StatusWords::ReferenceDataNotFoundError, 0x6b00 => StatusWords::IncorrectSlotError, 0x6d00 => StatusWords::NotSupportedError, 0x6f00 => StatusWords::CommandAbortedError, diff --git a/src/transaction.rs b/src/transaction.rs index ec89994..90bfbb3 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -163,17 +163,16 @@ impl<'tx> Transaction<'tx> { .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); + match response.status_words() { + StatusWords::Success => { + let buf = Buffer::new(response.data().into()); + piv::SlotMetadata::try_from(buf) } + StatusWords::ReferenceDataNotFoundError => Err(Error::NotFound), + // Requires firmware 5.2.3 + StatusWords::NotSupportedError => Err(Error::NotSupported), + _ => Err(Error::GenericError), } - - let buf = Buffer::new(response.data().into()); - - piv::SlotMetadata::try_from(buf) } /// Verify device PIN. diff --git a/tests/integration.rs b/tests/integration.rs index ebbab60..e3c7164 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -340,6 +340,52 @@ fn test_read_metadata() { } } +#[test] +#[ignore] +fn test_read_metadata_missing_key() { + let mut yubikey = YUBIKEY.lock().unwrap(); + + assert!(yubikey.verify_pin(b"123456").is_ok()); + assert!(yubikey.authenticate(MgmKey::default()).is_ok()); + + // we assume that at least one of these slots is empty + let slots_to_check = [ + RetiredSlotId::R10, + RetiredSlotId::R11, + RetiredSlotId::R12, + RetiredSlotId::R13, + RetiredSlotId::R14, + RetiredSlotId::R15, + RetiredSlotId::R16, + RetiredSlotId::R17, + RetiredSlotId::R18, + RetiredSlotId::R19, + RetiredSlotId::R20, + ]; + + for slot in slots_to_check { + let slot = SlotId::Retired(slot); + + match piv::metadata(&mut yubikey, slot) { + Ok(_) => { + eprintln!("Key {} exists", slot); + } + Err(Error::NotSupported) => { + // Some YubiKeys don't support metadata + eprintln!("metadata not supported by this YubiKey"); + return; + } + Err(Error::NotFound) => { + eprintln!("Key {} doesn't exist, ok.", slot); + return; + } + Err(err) => panic!("{}", err), + } + } + + panic!("No empty slots to check"); +} + #[test] #[ignore] fn test_parse_cert_from_der() {