From 104020d518788fc469192c19cb54a2289dd1786b Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Sun, 8 Dec 2019 08:39:21 -0800 Subject: [PATCH] consts: Whittle down to the essentials This factors the junk drawer of constants into the relevant files. There are still a few "global" ones left but they can be addressed in a followup commit. --- src/certificate.rs | 7 +++- src/config.rs | 3 ++ src/consts.rs | 85 +------------------------------------------- src/container.rs | 14 ++++++-- src/key.rs | 5 +++ src/lib.rs | 2 +- src/metadata.rs | 11 +++--- src/mgm.rs | 17 +++++++-- src/msroots.rs | 19 ++++++---- src/serialization.rs | 8 +++-- src/transaction.rs | 8 +++-- src/yubikey.rs | 34 +++++++++++++++--- 12 files changed, 102 insertions(+), 111 deletions(-) diff --git a/src/certificate.rs b/src/certificate.rs index 38709c4..3372ac0 100644 --- a/src/certificate.rs +++ b/src/certificate.rs @@ -55,6 +55,11 @@ const OID_EC_PUBLIC_KEY: &str = "1.2.840.10045.2.1"; const OID_NIST_P256: &str = "1.2.840.10045.3.1.7"; const OID_NIST_P384: &str = "1.3.132.0.34"; +#[allow(dead_code)] +const CERTINFO_UNCOMPRESSED: u8 = 0; +#[cfg(feature = "untested")] +const CERTINFO_GZIP: u8 = 1; + /// Information about a public key within a [`Certificate`]. #[derive(Clone, Eq, PartialEq)] pub enum PublicKeyInfo { @@ -273,7 +278,7 @@ pub(crate) fn write_certificate( // write compression info and LRC trailer buf[offset] = TAG_CERT_COMPRESS; buf[offset + 1] = 0x01; - buf[offset + 2] = if certinfo == YKPIV_CERTINFO_GZIP { + buf[offset + 2] = if certinfo == CERTINFO_GZIP { 0x01 } else { 0x00 diff --git a/src/config.rs b/src/config.rs index b81c673..1720f70 100644 --- a/src/config.rs +++ b/src/config.rs @@ -37,6 +37,9 @@ use std::{ time::{Duration, SystemTime, UNIX_EPOCH}, }; +const CB_ADMIN_TIMESTAMP: usize = 0x04; +const PROTECTED_FLAGS_1_PUK_NOBLOCK: u8 = 0x01; + /// Config #[derive(Copy, Clone, Debug)] pub struct Config { diff --git a/src/consts.rs b/src/consts.rs index 6589472..39af892 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -33,75 +33,17 @@ // TODO(tarcieri): document these! #![allow(missing_docs, non_upper_case_globals)] - -pub const szLOG_SOURCE: &str = "yubikey-piv.rs"; +#![cfg_attr(not(feature = "untested"), allow(dead_code))] pub const ADMIN_FLAGS_1_PUK_BLOCKED: u8 = 0x01; pub const ADMIN_FLAGS_1_PROTECTED_MGM: u8 = 0x02; -/// PIV Applet ID -pub const PIV_AID: [u8; 5] = [0xa0, 0x00, 0x00, 0x03, 0x08]; - -/// MGMT Applet ID. -/// -pub const MGMT_AID: [u8; 8] = [0xa0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17]; - -/// YubiKey OTP Applet ID. Needed to query serial on YK4. -pub const YK_AID: [u8; 8] = [0xa0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01, 0x01]; - -pub const CB_ADMIN_TIMESTAMP: usize = 0x04; -pub const CB_ADMIN_SALT: usize = 16; - -pub const CB_ATR_MAX: usize = 33; - pub const CB_BUF_MAX: usize = 3072; -pub const CB_ECC_POINTP256: usize = 65; -pub const CB_ECC_POINTP384: usize = 97; - pub const CB_OBJ_MAX: usize = CB_BUF_MAX - 9; - pub const CB_OBJ_TAG_MIN: usize = 2; // 1 byte tag + 1 byte len pub const CB_OBJ_TAG_MAX: usize = (CB_OBJ_TAG_MIN + 2); // 1 byte tag + 3 bytes len -pub const CB_PAGE: usize = 4096; -pub const CB_PIN_MAX: usize = 8; - -pub const CHREF_ACT_CHANGE_PIN: i32 = 0; -pub const CHREF_ACT_UNBLOCK_PIN: i32 = 1; -pub const CHREF_ACT_CHANGE_PUK: i32 = 2; - -pub const CONTAINER_NAME_LEN: usize = 40; -pub const CONTAINER_REC_LEN: usize = (2 * CONTAINER_NAME_LEN) + 27; // 80 + 1 + 1 + 2 + 1 + 1 + 1 + 20 - -pub const DES_TYPE_3DES: u8 = 1; - -pub const DES_LEN_DES: usize = 8; -pub const DES_LEN_3DES: usize = DES_LEN_DES * 3; - -// device types - -pub const DEVTYPE_UNKNOWN: u32 = 0x0000_0000; -pub const DEVTYPE_YK: u32 = 0x594B_0000; //"YK" -pub const DEVTYPE_YK4: u32 = (DEVTYPE_YK | 0x0000_0034); // "4" -pub const DEVYTPE_YK5: u32 = (DEVTYPE_YK | 0x0000_0035); // "5" - -pub const ITER_MGM_PBKDF2: usize = 10000; - -pub const PROTECTED_FLAGS_1_PUK_NOBLOCK: u8 = 0x01; - -// sw is status words, see NIST special publication 800-73-4, section 5.6 - -pub const SW_SUCCESS: i32 = 0x9000; -pub const SW_ERR_SECURITY_STATUS: i32 = 0x6982; -pub const SW_ERR_AUTH_BLOCKED: i32 = 0x6983; -pub const SW_ERR_INCORRECT_PARAM: i32 = 0x6a80; - -// this is a custom sw for yubikey - -pub const SW_ERR_INCORRECT_SLOT: i32 = 0x6b00; -pub const SW_ERR_NOT_SUPPORTED: i32 = 0x6d00; - pub const TAG_CERT: u8 = 0x70; pub const TAG_CERT_COMPRESS: u8 = 0x71; pub const TAG_CERT_LRC: u8 = 0xFE; @@ -119,28 +61,3 @@ pub const TAG_MSROOTS_MID: u8 = 0x83; pub const TAG_RSA_MODULUS: u8 = 0x81; pub const TAG_RSA_EXP: u8 = 0x82; pub const TAG_ECC_POINT: u8 = 0x86; - -pub const YKPIV_ALGO_3DES: u8 = 0x03; - -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_FINGERPRINTS: u32 = 0x005f_c103; -pub const YKPIV_OBJ_SECURITY: u32 = 0x005f_c106; -pub const YKPIV_OBJ_FACIAL: u32 = 0x005f_c108; -pub const YKPIV_OBJ_PRINTED: u32 = 0x005f_c109; -pub const YKPIV_OBJ_DISCOVERY: u32 = 0x7e; -pub const YKPIV_OBJ_KEY_HISTORY: u32 = 0x005f_c10c; -pub const YKPIV_OBJ_IRIS: u32 = 0x005f_c121; - -// Internal object IDs - -pub const YKPIV_OBJ_ADMIN_DATA: u32 = 0x005f_ff00; -pub const YKPIV_OBJ_MSCMAP: u32 = 0x005f_ff10; -pub const YKPIV_OBJ_MSROOTS1: u32 = 0x005f_ff11; -pub const YKPIV_OBJ_MSROOTS2: u32 = 0x005f_ff12; -pub const YKPIV_OBJ_MSROOTS3: u32 = 0x005f_ff13; -pub const YKPIV_OBJ_MSROOTS4: u32 = 0x005f_ff14; -pub const YKPIV_OBJ_MSROOTS5: u32 = 0x005f_ff15; diff --git a/src/container.rs b/src/container.rs index 003ac2f..6f62fe9 100644 --- a/src/container.rs +++ b/src/container.rs @@ -40,6 +40,14 @@ use std::{ fmt::{self, Debug}, }; +/// Container name length +const CONTAINER_NAME_LEN: usize = 40; + +/// Container record length: 27 = 80 + 1 + 1 + 2 + 1 + 1 + 1 + 20 +const CONTAINER_REC_LEN: usize = (2 * CONTAINER_NAME_LEN) + 27; + +const OBJ_MSCMAP: u32 = 0x005f_ff10; + /// MS Container Map(?) Records #[derive(Copy, Clone)] pub struct Container { @@ -72,7 +80,7 @@ impl Container { /// Read MS Container Map records pub fn read_mscmap(yubikey: &mut YubiKey) -> Result, Error> { let txn = yubikey.begin_transaction()?; - let response = txn.fetch_object(YKPIV_OBJ_MSCMAP)?; + let response = txn.fetch_object(OBJ_MSCMAP)?; let mut containers = vec![]; if response.len() < CB_OBJ_TAG_MIN { @@ -110,7 +118,7 @@ impl Container { let txn = yubikey.begin_transaction()?; if n_containers == 0 { - return txn.save_object(YKPIV_OBJ_MSCMAP, &[]); + return txn.save_object(OBJ_MSCMAP, &[]); } let req_len = 1 + set_length(&mut buf, data_len) + data_len; @@ -131,7 +139,7 @@ impl Container { } offset += data_len; - txn.save_object(YKPIV_OBJ_MSCMAP, &buf[..offset]) + txn.save_object(OBJ_MSCMAP, &buf[..offset]) } /// Parse a container record from a byte slice diff --git a/src/key.rs b/src/key.rs index e50264a..4cfac02 100644 --- a/src/key.rs +++ b/src/key.rs @@ -59,6 +59,11 @@ use log::{error, warn}; #[cfg(feature = "untested")] use zeroize::Zeroizing; +#[cfg(feature = "untested")] +const CB_ECC_POINTP256: usize = 65; +#[cfg(feature = "untested")] +const CB_ECC_POINTP384: usize = 97; + /// Slot identifiers. /// #[derive(Clone, Copy, Debug, PartialEq)] diff --git a/src/lib.rs b/src/lib.rs index 0d4da9b..1e88312 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -140,7 +140,7 @@ pub mod cccid; pub mod certificate; pub mod chuid; pub mod config; -pub mod consts; +mod consts; #[cfg(feature = "untested")] pub mod container; pub mod error; diff --git a/src/metadata.rs b/src/metadata.rs index 14a88d3..c3a6ef2 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -35,6 +35,9 @@ use crate::{consts::*, error::Error, serialization::*, transaction::Transaction, #[cfg(feature = "untested")] use zeroize::Zeroizing; +pub const OBJ_ADMIN_DATA: u32 = 0x005f_ff00; +pub const OBJ_PRINTED: u32 = 0x005f_c109; + /// Get metadata item pub(crate) fn get_item(data: &[u8], tag: u8) -> Result<&[u8], Error> { let mut cb_temp: usize = 0; @@ -166,8 +169,8 @@ pub(crate) fn set_item( /// Read metadata pub(crate) fn read(txn: &Transaction<'_>, tag: u8) -> Result { let obj_id = match tag { - TAG_ADMIN => YKPIV_OBJ_ADMIN_DATA, - TAG_PROTECTED => YKPIV_OBJ_PRINTED, + TAG_ADMIN => OBJ_ADMIN_DATA, + TAG_PROTECTED => OBJ_PRINTED, _ => return Err(Error::InvalidObject), }; @@ -203,8 +206,8 @@ pub(crate) fn write(txn: &Transaction<'_>, tag: u8, data: &[u8]) -> Result<(), E } let obj_id = match tag { - TAG_ADMIN => YKPIV_OBJ_ADMIN_DATA, - TAG_PROTECTED => YKPIV_OBJ_PRINTED, + TAG_ADMIN => OBJ_ADMIN_DATA, + TAG_PROTECTED => OBJ_PRINTED, _ => return Err(Error::InvalidObject), }; diff --git a/src/mgm.rs b/src/mgm.rs index 42c20fc..4de5171 100644 --- a/src/mgm.rs +++ b/src/mgm.rs @@ -30,14 +30,14 @@ // (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}; +use crate::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}; +use crate::{consts::*, metadata, yubikey::YubiKey}; #[cfg(feature = "untested")] use des::{ block_cipher_trait::{generic_array::GenericArray, BlockCipher}, @@ -50,11 +50,24 @@ use pbkdf2::pbkdf2; #[cfg(feature = "untested")] use sha1::Sha1; +#[cfg(feature = "untested")] +const CB_ADMIN_SALT: usize = 16; + /// Default MGM key configured on all YubiKeys const DEFAULT_MGM_KEY: [u8; DES_LEN_3DES] = [ 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, ]; +/// Size of a DES key +const DES_LEN_DES: usize = 8; + +/// Size of a 3DES key +pub(crate) const DES_LEN_3DES: usize = DES_LEN_DES * 3; + +/// Number of PBKDF2 iterations to use when deriving from a password +#[cfg(feature = "untested")] +const ITER_MGM_PBKDF2: usize = 10000; + /// Management Key (MGM) key types (manual/derived/protected) #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum MgmType { diff --git a/src/msroots.rs b/src/msroots.rs index 16f1846..21dc484 100644 --- a/src/msroots.rs +++ b/src/msroots.rs @@ -40,6 +40,15 @@ use crate::{consts::*, error::Error, serialization::*, yubikey::YubiKey}; use log::error; +const OBJ_MSROOTS1: u32 = 0x005f_ff11; +#[allow(dead_code)] +const OBJ_MSROOTS2: u32 = 0x005f_ff12; +#[allow(dead_code)] +const OBJ_MSROOTS3: u32 = 0x005f_ff13; +#[allow(dead_code)] +const OBJ_MSROOTS4: u32 = 0x005f_ff14; +const OBJ_MSROOTS5: u32 = 0x005f_ff15; + /// `msroots` file: PKCS#7-formatted certificate store for enterprise trust roots pub struct MsRoots(Vec); @@ -56,7 +65,7 @@ impl MsRoots { // allocate first page let mut data = Vec::with_capacity(CB_OBJ_MAX); - for object_id in YKPIV_OBJ_MSROOTS1..YKPIV_OBJ_MSROOTS5 { + for object_id in OBJ_MSROOTS1..OBJ_MSROOTS5 { let buf = txn.fetch_object(object_id)?; let cb_buf = buf.len(); @@ -66,9 +75,7 @@ impl MsRoots { let tag = buf[0]; - if (TAG_MSROOTS_MID != tag || YKPIV_OBJ_MSROOTS5 == object_id) - && (TAG_MSROOTS_END != tag) - { + if (TAG_MSROOTS_MID != tag || OBJ_MSROOTS5 == object_id) && (TAG_MSROOTS_END != tag) { // the current object doesn't contain a valid part of a msroots file // treat condition as object isn't found @@ -108,7 +115,7 @@ impl MsRoots { let txn = yubikey.begin_transaction()?; if data_len == 0 { - return txn.save_object(YKPIV_OBJ_MSROOTS1, &[]); + return txn.save_object(OBJ_MSROOTS1, &[]); } // Calculate number of objects required to store blob @@ -138,7 +145,7 @@ impl MsRoots { buf[offset..].copy_from_slice(&data[data_offset..(data_offset + data_chunk)]); offset += data_chunk; - txn.save_object(YKPIV_OBJ_MSROOTS1 + i as u32, &buf[..offset])?; + txn.save_object(OBJ_MSROOTS1 + i as u32, &buf[..offset])?; data_offset += data_chunk; } diff --git a/src/serialization.rs b/src/serialization.rs index 25a28ca..9145f3d 100644 --- a/src/serialization.rs +++ b/src/serialization.rs @@ -30,7 +30,9 @@ // (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::*, ObjectId}; +use crate::ObjectId; + +pub const OBJ_DISCOVERY: u32 = 0x7e; // TODO(tarcieri): refactor these into better serializers/message builders @@ -84,9 +86,9 @@ pub(crate) fn has_valid_length(buffer: &[u8], len: usize) -> bool { pub(crate) fn set_object(object_id: ObjectId, mut buffer: &mut [u8]) -> &mut [u8] { buffer[0] = 0x5c; - if object_id == YKPIV_OBJ_DISCOVERY { + if object_id == OBJ_DISCOVERY { buffer[1] = 1; - buffer[2] = YKPIV_OBJ_DISCOVERY as u8; + buffer[2] = OBJ_DISCOVERY as u8; buffer = &mut buffer[3..]; } else if object_id > 0xffff && object_id <= 0x00ff_ffff { buffer[1] = 3; diff --git a/src/transaction.rs b/src/transaction.rs index ebd6a8a..3c36d0b 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -16,9 +16,11 @@ use zeroize::Zeroizing; #[cfg(feature = "untested")] use crate::{ key::{AlgorithmId, SlotId}, - mgm::MgmKey, + mgm::{MgmKey, DES_LEN_3DES}, }; +const CB_PIN_MAX: usize = 8; + /// Exclusive transaction with the YubiKey's PC/SC card. pub(crate) struct Transaction<'tx> { inner: pcsc::Transaction<'tx>, @@ -227,8 +229,8 @@ impl<'tx> Transaction<'tx> { }; let mut data = [0u8; DES_LEN_3DES + 3]; - data[0] = YKPIV_ALGO_3DES; - data[1] = YKPIV_KEY_CARDMGM; + data[0] = ALGO_3DES; + data[1] = KEY_CARDMGM; data[2] = DES_LEN_3DES as u8; data[3..3 + DES_LEN_3DES].copy_from_slice(new_key.as_ref()); diff --git a/src/yubikey.rs b/src/yubikey.rs index 9ae88a3..b0bf4bf 100644 --- a/src/yubikey.rs +++ b/src/yubikey.rs @@ -63,6 +63,32 @@ use std::{ time::{SystemTime, UNIX_EPOCH}, }; +/// 3DES authentication +#[cfg(feature = "untested")] +pub(crate) const ALGO_3DES: u8 = 0x03; + +/// Card management key +#[cfg(feature = "untested")] +pub(crate) const KEY_CARDMGM: u8 = 0x9b; + +#[cfg(feature = "untested")] +pub(crate) const CHREF_ACT_CHANGE_PIN: i32 = 0; +#[cfg(feature = "untested")] +pub(crate) const CHREF_ACT_UNBLOCK_PIN: i32 = 1; +#[cfg(feature = "untested")] +pub(crate) const CHREF_ACT_CHANGE_PUK: i32 = 2; + +/// PIV Applet ID +pub(crate) const PIV_AID: [u8; 5] = [0xa0, 0x00, 0x00, 0x03, 0x08]; + +/// MGMT Applet ID. +/// +#[cfg(feature = "untested")] +pub(crate) const MGMT_AID: [u8; 8] = [0xa0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17]; + +/// YubiKey OTP Applet ID. Needed to query serial on YK4. +pub(crate) const YK_AID: [u8; 8] = [0xa0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01, 0x01]; + /// Cached YubiKey PIN pub type CachedPin = secrecy::SecretVec; @@ -227,7 +253,7 @@ impl YubiKey { // get a challenge from the card let challenge = APDU::new(Ins::Authenticate) - .params(YKPIV_ALGO_3DES, YKPIV_KEY_CARDMGM) + .params(ALGO_3DES, KEY_CARDMGM) .data(&[TAG_DYN_AUTH, 0x02, 0x80, 0x00]) .transmit(&txn, 261)?; @@ -256,7 +282,7 @@ impl YubiKey { challenge.copy_from_slice(&data[14..22]); let authentication = APDU::new(Ins::Authenticate) - .params(YKPIV_ALGO_3DES, YKPIV_KEY_CARDMGM) + .params(ALGO_3DES, KEY_CARDMGM) .data(&data) .transmit(&txn, 261)?; @@ -513,7 +539,7 @@ impl YubiKey { let txn = self.begin_transaction()?; let response = APDU::new(Ins::Authenticate) - .params(YKPIV_ALGO_3DES, YKPIV_KEY_CARDMGM) + .params(ALGO_3DES, KEY_CARDMGM) .data(&[0x7c, 0x02, 0x81, 0x00]) .transmit(&txn, 261)?; @@ -538,7 +564,7 @@ impl YubiKey { // send the response to the card and a challenge of our own. let status_words = APDU::new(Ins::Authenticate) - .params(YKPIV_ALGO_3DES, YKPIV_KEY_CARDMGM) + .params(ALGO_3DES, KEY_CARDMGM) .data(&data) .transmit(&txn, 261)? .status_words();