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