use crate::{oids, AlgorithmIdentifier, AlgorithmIdentifierParameters, EcParameters, EncapsulatedEcPoint};
use picky_asn1::tag::TagPeeker;
use picky_asn1::wrapper::{
BitStringAsn1, ExplicitContextTag0, ExplicitContextTag1, IntegerAsn1, ObjectIdentifierAsn1, OctetStringAsn1,
OctetStringAsn1Container, Optional,
};
#[cfg(not(feature = "legacy"))]
use serde::Deserialize;
use serde::{de, ser, Serialize};
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
use oid::ObjectIdentifier;
use picky_asn1::bit_string::BitString;
use picky_asn1::Asn1Type;
use std::fmt;
pub const PRIVATE_KEY_INFO_VERSION_1: u8 = 0;
pub const PRIVATE_KEY_INFO_VERSION_2: u8 = 1;
pub type EncapsulatedEcSecret = OctetStringAsn1;
#[derive(Serialize, Debug, Clone, PartialEq, Eq)]
pub struct PrivateKeyInfo {
pub version: u8,
pub private_key_algorithm: AlgorithmIdentifier,
pub private_key: PrivateKeyValue,
pub public_key: Option<ExplicitContextTag1<Optional<BitStringAsn1>>>,
}
impl PrivateKeyInfo {
pub fn new_rsa_encryption(
modulus: IntegerAsn1,
public_exponent: IntegerAsn1,
private_exponent: IntegerAsn1,
primes: (IntegerAsn1, IntegerAsn1),
exponents: (IntegerAsn1, IntegerAsn1),
coefficient: IntegerAsn1,
) -> Self {
let private_key = PrivateKeyValue::RSA(
RsaPrivateKey {
version: vec![0].into(),
modulus,
public_exponent,
private_exponent,
prime_1: primes.0,
prime_2: primes.1,
exponent_1: exponents.0,
exponent_2: exponents.1,
coefficient,
}
.into(),
);
Self {
version: PRIVATE_KEY_INFO_VERSION_1,
private_key_algorithm: AlgorithmIdentifier::new_rsa_encryption(),
private_key,
public_key: None,
}
}
pub fn new_ec_encryption(
curve_oid: ObjectIdentifier,
secret: impl Into<OctetStringAsn1>,
public_point: Option<BitString>,
skip_optional_params: bool,
) -> Self {
let curve_oid: ObjectIdentifierAsn1 = curve_oid.into();
let secret: OctetStringAsn1 = secret.into();
let point: Option<BitStringAsn1> = public_point.map(Into::into);
let parameters: ExplicitContextTag0<Option<EcParameters>> =
(!skip_optional_params).then(|| curve_oid.clone().into()).into();
let public_point: ExplicitContextTag1<Option<BitStringAsn1>> = point.into();
let private_key = PrivateKeyValue::EC(
ECPrivateKey {
version: vec![1].into(),
private_key: secret,
parameters: parameters.into(),
public_key: public_point.into(),
}
.into(),
);
Self {
version: PRIVATE_KEY_INFO_VERSION_1,
private_key_algorithm: AlgorithmIdentifier::new_elliptic_curve(curve_oid.into()),
private_key,
public_key: None,
}
}
pub fn new_ed_encryption(
algorithm: ObjectIdentifier,
secret: impl Into<OctetStringAsn1>,
public_key: Option<BitString>,
) -> Self {
let secret: OctetStringAsn1 = secret.into();
let (version, public_key) = if let Some(public_key) = public_key {
(
PRIVATE_KEY_INFO_VERSION_2,
Some(ExplicitContextTag1(Optional(public_key.into()))),
)
} else {
(PRIVATE_KEY_INFO_VERSION_1, None)
};
Self {
version,
private_key_algorithm: AlgorithmIdentifier::new_unchecked(algorithm, AlgorithmIdentifierParameters::None),
private_key: PrivateKeyValue::ED(secret.into()),
public_key,
}
}
}
impl<'de> de::Deserialize<'de> for PrivateKeyInfo {
fn deserialize<D>(deserializer: D) -> Result<Self, <D as de::Deserializer<'de>>::Error>
where
D: de::Deserializer<'de>,
{
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = PrivateKeyInfo;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a valid DER-encoded PrivateKeyInfo (pkcs8)")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
let version = seq_next_element!(seq, PrivateKeyInfo, "version");
if version != 0 && version != 1 {
return Err(serde_invalid_value!(
PrivateKeyInfo,
"unsupported version (valid versions: [v1(0), v2(1)])",
"a supported PrivateKeyInfo"
));
}
let private_key_algorithm: AlgorithmIdentifier =
seq_next_element!(seq, PrivateKeyInfo, "private key algorithm");
let private_key = if private_key_algorithm.is_a(oids::rsa_encryption()) {
PrivateKeyValue::RSA(seq_next_element!(seq, PrivateKeyInfo, "rsa oid"))
} else if matches!(private_key_algorithm.parameters(), AlgorithmIdentifierParameters::Ec(_)) {
PrivateKeyValue::EC(seq_next_element!(seq, PrivateKeyInfo, "ec private key"))
} else if private_key_algorithm.is_one_of([oids::ed25519(), oids::x25519()]) {
PrivateKeyValue::ED(seq_next_element!(seq, PrivateKeyInfo, "curve25519 private key"))
} else if private_key_algorithm.is_one_of([oids::ed448(), oids::x448()]) {
PrivateKeyValue::ED(seq_next_element!(seq, PrivateKeyInfo, "curve448 private key"))
} else {
return Err(serde_invalid_value!(
PrivateKeyInfo,
"unsupported algorithm",
"a supported algorithm"
));
};
let mut last_tag = seq.next_element::<TagPeeker>()?;
if let Some(tag) = &last_tag {
if tag.next_tag != ExplicitContextTag1::<BitStringAsn1>::TAG {
last_tag = seq.next_element::<TagPeeker>()?;
}
}
let public_key = match last_tag {
Some(tag) if tag.next_tag == ExplicitContextTag1::<BitStringAsn1>::TAG => {
let public_key =
seq_next_element!(seq, ExplicitContextTag1<BitStringAsn1>, PrivateKeyInfo, "BitStringAsn1");
Some(ExplicitContextTag1(Optional(public_key.0)))
}
_ => None,
};
Ok(PrivateKeyInfo {
version,
private_key_algorithm,
private_key,
public_key,
})
}
}
deserializer.deserialize_seq(Visitor)
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum PrivateKeyValue {
RSA(OctetStringAsn1Container<RsaPrivateKey>),
EC(OctetStringAsn1Container<ECPrivateKey>),
ED(OctetStringAsn1Container<OctetStringAsn1>),
}
impl ser::Serialize for PrivateKeyValue {
fn serialize<S>(&self, serializer: S) -> Result<<S as ser::Serializer>::Ok, <S as ser::Serializer>::Error>
where
S: ser::Serializer,
{
match self {
PrivateKeyValue::RSA(rsa) => rsa.serialize(serializer),
PrivateKeyValue::EC(ec) => ec.serialize(serializer),
PrivateKeyValue::ED(ed) => ed.serialize(serializer),
}
}
}
#[cfg(feature = "zeroize")]
impl Drop for PrivateKeyValue {
fn drop(&mut self) {
if let PrivateKeyValue::ED(ed) = self {
ed.0 .0.zeroize()
}
}
}
#[derive(Serialize, Debug, Clone, PartialEq, Eq)]
#[cfg_attr(not(feature = "legacy"), derive(Deserialize))]
pub struct RsaPrivateKey {
pub version: IntegerAsn1,
pub modulus: IntegerAsn1,
pub public_exponent: IntegerAsn1,
pub private_exponent: IntegerAsn1,
pub prime_1: IntegerAsn1,
pub prime_2: IntegerAsn1,
pub exponent_1: IntegerAsn1,
pub exponent_2: IntegerAsn1,
pub coefficient: IntegerAsn1,
}
#[cfg(feature = "zeroize")]
impl Drop for RsaPrivateKey {
fn drop(&mut self) {
self.private_exponent.zeroize();
}
}
#[cfg(feature = "legacy")]
impl<'de> de::Deserialize<'de> for RsaPrivateKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = RsaPrivateKey;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("struct RSAPrivateKey with 6 or 9 elements")
}
fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
where
V: de::SeqAccess<'de>,
{
let version: IntegerAsn1 = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(0, &self))?;
let modulus: IntegerAsn1 = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(1, &self))?;
let public_exponent: IntegerAsn1 =
seq.next_element()?.ok_or_else(|| de::Error::invalid_length(2, &self))?;
let private_exponent: IntegerAsn1 =
seq.next_element()?.ok_or_else(|| de::Error::invalid_length(3, &self))?;
let prime_1: IntegerAsn1 = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(4, &self))?;
let prime_2: IntegerAsn1 = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(5, &self))?;
let (exponent_1, exponent_2, coefficient) = if let Some(exponent_1) = seq.next_element()? {
let exponent_2 = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(7, &self))?;
let coefficient = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(8, &self))?;
(exponent_1, exponent_2, coefficient)
} else {
use num_bigint_dig::{BigUint, ModInverse};
let private_exponent = BigUint::from_bytes_be(private_exponent.as_unsigned_bytes_be());
let prime_1 = BigUint::from_bytes_be(prime_1.as_unsigned_bytes_be());
let prime_2 = BigUint::from_bytes_be(prime_2.as_unsigned_bytes_be());
let exponent_1 = &private_exponent % (&prime_1 - 1u8);
let exponent_2 = &private_exponent % (&prime_2 - 1u8);
let coefficient = prime_2
.mod_inverse(prime_1)
.ok_or_else(|| {
de::Error::invalid_value(
de::Unexpected::Other("[RSAPrivateKey] no modular inverse for prime 1"),
&"an invertible prime 1 value",
)
})?
.to_biguint()
.ok_or_else(|| {
de::Error::invalid_value(
de::Unexpected::Other("[RSAPrivateKey] BigUint conversion failed"),
&"a valid prime 1 value",
)
})?;
let exponent_1 = IntegerAsn1::from_bytes_be_unsigned(exponent_1.to_bytes_be());
let exponent_2 = IntegerAsn1::from_bytes_be_unsigned(exponent_2.to_bytes_be());
let coefficient = IntegerAsn1::from_bytes_be_unsigned(coefficient.to_bytes_be());
(exponent_1, exponent_2, coefficient)
};
Ok(RsaPrivateKey {
version,
modulus,
public_exponent,
private_exponent,
prime_1,
prime_2,
exponent_1,
exponent_2,
coefficient,
})
}
}
deserializer.deserialize_seq(Visitor)
}
}
impl RsaPrivateKey {
#[deprecated(note = "field is now public")]
pub fn modulus(&self) -> &IntegerAsn1 {
&self.modulus
}
#[deprecated(note = "field is now public")]
pub fn public_exponent(&self) -> &IntegerAsn1 {
&self.public_exponent
}
#[deprecated(note = "field is now public")]
pub fn private_exponent(&self) -> &IntegerAsn1 {
&self.private_exponent
}
#[deprecated(note = "field is now public")]
pub fn prime_1(&self) -> &IntegerAsn1 {
&self.prime_1
}
#[deprecated(note = "field is now public")]
pub fn prime_2(&self) -> &IntegerAsn1 {
&self.prime_2
}
#[deprecated(note = "field is now public")]
pub fn primes(&self) -> (&IntegerAsn1, &IntegerAsn1) {
(&self.prime_1, &self.prime_2)
}
#[deprecated(note = "field is now public")]
pub fn exponent_1(&self) -> &IntegerAsn1 {
&self.exponent_1
}
#[deprecated(note = "field is now public")]
pub fn exponent_2(&self) -> &IntegerAsn1 {
&self.exponent_2
}
#[deprecated(note = "field is now public")]
pub fn exponents(&self) -> (&IntegerAsn1, &IntegerAsn1) {
(&self.exponent_1, &self.exponent_2)
}
#[deprecated(note = "field is now public")]
pub fn coefficient(&self) -> &IntegerAsn1 {
&self.coefficient
}
#[deprecated(note = "field is now public")]
pub fn into_public_components(mut self) -> (IntegerAsn1, IntegerAsn1) {
(
std::mem::take(&mut self.modulus),
std::mem::take(&mut self.public_exponent),
)
}
}
#[derive(Serialize, Debug, Clone, PartialEq, Eq)]
pub struct ECPrivateKey {
pub version: IntegerAsn1,
pub private_key: EncapsulatedEcSecret,
#[serde(skip_serializing_if = "Optional::is_default")]
pub parameters: Optional<ExplicitContextTag0<Option<EcParameters>>>,
#[serde(skip_serializing_if = "Optional::is_default")]
pub public_key: Optional<ExplicitContextTag1<Option<EncapsulatedEcPoint>>>,
}
impl<'de> serde::Deserialize<'de> for ECPrivateKey {
fn deserialize<D>(deserializer: D) -> Result<Self, <D as de::Deserializer<'de>>::Error>
where
D: de::Deserializer<'de>,
{
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = ECPrivateKey;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a valid DER-encoded PrivateKeyInfo (pkcs8)")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
let version: IntegerAsn1 = seq_next_element!(seq, IntegerAsn1, "IntegerAsn1");
if version.0 != [1] {
return Err(serde_invalid_value!(
ECPrivateKey,
"ECPrivateKey's version is not 1",
"ECPrivateKey's version equals to 1"
));
}
let private_key = seq_next_element!(seq, OctetStringAsn1, "OctetStringAsn1");
let mut ec_private_key = ECPrivateKey {
version,
private_key,
parameters: Optional::default(),
public_key: Optional::default(),
};
let mut last_tag = seq.next_element::<TagPeeker>()?;
if let Some(tag) = &last_tag {
if tag.next_tag == ExplicitContextTag0::<EcParameters>::TAG {
let parameters =
seq_next_element!(seq, ExplicitContextTag0<EcParameters>, ECPrivateKey, "EcParameters");
ec_private_key.parameters = Optional(ExplicitContextTag0(Some(parameters.0)));
last_tag = seq.next_element::<TagPeeker>()?;
}
}
if let Some(tag) = last_tag {
if tag.next_tag == ExplicitContextTag1::<BitStringAsn1>::TAG {
let public_key =
seq_next_element!(seq, ExplicitContextTag1<BitStringAsn1>, ECPrivateKey, "BitStringAsn1");
ec_private_key.public_key = Optional(ExplicitContextTag1(Some(public_key.0)));
}
}
Ok(ec_private_key)
}
}
deserializer.deserialize_seq(Visitor)
}
}
#[cfg(feature = "zeroize")]
impl Drop for ECPrivateKey {
fn drop(&mut self) {
self.private_key.zeroize();
}
}
#[cfg(test)]
mod tests {
use super::*;
use base64::{engine::general_purpose, Engine as _};
use picky_asn1::bit_string::BitString;
#[test]
fn pkcs_8_private_key() {
let encoded = general_purpose::STANDARD
.decode(
"MIIBVgIBADANBgkqhkiG9w0BAQEFAASCAUAwggE8AgEAAkEAq7BFUpkGp3+LQmlQ\
Yx2eqzDV+xeG8kx/sQFV18S5JhzGeIJNA72wSeukEPojtqUyX2J0CciPBh7eqclQ\
2zpAswIDAQABAkAgisq4+zRdrzkwH1ITV1vpytnkO/NiHcnePQiOW0VUybPyHoGM\
/jf75C5xET7ZQpBe5kx5VHsPZj0CBb3b+wSRAiEA2mPWCBytosIU/ODRfq6EiV04\
lt6waE7I2uSPqIC20LcCIQDJQYIHQII+3YaPqyhGgqMexuuuGx+lDKD6/Fu/JwPb\
5QIhAKthiYcYKlL9h8bjDsQhZDUACPasjzdsDEdq8inDyLOFAiEAmCr/tZwA3qeA\
ZoBzI10DGPIuoKXBd3nk/eBxPkaxlEECIQCNymjsoI7GldtujVnr1qT+3yedLfHK\
srDVjIT3LsvTqw==",
)
.expect("invalid base64");
let modulus = IntegerAsn1::from(encoded[35..100].to_vec());
let public_exponent = IntegerAsn1::from(encoded[102..105].to_vec());
let private_exponent = IntegerAsn1::from(encoded[107..171].to_vec());
let prime_1 = IntegerAsn1::from(encoded[173..206].to_vec());
let prime_2 = IntegerAsn1::from(encoded[208..241].to_vec());
let exponent_1 = IntegerAsn1::from(encoded[243..276].to_vec());
let exponent_2 = IntegerAsn1::from(encoded[278..311].to_vec());
let coefficient = IntegerAsn1::from(encoded[313..346].to_vec());
let private_key = PrivateKeyInfo::new_rsa_encryption(
modulus,
public_exponent,
private_exponent,
(prime_1, prime_2),
(exponent_1, exponent_2),
coefficient,
);
check_serde!(private_key: PrivateKeyInfo in encoded);
}
#[test]
#[cfg(feature = "legacy")]
fn old_broken_key_legacy_support() {
let encoded = general_purpose::STANDARD
.decode(
"MIIDMAIBADANBgkqhkiG9w0BAQEFAASCAxowggMWAgEAAoIBAOB9jOJvCkMHOc98Q\
GPFikxAvBKANkme5f/nNuNnEnbefoKDFkS6ElfqASAAkIHxUREnRvBTTa6b+qba/0\
DhBuXsYGCl8VF0pUE4JGujv1HIi5aRCar0WmY66s7DJ4uR3Nk9Jy0WeRiH4yyzEIG\
8+6QDu4d/U6slWTmE8eZtQEE7rz4FGpQU9OhrGM3xJOIIbLX/xU2SFt83Xs3JREEt\
bfrXQpSxAHmtwvlBKpeZacrcobm6eQKsoI2MIg3LFvoHs0+40dadm14ngpgwx4qqk\
bG34jvWH13OhHRweFGNkQpcg99rlzZYkCM13e9EcmirQ9XYHuB5pHS31eznolZKbx\
cCAwEAAQKCAQCrPFlopxaGxk48jCR5dkbln0NWQWInigMazf06PHcDIPgTCXbE+cH\
gOWieRo/z7mTN1s3vpztMA0KQX9/wVzVx0Ho7fpiyb21WcEKnsIHRGk4PjZZ4Rmdm\
L27IRGg3uA1jz5fAdrHsGksY34Wp0MOJ+ibjViY2GAkVLOlvwMoQds6eNIGO88T5O\
fcmvutjK43ObU1vgx2ptTaLNAVczEE5VHqcLx4GZPv6k71afOQfIDQerIpsGb4gvr\
1JdwYKb4z02z2SaNIA3Vly0q5s4r8uU36eg9z65utu93M7zI7f8/MX2byZ2Jz4b3T\
nH10FURmbPoNQH/O2T0TbtT4M1y0xAoGA72JW0IcFxze7j7PPaP6cQN1IXvFDZUFF\
dZHqFI8+4VPcv3EKTs+iQflM7pqtRuEWtwonIn3f7CGOx317uKwpVsZvfnDhXCUPJ\
Q3pns7KgaROGXyruFFQ9gl6XsXGK02Wop9nX0/iRK3ruwZ4uJwDioEYcvGw+ocqAc\
yOdodNnpUCgYDwEo/sPJNaPOzc7fpaQr2PUUJ3ksL0ncGRO2h1JGYgDtWe5u1srSI\
DlpM2NdYSZyT04ebOF2SqNUBwY3LB1tPOFnjYwCutp4c75OYhOor7TodZHlzt3GeQ\
ntUw6XbHX0ohTgs4u2NXwOTq5yKeW4VYzuevN5ksF8GoW2noalpn+w==",
)
.unwrap();
picky_asn1_der::from_bytes::<PrivateKeyInfo>(&encoded).unwrap();
}
#[test]
fn decode_ec_key() {
let decoded = general_purpose::STANDARD
.decode(
"\
MIHcAgEBBEIBhqphIGu2PmlcEb6xADhhSCpgPUulB0s4L2qOgolRgaBx4fNgINFE\
mBsSyHJncsWG8WFEuUzAYy/YKz2lP0Qx6Z2gBwYFK4EEACOhgYkDgYYABABwBevJ\
w/+Xh6I98ruzoTX3MNTsbgnc+glenJRCbEJkjbJrObFhbfgqP52r1lAy2RxuShGi\
NYJJzNPT6vR1abS32QFtvTH7YbYa6OWk9dtGNY/cYxgx1nQyhUuofdW7qbbfu/Ww\
TP2oFsPXRAavZCh4AbWUn8bAHmzNRyuJonQBKlQlVQ==",
)
.unwrap();
let ec_key = ECPrivateKey {
version: IntegerAsn1([1].into()),
private_key: OctetStringAsn1::from(decoded[8..74].to_vec()),
parameters: ExplicitContextTag0(Some(EcParameters::NamedCurve(oids::secp521r1().into()))).into(),
public_key: Optional(ExplicitContextTag1(Some(
BitString::with_bytes(decoded[90..].to_vec()).into(),
))),
};
check_serde!(ec_key: ECPrivateKey in decoded);
}
#[test]
fn decode_pkcs8_ec_key() {
let decoded = general_purpose::STANDARD.decode("MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgKZqrmOg/cDZ4tPCn\
4LROs145nxx+ssufvflL8cROxFmhRANCAARmU90fCSTsncefY7hVeKw1WIg/YQmT\
4DGJ7nJPZ+WXAd/xxp4c0bHGlIOju/U95ITPN9dAmro7OUTDJpz+rzGW").unwrap();
let expected_pkcs8_ec_key = PrivateKeyInfo {
version: 0,
private_key_algorithm: AlgorithmIdentifier::new_elliptic_curve(EcParameters::NamedCurve(
oids::secp256r1().into(),
)),
private_key: PrivateKeyValue::EC(OctetStringAsn1Container(ECPrivateKey {
version: IntegerAsn1([1].into()),
private_key: OctetStringAsn1(decoded[36..68].to_vec()),
parameters: Optional(Default::default()),
public_key: Optional(ExplicitContextTag1(Some(
BitString::with_bytes(decoded[73..].to_vec()).into(),
))),
})),
public_key: None,
};
check_serde!(expected_pkcs8_ec_key: PrivateKeyInfo in decoded);
}
}