Merge pull request #64 from iqlusioninc/config-tests

Test `Config::get`
This commit is contained in:
Tony Arcieri
2019-12-07 12:10:24 -08:00
committed by GitHub
7 changed files with 69 additions and 19 deletions
+2 -1
View File
@@ -68,7 +68,7 @@ functions of the YubiKey:
| ⚠️ | `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 |
@@ -78,6 +78,7 @@ Legend:
| | Description | | | Description |
|----|------------------------------------| |----|------------------------------------|
| ✅ | Working |
| 🚧 | Testing and validation in progress | | 🚧 | Testing and validation in progress |
| ⚠️ | Untested support | | ⚠️ | Untested support |
+14 -6
View File
@@ -32,10 +32,13 @@
use crate::{consts::*, error::Error, metadata, mgm::MgmType, yubikey::YubiKey}; use crate::{consts::*, error::Error, metadata, mgm::MgmType, yubikey::YubiKey};
use log::error; use log::error;
use std::convert::TryInto; use std::{
convert::TryInto,
time::{Duration, SystemTime, UNIX_EPOCH},
};
/// Config /// Config
#[derive(Copy, Clone)] #[derive(Copy, Clone, Debug)]
pub struct Config { pub struct Config {
/// Protected data available /// Protected data available
protected_data_available: bool, protected_data_available: bool,
@@ -47,7 +50,7 @@ pub struct Config {
puk_noblock_on_upgrade: bool, puk_noblock_on_upgrade: bool,
/// PIN last changed /// PIN last changed
pin_last_changed: u32, pin_last_changed: Option<SystemTime>,
/// MGM type /// MGM type
mgm_type: MgmType, mgm_type: MgmType,
@@ -60,7 +63,7 @@ impl Config {
protected_data_available: false, protected_data_available: false,
puk_blocked: false, puk_blocked: false,
puk_noblock_on_upgrade: false, puk_noblock_on_upgrade: false,
pin_last_changed: 0, pin_last_changed: None,
mgm_type: MgmType::Manual, mgm_type: MgmType::Manual,
}; };
@@ -93,8 +96,13 @@ impl Config {
if item.len() != CB_ADMIN_TIMESTAMP { if item.len() != CB_ADMIN_TIMESTAMP {
error!("pin timestamp in admin metadata is an invalid size"); error!("pin timestamp in admin metadata is an invalid size");
} else { } else {
// TODO(tarcieri): double check this is little endian // TODO(tarcieri): double-check endianness is correct
config.pin_last_changed = u32::from_le_bytes(item.try_into().unwrap()); 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));
}
} }
} }
} }
-3
View File
@@ -141,16 +141,13 @@ pub mod cccid;
pub mod certificate; pub mod certificate;
#[cfg(feature = "untested")] #[cfg(feature = "untested")]
pub mod chuid; pub mod chuid;
#[cfg(feature = "untested")]
pub mod config; pub mod config;
pub mod consts; pub mod consts;
#[cfg(feature = "untested")] #[cfg(feature = "untested")]
pub mod container; pub mod container;
pub mod error; pub mod error;
pub mod key; pub mod key;
#[cfg(feature = "untested")]
mod metadata; mod metadata;
#[cfg(feature = "untested")]
pub mod mgm; pub mod mgm;
#[cfg(feature = "untested")] #[cfg(feature = "untested")]
pub mod msroots; pub mod msroots;
+5
View File
@@ -31,6 +31,8 @@
// 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::{consts::*, error::Error, serialization::*, transaction::Transaction, Buffer}; use crate::{consts::*, error::Error, serialization::*, transaction::Transaction, Buffer};
#[cfg(feature = "untested")]
use zeroize::Zeroizing; use zeroize::Zeroizing;
/// Get metadata item /// Get metadata item
@@ -64,6 +66,7 @@ pub(crate) fn get_item(data: &[u8], tag: u8) -> Result<&[u8], Error> {
} }
/// Set metadata item /// Set metadata item
#[cfg(feature = "untested")]
pub(crate) fn set_item( pub(crate) fn set_item(
data: &mut [u8], data: &mut [u8],
pcb_data: &mut usize, pcb_data: &mut usize,
@@ -191,6 +194,7 @@ pub(crate) fn read(txn: &Transaction<'_>, tag: u8) -> Result<Buffer, Error> {
} }
/// Write metadata /// Write metadata
#[cfg(feature = "untested")]
pub(crate) fn write(txn: &Transaction<'_>, tag: u8, data: &[u8]) -> Result<(), Error> { pub(crate) fn write(txn: &Transaction<'_>, tag: u8, data: &[u8]) -> Result<(), Error> {
let mut buf = Zeroizing::new(vec![0u8; CB_OBJ_MAX]); 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 /// Get the size of a length tag for the given length
#[cfg(feature = "untested")]
fn get_length_size(length: usize) -> usize { fn get_length_size(length: usize) -> usize {
if length < 0x80 { if length < 0x80 {
1 1
+18 -6
View File
@@ -30,18 +30,25 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// 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::{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::{ use des::{
block_cipher_trait::{generic_array::GenericArray, BlockCipher}, block_cipher_trait::{generic_array::GenericArray, BlockCipher},
TdesEde3, TdesEde3,
}; };
use getrandom::getrandom; #[cfg(feature = "untested")]
use hmac::Hmac; use hmac::Hmac;
use log::error; #[cfg(feature = "untested")]
use pbkdf2::pbkdf2; use pbkdf2::pbkdf2;
#[cfg(feature = "untested")]
use sha1::Sha1; use sha1::Sha1;
use std::convert::{TryFrom, TryInto};
use zeroize::{Zeroize, Zeroizing};
/// Default MGM key configured on all YubiKeys /// Default MGM key configured on all YubiKeys
const DEFAULT_MGM_KEY: [u8; DES_LEN_3DES] = [ 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) /// Management Key (MGM) key types (manual/derived/protected)
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[allow(non_camel_case_types)]
pub enum MgmType { pub enum MgmType {
/// Manual /// Manual
Manual = 0, Manual = 0,
@@ -107,6 +113,7 @@ impl MgmKey {
} }
/// Get derived management key (MGM) /// Get derived management key (MGM)
#[cfg(feature = "untested")]
pub fn get_derived(yubikey: &mut YubiKey, pin: &[u8]) -> Result<Self, Error> { pub fn get_derived(yubikey: &mut YubiKey, pin: &[u8]) -> Result<Self, Error> {
let txn = yubikey.begin_transaction()?; let txn = yubikey.begin_transaction()?;
@@ -131,6 +138,7 @@ impl MgmKey {
} }
/// Get protected management key (MGM) /// Get protected management key (MGM)
#[cfg(feature = "untested")]
pub fn get_protected(yubikey: &mut YubiKey) -> Result<Self, Error> { pub fn get_protected(yubikey: &mut YubiKey) -> Result<Self, Error> {
let txn = yubikey.begin_transaction()?; let txn = yubikey.begin_transaction()?;
@@ -158,12 +166,14 @@ impl MgmKey {
} }
/// Set the management key (MGM) /// Set the management key (MGM)
#[cfg(feature = "untested")]
pub fn set(&self, yubikey: &mut YubiKey, touch: Option<u8>) -> Result<(), Error> { pub fn set(&self, yubikey: &mut YubiKey, touch: Option<u8>) -> Result<(), Error> {
let txn = yubikey.begin_transaction()?; let txn = yubikey.begin_transaction()?;
txn.set_mgm_key(&self, touch) txn.set_mgm_key(&self, touch)
} }
/// Set protected management key (MGM) /// Set protected management key (MGM)
#[cfg(feature = "untested")]
pub fn set_protected(&self, yubikey: &mut YubiKey) -> Result<(), Error> { pub fn set_protected(&self, yubikey: &mut YubiKey) -> Result<(), Error> {
let mut data = Zeroizing::new(vec![0u8; CB_BUF_MAX]); let mut data = Zeroizing::new(vec![0u8; CB_BUF_MAX]);
@@ -254,6 +264,7 @@ impl MgmKey {
} }
/// Encrypt with 3DES key /// Encrypt with 3DES key
#[cfg(feature = "untested")]
#[allow(clippy::trivially_copy_pass_by_ref)] #[allow(clippy::trivially_copy_pass_by_ref)]
pub(crate) fn encrypt(&self, input: &[u8; DES_LEN_DES]) -> [u8; DES_LEN_DES] { pub(crate) fn encrypt(&self, input: &[u8; DES_LEN_DES]) -> [u8; DES_LEN_DES] {
let mut output = input.to_owned(); let mut output = input.to_owned();
@@ -263,6 +274,7 @@ impl MgmKey {
} }
/// Decrypt with 3DES key /// Decrypt with 3DES key
#[cfg(feature = "untested")]
#[allow(clippy::trivially_copy_pass_by_ref)] #[allow(clippy::trivially_copy_pass_by_ref)]
pub(crate) fn decrypt(&self, input: &[u8; DES_LEN_DES]) -> [u8; DES_LEN_DES] { pub(crate) fn decrypt(&self, input: &[u8; DES_LEN_DES]) -> [u8; DES_LEN_DES] {
let mut output = input.to_owned(); let mut output = input.to_owned();
+6
View File
@@ -31,6 +31,7 @@
// 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::{
config::Config,
error::Error, error::Error,
readers::{Reader, Readers}, readers::{Reader, Readers},
transaction::Transaction, transaction::Transaction,
@@ -200,6 +201,11 @@ impl YubiKey {
self.serial self.serial
} }
/// Get device configuration.
pub fn config(&mut self) -> Result<Config, Error> {
Config::get(self)
}
/// Authenticate to the card using the provided management key (MGM). /// Authenticate to the card using the provided management key (MGM).
#[cfg(feature = "untested")] #[cfg(feature = "untested")]
pub fn authenticate(&mut self, mgm_key: MgmKey) -> Result<(), Error> { pub fn authenticate(&mut self, mgm_key: MgmKey) -> Result<(), Error> {
+24 -3
View File
@@ -27,14 +27,23 @@ fn init_yubikey() -> Mutex<YubiKey> {
Mutex::new(yubikey) Mutex::new(yubikey)
} }
//
// Device config support
//
#[test] #[test]
#[ignore] #[ignore]
fn test_verify_pin() { fn test_get_config() {
let mut yubikey = YUBIKEY.lock().unwrap(); let mut yubikey = YUBIKEY.lock().unwrap();
assert!(yubikey.verify_pin(b"000000").is_err()); let config_result = yubikey.config();
assert!(yubikey.verify_pin(b"123456").is_ok()); assert!(config_result.is_ok());
trace!("config: {:?}", config_result.unwrap());
} }
//
// Cryptographic key support
//
#[test] #[test]
#[ignore] #[ignore]
fn test_list_keys() { fn test_list_keys() {
@@ -43,3 +52,15 @@ fn test_list_keys() {
assert!(keys_result.is_ok()); assert!(keys_result.is_ok());
trace!("keys: {:?}", keys_result.unwrap()); 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());
}