Skip to content

Commit

Permalink
Migrate parsing of unencrypted PKCS#8 private keys to Rust
Browse files Browse the repository at this point in the history
  • Loading branch information
alex committed Jan 16, 2025
1 parent 06065d4 commit 81ab411
Show file tree
Hide file tree
Showing 7 changed files with 305 additions and 58 deletions.
32 changes: 32 additions & 0 deletions src/rust/cryptography-key-parsing/src/dsa.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// This file is dual licensed under the terms of the Apache License, Version
// 2.0, and the BSD License. See the LICENSE file in the root of this repository
// for complete details.

use crate::KeyParsingResult;

#[derive(asn1::Asn1Read)]
struct DsaPrivateKey<'a> {
version: u8,
p: asn1::BigUint<'a>,
q: asn1::BigUint<'a>,
g: asn1::BigUint<'a>,
pub_key: asn1::BigUint<'a>,
priv_key: asn1::BigUint<'a>,
}

pub fn parse_pkcs1_private_key(
data: &[u8],
) -> KeyParsingResult<openssl::pkey::PKey<openssl::pkey::Private>> {
let dsa_private_key = asn1::parse_single::<DsaPrivateKey<'_>>(data)?;
if dsa_private_key.version != 0 {
return Err(crate::KeyParsingError::InvalidKey);
}
let dsa = openssl::dsa::Dsa::from_private_components(
openssl::bn::BigNum::from_slice(dsa_private_key.p.as_bytes())?,
openssl::bn::BigNum::from_slice(dsa_private_key.q.as_bytes())?,
openssl::bn::BigNum::from_slice(dsa_private_key.g.as_bytes())?,
openssl::bn::BigNum::from_slice(dsa_private_key.priv_key.as_bytes())?,
openssl::bn::BigNum::from_slice(dsa_private_key.pub_key.as_bytes())?,
)?;
Ok(openssl::pkey::PKey::from_dsa(dsa)?)
}
103 changes: 103 additions & 0 deletions src/rust/cryptography-key-parsing/src/ec.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// This file is dual licensed under the terms of the Apache License, Version
// 2.0, and the BSD License. See the LICENSE file in the root of this repository
// for complete details.

use crate::{KeyParsingError, KeyParsingResult};

use cryptography_x509::common::EcParameters;

// From RFC 5915 Section 3
#[derive(asn1::Asn1Read)]
pub(crate) struct EcPrivateKey<'a> {
pub(crate) version: u8,
pub(crate) private_key: &'a [u8],
#[explicit(0)]
pub(crate) parameters: Option<EcParameters<'a>>,
#[explicit(1)]
pub(crate) public_key: Option<asn1::BitString<'a>>,
}

pub(crate) fn ec_params_to_group(
params: &EcParameters<'_>,
) -> KeyParsingResult<openssl::ec::EcGroup> {
match params {
EcParameters::NamedCurve(curve_oid) => {
let curve_nid = match curve_oid {
&cryptography_x509::oid::EC_SECP192R1 => openssl::nid::Nid::X9_62_PRIME192V1,
&cryptography_x509::oid::EC_SECP224R1 => openssl::nid::Nid::SECP224R1,
&cryptography_x509::oid::EC_SECP256R1 => openssl::nid::Nid::X9_62_PRIME256V1,
&cryptography_x509::oid::EC_SECP384R1 => openssl::nid::Nid::SECP384R1,
&cryptography_x509::oid::EC_SECP521R1 => openssl::nid::Nid::SECP521R1,

&cryptography_x509::oid::EC_SECP256K1 => openssl::nid::Nid::SECP256K1,

&cryptography_x509::oid::EC_SECT233R1 => openssl::nid::Nid::SECT233R1,
&cryptography_x509::oid::EC_SECT283R1 => openssl::nid::Nid::SECT283R1,
&cryptography_x509::oid::EC_SECT409R1 => openssl::nid::Nid::SECT409R1,
&cryptography_x509::oid::EC_SECT571R1 => openssl::nid::Nid::SECT571R1,

&cryptography_x509::oid::EC_SECT163R2 => openssl::nid::Nid::SECT163R2,

&cryptography_x509::oid::EC_SECT163K1 => openssl::nid::Nid::SECT163K1,
&cryptography_x509::oid::EC_SECT233K1 => openssl::nid::Nid::SECT233K1,
&cryptography_x509::oid::EC_SECT283K1 => openssl::nid::Nid::SECT283K1,
&cryptography_x509::oid::EC_SECT409K1 => openssl::nid::Nid::SECT409K1,
&cryptography_x509::oid::EC_SECT571K1 => openssl::nid::Nid::SECT571K1,

#[cfg(not(CRYPTOGRAPHY_IS_BORINGSSL))]
&cryptography_x509::oid::EC_BRAINPOOLP256R1 => openssl::nid::Nid::BRAINPOOL_P256R1,
#[cfg(not(CRYPTOGRAPHY_IS_BORINGSSL))]
&cryptography_x509::oid::EC_BRAINPOOLP384R1 => openssl::nid::Nid::BRAINPOOL_P384R1,
#[cfg(not(CRYPTOGRAPHY_IS_BORINGSSL))]
&cryptography_x509::oid::EC_BRAINPOOLP512R1 => openssl::nid::Nid::BRAINPOOL_P512R1,

_ => return Err(KeyParsingError::UnsupportedEllipticCurve(curve_oid.clone())),
};

Ok(openssl::ec::EcGroup::from_curve_name(curve_nid)
.map_err(|_| KeyParsingError::UnsupportedEllipticCurve(curve_oid.clone()))?)
}
EcParameters::ImplicitCurve(_) | EcParameters::SpecifiedCurve(_) => {
Err(KeyParsingError::ExplicitCurveUnsupported)
}
}
}

pub fn parse_pkcs1_private_key(
data: &[u8],
ec_params: Option<EcParameters<'_>>,
) -> KeyParsingResult<openssl::pkey::PKey<openssl::pkey::Private>> {
let ec_private_key = asn1::parse_single::<EcPrivateKey<'_>>(data)?;
if ec_private_key.version != 1 {
return Err(crate::KeyParsingError::InvalidKey);
}

let group = match (ec_params, ec_private_key.parameters) {
(Some(outer_params), Some(inner_params)) => {
if outer_params != inner_params {
return Err(crate::KeyParsingError::InvalidKey);
}
ec_params_to_group(&outer_params)?
}
(Some(outer_params), None) => ec_params_to_group(&outer_params)?,
(None, Some(inner_params)) => ec_params_to_group(&inner_params)?,
(None, None) => return Err(crate::KeyParsingError::InvalidKey),
};

let private_number = openssl::bn::BigNum::from_slice(ec_private_key.private_key)?;
let mut bn_ctx = openssl::bn::BigNumContext::new()?;
let public_point = if let Some(point_bytes) = ec_private_key.public_key {
openssl::ec::EcPoint::from_bytes(&group, point_bytes.as_bytes(), &mut bn_ctx)
.map_err(|_| crate::KeyParsingError::InvalidKey)?
} else {
let mut public_point = openssl::ec::EcPoint::new(&group)?;
public_point
.mul(&group, group.generator(), &private_number, &bn_ctx)
.map_err(|_| crate::KeyParsingError::InvalidKey)?;
public_point
};

let ec_key =
openssl::ec::EcKey::from_private_components(&group, &private_number, &public_point)?;
Ok(openssl::pkey::PKey::from_ec_key(ec_key)?)
}
3 changes: 3 additions & 0 deletions src/rust/cryptography-key-parsing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
#![deny(rust_2018_idioms, clippy::undocumented_unsafe_blocks)]
#![allow(unknown_lints, clippy::result_large_err)]

pub mod dsa;
pub mod ec;
pub mod pkcs8;
pub mod rsa;
pub mod spki;

Expand Down
100 changes: 100 additions & 0 deletions src/rust/cryptography-key-parsing/src/pkcs8.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// This file is dual licensed under the terms of the Apache License, Version
// 2.0, and the BSD License. See the LICENSE file in the root of this repository
// for complete details.

use cryptography_x509::common::{AlgorithmIdentifier, AlgorithmParameters};
use cryptography_x509::csr::Attributes;

use crate::{ec, rsa, KeyParsingError, KeyParsingResult};

// RFC 5208 Section 5
#[derive(asn1::Asn1Read)]
struct PrivateKeyInfo<'a> {
version: u8,
algorithm: AlgorithmIdentifier<'a>,
private_key: &'a [u8],
#[implicit(0)]
_attributes: Option<Attributes<'a>>,
}

pub fn parse_private_key(
data: &[u8],
) -> KeyParsingResult<openssl::pkey::PKey<openssl::pkey::Private>> {
let k = asn1::parse_single::<PrivateKeyInfo<'_>>(data)?;
if k.version != 0 {
return Err(crate::KeyParsingError::InvalidKey);
}
match k.algorithm.params {
AlgorithmParameters::Rsa(_) => rsa::parse_pkcs1_private_key(k.private_key),
AlgorithmParameters::Ec(ec_params) => {
ec::parse_pkcs1_private_key(k.private_key, Some(ec_params))
}

AlgorithmParameters::Dsa(dsa_params) => {
let dsa_private_key = openssl::bn::BigNum::from_slice(
asn1::parse_single::<asn1::BigUint<'_>>(k.private_key)?.as_bytes(),
)?;
let p = openssl::bn::BigNum::from_slice(dsa_params.p.as_bytes())?;
let q = openssl::bn::BigNum::from_slice(dsa_params.q.as_bytes())?;
let g = openssl::bn::BigNum::from_slice(dsa_params.g.as_bytes())?;

let mut bn_ctx = openssl::bn::BigNumContext::new()?;
let mut pub_key = openssl::bn::BigNum::new()?;
pub_key.mod_exp(&g, &dsa_private_key, &p, &mut bn_ctx)?;

let dsa =
openssl::dsa::Dsa::from_private_components(p, q, g, dsa_private_key, pub_key)?;
Ok(openssl::pkey::PKey::from_dsa(dsa)?)
}

#[cfg(not(CRYPTOGRAPHY_IS_BORINGSSL))]
AlgorithmParameters::Dh(dh_params) => {
let dh_private_key = openssl::bn::BigNum::from_slice(
asn1::parse_single::<asn1::BigUint<'_>>(k.private_key)?.as_bytes(),
)?;
let p = openssl::bn::BigNum::from_slice(dh_params.p.as_bytes())?;
let g = openssl::bn::BigNum::from_slice(dh_params.g.as_bytes())?;
let q = openssl::bn::BigNum::from_slice(dh_params.q.as_bytes())?;

let dh = openssl::dh::Dh::from_params(p, g, q)?;
let dh = dh.set_private_key(dh_private_key)?;
Ok(openssl::pkey::PKey::from_dh(dh)?)
}

#[cfg(not(CRYPTOGRAPHY_IS_BORINGSSL))]
AlgorithmParameters::DhKeyAgreement(dh_params) => {
let dh_private_key = openssl::bn::BigNum::from_slice(
asn1::parse_single::<asn1::BigUint<'_>>(k.private_key)?.as_bytes(),
)?;
let p = openssl::bn::BigNum::from_slice(dh_params.p.as_bytes())?;
let g = openssl::bn::BigNum::from_slice(dh_params.g.as_bytes())?;

let dh = openssl::dh::Dh::from_pqg(p, None, g)?;
let dh = dh.set_private_key(dh_private_key)?;
Ok(openssl::pkey::PKey::from_dh(dh)?)
}

AlgorithmParameters::X25519 => Ok(openssl::pkey::PKey::private_key_from_raw_bytes(
asn1::parse_single(k.private_key)?,
openssl::pkey::Id::X25519,
)?),
#[cfg(all(not(CRYPTOGRAPHY_IS_LIBRESSL), not(CRYPTOGRAPHY_IS_BORINGSSL)))]
AlgorithmParameters::X448 => Ok(openssl::pkey::PKey::private_key_from_raw_bytes(
asn1::parse_single(k.private_key)?,
openssl::pkey::Id::X448,
)?),
AlgorithmParameters::Ed25519 => Ok(openssl::pkey::PKey::private_key_from_raw_bytes(
asn1::parse_single(k.private_key)?,
openssl::pkey::Id::ED25519,
)?),
#[cfg(all(not(CRYPTOGRAPHY_IS_LIBRESSL), not(CRYPTOGRAPHY_IS_BORINGSSL)))]
AlgorithmParameters::Ed448 => Ok(openssl::pkey::PKey::private_key_from_raw_bytes(
asn1::parse_single(k.private_key)?,
openssl::pkey::Id::ED448,
)?),

_ => Err(KeyParsingError::UnsupportedKeyType(
k.algorithm.oid().clone(),
)),
}
}
36 changes: 36 additions & 0 deletions src/rust/cryptography-key-parsing/src/rsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,22 @@ pub struct Pkcs1RsaPublicKey<'a> {
e: asn1::BigUint<'a>,
}

// RFC 8017, Section A.1.2
#[derive(asn1::Asn1Read)]
pub(crate) struct RsaPrivateKey<'a> {
pub(crate) version: u8,
pub(crate) n: asn1::BigUint<'a>,
pub(crate) e: asn1::BigUint<'a>,
pub(crate) d: asn1::BigUint<'a>,
pub(crate) p: asn1::BigUint<'a>,
pub(crate) q: asn1::BigUint<'a>,
pub(crate) dmp1: asn1::BigUint<'a>,
pub(crate) dmq1: asn1::BigUint<'a>,
pub(crate) iqmp: asn1::BigUint<'a>,
// We don't support these, so don't bother to parse the inner fields.
pub(crate) other_prime_infos: Option<asn1::SequenceOf<'a, asn1::Sequence<'a>, 1>>,
}

pub fn parse_pkcs1_public_key(
data: &[u8],
) -> KeyParsingResult<openssl::pkey::PKey<openssl::pkey::Public>> {
Expand All @@ -21,3 +37,23 @@ pub fn parse_pkcs1_public_key(
let rsa = openssl::rsa::Rsa::from_public_components(n, e)?;
Ok(openssl::pkey::PKey::from_rsa(rsa)?)
}

pub fn parse_pkcs1_private_key(
data: &[u8],
) -> KeyParsingResult<openssl::pkey::PKey<openssl::pkey::Private>> {
let rsa_private_key = asn1::parse_single::<RsaPrivateKey<'_>>(data)?;
if rsa_private_key.version != 0 || rsa_private_key.other_prime_infos.is_some() {
return Err(crate::KeyParsingError::InvalidKey);
}
let rsa_key = openssl::rsa::Rsa::from_private_components(
openssl::bn::BigNum::from_slice(rsa_private_key.n.as_bytes())?,
openssl::bn::BigNum::from_slice(rsa_private_key.e.as_bytes())?,
openssl::bn::BigNum::from_slice(rsa_private_key.d.as_bytes())?,
openssl::bn::BigNum::from_slice(rsa_private_key.p.as_bytes())?,
openssl::bn::BigNum::from_slice(rsa_private_key.q.as_bytes())?,
openssl::bn::BigNum::from_slice(rsa_private_key.dmp1.as_bytes())?,
openssl::bn::BigNum::from_slice(rsa_private_key.dmq1.as_bytes())?,
openssl::bn::BigNum::from_slice(rsa_private_key.iqmp.as_bytes())?,
)?;
Ok(openssl::pkey::PKey::from_rsa(rsa_key)?)
}
70 changes: 13 additions & 57 deletions src/rust/cryptography-key-parsing/src/spki.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// 2.0, and the BSD License. See the LICENSE file in the root of this repository
// for complete details.

use cryptography_x509::common::{AlgorithmParameters, EcParameters, SubjectPublicKeyInfo};
use cryptography_x509::common::{AlgorithmParameters, SubjectPublicKeyInfo};

use crate::{KeyParsingError, KeyParsingResult};

Expand All @@ -12,62 +12,18 @@ pub fn parse_public_key(
let k = asn1::parse_single::<SubjectPublicKeyInfo<'_>>(data)?;

match k.algorithm.params {
AlgorithmParameters::Ec(ec_params) => match ec_params {
EcParameters::NamedCurve(curve_oid) => {
let curve_nid = match curve_oid {
cryptography_x509::oid::EC_SECP192R1 => openssl::nid::Nid::X9_62_PRIME192V1,
cryptography_x509::oid::EC_SECP224R1 => openssl::nid::Nid::SECP224R1,
cryptography_x509::oid::EC_SECP256R1 => openssl::nid::Nid::X9_62_PRIME256V1,
cryptography_x509::oid::EC_SECP384R1 => openssl::nid::Nid::SECP384R1,
cryptography_x509::oid::EC_SECP521R1 => openssl::nid::Nid::SECP521R1,

cryptography_x509::oid::EC_SECP256K1 => openssl::nid::Nid::SECP256K1,

cryptography_x509::oid::EC_SECT233R1 => openssl::nid::Nid::SECT233R1,
cryptography_x509::oid::EC_SECT283R1 => openssl::nid::Nid::SECT283R1,
cryptography_x509::oid::EC_SECT409R1 => openssl::nid::Nid::SECT409R1,
cryptography_x509::oid::EC_SECT571R1 => openssl::nid::Nid::SECT571R1,

cryptography_x509::oid::EC_SECT163R2 => openssl::nid::Nid::SECT163R2,

cryptography_x509::oid::EC_SECT163K1 => openssl::nid::Nid::SECT163K1,
cryptography_x509::oid::EC_SECT233K1 => openssl::nid::Nid::SECT233K1,
cryptography_x509::oid::EC_SECT283K1 => openssl::nid::Nid::SECT283K1,
cryptography_x509::oid::EC_SECT409K1 => openssl::nid::Nid::SECT409K1,
cryptography_x509::oid::EC_SECT571K1 => openssl::nid::Nid::SECT571K1,

#[cfg(not(CRYPTOGRAPHY_IS_BORINGSSL))]
cryptography_x509::oid::EC_BRAINPOOLP256R1 => {
openssl::nid::Nid::BRAINPOOL_P256R1
}
#[cfg(not(CRYPTOGRAPHY_IS_BORINGSSL))]
cryptography_x509::oid::EC_BRAINPOOLP384R1 => {
openssl::nid::Nid::BRAINPOOL_P384R1
}
#[cfg(not(CRYPTOGRAPHY_IS_BORINGSSL))]
cryptography_x509::oid::EC_BRAINPOOLP512R1 => {
openssl::nid::Nid::BRAINPOOL_P512R1
}

_ => return Err(KeyParsingError::UnsupportedEllipticCurve(curve_oid)),
};

let group = openssl::ec::EcGroup::from_curve_name(curve_nid)
.map_err(|_| KeyParsingError::UnsupportedEllipticCurve(curve_oid))?;
let mut bn_ctx = openssl::bn::BigNumContext::new()?;
let ec_point = openssl::ec::EcPoint::from_bytes(
&group,
k.subject_public_key.as_bytes(),
&mut bn_ctx,
)
.map_err(|_| KeyParsingError::InvalidKey)?;
let ec_key = openssl::ec::EcKey::from_public_key(&group, &ec_point)?;
Ok(openssl::pkey::PKey::from_ec_key(ec_key)?)
}
EcParameters::ImplicitCurve(_) | EcParameters::SpecifiedCurve(_) => {
Err(KeyParsingError::ExplicitCurveUnsupported)
}
},
AlgorithmParameters::Ec(ec_params) => {
let group = crate::ec::ec_params_to_group(&ec_params)?;
let mut bn_ctx = openssl::bn::BigNumContext::new()?;
let ec_point = openssl::ec::EcPoint::from_bytes(
&group,
k.subject_public_key.as_bytes(),
&mut bn_ctx,
)
.map_err(|_| KeyParsingError::InvalidKey)?;
let ec_key = openssl::ec::EcKey::from_public_key(&group, &ec_point)?;
Ok(openssl::pkey::PKey::from_ec_key(ec_key)?)
}
AlgorithmParameters::Ed25519 => Ok(openssl::pkey::PKey::public_key_from_raw_bytes(
k.subject_public_key.as_bytes(),
openssl::pkey::Id::ED25519,
Expand Down
Loading

0 comments on commit 81ab411

Please sign in to comment.