Test Key::list

Adds a live-against-the-device test which ensures keys can be
successfully listed.
This commit is contained in:
Tony Arcieri
2019-12-07 09:42:18 -08:00
parent cb9d5221b2
commit d1d384d304
9 changed files with 55 additions and 37 deletions
+3 -3
View File
@@ -66,12 +66,12 @@ functions of the YubiKey:
|----|---------------|-------|-------------| |----|---------------|-------|-------------|
| 🚧 | `yubikey` | [#20] | Core functionality: auth, keys, PIN/PUK, encrypt, sign, attest | | 🚧 | `yubikey` | [#20] | Core functionality: auth, keys, PIN/PUK, encrypt, sign, attest |
| ⚠️ | `cccid` | [#21] | Cardholder Capability Container (CCC) IDs | | ⚠️ | `cccid` | [#21] | Cardholder Capability Container (CCC) IDs |
| | `certificate` | [#22] | Certificates for stored keys | | 🚧 | `certificate` | [#22] | Certificates for stored keys |
| ⚠️ | `chuid` | [#23] | Cardholder Unique Identifier (CHUID) | | ⚠️ | `chuid` | [#23] | Cardholder Unique Identifier (CHUID) |
| ⚠️ | `config` | [#24] | Support for reading on-key configuration | | ⚠️ | `config` | [#24] | Support for reading on-key configuration |
| ⚠️ | `container` | [#25] | MS Container Map Records | | ⚠️ | `container` | [#25] | MS Container Map Records |
| ⚠️ | `key` | [#26] | Crypto key management: list, generate, import | | 🚧 | `key` | [#26] | Crypto key management: list, generate, import |
| ⚠️ | `mgm` | [#26] | Management Key (MGM) support: set, get, derive | ⚠️ | `mgm` | [#26] | Management Key (MGM) support: set, get, derive |
| ⚠️ | `msroots` | [#28] | `msroots` file: PKCS#7 formatted certificate store for enterprise trusted roots | | ⚠️ | `msroots` | [#28] | `msroots` file: PKCS#7 formatted certificate store for enterprise trusted roots |
Legend: Legend:
-2
View File
@@ -71,7 +71,6 @@ impl APDU {
} }
/// Set this APDU's class /// Set this APDU's class
#[cfg(feature = "untested")]
pub fn cla(&mut self, value: u8) -> &mut Self { pub fn cla(&mut self, value: u8) -> &mut Self {
self.cla = value; self.cla = value;
self self
@@ -267,7 +266,6 @@ pub(crate) struct Response {
impl Response { impl Response {
/// Create a new response from the given status words and buffer /// Create a new response from the given status words and buffer
#[cfg(feature = "untested")]
pub fn new(status_words: StatusWords, data: Vec<u8>) -> Response { pub fn new(status_words: StatusWords, data: Vec<u8>) -> Response {
Response { status_words, data } Response { status_words, data }
} }
+3
View File
@@ -143,6 +143,7 @@ impl Certificate {
} }
/// Write this certificate into the YubiKey in the given slot /// Write this certificate into the YubiKey in the given slot
#[cfg(feature = "untested")]
pub fn write(&self, yubikey: &mut YubiKey, slot: SlotId, certinfo: u8) -> Result<(), Error> { pub fn write(&self, yubikey: &mut YubiKey, slot: SlotId, certinfo: u8) -> Result<(), Error> {
let max_size = yubikey.obj_size_max(); let max_size = yubikey.obj_size_max();
let txn = yubikey.begin_transaction()?; let txn = yubikey.begin_transaction()?;
@@ -150,6 +151,7 @@ impl Certificate {
} }
/// Delete a certificate located at the given slot of the given YubiKey /// Delete a certificate located at the given slot of the given YubiKey
#[cfg(feature = "untested")]
pub fn delete(yubikey: &mut YubiKey, slot: SlotId) -> Result<(), Error> { pub fn delete(yubikey: &mut YubiKey, slot: SlotId) -> Result<(), Error> {
let max_size = yubikey.obj_size_max(); let max_size = yubikey.obj_size_max();
let txn = yubikey.begin_transaction()?; let txn = yubikey.begin_transaction()?;
@@ -236,6 +238,7 @@ pub(crate) fn read_certificate(txn: &Transaction<'_>, slot: SlotId) -> Result<Bu
} }
/// Write certificate /// Write certificate
#[cfg(feature = "untested")]
pub(crate) fn write_certificate( pub(crate) fn write_certificate(
txn: &Transaction<'_>, txn: &Transaction<'_>,
slot: SlotId, slot: SlotId,
+28 -18
View File
@@ -38,18 +38,24 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
use crate::{ use crate::{
apdu::{Ins, StatusWords},
certificate::{self, Certificate}, certificate::{self, Certificate},
consts::*,
error::Error, error::Error,
yubikey::YubiKey,
ObjectId,
};
use log::debug;
use std::convert::TryFrom;
#[cfg(feature = "untested")]
use crate::{
apdu::{Ins, StatusWords},
consts::*,
policy::{PinPolicy, TouchPolicy}, policy::{PinPolicy, TouchPolicy},
serialization::*, serialization::*,
settings, settings, Buffer,
yubikey::YubiKey,
Buffer, ObjectId,
}; };
use log::{debug, error, warn}; #[cfg(feature = "untested")]
use std::convert::TryFrom; use log::{error, warn};
/// Slot identifiers. /// Slot identifiers.
/// <https://developers.yubico.com/PIV/Introduction/Certificate_slots.html> /// <https://developers.yubico.com/PIV/Introduction/Certificate_slots.html>
@@ -312,6 +318,7 @@ impl From<AlgorithmId> for u8 {
} }
} }
#[cfg(feature = "untested")]
impl AlgorithmId { impl AlgorithmId {
/// Writes the `AlgorithmId` in the format the YubiKey expects during key generation. /// Writes the `AlgorithmId` in the format the YubiKey expects during key generation.
pub(crate) fn write(self, buf: &mut [u8]) -> usize { pub(crate) fn write(self, buf: &mut [u8]) -> usize {
@@ -367,19 +374,9 @@ impl Key {
} }
} }
// Keygen messages
// TODO(tarcieri): extract these into an I18N-handling type?
const SZ_SETTING_ROCA: &str = "Enable_Unsafe_Keygen_ROCA";
const SZ_ROCA_ALLOW_USER: &str =
"was permitted by an end-user configuration setting, but is not recommended.";
const SZ_ROCA_ALLOW_ADMIN: &str =
"was permitted by an administrator configuration setting, but is not recommended.";
const SZ_ROCA_BLOCK_USER: &str = "was blocked due to an end-user configuration setting.";
const SZ_ROCA_BLOCK_ADMIN: &str = "was blocked due to an administrator configuration setting.";
const SZ_ROCA_DEFAULT: &str = "was permitted by default, but is not recommended. The default behavior will change in a future Yubico release.";
/// Information about a generated key /// Information about a generated key
// TODO(tarcieri): this could use some more work // TODO(tarcieri): this could use some more work
#[cfg(feature = "untested")]
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub enum GeneratedKey { pub enum GeneratedKey {
/// RSA keys /// RSA keys
@@ -403,6 +400,7 @@ pub enum GeneratedKey {
}, },
} }
#[cfg(feature = "untested")]
impl GeneratedKey { impl GeneratedKey {
/// Get the algorithm /// Get the algorithm
pub fn algorithm(&self) -> AlgorithmId { pub fn algorithm(&self) -> AlgorithmId {
@@ -414,6 +412,7 @@ impl GeneratedKey {
} }
/// Generate key /// Generate key
#[cfg(feature = "untested")]
#[allow(clippy::cognitive_complexity)] #[allow(clippy::cognitive_complexity)]
pub fn generate( pub fn generate(
yubikey: &mut YubiKey, yubikey: &mut YubiKey,
@@ -422,6 +421,17 @@ pub fn generate(
pin_policy: PinPolicy, pin_policy: PinPolicy,
touch_policy: TouchPolicy, touch_policy: TouchPolicy,
) -> Result<GeneratedKey, Error> { ) -> Result<GeneratedKey, Error> {
// Keygen messages
// TODO(tarcieri): extract these into an I18N-handling type?
const SZ_SETTING_ROCA: &str = "Enable_Unsafe_Keygen_ROCA";
const SZ_ROCA_ALLOW_USER: &str =
"was permitted by an end-user configuration setting, but is not recommended.";
const SZ_ROCA_ALLOW_ADMIN: &str =
"was permitted by an administrator configuration setting, but is not recommended.";
const SZ_ROCA_BLOCK_USER: &str = "was blocked due to an end-user configuration setting.";
const SZ_ROCA_BLOCK_ADMIN: &str = "was blocked due to an administrator configuration setting.";
const SZ_ROCA_DEFAULT: &str = "was permitted by default, but is not recommended. The default behavior will change in a future Yubico release.";
let setting_roca: settings::BoolValue; let setting_roca: settings::BoolValue;
match algorithm { match algorithm {
-4
View File
@@ -138,7 +138,6 @@
mod apdu; mod apdu;
#[cfg(feature = "untested")] #[cfg(feature = "untested")]
pub mod cccid; pub mod cccid;
#[cfg(feature = "untested")]
pub mod certificate; pub mod certificate;
#[cfg(feature = "untested")] #[cfg(feature = "untested")]
pub mod chuid; pub mod chuid;
@@ -148,7 +147,6 @@ pub mod consts;
#[cfg(feature = "untested")] #[cfg(feature = "untested")]
pub mod container; pub mod container;
pub mod error; pub mod error;
#[cfg(feature = "untested")]
pub mod key; pub mod key;
#[cfg(feature = "untested")] #[cfg(feature = "untested")]
mod metadata; mod metadata;
@@ -156,10 +154,8 @@ mod metadata;
pub mod mgm; pub mod mgm;
#[cfg(feature = "untested")] #[cfg(feature = "untested")]
pub mod msroots; pub mod msroots;
#[cfg(feature = "untested")]
pub mod policy; pub mod policy;
pub mod readers; pub mod readers;
#[cfg(feature = "untested")]
mod serialization; mod serialization;
#[cfg(feature = "untested")] #[cfg(feature = "untested")]
pub mod settings; pub mod settings;
+2
View File
@@ -35,6 +35,7 @@ impl From<PinPolicy> for u8 {
impl PinPolicy { impl PinPolicy {
/// Writes the `PinPolicy` in the format the YubiKey expects during key generation or /// Writes the `PinPolicy` in the format the YubiKey expects during key generation or
/// importation. /// importation.
#[cfg(feature = "untested")]
pub(crate) fn write(self, buf: &mut [u8]) -> usize { pub(crate) fn write(self, buf: &mut [u8]) -> usize {
match self { match self {
PinPolicy::Default => 0, PinPolicy::Default => 0,
@@ -84,6 +85,7 @@ impl From<TouchPolicy> for u8 {
impl TouchPolicy { impl TouchPolicy {
/// Writes the `TouchPolicy` in the format the YubiKey expects during key generation /// Writes the `TouchPolicy` in the format the YubiKey expects during key generation
/// or importation. /// or importation.
#[cfg(feature = "untested")]
pub(crate) fn write(self, buf: &mut [u8]) -> usize { pub(crate) fn write(self, buf: &mut [u8]) -> usize {
match self { match self {
TouchPolicy::Default => 0, TouchPolicy::Default => 0,
+1
View File
@@ -35,6 +35,7 @@ use crate::{consts::*, ObjectId};
// TODO(tarcieri): refactor these into better serializers/message builders // TODO(tarcieri): refactor these into better serializers/message builders
/// Set length /// Set length
#[cfg(feature = "untested")]
pub(crate) fn set_length(buffer: &mut [u8], length: usize) -> usize { pub(crate) fn set_length(buffer: &mut [u8], length: usize) -> usize {
if length < 0x80 { if length < 0x80 {
buffer[0] = length as u8; buffer[0] = length as u8;
+8 -9
View File
@@ -1,23 +1,24 @@
//! YubiKey PC/SC transactions //! YubiKey PC/SC transactions
#[cfg(feature = "untested")]
use crate::{ use crate::{
apdu::Response, apdu::Response,
key::{AlgorithmId, SlotId},
mgm::MgmKey,
serialization::*,
Buffer, ObjectId,
};
use crate::{
apdu::{Ins, StatusWords, APDU}, apdu::{Ins, StatusWords, APDU},
consts::*, consts::*,
error::Error, error::Error,
serialization::*,
yubikey::*, yubikey::*,
Buffer, ObjectId,
}; };
use log::{error, trace}; use log::{error, trace};
use std::convert::TryInto; use std::convert::TryInto;
use zeroize::Zeroizing; use zeroize::Zeroizing;
#[cfg(feature = "untested")]
use crate::{
key::{AlgorithmId, SlotId},
mgm::MgmKey,
};
/// Exclusive transaction with the YubiKey's PC/SC card. /// Exclusive transaction with the YubiKey's PC/SC card.
pub(crate) struct Transaction<'tx> { pub(crate) struct Transaction<'tx> {
inner: pcsc::Transaction<'tx>, inner: pcsc::Transaction<'tx>,
@@ -364,7 +365,6 @@ impl<'tx> Transaction<'tx> {
/// messages into smaller APDU-sized messages (using the provided APDU /// messages into smaller APDU-sized messages (using the provided APDU
/// template to construct them), and then sending those via /// template to construct them), and then sending those via
/// [`Transaction::transmit`]. /// [`Transaction::transmit`].
#[cfg(feature = "untested")]
pub fn transfer_data( pub fn transfer_data(
&self, &self,
templ: &[u8], templ: &[u8],
@@ -448,7 +448,6 @@ impl<'tx> Transaction<'tx> {
} }
/// Fetch an object. /// Fetch an object.
#[cfg(feature = "untested")]
pub fn fetch_object(&self, object_id: ObjectId) -> Result<Buffer, Error> { pub fn fetch_object(&self, object_id: ObjectId) -> Result<Buffer, Error> {
let mut indata = [0u8; 5]; let mut indata = [0u8; 5];
let templ = [0, Ins::GetData.code(), 0x3f, 0xff]; let templ = [0, Ins::GetData.code(), 0x3f, 0xff];
+10 -1
View File
@@ -5,7 +5,7 @@
use lazy_static::lazy_static; use lazy_static::lazy_static;
use std::{env, sync::Mutex}; use std::{env, sync::Mutex};
use yubikey_piv::YubiKey; use yubikey_piv::{key::Key, YubiKey};
lazy_static! { lazy_static! {
/// Provide thread-safe access to a YubiKey /// Provide thread-safe access to a YubiKey
@@ -29,3 +29,12 @@ fn test_verify_pin() {
assert!(yubikey.verify_pin(b"000000").is_err()); assert!(yubikey.verify_pin(b"000000").is_err());
assert!(yubikey.verify_pin(b"123456").is_ok()); assert!(yubikey.verify_pin(b"123456").is_ok());
} }
#[test]
#[ignore]
fn test_list_keys() {
let mut yubikey = YUBIKEY.lock().unwrap();
let keys_result = Key::list(&mut yubikey);
assert!(keys_result.is_ok());
dbg!(keys_result.unwrap());
}