CCCID/CHUID: add basic tests and do some cleanups

- Adds tests for CCCID/CHUID, allowing not found (is that ok?)
- Move constants under their respective modules and remove `YKPIV_`
This commit is contained in:
Tony Arcieri
2019-12-07 13:08:30 -08:00
parent 2587a4ac1e
commit 9482ae62ab
9 changed files with 142 additions and 59 deletions
+30 -10
View File
@@ -30,8 +30,21 @@
// (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, yubikey::YubiKey};
use crate::{error::Error, yubikey::YubiKey};
use getrandom::getrandom;
use std::fmt::{self, Debug};
/// CCCID size
pub const CCCID_SIZE: usize = 14;
/// CCC size
pub const CCC_SIZE: usize = 51;
/// CCCID offset
const CCC_ID_OFFS: usize = 9;
/// CCC Object ID
const OBJ_CAPABILITY: u32 = 0x005f_c107;
/// Cardholder Capability Container (CCC) Template
///
@@ -50,12 +63,12 @@ const CCC_TMPL: &[u8] = &[
/// Cardholder Capability Container (CCC) Identifier Card ID
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct CardId(pub [u8; YKPIV_CCCID_SIZE]);
pub struct CardId(pub [u8; CCCID_SIZE]);
impl CardId {
/// Generate a random CCC Card ID
pub fn generate() -> Result<Self, Error> {
let mut id = [0u8; YKPIV_CCCID_SIZE];
let mut id = [0u8; CCCID_SIZE];
getrandom(&mut id).map_err(|_| Error::RandomnessError)?;
Ok(Self(id))
}
@@ -63,36 +76,43 @@ impl CardId {
/// Cardholder Capability Container (CCC) Identifier
#[derive(Copy, Clone)]
pub struct CCC(pub [u8; YKPIV_CCC_SIZE]);
pub struct CCC(pub [u8; CCC_SIZE]);
impl CCC {
/// Return CardId component of CCC
pub fn cccid(&self) -> Result<CardId, Error> {
let mut cccid = [0u8; YKPIV_CCCID_SIZE];
cccid.copy_from_slice(&self.0[CCC_ID_OFFS..(CCC_ID_OFFS + YKPIV_CCCID_SIZE)]);
let mut cccid = [0u8; CCCID_SIZE];
cccid.copy_from_slice(&self.0[CCC_ID_OFFS..(CCC_ID_OFFS + CCCID_SIZE)]);
Ok(CardId(cccid))
}
/// Get Cardholder Capability Container (CCC) ID
pub fn get(yubikey: &mut YubiKey) -> Result<Self, Error> {
let txn = yubikey.begin_transaction()?;
let response = txn.fetch_object(YKPIV_OBJ_CAPABILITY)?;
let response = txn.fetch_object(OBJ_CAPABILITY)?;
if response.len() != CCC_TMPL.len() {
return Err(Error::GenericError);
}
let mut ccc = [0u8; YKPIV_CCC_SIZE];
ccc.copy_from_slice(&response[0..YKPIV_CCC_SIZE]);
let mut ccc = [0u8; CCC_SIZE];
ccc.copy_from_slice(&response[0..CCC_SIZE]);
Ok(Self(ccc))
}
/// Get Cardholder Capability Container (CCC) ID
#[cfg(feature = "untested")]
pub fn set(&self, yubikey: &mut YubiKey) -> Result<(), Error> {
let mut buf = CCC_TMPL.to_vec();
buf[0..self.0.len()].copy_from_slice(&self.0);
let txn = yubikey.begin_transaction()?;
txn.save_object(YKPIV_OBJ_CAPABILITY, &buf)
txn.save_object(OBJ_CAPABILITY, &buf)
}
}
impl Debug for CCC {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "CCC({:?})", &self.0[..])
}
}
+52 -18
View File
@@ -30,8 +30,33 @@
// (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, yubikey::YubiKey};
use crate::{error::Error, yubikey::YubiKey};
use getrandom::getrandom;
use std::fmt::{self, Debug};
/// CHUID size
pub const CHUID_SIZE: usize = 59;
/// CARDID size
pub const CARDID_SIZE: usize = 16;
/// FASC-N component size
pub const FASCN_SIZE: usize = 25;
/// Expiration size
pub const EXPIRATION_SIZE: usize = 8;
/// FASC-N offset
const CHUID_FASCN_OFFS: usize = 2;
/// GUID offset
const CHUID_GUID_OFFS: usize = 29;
/// Expiration offset
const CHUID_EXPIRATION_OFFS: usize = 47;
/// CHUID Object ID
const OBJ_CHUID: u32 = 0x005f_c102;
/// Cardholder Unique Identifier (CHUID) Template
///
@@ -57,12 +82,12 @@ const CHUID_TMPL: &[u8] = &[
/// Cardholder Unique Identifier (CHUID) Card UUID/GUID value
#[derive(Copy, Clone, Debug)]
pub struct Uuid(pub [u8; YKPIV_CARDID_SIZE]);
pub struct Uuid(pub [u8; CARDID_SIZE]);
impl Uuid {
/// Generate a random Cardholder Unique Identifier (CHUID)
/// Generate a random Cardholder Unique Identifier (CHUID) UUID
pub fn generate() -> Result<Self, Error> {
let mut id = [0u8; YKPIV_CARDID_SIZE];
let mut id = [0u8; CARDID_SIZE];
getrandom(&mut id).map_err(|_| Error::RandomnessError)?;
Ok(Self(id))
}
@@ -70,28 +95,29 @@ impl Uuid {
/// Cardholder Unique Identifier (CHUID)
#[derive(Copy, Clone)]
pub struct CHUID(pub [u8; YKPIV_CHUID_SIZE]);
pub struct CHUID(pub [u8; CHUID_SIZE]);
impl CHUID {
/// Return FASC-N component of CHUID
pub fn fascn(&self) -> Result<[u8; YKPIV_FASCN_SIZE], Error> {
let mut fascn = [0u8; YKPIV_FASCN_SIZE];
fascn.copy_from_slice(&self.0[CHUID_FASCN_OFFS..(CHUID_FASCN_OFFS + YKPIV_FASCN_SIZE)]);
pub fn fascn(&self) -> Result<[u8; FASCN_SIZE], Error> {
let mut fascn = [0u8; FASCN_SIZE];
fascn.copy_from_slice(&self.0[CHUID_FASCN_OFFS..(CHUID_FASCN_OFFS + FASCN_SIZE)]);
Ok(fascn)
}
/// Return Card UUID/GUID component of CHUID
pub fn uuid(&self) -> Result<[u8; YKPIV_CARDID_SIZE], Error> {
let mut uuid = [0u8; YKPIV_CARDID_SIZE];
uuid.copy_from_slice(&self.0[CHUID_GUID_OFFS..(CHUID_GUID_OFFS + YKPIV_CARDID_SIZE)]);
pub fn uuid(&self) -> Result<[u8; CARDID_SIZE], Error> {
let mut uuid = [0u8; CARDID_SIZE];
uuid.copy_from_slice(&self.0[CHUID_GUID_OFFS..(CHUID_GUID_OFFS + CARDID_SIZE)]);
Ok(uuid)
}
/// Return expiration date component of CHUID
pub fn expiration(&self) -> Result<[u8; YKPIV_EXPIRATION_SIZE], Error> {
let mut expiration = [0u8; YKPIV_EXPIRATION_SIZE];
// TODO(tarcieri): parse expiration?
pub fn expiration(&self) -> Result<[u8; EXPIRATION_SIZE], Error> {
let mut expiration = [0u8; EXPIRATION_SIZE];
expiration.copy_from_slice(
&self.0[CHUID_EXPIRATION_OFFS..(CHUID_EXPIRATION_OFFS + YKPIV_EXPIRATION_SIZE)],
&self.0[CHUID_EXPIRATION_OFFS..(CHUID_EXPIRATION_OFFS + EXPIRATION_SIZE)],
);
Ok(expiration)
}
@@ -99,24 +125,32 @@ impl CHUID {
/// Get Cardholder Unique Identifier (CHUID)
pub fn get(yubikey: &mut YubiKey) -> Result<CHUID, Error> {
let txn = yubikey.begin_transaction()?;
let response = txn.fetch_object(YKPIV_OBJ_CHUID)?;
let response = txn.fetch_object(OBJ_CHUID)?;
if response.len() != CHUID_TMPL.len() {
return Err(Error::GenericError);
}
let mut chuid = [0u8; YKPIV_CHUID_SIZE];
chuid.copy_from_slice(&response[0..YKPIV_CHUID_SIZE]);
let mut chuid = [0u8; CHUID_SIZE];
chuid.copy_from_slice(&response[0..CHUID_SIZE]);
let retval = CHUID { 0: chuid };
Ok(retval)
}
/// Set Cardholder Unique Identifier (CHUID)
#[cfg(feature = "untested")]
pub fn set(&self, yubikey: &mut YubiKey) -> Result<(), Error> {
let mut buf = CHUID_TMPL.to_vec();
buf[0..self.0.len()].copy_from_slice(&self.0);
let txn = yubikey.begin_transaction()?;
txn.save_object(YKPIV_OBJ_CHUID, &buf)
txn.save_object(OBJ_CHUID, &buf)
}
}
impl Debug for CHUID {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "CHUID({:?})", &self.0[..])
}
}
-16
View File
@@ -67,12 +67,6 @@ pub const CB_OBJ_TAG_MAX: usize = (CB_OBJ_TAG_MIN + 2); // 1 byte tag + 3 bytes
pub const CB_PAGE: usize = 4096;
pub const CB_PIN_MAX: usize = 8;
pub const CCC_ID_OFFS: usize = 9;
pub const CHUID_FASCN_OFFS: usize = 2;
pub const CHUID_GUID_OFFS: usize = 29;
pub const CHUID_EXPIRATION_OFFS: usize = 47;
pub const CHREF_ACT_CHANGE_PIN: i32 = 0;
pub const CHREF_ACT_UNBLOCK_PIN: i32 = 1;
pub const CHREF_ACT_CHANGE_PUK: i32 = 2;
@@ -128,21 +122,11 @@ pub const TAG_ECC_POINT: u8 = 0x86;
pub const YKPIV_ALGO_3DES: u8 = 0x03;
pub const YKPIV_CHUID_SIZE: usize = 59;
pub const YKPIV_CARDID_SIZE: usize = 16;
pub const YKPIV_FASCN_SIZE: usize = 25;
pub const YKPIV_EXPIRATION_SIZE: usize = 8;
pub const YKPIV_CCCID_SIZE: usize = 14;
pub const YKPIV_CCC_SIZE: usize = 51;
pub const YKPIV_CERTINFO_UNCOMPRESSED: u8 = 0;
pub const YKPIV_CERTINFO_GZIP: u8 = 1;
pub const YKPIV_KEY_CARDMGM: u8 = 0x9b;
pub const YKPIV_OBJ_CAPABILITY: u32 = 0x005f_c107;
pub const YKPIV_OBJ_CHUID: u32 = 0x005f_c102;
pub const YKPIV_OBJ_FINGERPRINTS: u32 = 0x005f_c103;
pub const YKPIV_OBJ_SECURITY: u32 = 0x005f_c106;
pub const YKPIV_OBJ_FACIAL: u32 = 0x005f_c108;
+5
View File
@@ -88,6 +88,9 @@ pub enum Error {
/// Not supported
NotSupported,
/// Not found
NotFound,
}
impl Error {
@@ -113,6 +116,7 @@ impl Error {
Error::ArgumentError => "YKPIV_ARGUMENT_ERROR",
Error::RangeError => "YKPIV_RANGE_ERROR",
Error::NotSupported => "YKPIV_NOT_SUPPORTED",
Error::NotFound => "<not found>",
}
}
@@ -135,6 +139,7 @@ impl Error {
Error::ArgumentError => "argument error",
Error::RangeError => "range error",
Error::NotSupported => "not supported",
Error::NotFound => "not found",
}
}
}
+1 -6
View File
@@ -136,10 +136,8 @@
)]
mod apdu;
#[cfg(feature = "untested")]
pub mod cccid;
pub mod certificate;
#[cfg(feature = "untested")]
pub mod chuid;
pub mod config;
pub mod consts;
@@ -159,10 +157,7 @@ pub mod settings;
mod transaction;
pub mod yubikey;
pub use self::{readers::Readers, yubikey::YubiKey};
#[cfg(feature = "untested")]
pub use self::{key::Key, mgm::MgmKey};
pub use self::{error::Error, key::Key, mgm::MgmKey, readers::Readers, yubikey::YubiKey};
/// Object identifiers
pub type ObjectId = u32;
+5 -1
View File
@@ -448,7 +448,11 @@ impl<'tx> Transaction<'tx> {
let response = self.transfer_data(&templ, &indata[..inlen], CB_BUF_MAX)?;
if !response.is_success() {
return Err(Error::GenericError);
if response.status_words() == StatusWords::NotFoundError {
return Err(Error::NotFound);
} else {
return Err(Error::GenericError);
}
}
let data = Buffer::new(response.data().into());
+13 -4
View File
@@ -31,6 +31,8 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
use crate::{
cccid::CCC,
chuid::CHUID,
config::Config,
error::Error,
readers::{Reader, Readers},
@@ -152,12 +154,9 @@ impl YubiKey {
}
error!("no YubiKey detected!");
Err(Error::GenericError)
Err(Error::NotFound)
}
/// Open a connection to a YubiKey with the given serial number.
/// Reconnect to a YubiKey
#[cfg(feature = "untested")]
pub fn reconnect(&mut self) -> Result<(), Error> {
@@ -209,6 +208,16 @@ impl YubiKey {
Config::get(self)
}
/// Get CHUID
pub fn chuid(&mut self) -> Result<CHUID, Error> {
CHUID::get(self)
}
/// Get CCCID
pub fn cccid(&mut self) -> Result<CCC, Error> {
CCC::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> {