From cdecfd92dd2a65b082eefc3a99268f5732e56cac Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Sat, 7 Dec 2019 11:40:25 -0800 Subject: [PATCH] Test `Config::get` Tests reading configuration from a live device: Config { protected_data_available: false, puk_blocked: false, puk_noblock_on_upgrade: false, pin_last_changed: 0, mgm_type: Manual } --- README.md | 3 ++- src/config.rs | 20 ++++++++++++++------ src/lib.rs | 3 --- src/metadata.rs | 5 +++++ src/mgm.rs | 24 ++++++++++++++++++------ src/yubikey.rs | 6 ++++++ tests/integration.rs | 27 ++++++++++++++++++++++++--- 7 files changed, 69 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 6f02ac7..1242f42 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ functions of the YubiKey: | ⚠️ | `cccid` | [#21] | Cardholder Capability Container (CCC) IDs | | 🚧️ | `certificate` | [#22] | Certificates for stored keys | | ⚠️ | `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 | | 🚧 | `key` | [#26] | Crypto key management: list, generate, import | | ⚠️ | `mgm` | [#26] | Management Key (MGM) support: set, get, derive | @@ -78,6 +78,7 @@ Legend: | | Description | |----|------------------------------------| +| ✅ | Working | | 🚧 | Testing and validation in progress | | ⚠️ | Untested support | diff --git a/src/config.rs b/src/config.rs index a368754..b81c673 100644 --- a/src/config.rs +++ b/src/config.rs @@ -32,10 +32,13 @@ use crate::{consts::*, error::Error, metadata, mgm::MgmType, yubikey::YubiKey}; use log::error; -use std::convert::TryInto; +use std::{ + convert::TryInto, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; /// Config -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub struct Config { /// Protected data available protected_data_available: bool, @@ -47,7 +50,7 @@ pub struct Config { puk_noblock_on_upgrade: bool, /// PIN last changed - pin_last_changed: u32, + pin_last_changed: Option, /// MGM type mgm_type: MgmType, @@ -60,7 +63,7 @@ impl Config { protected_data_available: false, puk_blocked: false, puk_noblock_on_upgrade: false, - pin_last_changed: 0, + pin_last_changed: None, mgm_type: MgmType::Manual, }; @@ -93,8 +96,13 @@ impl Config { if item.len() != CB_ADMIN_TIMESTAMP { error!("pin timestamp in admin metadata is an invalid size"); } else { - // TODO(tarcieri): double check this is little endian - config.pin_last_changed = u32::from_le_bytes(item.try_into().unwrap()); + // TODO(tarcieri): double-check endianness is correct + let pin_last_changed = u32::from_le_bytes(item.try_into().unwrap()); + + if pin_last_changed != 0 { + config.pin_last_changed = + Some(UNIX_EPOCH + Duration::from_secs(pin_last_changed as u64)); + } } } } diff --git a/src/lib.rs b/src/lib.rs index 65ea3cc..0f5a51c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -141,16 +141,13 @@ pub mod cccid; pub mod certificate; #[cfg(feature = "untested")] pub mod chuid; -#[cfg(feature = "untested")] pub mod config; pub mod consts; #[cfg(feature = "untested")] pub mod container; pub mod error; pub mod key; -#[cfg(feature = "untested")] mod metadata; -#[cfg(feature = "untested")] pub mod mgm; #[cfg(feature = "untested")] pub mod msroots; diff --git a/src/metadata.rs b/src/metadata.rs index 869f068..14a88d3 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -31,6 +31,8 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::{consts::*, error::Error, serialization::*, transaction::Transaction, Buffer}; + +#[cfg(feature = "untested")] use zeroize::Zeroizing; /// Get metadata item @@ -64,6 +66,7 @@ pub(crate) fn get_item(data: &[u8], tag: u8) -> Result<&[u8], Error> { } /// Set metadata item +#[cfg(feature = "untested")] pub(crate) fn set_item( data: &mut [u8], pcb_data: &mut usize, @@ -191,6 +194,7 @@ pub(crate) fn read(txn: &Transaction<'_>, tag: u8) -> Result { } /// Write metadata +#[cfg(feature = "untested")] pub(crate) fn write(txn: &Transaction<'_>, tag: u8, data: &[u8]) -> Result<(), Error> { let mut buf = Zeroizing::new(vec![0u8; CB_OBJ_MAX]); @@ -218,6 +222,7 @@ pub(crate) fn write(txn: &Transaction<'_>, tag: u8, data: &[u8]) -> Result<(), E } /// Get the size of a length tag for the given length +#[cfg(feature = "untested")] fn get_length_size(length: usize) -> usize { if length < 0x80 { 1 diff --git a/src/mgm.rs b/src/mgm.rs index d9e33d2..42c20fc 100644 --- a/src/mgm.rs +++ b/src/mgm.rs @@ -30,18 +30,25 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{consts::*, error::Error, metadata, yubikey::YubiKey}; +use crate::{consts::*, error::Error}; +use getrandom::getrandom; +use log::error; +use std::convert::{TryFrom, TryInto}; +use zeroize::{Zeroize, Zeroizing}; + +#[cfg(feature = "untested")] +use crate::{metadata, yubikey::YubiKey}; +#[cfg(feature = "untested")] use des::{ block_cipher_trait::{generic_array::GenericArray, BlockCipher}, TdesEde3, }; -use getrandom::getrandom; +#[cfg(feature = "untested")] use hmac::Hmac; -use log::error; +#[cfg(feature = "untested")] use pbkdf2::pbkdf2; +#[cfg(feature = "untested")] use sha1::Sha1; -use std::convert::{TryFrom, TryInto}; -use zeroize::{Zeroize, Zeroizing}; /// Default MGM key configured on all YubiKeys const DEFAULT_MGM_KEY: [u8; DES_LEN_3DES] = [ @@ -50,7 +57,6 @@ const DEFAULT_MGM_KEY: [u8; DES_LEN_3DES] = [ /// Management Key (MGM) key types (manual/derived/protected) #[derive(Copy, Clone, Debug, Eq, PartialEq)] -#[allow(non_camel_case_types)] pub enum MgmType { /// Manual Manual = 0, @@ -107,6 +113,7 @@ impl MgmKey { } /// Get derived management key (MGM) + #[cfg(feature = "untested")] pub fn get_derived(yubikey: &mut YubiKey, pin: &[u8]) -> Result { let txn = yubikey.begin_transaction()?; @@ -131,6 +138,7 @@ impl MgmKey { } /// Get protected management key (MGM) + #[cfg(feature = "untested")] pub fn get_protected(yubikey: &mut YubiKey) -> Result { let txn = yubikey.begin_transaction()?; @@ -158,12 +166,14 @@ impl MgmKey { } /// Set the management key (MGM) + #[cfg(feature = "untested")] pub fn set(&self, yubikey: &mut YubiKey, touch: Option) -> Result<(), Error> { let txn = yubikey.begin_transaction()?; txn.set_mgm_key(&self, touch) } /// Set protected management key (MGM) + #[cfg(feature = "untested")] pub fn set_protected(&self, yubikey: &mut YubiKey) -> Result<(), Error> { let mut data = Zeroizing::new(vec![0u8; CB_BUF_MAX]); @@ -254,6 +264,7 @@ impl MgmKey { } /// Encrypt with 3DES key + #[cfg(feature = "untested")] #[allow(clippy::trivially_copy_pass_by_ref)] pub(crate) fn encrypt(&self, input: &[u8; DES_LEN_DES]) -> [u8; DES_LEN_DES] { let mut output = input.to_owned(); @@ -263,6 +274,7 @@ impl MgmKey { } /// Decrypt with 3DES key + #[cfg(feature = "untested")] #[allow(clippy::trivially_copy_pass_by_ref)] pub(crate) fn decrypt(&self, input: &[u8; DES_LEN_DES]) -> [u8; DES_LEN_DES] { let mut output = input.to_owned(); diff --git a/src/yubikey.rs b/src/yubikey.rs index 2e619b6..177fb4a 100644 --- a/src/yubikey.rs +++ b/src/yubikey.rs @@ -31,6 +31,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::{ + config::Config, error::Error, readers::{Reader, Readers}, transaction::Transaction, @@ -200,6 +201,11 @@ impl YubiKey { self.serial } + /// Get device configuration. + pub fn config(&mut self) -> Result { + Config::get(self) + } + /// Authenticate to the card using the provided management key (MGM). #[cfg(feature = "untested")] pub fn authenticate(&mut self, mgm_key: MgmKey) -> Result<(), Error> { diff --git a/tests/integration.rs b/tests/integration.rs index 78b17a3..62a24cc 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -27,14 +27,23 @@ fn init_yubikey() -> Mutex { Mutex::new(yubikey) } +// +// Device config support +// + #[test] #[ignore] -fn test_verify_pin() { +fn test_get_config() { let mut yubikey = YUBIKEY.lock().unwrap(); - assert!(yubikey.verify_pin(b"000000").is_err()); - assert!(yubikey.verify_pin(b"123456").is_ok()); + let config_result = yubikey.config(); + assert!(config_result.is_ok()); + trace!("config: {:?}", config_result.unwrap()); } +// +// Cryptographic key support +// + #[test] #[ignore] fn test_list_keys() { @@ -43,3 +52,15 @@ fn test_list_keys() { assert!(keys_result.is_ok()); trace!("keys: {:?}", keys_result.unwrap()); } + +// +// PIN support +// + +#[test] +#[ignore] +fn test_verify_pin() { + let mut yubikey = YUBIKEY.lock().unwrap(); + assert!(yubikey.verify_pin(b"000000").is_err()); + assert!(yubikey.verify_pin(b"123456").is_ok()); +}