Parse EC public keys within certificates
This commit is contained in:
+113
-1
@@ -44,6 +44,28 @@ use rsa::{PublicKey, RSAPublicKey};
|
|||||||
use x509_parser::{parse_x509_der, x509::SubjectPublicKeyInfo};
|
use x509_parser::{parse_x509_der, x509::SubjectPublicKeyInfo};
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
|
/// An encoded point for some elliptic curve.
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum EcPoint {
|
||||||
|
/// A point in compressed form.
|
||||||
|
Compressed {
|
||||||
|
/// EC algorithm
|
||||||
|
algorithm: AlgorithmId,
|
||||||
|
|
||||||
|
/// Encoded point
|
||||||
|
bytes: Buffer,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// A point in uncompressed form.
|
||||||
|
Uncompressed {
|
||||||
|
/// EC algorithm
|
||||||
|
algorithm: AlgorithmId,
|
||||||
|
|
||||||
|
/// Encoded point
|
||||||
|
bytes: Buffer,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
/// Information about a public key within a [`Certificate`].
|
/// Information about a public key within a [`Certificate`].
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum PublicKeyInfo {
|
pub enum PublicKeyInfo {
|
||||||
@@ -55,6 +77,9 @@ pub enum PublicKeyInfo {
|
|||||||
/// Public key
|
/// Public key
|
||||||
pubkey: RSAPublicKey,
|
pubkey: RSAPublicKey,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// EC keys
|
||||||
|
Ec(EcPoint),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PublicKeyInfo {
|
impl PublicKeyInfo {
|
||||||
@@ -73,6 +98,12 @@ impl PublicKeyInfo {
|
|||||||
pubkey,
|
pubkey,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// EC Public Key
|
||||||
|
"1.2.840.10045.2.1" => {
|
||||||
|
let algorithm = read_pki::ec_parameters(&subject_pki.algorithm.parameters)?;
|
||||||
|
read_pki::ec_point(algorithm, subject_pki.subject_public_key.data)
|
||||||
|
.map(PublicKeyInfo::Ec)
|
||||||
|
}
|
||||||
_ => Err(Error::InvalidObject),
|
_ => Err(Error::InvalidObject),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,6 +112,10 @@ impl PublicKeyInfo {
|
|||||||
pub fn algorithm(&self) -> AlgorithmId {
|
pub fn algorithm(&self) -> AlgorithmId {
|
||||||
match self {
|
match self {
|
||||||
PublicKeyInfo::Rsa { algorithm, .. } => *algorithm,
|
PublicKeyInfo::Rsa { algorithm, .. } => *algorithm,
|
||||||
|
PublicKeyInfo::Ec(point) => match point {
|
||||||
|
EcPoint::Compressed { algorithm, .. } => *algorithm,
|
||||||
|
EcPoint::Uncompressed { algorithm, .. } => *algorithm,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -259,8 +294,10 @@ mod read_pki {
|
|||||||
};
|
};
|
||||||
use nom::{combinator, IResult};
|
use nom::{combinator, IResult};
|
||||||
use rsa::{BigUint, RSAPublicKey};
|
use rsa::{BigUint, RSAPublicKey};
|
||||||
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
use crate::error::Error;
|
use super::EcPoint;
|
||||||
|
use crate::{error::Error, key::AlgorithmId};
|
||||||
|
|
||||||
/// From [RFC 8017](https://tools.ietf.org/html/rfc8017#appendix-A.1.1):
|
/// From [RFC 8017](https://tools.ietf.org/html/rfc8017#appendix-A.1.1):
|
||||||
/// ```text
|
/// ```text
|
||||||
@@ -299,4 +336,79 @@ mod read_pki {
|
|||||||
|
|
||||||
RSAPublicKey::new(n, e).map_err(|_| Error::InvalidObject)
|
RSAPublicKey::new(n, e).map_err(|_| Error::InvalidObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// From [RFC 5480](https://tools.ietf.org/html/rfc5480#section-2.1.1):
|
||||||
|
/// ```text
|
||||||
|
/// ECParameters ::= CHOICE {
|
||||||
|
/// namedCurve OBJECT IDENTIFIER
|
||||||
|
/// -- implicitCurve NULL
|
||||||
|
/// -- specifiedCurve SpecifiedECDomain
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub(super) fn ec_parameters(parameters: &DerObject<'_>) -> Result<AlgorithmId, Error> {
|
||||||
|
let curve_oid = match parameters.as_context_specific() {
|
||||||
|
Ok((_, Some(named_curve))) => {
|
||||||
|
named_curve.as_oid_val().map_err(|_| Error::InvalidObject)
|
||||||
|
}
|
||||||
|
_ => Err(Error::InvalidObject),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
match curve_oid.to_string().as_str() {
|
||||||
|
"1.2.840.10045.3.1.7" => Ok(AlgorithmId::EccP256),
|
||||||
|
"1.3.132.0.34" => Ok(AlgorithmId::EccP384),
|
||||||
|
_ => Err(Error::InvalidObject),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// From [RFC 5480](https://tools.ietf.org/html/rfc5480#section-2.2):
|
||||||
|
/// ```text
|
||||||
|
/// ECPoint ::= OCTET STRING
|
||||||
|
///
|
||||||
|
/// o The first octet of the OCTET STRING indicates whether the key is
|
||||||
|
/// compressed or uncompressed. The uncompressed form is indicated
|
||||||
|
/// by 0x04 and the compressed form is indicated by either 0x02 or
|
||||||
|
/// 0x03 (see 2.3.3 in [SEC1]). The public key MUST be rejected if
|
||||||
|
/// any other value is included in the first octet.
|
||||||
|
/// ```
|
||||||
|
pub(super) fn ec_point(algorithm: AlgorithmId, encoded: &[u8]) -> Result<EcPoint, Error> {
|
||||||
|
if encoded.is_empty() {
|
||||||
|
return Err(Error::InvalidObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
match encoded[0] {
|
||||||
|
0x02 | 0x03 => {
|
||||||
|
if encoded.len()
|
||||||
|
== match algorithm {
|
||||||
|
AlgorithmId::EccP256 => 33,
|
||||||
|
AlgorithmId::EccP384 => 49,
|
||||||
|
_ => return Err(Error::AlgorithmError),
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Ok(EcPoint::Compressed {
|
||||||
|
algorithm,
|
||||||
|
bytes: Zeroizing::new(encoded[1..].to_vec()),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(Error::InvalidObject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0x04 => {
|
||||||
|
if encoded.len()
|
||||||
|
== match algorithm {
|
||||||
|
AlgorithmId::EccP256 => 65,
|
||||||
|
AlgorithmId::EccP384 => 97,
|
||||||
|
_ => return Err(Error::AlgorithmError),
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Ok(EcPoint::Uncompressed {
|
||||||
|
algorithm,
|
||||||
|
bytes: Zeroizing::new(encoded[1..].to_vec()),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(Error::InvalidObject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Err(Error::InvalidObject),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user