add support for rsa3072 and rsa4096 (#598)
This commit is contained in:
+1
-1
@@ -4,7 +4,7 @@ version = "0.8.0"
|
||||
description = """
|
||||
Pure Rust cross-platform host-side driver for YubiKey devices from Yubico with
|
||||
support for hardware-backed public-key decryption and digital signatures using
|
||||
the Personal Identity Verification (PIV) application. Supports RSA (1024/2048)
|
||||
the Personal Identity Verification (PIV) application. Supports RSA (1024/2048/3072/4096)
|
||||
or ECC (NIST P-256/P-384) algorithms e.g, PKCS#1v1.5, ECDSA
|
||||
"""
|
||||
authors = ["Tony Arcieri <tony@iqlusion.io>", "Yubico AB"]
|
||||
|
||||
@@ -22,7 +22,7 @@ access provided by the [`pcsc` crate].
|
||||
## About
|
||||
|
||||
YubiKeys are versatile devices and through their PIV support, you can use them
|
||||
to store a number of RSA (2048/1024) and ECC (NIST P-256/P-384) private keys
|
||||
to store a number of RSA (1024/2048/3072/4096) and ECC (NIST P-256/P-384) private keys
|
||||
with configurable access control policies. Both the signing (RSASSA/ECDSA) and
|
||||
encryption (PKCS#1v1.5/ECIES) use cases are supported for either key type.
|
||||
|
||||
@@ -56,13 +56,16 @@ on which devices support PIV and the available functionality.
|
||||
### Supported Algorithms
|
||||
- **Authentication**: `3DES`
|
||||
- **Encryption**:
|
||||
- RSA: `RSA1024`, `RSA2048`
|
||||
- RSA: `RSA1024`, `RSA2048`, `RSA3072`, `RSA4096`
|
||||
- ECC: `ECCP256`, `ECCP384` (NIST curves: P-256, P-384)
|
||||
- **Signatures**:
|
||||
- RSASSA-PKCS#1v1.5: `RSA1024`, `RSA2048`
|
||||
- RSASSA-PKCS#1v1.5: `RSA1024`, `RSA2048`, `RSA3072`, `RSA4096`
|
||||
- ECDSA: `ECCP256`, `ECCP384` (NIST curves: P-256, P-384)
|
||||
|
||||
NOTE: RSASSA-PSS signatures and RSA-OAEP encryption may be supportable (TBD)
|
||||
NOTE:
|
||||
|
||||
- RSASSA-PSS signatures and RSA-OAEP encryption may be supportable (TBD)
|
||||
- `RSA3072` and `RSA4096` require a YubiKey with firmware 5.7 or newer.
|
||||
|
||||
## Minimum Supported Rust Version
|
||||
|
||||
|
||||
@@ -369,6 +369,22 @@ pub mod yubikey_signer {
|
||||
const ALGORITHM: AlgorithmId = AlgorithmId::Rsa2048;
|
||||
}
|
||||
|
||||
/// RSA 3072 bits key
|
||||
pub struct Rsa3072;
|
||||
|
||||
impl RsaLength for Rsa3072 {
|
||||
const BIT_LENGTH: usize = 3072;
|
||||
const ALGORITHM: AlgorithmId = AlgorithmId::Rsa3072;
|
||||
}
|
||||
|
||||
/// RSA 4096 bits key
|
||||
pub struct Rsa4096;
|
||||
|
||||
impl RsaLength for Rsa4096 {
|
||||
const BIT_LENGTH: usize = 4096;
|
||||
const ALGORITHM: AlgorithmId = AlgorithmId::Rsa4096;
|
||||
}
|
||||
|
||||
/// RSA keys used to sign certificates
|
||||
pub struct YubiRsa<N: RsaLength> {
|
||||
_len: PhantomData<N>,
|
||||
|
||||
+31
-7
@@ -6,10 +6,10 @@
|
||||
//! Supported algorithms:
|
||||
//!
|
||||
//! - **Encryption**:
|
||||
//! - RSA: `RSA1024`, `RSA2048`
|
||||
//! - RSA: `RSA1024`, `RSA2048`, `RSA3072`, `RSA4096`
|
||||
//! - ECC: `ECCP256`, `ECCP384` (NIST curves: P-256, P-384)
|
||||
//! - **Signatures**:
|
||||
//! - RSASSA-PKCS#1v1.5: `RSA1024`, `RSA2048`
|
||||
//! - RSASSA-PKCS#1v1.5: `RSA1024`, `RSA2048`, `RSA3072`, `RSA4096`
|
||||
//! - ECDSA: `ECCP256`, `ECCP384` (NIST curves: P-256, P-384)
|
||||
|
||||
// Adapted from yubico-piv-tool:
|
||||
@@ -485,6 +485,12 @@ pub enum AlgorithmId {
|
||||
/// 2048-bit RSA.
|
||||
Rsa2048,
|
||||
|
||||
/// 3072-bit RSA. Requires firmware 5.7 or newer
|
||||
Rsa3072,
|
||||
|
||||
/// 4096-bit RSA. Requires firmware 5.7 or newer
|
||||
Rsa4096,
|
||||
|
||||
/// ECDSA with the NIST P256 curve.
|
||||
EccP256,
|
||||
|
||||
@@ -499,6 +505,8 @@ impl TryFrom<u8> for AlgorithmId {
|
||||
match value {
|
||||
0x06 => Ok(AlgorithmId::Rsa1024),
|
||||
0x07 => Ok(AlgorithmId::Rsa2048),
|
||||
0x05 => Ok(AlgorithmId::Rsa3072),
|
||||
0x16 => Ok(AlgorithmId::Rsa4096),
|
||||
0x11 => Ok(AlgorithmId::EccP256),
|
||||
0x14 => Ok(AlgorithmId::EccP384),
|
||||
_ => Err(Error::AlgorithmError),
|
||||
@@ -511,6 +519,8 @@ impl From<AlgorithmId> for u8 {
|
||||
match id {
|
||||
AlgorithmId::Rsa1024 => 0x06,
|
||||
AlgorithmId::Rsa2048 => 0x07,
|
||||
AlgorithmId::Rsa3072 => 0x05,
|
||||
AlgorithmId::Rsa4096 => 0x16,
|
||||
AlgorithmId::EccP256 => 0x11,
|
||||
AlgorithmId::EccP384 => 0x14,
|
||||
}
|
||||
@@ -528,6 +538,8 @@ impl AlgorithmId {
|
||||
match self {
|
||||
AlgorithmId::Rsa1024 => 64,
|
||||
AlgorithmId::Rsa2048 => 128,
|
||||
AlgorithmId::Rsa3072 => 192,
|
||||
AlgorithmId::Rsa4096 => 256,
|
||||
AlgorithmId::EccP256 => 32,
|
||||
AlgorithmId::EccP384 => 48,
|
||||
}
|
||||
@@ -536,7 +548,10 @@ impl AlgorithmId {
|
||||
#[cfg(feature = "untested")]
|
||||
fn get_param_tag(self) -> u8 {
|
||||
match self {
|
||||
AlgorithmId::Rsa1024 | AlgorithmId::Rsa2048 => 0x01,
|
||||
AlgorithmId::Rsa1024
|
||||
| AlgorithmId::Rsa2048
|
||||
| AlgorithmId::Rsa3072
|
||||
| AlgorithmId::Rsa4096 => 0x01,
|
||||
AlgorithmId::EccP256 | AlgorithmId::EccP384 => 0x6,
|
||||
}
|
||||
}
|
||||
@@ -609,7 +624,10 @@ pub fn generate(
|
||||
let setting_roca: setting::Setting;
|
||||
|
||||
match algorithm {
|
||||
AlgorithmId::Rsa1024 | AlgorithmId::Rsa2048 => {
|
||||
AlgorithmId::Rsa1024
|
||||
| AlgorithmId::Rsa2048
|
||||
| AlgorithmId::Rsa3072
|
||||
| AlgorithmId::Rsa4096 => {
|
||||
if yubikey.version.major == 4
|
||||
&& (yubikey.version.minor < 3
|
||||
|| yubikey.version.minor == 3 && (yubikey.version.patch < 5))
|
||||
@@ -813,7 +831,7 @@ impl RsaKeyData {
|
||||
|
||||
/// Imports a private RSA encryption or signing key into the YubiKey.
|
||||
///
|
||||
/// Errors if `algorithm` isn't `AlgorithmId::Rsa1024` or `AlgorithmId::Rsa2048`.
|
||||
/// Errors if `algorithm` isn't `AlgorithmId::Rsa1024` or `AlgorithmId::Rsa2048` or `AlgorithmId::Rsa3072` or `AlgorithmId::Rsa4096`.
|
||||
#[cfg(feature = "untested")]
|
||||
pub fn import_rsa_key(
|
||||
yubikey: &mut YubiKey,
|
||||
@@ -824,7 +842,10 @@ pub fn import_rsa_key(
|
||||
pin_policy: PinPolicy,
|
||||
) -> Result<()> {
|
||||
match algorithm {
|
||||
AlgorithmId::Rsa1024 | AlgorithmId::Rsa2048 => (),
|
||||
AlgorithmId::Rsa1024
|
||||
| AlgorithmId::Rsa2048
|
||||
| AlgorithmId::Rsa3072
|
||||
| AlgorithmId::Rsa4096 => (),
|
||||
_ => return Err(Error::AlgorithmError),
|
||||
}
|
||||
|
||||
@@ -1101,7 +1122,10 @@ fn read_public_key(
|
||||
//
|
||||
// 0x7f 0x49 -> Application | Constructed | 0x49
|
||||
match algorithm {
|
||||
AlgorithmId::Rsa1024 | AlgorithmId::Rsa2048 => {
|
||||
AlgorithmId::Rsa1024
|
||||
| AlgorithmId::Rsa2048
|
||||
| AlgorithmId::Rsa3072
|
||||
| AlgorithmId::Rsa4096 => {
|
||||
// It appears that the inner application-specific value returned by the
|
||||
// YubiKey is constructed such that RSA pubkeys can be parsed in two ways:
|
||||
//
|
||||
|
||||
+17
-8
@@ -289,14 +289,23 @@ impl<'tx> Transaction<'tx> {
|
||||
let templ = [0, Ins::Authenticate.code(), algorithm.into(), key.into()];
|
||||
|
||||
match algorithm {
|
||||
AlgorithmId::Rsa1024 | AlgorithmId::Rsa2048 => {
|
||||
let key_len = if let AlgorithmId::Rsa1024 = algorithm {
|
||||
128
|
||||
} else {
|
||||
256
|
||||
};
|
||||
|
||||
if in_len != key_len {
|
||||
AlgorithmId::Rsa1024 => {
|
||||
if in_len != 128 {
|
||||
return Err(Error::SizeError);
|
||||
}
|
||||
}
|
||||
AlgorithmId::Rsa2048 => {
|
||||
if in_len != 256 {
|
||||
return Err(Error::SizeError);
|
||||
}
|
||||
}
|
||||
AlgorithmId::Rsa3072 => {
|
||||
if in_len != 384 {
|
||||
return Err(Error::SizeError);
|
||||
}
|
||||
}
|
||||
AlgorithmId::Rsa4096 => {
|
||||
if in_len != 512 {
|
||||
return Err(Error::SizeError);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,6 +209,34 @@ fn generate_self_signed_rsa_cert() {
|
||||
assert!(pubkey.verify_prehash(&hash, &sig).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn generate_rsa3072() {
|
||||
let mut yubikey = YUBIKEY.lock().unwrap();
|
||||
let version = yubikey.version();
|
||||
|
||||
assert!(yubikey.authenticate(MgmKey::default()).is_ok());
|
||||
|
||||
let slot = SlotId::Retired(RetiredSlotId::R1);
|
||||
|
||||
// Generate a new key in the selected slot.
|
||||
let generated = piv::generate(
|
||||
&mut yubikey,
|
||||
slot,
|
||||
AlgorithmId::Rsa3072,
|
||||
PinPolicy::Default,
|
||||
TouchPolicy::Default,
|
||||
);
|
||||
|
||||
match generated {
|
||||
Ok(key) => {
|
||||
let pubkey = key.subject_public_key;
|
||||
assert!(pubkey.bit_len() > 3072)
|
||||
}
|
||||
Err(e) => assert!((version.major, version.minor) < (5, 7) && e == Error::AlgorithmError),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn generate_self_signed_ec_cert() {
|
||||
|
||||
Reference in New Issue
Block a user