Merge pull request #44 from str4d/more-enums

Convert SlotId and AlgorithmId into enums
This commit is contained in:
Tony Arcieri
2019-11-30 14:28:36 -08:00
committed by GitHub
7 changed files with 333 additions and 202 deletions
+4 -9
View File
@@ -31,13 +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::{ use crate::{
consts::*, consts::*, error::Error, key::SlotId, serialization::*, transaction::Transaction,
error::Error, yubikey::YubiKey, Buffer,
key::{self, SlotId},
serialization::*,
transaction::Transaction,
yubikey::YubiKey,
Buffer,
}; };
use log::error; use log::error;
use zeroize::Zeroizing; use zeroize::Zeroizing;
@@ -100,7 +95,7 @@ impl AsRef<[u8]> for Certificate {
/// Read certificate /// Read certificate
pub(crate) fn read_certificate(txn: &Transaction<'_>, slot: SlotId) -> Result<Buffer, Error> { pub(crate) fn read_certificate(txn: &Transaction<'_>, slot: SlotId) -> Result<Buffer, Error> {
let mut len: usize = 0; let mut len: usize = 0;
let object_id = key::slot_object(slot)?; let object_id = slot.object_id();
let mut buf = match txn.fetch_object(object_id) { let mut buf = match txn.fetch_object(object_id) {
Ok(b) => b, Ok(b) => b,
@@ -141,7 +136,7 @@ pub(crate) fn write_certificate(
let mut buf = [0u8; CB_OBJ_MAX]; let mut buf = [0u8; CB_OBJ_MAX];
let mut offset = 0; let mut offset = 0;
let object_id = key::slot_object(slot)?; let object_id = slot.object_id();
if data.is_none() { if data.is_none() {
return txn.save_object(object_id, &[]); return txn.save_object(object_id, &[]);
-55
View File
@@ -124,10 +124,6 @@ pub const TAG_ECC_POINT: u8 = 0x86;
pub const YKPIV_ALGO_TAG: u8 = 0x80; pub const YKPIV_ALGO_TAG: u8 = 0x80;
pub const YKPIV_ALGO_3DES: u8 = 0x03; pub const YKPIV_ALGO_3DES: u8 = 0x03;
pub const YKPIV_ALGO_RSA1024: u8 = 0x06;
pub const YKPIV_ALGO_RSA2048: u8 = 0x07;
pub const YKPIV_ALGO_ECCP256: u8 = 0x11;
pub const YKPIV_ALGO_ECCP384: u8 = 0x14;
pub const YKPIV_ATR_NEO_R3: &[u8] = b";\xFC\x13\0\0\x811\xFE\x15YubikeyNEOr3\xE1\0"; pub const YKPIV_ATR_NEO_R3: &[u8] = b";\xFC\x13\0\0\x811\xFE\x15YubikeyNEOr3\xE1\0";
@@ -141,72 +137,21 @@ pub const YKPIV_CCCID_SIZE: usize = 14;
pub const YKPIV_CERTINFO_UNCOMPRESSED: u8 = 0; pub const YKPIV_CERTINFO_UNCOMPRESSED: u8 = 0;
pub const YKPIV_CERTINFO_GZIP: u8 = 1; pub const YKPIV_CERTINFO_GZIP: u8 = 1;
pub const YKPIV_KEY_AUTHENTICATION: u8 = 0x9a;
pub const YKPIV_KEY_CARDMGM: u8 = 0x9b; pub const YKPIV_KEY_CARDMGM: u8 = 0x9b;
pub const YKPIV_KEY_SIGNATURE: u8 = 0x9c;
pub const YKPIV_KEY_KEYMGM: u8 = 0x9d;
pub const YKPIV_KEY_CARDAUTH: u8 = 0x9e;
pub const YKPIV_KEY_RETIRED1: u8 = 0x82;
pub const YKPIV_KEY_RETIRED2: u8 = 0x83;
pub const YKPIV_KEY_RETIRED3: u8 = 0x84;
pub const YKPIV_KEY_RETIRED4: u8 = 0x85;
pub const YKPIV_KEY_RETIRED5: u8 = 0x86;
pub const YKPIV_KEY_RETIRED6: u8 = 0x87;
pub const YKPIV_KEY_RETIRED7: u8 = 0x88;
pub const YKPIV_KEY_RETIRED8: u8 = 0x89;
pub const YKPIV_KEY_RETIRED9: u8 = 0x8a;
pub const YKPIV_KEY_RETIRED10: u8 = 0x8b;
pub const YKPIV_KEY_RETIRED11: u8 = 0x8c;
pub const YKPIV_KEY_RETIRED12: u8 = 0x8d;
pub const YKPIV_KEY_RETIRED13: u8 = 0x8e;
pub const YKPIV_KEY_RETIRED14: u8 = 0x8f;
pub const YKPIV_KEY_RETIRED15: u8 = 0x90;
pub const YKPIV_KEY_RETIRED16: u8 = 0x91;
pub const YKPIV_KEY_RETIRED17: u8 = 0x92;
pub const YKPIV_KEY_RETIRED18: u8 = 0x93;
pub const YKPIV_KEY_RETIRED19: u8 = 0x94;
pub const YKPIV_KEY_RETIRED20: u8 = 0x95;
pub const YKPIV_KEY_ATTESTATION: u8 = 0xf9;
pub const YKPIV_OBJ_CAPABILITY: u32 = 0x005f_c107; pub const YKPIV_OBJ_CAPABILITY: u32 = 0x005f_c107;
pub const YKPIV_OBJ_CHUID: u32 = 0x005f_c102; pub const YKPIV_OBJ_CHUID: u32 = 0x005f_c102;
pub const YKPIV_OBJ_AUTHENTICATION: u32 = 0x005f_c105; // cert for 9a key
pub const YKPIV_OBJ_FINGERPRINTS: u32 = 0x005f_c103; pub const YKPIV_OBJ_FINGERPRINTS: u32 = 0x005f_c103;
pub const YKPIV_OBJ_SECURITY: u32 = 0x005f_c106; pub const YKPIV_OBJ_SECURITY: u32 = 0x005f_c106;
pub const YKPIV_OBJ_FACIAL: u32 = 0x005f_c108; pub const YKPIV_OBJ_FACIAL: u32 = 0x005f_c108;
pub const YKPIV_OBJ_PRINTED: u32 = 0x005f_c109; pub const YKPIV_OBJ_PRINTED: u32 = 0x005f_c109;
pub const YKPIV_OBJ_SIGNATURE: u32 = 0x005f_c10a; // cert for 9c key
pub const YKPIV_OBJ_KEY_MANAGEMENT: u32 = 0x005f_c10b; // cert for 9d key
pub const YKPIV_OBJ_CARD_AUTH: u32 = 0x005f_c101; // cert for 9e key
pub const YKPIV_OBJ_DISCOVERY: u32 = 0x7e; pub const YKPIV_OBJ_DISCOVERY: u32 = 0x7e;
pub const YKPIV_OBJ_KEY_HISTORY: u32 = 0x005f_c10c; pub const YKPIV_OBJ_KEY_HISTORY: u32 = 0x005f_c10c;
pub const YKPIV_OBJ_IRIS: u32 = 0x005f_c121; pub const YKPIV_OBJ_IRIS: u32 = 0x005f_c121;
pub const YKPIV_OBJ_RETIRED1: u32 = 0x005f_c10d;
pub const YKPIV_OBJ_RETIRED2: u32 = 0x005f_c10e;
pub const YKPIV_OBJ_RETIRED3: u32 = 0x005f_c10f;
pub const YKPIV_OBJ_RETIRED4: u32 = 0x005f_c110;
pub const YKPIV_OBJ_RETIRED5: u32 = 0x005f_c111;
pub const YKPIV_OBJ_RETIRED6: u32 = 0x005f_c112;
pub const YKPIV_OBJ_RETIRED7: u32 = 0x005f_c113;
pub const YKPIV_OBJ_RETIRED8: u32 = 0x005f_c114;
pub const YKPIV_OBJ_RETIRED9: u32 = 0x005f_c115;
pub const YKPIV_OBJ_RETIRED10: u32 = 0x005f_c116;
pub const YKPIV_OBJ_RETIRED11: u32 = 0x005f_c117;
pub const YKPIV_OBJ_RETIRED12: u32 = 0x005f_c118;
pub const YKPIV_OBJ_RETIRED13: u32 = 0x005f_c119;
pub const YKPIV_OBJ_RETIRED14: u32 = 0x005f_c11a;
pub const YKPIV_OBJ_RETIRED15: u32 = 0x005f_c11b;
pub const YKPIV_OBJ_RETIRED16: u32 = 0x005f_c11c;
pub const YKPIV_OBJ_RETIRED17: u32 = 0x005f_c11d;
pub const YKPIV_OBJ_RETIRED18: u32 = 0x005f_c11e;
pub const YKPIV_OBJ_RETIRED19: u32 = 0x005f_c11f;
pub const YKPIV_OBJ_RETIRED20: u32 = 0x005f_c120;
// Internal object IDs // Internal object IDs
pub const YKPIV_OBJ_ADMIN_DATA: u32 = 0x005f_ff00; pub const YKPIV_OBJ_ADMIN_DATA: u32 = 0x005f_ff00;
pub const YKPIV_OBJ_ATTESTATION: u32 = 0x005f_ff01;
pub const YKPIV_OBJ_MSCMAP: u32 = 0x005f_ff10; pub const YKPIV_OBJ_MSCMAP: u32 = 0x005f_ff10;
pub const YKPIV_OBJ_MSROOTS1: u32 = 0x005f_ff11; pub const YKPIV_OBJ_MSROOTS1: u32 = 0x005f_ff11;
pub const YKPIV_OBJ_MSROOTS2: u32 = 0x005f_ff12; pub const YKPIV_OBJ_MSROOTS2: u32 = 0x005f_ff12;
+3 -3
View File
@@ -158,7 +158,7 @@ impl Container {
Ok(Container { Ok(Container {
name, name,
slot: bytes[name_bytes_len], slot: bytes[name_bytes_len].try_into()?,
key_spec: bytes[name_bytes_len + 1], key_spec: bytes[name_bytes_len + 1],
key_size_bits: u16::from_le_bytes( key_size_bits: u16::from_le_bytes(
bytes[(name_bytes_len + 2)..(name_bytes_len + 4)] bytes[(name_bytes_len + 2)..(name_bytes_len + 4)]
@@ -185,7 +185,7 @@ impl Container {
bytes.extend_from_slice(&self.name[i].to_le_bytes()); bytes.extend_from_slice(&self.name[i].to_le_bytes());
} }
bytes.push(self.slot); bytes.push(self.slot.into());
bytes.push(self.key_spec); bytes.push(self.key_spec);
bytes.extend_from_slice(&self.key_size_bits.to_le_bytes()); bytes.extend_from_slice(&self.key_size_bits.to_le_bytes());
bytes.push(self.flags); bytes.push(self.flags);
@@ -204,7 +204,7 @@ impl Debug for Container {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!( write!(
f, f,
"PivContainer {{ name: {:?}, slot: {}, key_spec: {}, key_size_bits: {}, \ "PivContainer {{ name: {:?}, slot: {:?}, key_spec: {}, key_size_bits: {}, \
flags: {}, pin_id: {}, associated_echd_container: {}, cert_fingerprint: {:?} }}", flags: {}, pin_id: {}, associated_echd_container: {}, cert_fingerprint: {:?} }}",
&self.name[..], &self.name[..],
self.slot, self.slot,
+302 -96
View File
@@ -45,61 +45,272 @@ use crate::{
serialization::*, serialization::*,
settings, settings,
yubikey::YubiKey, yubikey::YubiKey,
AlgorithmId, Buffer, ObjectId, Buffer, ObjectId,
}; };
use log::{debug, error, warn}; use log::{debug, error, warn};
use std::convert::TryFrom;
/// Slot identifiers. /// Slot identifiers.
/// <https://developers.yubico.com/PIV/Introduction/Certificate_slots.html> /// <https://developers.yubico.com/PIV/Introduction/Certificate_slots.html>
// TODO(tarcieri): replace these with enums #[derive(Clone, Copy, Debug)]
pub type SlotId = u8; pub enum SlotId {
/// This certificate and its associated private key is used to authenticate the card
/// and the cardholder. This slot is used for things like system login. The end user
/// PIN is required to perform any private key operations. Once the PIN has been
/// provided successfully, multiple private key operations may be performed without
/// additional cardholder consent.
Authentication,
/// Get the [`ObjectId`] that corresponds to a given [`SlotId`] /// This certificate and its associated private key is used for digital signatures for
// TODO(tarcieri): factor this into a slot ID enum /// the purpose of document signing, or signing files and executables. The end user
pub(crate) fn slot_object(slot: SlotId) -> Result<ObjectId, Error> { /// PIN is required to perform any private key operations. The PIN must be submitted
let id = match slot { /// every time immediately before a sign operation, to ensure cardholder participation
YKPIV_KEY_AUTHENTICATION => YKPIV_OBJ_AUTHENTICATION, /// for every digital signature generated.
YKPIV_KEY_SIGNATURE => YKPIV_OBJ_SIGNATURE, Signature,
YKPIV_KEY_KEYMGM => YKPIV_OBJ_KEY_MANAGEMENT,
YKPIV_KEY_CARDAUTH => YKPIV_OBJ_CARD_AUTH, /// This certificate and its associated private key is used for encryption for the
YKPIV_KEY_ATTESTATION => YKPIV_OBJ_ATTESTATION, /// purpose of confidentiality. This slot is used for things like encrypting e-mails
slot if slot >= YKPIV_KEY_RETIRED1 && (slot <= YKPIV_KEY_RETIRED20) => { /// or files. The end user PIN is required to perform any private key operations. Once
YKPIV_OBJ_RETIRED1 + (slot - YKPIV_KEY_RETIRED1) as u32 /// the PIN has been provided successfully, multiple private key operations may be
/// performed without additional cardholder consent.
KeyManagement,
/// This certificate and its associated private key is used to support additional
/// physical access applications, such as providing physical access to buildings via
/// PIV-enabled door locks. The end user PIN is NOT required to perform private key
/// operations for this slot.
CardAuthentication,
/// These slots are only available on the YubiKey 4 & 5. They are meant for previously
/// used Key Management keys to be able to decrypt earlier encrypted documents or
/// emails. In the YubiKey 4 & 5 all 20 of them are fully available for use.
Retired(RetiredSlotId),
/// This slot is only available on YubiKey version 4.3 and newer. It is only used for
/// attestation of other keys generated on device with instruction `f9`. This slot is
/// not cleared on reset, but can be overwritten.
Attestation,
}
impl TryFrom<u8> for SlotId {
type Error = Error;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x9a => Ok(SlotId::Authentication),
0x9c => Ok(SlotId::Signature),
0x9d => Ok(SlotId::KeyManagement),
0x9e => Ok(SlotId::CardAuthentication),
0xf9 => Ok(SlotId::Attestation),
_ => RetiredSlotId::try_from(value).map(SlotId::Retired),
} }
_ => return Err(Error::InvalidObject), }
}; }
Ok(id) impl From<SlotId> for u8 {
fn from(slot: SlotId) -> u8 {
match slot {
SlotId::Authentication => 0x9a,
SlotId::Signature => 0x9c,
SlotId::KeyManagement => 0x9d,
SlotId::CardAuthentication => 0x9e,
SlotId::Retired(retired) => retired.into(),
SlotId::Attestation => 0xf9,
}
}
}
impl SlotId {
/// Returns the [`ObjectId`] that corresponds to a given [`SlotId`].
pub(crate) fn object_id(self) -> ObjectId {
match self {
SlotId::Authentication => 0x005f_c105,
SlotId::Signature => 0x005f_c10a,
SlotId::KeyManagement => 0x005f_c10b,
SlotId::CardAuthentication => 0x005f_c101,
SlotId::Retired(retired) => retired.object_id(),
SlotId::Attestation => 0x005f_ff01,
}
}
}
/// Retired slot IDs.
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug)]
pub enum RetiredSlotId {
R1,
R2,
R3,
R4,
R5,
R6,
R7,
R8,
R9,
R10,
R11,
R12,
R13,
R14,
R15,
R16,
R17,
R18,
R19,
R20,
}
impl TryFrom<u8> for RetiredSlotId {
type Error = Error;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x82 => Ok(RetiredSlotId::R1),
0x83 => Ok(RetiredSlotId::R2),
0x84 => Ok(RetiredSlotId::R3),
0x85 => Ok(RetiredSlotId::R4),
0x86 => Ok(RetiredSlotId::R5),
0x87 => Ok(RetiredSlotId::R6),
0x88 => Ok(RetiredSlotId::R7),
0x89 => Ok(RetiredSlotId::R8),
0x8a => Ok(RetiredSlotId::R9),
0x8b => Ok(RetiredSlotId::R10),
0x8c => Ok(RetiredSlotId::R11),
0x8d => Ok(RetiredSlotId::R12),
0x8e => Ok(RetiredSlotId::R13),
0x8f => Ok(RetiredSlotId::R14),
0x90 => Ok(RetiredSlotId::R15),
0x91 => Ok(RetiredSlotId::R16),
0x92 => Ok(RetiredSlotId::R17),
0x93 => Ok(RetiredSlotId::R18),
0x94 => Ok(RetiredSlotId::R19),
0x95 => Ok(RetiredSlotId::R20),
_ => Err(Error::InvalidObject),
}
}
}
impl From<RetiredSlotId> for u8 {
fn from(slot: RetiredSlotId) -> u8 {
match slot {
RetiredSlotId::R1 => 0x82,
RetiredSlotId::R2 => 0x83,
RetiredSlotId::R3 => 0x84,
RetiredSlotId::R4 => 0x85,
RetiredSlotId::R5 => 0x86,
RetiredSlotId::R6 => 0x87,
RetiredSlotId::R7 => 0x88,
RetiredSlotId::R8 => 0x89,
RetiredSlotId::R9 => 0x8a,
RetiredSlotId::R10 => 0x8b,
RetiredSlotId::R11 => 0x8c,
RetiredSlotId::R12 => 0x8d,
RetiredSlotId::R13 => 0x8e,
RetiredSlotId::R14 => 0x8f,
RetiredSlotId::R15 => 0x90,
RetiredSlotId::R16 => 0x91,
RetiredSlotId::R17 => 0x92,
RetiredSlotId::R18 => 0x93,
RetiredSlotId::R19 => 0x94,
RetiredSlotId::R20 => 0x95,
}
}
}
impl RetiredSlotId {
/// Returns the [`ObjectId`] that corresponds to a given [`RetiredSlotId`].
pub(crate) fn object_id(self) -> ObjectId {
match self {
RetiredSlotId::R1 => 0x005f_c10d,
RetiredSlotId::R2 => 0x005f_c10e,
RetiredSlotId::R3 => 0x005f_c10f,
RetiredSlotId::R4 => 0x005f_c110,
RetiredSlotId::R5 => 0x005f_c111,
RetiredSlotId::R6 => 0x005f_c112,
RetiredSlotId::R7 => 0x005f_c113,
RetiredSlotId::R8 => 0x005f_c114,
RetiredSlotId::R9 => 0x005f_c115,
RetiredSlotId::R10 => 0x005f_c116,
RetiredSlotId::R11 => 0x005f_c117,
RetiredSlotId::R12 => 0x005f_c118,
RetiredSlotId::R13 => 0x005f_c119,
RetiredSlotId::R14 => 0x005f_c11a,
RetiredSlotId::R15 => 0x005f_c11b,
RetiredSlotId::R16 => 0x005f_c11c,
RetiredSlotId::R17 => 0x005f_c11d,
RetiredSlotId::R18 => 0x005f_c11e,
RetiredSlotId::R19 => 0x005f_c11f,
RetiredSlotId::R20 => 0x005f_c120,
}
}
} }
/// Personal Identity Verification (PIV) key slots /// Personal Identity Verification (PIV) key slots
pub const SLOTS: [u8; 24] = [ pub const SLOTS: [SlotId; 24] = [
YKPIV_KEY_AUTHENTICATION, SlotId::Authentication,
YKPIV_KEY_SIGNATURE, SlotId::Signature,
YKPIV_KEY_KEYMGM, SlotId::KeyManagement,
YKPIV_KEY_RETIRED1, SlotId::Retired(RetiredSlotId::R1),
YKPIV_KEY_RETIRED2, SlotId::Retired(RetiredSlotId::R2),
YKPIV_KEY_RETIRED3, SlotId::Retired(RetiredSlotId::R3),
YKPIV_KEY_RETIRED4, SlotId::Retired(RetiredSlotId::R4),
YKPIV_KEY_RETIRED5, SlotId::Retired(RetiredSlotId::R5),
YKPIV_KEY_RETIRED6, SlotId::Retired(RetiredSlotId::R6),
YKPIV_KEY_RETIRED7, SlotId::Retired(RetiredSlotId::R7),
YKPIV_KEY_RETIRED8, SlotId::Retired(RetiredSlotId::R8),
YKPIV_KEY_RETIRED9, SlotId::Retired(RetiredSlotId::R9),
YKPIV_KEY_RETIRED10, SlotId::Retired(RetiredSlotId::R10),
YKPIV_KEY_RETIRED11, SlotId::Retired(RetiredSlotId::R11),
YKPIV_KEY_RETIRED12, SlotId::Retired(RetiredSlotId::R12),
YKPIV_KEY_RETIRED13, SlotId::Retired(RetiredSlotId::R13),
YKPIV_KEY_RETIRED14, SlotId::Retired(RetiredSlotId::R14),
YKPIV_KEY_RETIRED15, SlotId::Retired(RetiredSlotId::R15),
YKPIV_KEY_RETIRED16, SlotId::Retired(RetiredSlotId::R16),
YKPIV_KEY_RETIRED17, SlotId::Retired(RetiredSlotId::R17),
YKPIV_KEY_RETIRED18, SlotId::Retired(RetiredSlotId::R18),
YKPIV_KEY_RETIRED19, SlotId::Retired(RetiredSlotId::R19),
YKPIV_KEY_RETIRED20, SlotId::Retired(RetiredSlotId::R20),
YKPIV_KEY_CARDAUTH, SlotId::CardAuthentication,
]; ];
/// Algorithm identifiers
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AlgorithmId {
/// 1024-bit RSA.
Rsa1024,
/// 2048-bit RSA.
Rsa2048,
/// ECDSA with the NIST P256 curve.
EccP256,
/// ECDSA with the NIST P384 curve.
EccP384,
}
impl TryFrom<u8> for AlgorithmId {
type Error = Error;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x06 => Ok(AlgorithmId::Rsa1024),
0x07 => Ok(AlgorithmId::Rsa2048),
0x11 => Ok(AlgorithmId::EccP256),
0x14 => Ok(AlgorithmId::EccP384),
_ => Err(Error::AlgorithmError),
}
}
}
impl From<AlgorithmId> for u8 {
fn from(id: AlgorithmId) -> u8 {
match id {
AlgorithmId::Rsa1024 => 0x06,
AlgorithmId::Rsa2048 => 0x07,
AlgorithmId::EccP256 => 0x11,
AlgorithmId::EccP384 => 0x14,
}
}
}
/// PIV cryptographic keys stored in a YubiKey /// PIV cryptographic keys stored in a YubiKey
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Key { pub struct Key {
@@ -120,13 +331,15 @@ impl Key {
let buf = match certificate::read_certificate(&txn, slot) { let buf = match certificate::read_certificate(&txn, slot) {
Ok(b) => b, Ok(b) => b,
Err(e) => { Err(e) => {
debug!("error reading certificate in slot {}: {}", slot, e); debug!("error reading certificate in slot {:?}: {}", slot, e);
continue; continue;
} }
}; };
let cert = Certificate::new(buf)?; if !buf.is_empty() {
keys.push(Key { slot, cert }); let cert = Certificate::new(buf)?;
keys.push(Key { slot, cert });
}
} }
Ok(keys) Ok(keys)
@@ -202,55 +415,52 @@ pub fn generate(
let mut templ = [0, Ins::GenerateAsymmetric.code(), 0, 0]; let mut templ = [0, Ins::GenerateAsymmetric.code(), 0, 0];
let setting_roca: settings::BoolValue; let setting_roca: settings::BoolValue;
if yubikey.device_model() == DEVTYPE_YK4
&& (algorithm == YKPIV_ALGO_RSA1024 || algorithm == YKPIV_ALGO_RSA2048)
&& yubikey.version.major == 4
&& (yubikey.version.minor < 3 || yubikey.version.minor == 3 && (yubikey.version.patch < 5))
{
setting_roca = settings::BoolValue::get(SZ_SETTING_ROCA, true);
let psz_msg = match setting_roca.source {
settings::Source::User => {
if setting_roca.value {
SZ_ROCA_ALLOW_USER
} else {
SZ_ROCA_BLOCK_USER
}
}
settings::Source::Admin => {
if setting_roca.value {
SZ_ROCA_ALLOW_ADMIN
} else {
SZ_ROCA_BLOCK_ADMIN
}
}
_ => SZ_ROCA_DEFAULT,
};
warn!(
"YubiKey serial number {} is affected by vulnerability CVE-2017-15361 \
(ROCA) and should be replaced. On-chip key generation {} See \
YSA-2017-01 <https://www.yubico.com/support/security-advisories/ysa-2017-01/> \
for additional information on device replacement and mitigation assistance",
yubikey.serial, psz_msg
);
if !setting_roca.value {
return Err(Error::NotSupported);
}
}
match algorithm { match algorithm {
YKPIV_ALGO_RSA1024 | YKPIV_ALGO_RSA2048 | YKPIV_ALGO_ECCP256 | YKPIV_ALGO_ECCP384 => (), AlgorithmId::Rsa1024 | AlgorithmId::Rsa2048 => {
_ => { if yubikey.device_model() == DEVTYPE_YK4
error!("invalid algorithm specified"); && yubikey.version.major == 4
return Err(Error::GenericError); && (yubikey.version.minor < 3
|| yubikey.version.minor == 3 && (yubikey.version.patch < 5))
{
setting_roca = settings::BoolValue::get(SZ_SETTING_ROCA, true);
let psz_msg = match setting_roca.source {
settings::Source::User => {
if setting_roca.value {
SZ_ROCA_ALLOW_USER
} else {
SZ_ROCA_BLOCK_USER
}
}
settings::Source::Admin => {
if setting_roca.value {
SZ_ROCA_ALLOW_ADMIN
} else {
SZ_ROCA_BLOCK_ADMIN
}
}
_ => SZ_ROCA_DEFAULT,
};
warn!(
"YubiKey serial number {} is affected by vulnerability CVE-2017-15361 \
(ROCA) and should be replaced. On-chip key generation {} See \
YSA-2017-01 <https://www.yubico.com/support/security-advisories/ysa-2017-01/> \
for additional information on device replacement and mitigation assistance",
yubikey.serial, psz_msg
);
if !setting_roca.value {
return Err(Error::NotSupported);
}
}
} }
_ => (),
} }
let txn = yubikey.begin_transaction()?; let txn = yubikey.begin_transaction()?;
templ[3] = slot; templ[3] = slot.into();
let mut offset = 5; let mut offset = 5;
in_data[..offset].copy_from_slice(&[ in_data[..offset].copy_from_slice(&[
@@ -258,7 +468,7 @@ pub fn generate(
3, // length sans this 2-byte header 3, // length sans this 2-byte header
YKPIV_ALGO_TAG, YKPIV_ALGO_TAG,
1, 1,
algorithm, algorithm.into(),
]); ]);
if in_data[4] == 0 { if in_data[4] == 0 {
@@ -312,7 +522,7 @@ pub fn generate(
let data = Buffer::new(response.data().into()); let data = Buffer::new(response.data().into());
match algorithm { match algorithm {
YKPIV_ALGO_RSA1024 | YKPIV_ALGO_RSA2048 => { AlgorithmId::Rsa1024 | AlgorithmId::Rsa2048 => {
let mut offset = 5; let mut offset = 5;
let mut len = 0; let mut len = 0;
@@ -340,10 +550,10 @@ pub fn generate(
exp, exp,
}) })
} }
YKPIV_ALGO_ECCP256 | YKPIV_ALGO_ECCP384 => { AlgorithmId::EccP256 | AlgorithmId::EccP384 => {
let mut offset = 3; let mut offset = 3;
let len = if algorithm == YKPIV_ALGO_ECCP256 { let len = if let AlgorithmId::EccP256 = algorithm {
CB_ECC_POINTP256 CB_ECC_POINTP256
} else { } else {
CB_ECC_POINTP384 CB_ECC_POINTP384
@@ -367,9 +577,5 @@ pub fn generate(
let point = data[offset..(offset + len)].to_vec(); let point = data[offset..(offset + len)].to_vec();
Ok(GeneratedKey::Ecc { algorithm, point }) Ok(GeneratedKey::Ecc { algorithm, point })
} }
_ => {
error!("wrong algorithm");
Err(Error::AlgorithmError)
}
} }
} }
-4
View File
@@ -167,10 +167,6 @@ pub mod yubikey;
pub use self::{key::Key, mgm::MgmKey}; pub use self::{key::Key, mgm::MgmKey};
pub use yubikey::YubiKey; pub use yubikey::YubiKey;
/// Algorithm identifiers
// TODO(tarcieri): make this an enum
pub type AlgorithmId = u8;
/// Object identifiers /// Object identifiers
pub type ObjectId = u32; pub type ObjectId = u32;
+12 -14
View File
@@ -9,6 +9,7 @@ use crate::{
use crate::{ use crate::{
apdu::{Response, StatusWords}, apdu::{Response, StatusWords},
consts::*, consts::*,
key::{AlgorithmId, SlotId},
mgm::MgmKey, mgm::MgmKey,
serialization::*, serialization::*,
Buffer, ObjectId, Buffer, ObjectId,
@@ -266,18 +267,18 @@ impl<'tx> Transaction<'tx> {
pub(crate) fn authenticated_command( pub(crate) fn authenticated_command(
&self, &self,
sign_in: &[u8], sign_in: &[u8],
algorithm: u8, algorithm: AlgorithmId,
key: u8, key: SlotId,
decipher: bool, decipher: bool,
) -> Result<Buffer, Error> { ) -> Result<Buffer, Error> {
let in_len = sign_in.len(); let in_len = sign_in.len();
let mut indata = [0u8; 1024]; let mut indata = [0u8; 1024];
let templ = [0, Ins::Authenticate.code(), algorithm, key]; let templ = [0, Ins::Authenticate.code(), algorithm.into(), key.into()];
let mut len: usize = 0; let mut len: usize = 0;
match algorithm { match algorithm {
YKPIV_ALGO_RSA1024 | YKPIV_ALGO_RSA2048 => { AlgorithmId::Rsa1024 | AlgorithmId::Rsa2048 => {
let key_len = if algorithm == YKPIV_ALGO_RSA1024 { let key_len = if let AlgorithmId::Rsa1024 = algorithm {
128 128
} else { } else {
256 256
@@ -287,8 +288,8 @@ impl<'tx> Transaction<'tx> {
return Err(Error::SizeError); return Err(Error::SizeError);
} }
} }
YKPIV_ALGO_ECCP256 | YKPIV_ALGO_ECCP384 => { AlgorithmId::EccP256 | AlgorithmId::EccP384 => {
let key_len = if algorithm == YKPIV_ALGO_ECCP256 { let key_len = if let AlgorithmId::EccP256 = algorithm {
32 32
} else { } else {
48 48
@@ -299,7 +300,6 @@ impl<'tx> Transaction<'tx> {
return Err(Error::SizeError); return Err(Error::SizeError);
} }
} }
_ => return Err(Error::AlgorithmError),
} }
let bytes = if in_len < 0x80 { let bytes = if in_len < 0x80 {
@@ -314,12 +314,10 @@ impl<'tx> Transaction<'tx> {
let mut offset = 1 + set_length(&mut indata[1..], in_len + bytes + 3); let mut offset = 1 + set_length(&mut indata[1..], in_len + bytes + 3);
indata[offset] = 0x82; indata[offset] = 0x82;
indata[offset + 1] = 0x00; indata[offset + 1] = 0x00;
indata[offset + 2] = indata[offset + 2] = match (algorithm, decipher) {
if (algorithm == YKPIV_ALGO_ECCP256 || algorithm == YKPIV_ALGO_ECCP384) && decipher { (AlgorithmId::EccP256, true) | (AlgorithmId::EccP384, true) => 0x85,
0x85 _ => 0x81,
} else { };
0x81
};
offset += 3; offset += 3;
offset += set_length(&mut indata[offset..], in_len); offset += set_length(&mut indata[offset..], in_len);
+12 -21
View File
@@ -36,7 +36,7 @@
#[cfg(feature = "untested")] #[cfg(feature = "untested")]
use crate::{ use crate::{
apdu::{Ins, StatusWords, APDU}, apdu::{Ins, StatusWords, APDU},
key::SlotId, key::{AlgorithmId, SlotId},
metadata, metadata,
mgm::MgmKey, mgm::MgmKey,
serialization::*, serialization::*,
@@ -363,7 +363,7 @@ impl YubiKey {
pub fn sign_data( pub fn sign_data(
&mut self, &mut self,
raw_in: &[u8], raw_in: &[u8],
algorithm: u8, algorithm: AlgorithmId,
key: SlotId, key: SlotId,
) -> Result<Buffer, Error> { ) -> Result<Buffer, Error> {
let txn = self.begin_transaction()?; let txn = self.begin_transaction()?;
@@ -377,7 +377,7 @@ impl YubiKey {
pub fn decrypt_data( pub fn decrypt_data(
&mut self, &mut self,
input: &[u8], input: &[u8],
algorithm: u8, algorithm: AlgorithmId,
key: SlotId, key: SlotId,
) -> Result<Buffer, Error> { ) -> Result<Buffer, Error> {
let txn = self.begin_transaction()?; let txn = self.begin_transaction()?;
@@ -605,7 +605,7 @@ impl YubiKey {
pub fn import_private_key( pub fn import_private_key(
&mut self, &mut self,
key: SlotId, key: SlotId,
algorithm: u8, algorithm: AlgorithmId,
p: Option<&[u8]>, p: Option<&[u8]>,
q: Option<&[u8]>, q: Option<&[u8]>,
dp: Option<&[u8]>, dp: Option<&[u8]>,
@@ -616,15 +616,7 @@ impl YubiKey {
touch_policy: u8, touch_policy: u8,
) -> Result<(), Error> { ) -> Result<(), Error> {
let mut key_data = Zeroizing::new(vec![0u8; 1024]); let mut key_data = Zeroizing::new(vec![0u8; 1024]);
let templ = [0, Ins::ImportKey.code(), algorithm, key]; let templ = [0, Ins::ImportKey.code(), algorithm.into(), key.into()];
if key == YKPIV_KEY_CARDMGM
|| key < YKPIV_KEY_RETIRED1
|| (key > YKPIV_KEY_RETIRED20 && key < YKPIV_KEY_AUTHENTICATION)
|| (key > YKPIV_KEY_CARDAUTH && key != YKPIV_KEY_ATTESTATION)
{
return Err(Error::KeyError);
}
if pin_policy != YKPIV_PINPOLICY_DEFAULT if pin_policy != YKPIV_PINPOLICY_DEFAULT
&& pin_policy != YKPIV_PINPOLICY_NEVER && pin_policy != YKPIV_PINPOLICY_NEVER
@@ -643,7 +635,7 @@ impl YubiKey {
} }
let (elem_len, params, param_tag) = match algorithm { let (elem_len, params, param_tag) = match algorithm {
YKPIV_ALGO_RSA1024 | YKPIV_ALGO_RSA2048 => match (p, q, dp, dq, qinv) { AlgorithmId::Rsa1024 | AlgorithmId::Rsa2048 => match (p, q, dp, dq, qinv) {
(Some(p), Some(q), Some(dp), Some(dq), Some(qinv)) => { (Some(p), Some(q), Some(dp), Some(dq), Some(qinv)) => {
if p.len() + q.len() + dp.len() + dq.len() + qinv.len() >= key_data.len() { if p.len() + q.len() + dp.len() + dq.len() + qinv.len() >= key_data.len() {
return Err(Error::SizeError); return Err(Error::SizeError);
@@ -651,8 +643,8 @@ impl YubiKey {
( (
match algorithm { match algorithm {
YKPIV_ALGO_RSA1024 => 64, AlgorithmId::Rsa1024 => 64,
YKPIV_ALGO_RSA2048 => 128, AlgorithmId::Rsa2048 => 128,
_ => unreachable!(), _ => unreachable!(),
}, },
vec![p, q, dp, dq, qinv], vec![p, q, dp, dq, qinv],
@@ -661,7 +653,7 @@ impl YubiKey {
} }
_ => return Err(Error::GenericError), _ => return Err(Error::GenericError),
}, },
YKPIV_ALGO_ECCP256 | YKPIV_ALGO_ECCP384 => match ec_data { AlgorithmId::EccP256 | AlgorithmId::EccP384 => match ec_data {
Some(ec_data) => { Some(ec_data) => {
if ec_data.len() >= key_data.len() { if ec_data.len() >= key_data.len() {
// This can never be true, but check to be explicit. // This can never be true, but check to be explicit.
@@ -670,8 +662,8 @@ impl YubiKey {
( (
match algorithm { match algorithm {
YKPIV_ALGO_ECCP256 => 32, AlgorithmId::EccP256 => 32,
YKPIV_ALGO_ECCP384 => 48, AlgorithmId::EccP384 => 48,
_ => unreachable!(), _ => unreachable!(),
}, },
vec![ec_data], vec![ec_data],
@@ -680,7 +672,6 @@ impl YubiKey {
} }
_ => return Err(Error::GenericError), _ => return Err(Error::GenericError),
}, },
_ => return Err(Error::AlgorithmError),
}; };
let mut offset = 0; let mut offset = 0;
@@ -737,7 +728,7 @@ impl YubiKey {
/// <https://developers.yubico.com/PIV/Introduction/PIV_attestation.html> /// <https://developers.yubico.com/PIV/Introduction/PIV_attestation.html>
#[cfg(feature = "untested")] #[cfg(feature = "untested")]
pub fn attest(&mut self, key: SlotId) -> Result<Buffer, Error> { pub fn attest(&mut self, key: SlotId) -> Result<Buffer, Error> {
let templ = [0, Ins::Attest.code(), key, 0]; let templ = [0, Ins::Attest.code(), key.into(), 0];
let txn = self.begin_transaction()?; let txn = self.begin_transaction()?;
let response = txn.transfer_data(&templ, &[], CB_OBJ_MAX)?; let response = txn.transfer_data(&templ, &[], CB_OBJ_MAX)?;