diff --git a/src/consts.rs b/src/consts.rs index 75ef5dd..ab903e7 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -124,10 +124,6 @@ pub const TAG_ECC_POINT: u8 = 0x86; pub const YKPIV_ALGO_TAG: u8 = 0x80; 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"; diff --git a/src/key.rs b/src/key.rs index 53dbb0d..59cd178 100644 --- a/src/key.rs +++ b/src/key.rs @@ -45,7 +45,7 @@ use crate::{ serialization::*, settings, yubikey::YubiKey, - AlgorithmId, Buffer, ObjectId, + Buffer, ObjectId, }; use log::{debug, error, warn}; use std::convert::TryFrom; @@ -273,6 +273,44 @@ pub const SLOTS: [SlotId; 24] = [ 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 for AlgorithmId { + type Error = Error; + + fn try_from(value: u8) -> Result { + match value { + 0x06 => Ok(AlgorithmId::Rsa1024), + 0x07 => Ok(AlgorithmId::Rsa2048), + 0x11 => Ok(AlgorithmId::EccP256), + 0x14 => Ok(AlgorithmId::EccP384), + _ => Err(Error::AlgorithmError), + } + } +} + +impl From 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 #[derive(Clone, Debug)] pub struct Key { @@ -377,50 +415,47 @@ pub fn generate( let mut templ = [0, Ins::GenerateAsymmetric.code(), 0, 0]; 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 \ - for additional information on device replacement and mitigation assistance", - yubikey.serial, psz_msg - ); - - if !setting_roca.value { - return Err(Error::NotSupported); - } - } - match algorithm { - YKPIV_ALGO_RSA1024 | YKPIV_ALGO_RSA2048 | YKPIV_ALGO_ECCP256 | YKPIV_ALGO_ECCP384 => (), - _ => { - error!("invalid algorithm specified"); - return Err(Error::GenericError); + AlgorithmId::Rsa1024 | AlgorithmId::Rsa2048 => { + if yubikey.device_model() == DEVTYPE_YK4 + && 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 \ + 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()?; @@ -433,7 +468,7 @@ pub fn generate( 3, // length sans this 2-byte header YKPIV_ALGO_TAG, 1, - algorithm, + algorithm.into(), ]); if in_data[4] == 0 { @@ -487,7 +522,7 @@ pub fn generate( let data = Buffer::new(response.data().into()); match algorithm { - YKPIV_ALGO_RSA1024 | YKPIV_ALGO_RSA2048 => { + AlgorithmId::Rsa1024 | AlgorithmId::Rsa2048 => { let mut offset = 5; let mut len = 0; @@ -515,10 +550,10 @@ pub fn generate( exp, }) } - YKPIV_ALGO_ECCP256 | YKPIV_ALGO_ECCP384 => { + AlgorithmId::EccP256 | AlgorithmId::EccP384 => { let mut offset = 3; - let len = if algorithm == YKPIV_ALGO_ECCP256 { + let len = if let AlgorithmId::EccP256 = algorithm { CB_ECC_POINTP256 } else { CB_ECC_POINTP384 @@ -542,9 +577,5 @@ pub fn generate( let point = data[offset..(offset + len)].to_vec(); Ok(GeneratedKey::Ecc { algorithm, point }) } - _ => { - error!("wrong algorithm"); - Err(Error::AlgorithmError) - } } } diff --git a/src/lib.rs b/src/lib.rs index b4d9851..37fd0c2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -167,10 +167,6 @@ pub mod yubikey; pub use self::{key::Key, mgm::MgmKey}; pub use yubikey::YubiKey; -/// Algorithm identifiers -// TODO(tarcieri): make this an enum -pub type AlgorithmId = u8; - /// Object identifiers pub type ObjectId = u32; diff --git a/src/transaction.rs b/src/transaction.rs index bda9caf..2514024 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -9,7 +9,7 @@ use crate::{ use crate::{ apdu::{Response, StatusWords}, consts::*, - key::SlotId, + key::{AlgorithmId, SlotId}, mgm::MgmKey, serialization::*, Buffer, ObjectId, @@ -267,18 +267,18 @@ impl<'tx> Transaction<'tx> { pub(crate) fn authenticated_command( &self, sign_in: &[u8], - algorithm: u8, + algorithm: AlgorithmId, key: SlotId, decipher: bool, ) -> Result { let in_len = sign_in.len(); let mut indata = [0u8; 1024]; - let templ = [0, Ins::Authenticate.code(), algorithm, key.into()]; + let templ = [0, Ins::Authenticate.code(), algorithm.into(), key.into()]; let mut len: usize = 0; match algorithm { - YKPIV_ALGO_RSA1024 | YKPIV_ALGO_RSA2048 => { - let key_len = if algorithm == YKPIV_ALGO_RSA1024 { + AlgorithmId::Rsa1024 | AlgorithmId::Rsa2048 => { + let key_len = if let AlgorithmId::Rsa1024 = algorithm { 128 } else { 256 @@ -288,8 +288,8 @@ impl<'tx> Transaction<'tx> { return Err(Error::SizeError); } } - YKPIV_ALGO_ECCP256 | YKPIV_ALGO_ECCP384 => { - let key_len = if algorithm == YKPIV_ALGO_ECCP256 { + AlgorithmId::EccP256 | AlgorithmId::EccP384 => { + let key_len = if let AlgorithmId::EccP256 = algorithm { 32 } else { 48 @@ -300,7 +300,6 @@ impl<'tx> Transaction<'tx> { return Err(Error::SizeError); } } - _ => return Err(Error::AlgorithmError), } let bytes = if in_len < 0x80 { @@ -315,12 +314,10 @@ impl<'tx> Transaction<'tx> { let mut offset = 1 + set_length(&mut indata[1..], in_len + bytes + 3); indata[offset] = 0x82; indata[offset + 1] = 0x00; - indata[offset + 2] = - if (algorithm == YKPIV_ALGO_ECCP256 || algorithm == YKPIV_ALGO_ECCP384) && decipher { - 0x85 - } else { - 0x81 - }; + indata[offset + 2] = match (algorithm, decipher) { + (AlgorithmId::EccP256, true) | (AlgorithmId::EccP384, true) => 0x85, + _ => 0x81, + }; offset += 3; offset += set_length(&mut indata[offset..], in_len); diff --git a/src/yubikey.rs b/src/yubikey.rs index e18e5a6..eba6e04 100644 --- a/src/yubikey.rs +++ b/src/yubikey.rs @@ -36,7 +36,7 @@ #[cfg(feature = "untested")] use crate::{ apdu::{Ins, StatusWords, APDU}, - key::SlotId, + key::{AlgorithmId, SlotId}, metadata, mgm::MgmKey, serialization::*, @@ -356,7 +356,7 @@ impl YubiKey { pub fn sign_data( &mut self, raw_in: &[u8], - algorithm: u8, + algorithm: AlgorithmId, key: SlotId, ) -> Result { let txn = self.begin_transaction()?; @@ -370,7 +370,7 @@ impl YubiKey { pub fn decrypt_data( &mut self, input: &[u8], - algorithm: u8, + algorithm: AlgorithmId, key: SlotId, ) -> Result { let txn = self.begin_transaction()?; @@ -598,7 +598,7 @@ impl YubiKey { pub fn import_private_key( &mut self, key: SlotId, - algorithm: u8, + algorithm: AlgorithmId, p: Option<&[u8]>, q: Option<&[u8]>, dp: Option<&[u8]>, @@ -609,7 +609,7 @@ impl YubiKey { touch_policy: u8, ) -> Result<(), Error> { let mut key_data = Zeroizing::new(vec![0u8; 1024]); - let templ = [0, Ins::ImportKey.code(), algorithm, key.into()]; + let templ = [0, Ins::ImportKey.code(), algorithm.into(), key.into()]; // Only slot we want to exclude is CardManagement, which isn't in the enum. // TODO: Decide whether to add it or not. @@ -634,7 +634,7 @@ impl YubiKey { } 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)) => { if p.len() + q.len() + dp.len() + dq.len() + qinv.len() >= key_data.len() { return Err(Error::SizeError); @@ -642,8 +642,8 @@ impl YubiKey { ( match algorithm { - YKPIV_ALGO_RSA1024 => 64, - YKPIV_ALGO_RSA2048 => 128, + AlgorithmId::Rsa1024 => 64, + AlgorithmId::Rsa2048 => 128, _ => unreachable!(), }, vec![p, q, dp, dq, qinv], @@ -652,7 +652,7 @@ impl YubiKey { } _ => return Err(Error::GenericError), }, - YKPIV_ALGO_ECCP256 | YKPIV_ALGO_ECCP384 => match ec_data { + AlgorithmId::EccP256 | AlgorithmId::EccP384 => match ec_data { Some(ec_data) => { if ec_data.len() >= key_data.len() { // This can never be true, but check to be explicit. @@ -661,8 +661,8 @@ impl YubiKey { ( match algorithm { - YKPIV_ALGO_ECCP256 => 32, - YKPIV_ALGO_ECCP384 => 48, + AlgorithmId::EccP256 => 32, + AlgorithmId::EccP384 => 48, _ => unreachable!(), }, vec![ec_data], @@ -671,7 +671,6 @@ impl YubiKey { } _ => return Err(Error::GenericError), }, - _ => return Err(Error::AlgorithmError), }; let mut offset = 0;