use serde::{Deserialize, Serialize};
use x509_cert::Certificate;
use x509_cert::der::asn1::UintRef;
use x509_cert::der::oid::db::fips204::{ID_ML_DSA_44, ID_ML_DSA_65, ID_ML_DSA_87};
use x509_cert::der::oid::db::rfc5912::{
ID_EC_PUBLIC_KEY, RSA_ENCRYPTION, SECP_256_R_1, SECP_384_R_1,
};
use x509_cert::der::oid::db::rfc8410::ID_ED_25519;
use x509_cert::der::{Decode, Encode, Reader, SliceReader};
use x509_cert::spki::SubjectPublicKeyInfoRef;
use crate::backend::BackendError;
use crate::types::KeyAlgorithm;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(try_from = "Vec<u8>", into = "Vec<u8>")]
pub struct PublicKeyDer(Vec<u8>);
impl PublicKeyDer {
pub fn new(der: Vec<u8>) -> Result<Self, BackendError> {
SubjectPublicKeyInfoRef::from_der(&der).map_err(|e| {
BackendError::InvalidKeyFormat(format!("not a SubjectPublicKeyInfo: {e}"))
})?;
Ok(Self(der))
}
pub fn from_key_material(bytes: &[u8]) -> Result<Self, BackendError> {
if let Ok((label, der)) = x509_cert::der::pem::decode_vec(bytes) {
return match label {
"PUBLIC KEY" => Self::new(der),
"CERTIFICATE" => certificate_public_key(&der),
other => Err(BackendError::InvalidKeyFormat(format!(
"PEM block is labelled '{other}', expected 'PUBLIC KEY' or 'CERTIFICATE'"
))),
};
}
if let Ok(key) = Self::new(bytes.to_vec()) {
return Ok(key);
}
certificate_public_key(bytes).map_err(|e| {
BackendError::InvalidKeyFormat(format!(
"not a public key or certificate, in DER or PEM ({e})"
))
})
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
pub fn algorithm(&self) -> Result<KeyAlgorithm, BackendError> {
let spki = SubjectPublicKeyInfoRef::from_der(&self.0)
.map_err(|e| BackendError::InvalidKeyFormat(format!("SPKI is unreadable: {e}")))?;
match spki.algorithm.oid {
RSA_ENCRYPTION => {
let key = spki.subject_public_key.as_bytes().ok_or_else(|| {
BackendError::InvalidKeyFormat(
"RSA public key is not a whole number of bytes".to_string(),
)
})?;
rsa_algorithm(key)
}
ID_EC_PUBLIC_KEY => match spki.algorithm.parameters_oid() {
Ok(SECP_256_R_1) => Ok(KeyAlgorithm::EcdsaP256),
Ok(SECP_384_R_1) => Ok(KeyAlgorithm::EcdsaP384),
Ok(curve) => Err(BackendError::UnsupportedAlgorithm(format!(
"EC curve {curve} is not supported (expected P-256 or P-384)"
))),
Err(e) => Err(BackendError::InvalidKeyFormat(format!(
"EC key does not name a curve: {e}"
))),
},
ID_ED_25519 => Ok(KeyAlgorithm::Ed25519),
ID_ML_DSA_44 => Ok(KeyAlgorithm::MlDsa44),
ID_ML_DSA_65 => Ok(KeyAlgorithm::MlDsa65),
ID_ML_DSA_87 => Ok(KeyAlgorithm::MlDsa87),
oid => Err(BackendError::UnsupportedAlgorithm(format!(
"public key algorithm {oid} is not supported"
))),
}
}
}
impl From<PublicKeyDer> for Vec<u8> {
fn from(key: PublicKeyDer) -> Self {
key.0
}
}
impl TryFrom<Vec<u8>> for PublicKeyDer {
type Error = BackendError;
fn try_from(der: Vec<u8>) -> Result<Self, Self::Error> {
Self::new(der)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(try_from = "Vec<u8>", into = "Vec<u8>")]
pub struct CertificateDer(Vec<u8>);
impl CertificateDer {
pub fn new(der: Vec<u8>) -> Result<Self, BackendError> {
Certificate::from_der(&der).map_err(|e| {
BackendError::InvalidKeyFormat(format!("not an X.509 certificate: {e}"))
})?;
Ok(Self(der))
}
pub fn from_key_material(bytes: &[u8]) -> Result<Self, BackendError> {
if let Ok((label, der)) = x509_cert::der::pem::decode_vec(bytes) {
return match label {
"CERTIFICATE" => Self::new(der),
other => Err(BackendError::InvalidKeyFormat(format!(
"PEM block is labelled '{other}', expected 'CERTIFICATE'"
))),
};
}
Self::new(bytes.to_vec())
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
pub fn public_key(&self) -> Result<PublicKeyDer, BackendError> {
certificate_public_key(&self.0)
}
}
fn certificate_public_key(der: &[u8]) -> Result<PublicKeyDer, BackendError> {
let certificate = Certificate::from_der(der)
.map_err(|e| BackendError::InvalidKeyFormat(format!("not an X.509 certificate: {e}")))?;
let spki = certificate
.tbs_certificate()
.subject_public_key_info()
.to_der()
.map_err(|e| {
BackendError::InvalidKeyFormat(format!(
"certificate's public key cannot be encoded: {e}"
))
})?;
PublicKeyDer::new(spki)
}
impl From<CertificateDer> for Vec<u8> {
fn from(certificate: CertificateDer) -> Self {
certificate.0
}
}
impl TryFrom<Vec<u8>> for CertificateDer {
type Error = BackendError;
fn try_from(der: Vec<u8>) -> Result<Self, Self::Error> {
Self::new(der)
}
}
fn rsa_algorithm(key: &[u8]) -> Result<KeyAlgorithm, BackendError> {
let mut reader = SliceReader::new(key).map_err(|e| {
BackendError::InvalidKeyFormat(format!("RSA public key is unreadable: {e}"))
})?;
let modulus_bits = reader
.sequence(|body| {
let modulus: UintRef<'_> = body.decode()?;
Ok::<_, x509_cert::der::Error>(bit_length(modulus.as_bytes()))
})
.map_err(|e| BackendError::InvalidKeyFormat(format!("RSA modulus cannot be read: {e}")))?;
match modulus_bits {
2048 => Ok(KeyAlgorithm::Rsa2048),
4096 => Ok(KeyAlgorithm::Rsa4096),
bits => Err(BackendError::UnsupportedAlgorithm(format!(
"RSA key size {bits} bits not supported (expected 2048 or 4096)"
))),
}
}
fn bit_length(magnitude: &[u8]) -> u32 {
let Some((&top, rest)) = magnitude.split_first() else {
return 0;
};
u32::try_from(rest.len())
.unwrap_or(u32::MAX)
.saturating_mul(8)
.saturating_add(u8::BITS.saturating_sub(top.leading_zeros()))
}
#[cfg(test)]
mod tests {
use super::*;
mod vectors {
pub const RSA_2048: &[u8] = include_bytes!("../tests/vectors/rsa2048.spki.der");
pub const RSA_4096: &[u8] = include_bytes!("../tests/vectors/rsa4096.spki.der");
pub const P256: &[u8] = include_bytes!("../tests/vectors/p256.spki.der");
pub const P384: &[u8] = include_bytes!("../tests/vectors/p384.spki.der");
pub const ED25519: &[u8] = include_bytes!("../tests/vectors/ed25519.spki.der");
pub const ML_DSA_44: &[u8] = include_bytes!("../tests/vectors/mldsa44.spki.der");
pub const ML_DSA_65: &[u8] = include_bytes!("../tests/vectors/mldsa65.spki.der");
pub const ML_DSA_87: &[u8] = include_bytes!("../tests/vectors/mldsa87.spki.der");
pub const CERTIFICATE: &[u8] = include_bytes!("../tests/vectors/p256.cert.der");
}
#[test]
fn reads_the_algorithm_out_of_each_key_type() {
let cases = [
(vectors::RSA_2048, KeyAlgorithm::Rsa2048),
(vectors::RSA_4096, KeyAlgorithm::Rsa4096),
(vectors::P256, KeyAlgorithm::EcdsaP256),
(vectors::P384, KeyAlgorithm::EcdsaP384),
(vectors::ED25519, KeyAlgorithm::Ed25519),
(vectors::ML_DSA_44, KeyAlgorithm::MlDsa44),
(vectors::ML_DSA_65, KeyAlgorithm::MlDsa65),
(vectors::ML_DSA_87, KeyAlgorithm::MlDsa87),
];
for (der, expected) in cases {
let key = PublicKeyDer::new(der.to_vec()).expect("vector is valid SPKI");
assert_eq!(key.algorithm().unwrap(), expected);
}
}
#[test]
fn rejects_a_certificate_offered_as_a_public_key() {
let err = PublicKeyDer::new(vectors::CERTIFICATE.to_vec()).unwrap_err();
assert!(
err.to_string().contains("not a SubjectPublicKeyInfo"),
"{err}"
);
}
#[test]
fn reads_the_public_key_out_of_a_certificate() {
let certificate =
CertificateDer::new(vectors::CERTIFICATE.to_vec()).expect("vector is a certificate");
let key = certificate.public_key().unwrap();
assert_eq!(key.algorithm().unwrap(), KeyAlgorithm::EcdsaP256);
assert_eq!(key.as_bytes(), vectors::P256);
}
#[test]
fn rejects_a_public_key_offered_as_a_certificate() {
assert!(CertificateDer::new(vectors::P256.to_vec()).is_err());
}
#[test]
fn rejects_trailing_data() {
let mut der = vectors::P256.to_vec();
der.push(0);
assert!(PublicKeyDer::new(der).is_err());
}
#[test]
fn rejects_empty_and_truncated_input() {
assert!(PublicKeyDer::new(Vec::new()).is_err());
let truncated = vectors::P256
.get(..10)
.expect("vector is longer than 10 bytes");
assert!(PublicKeyDer::new(truncated.to_vec()).is_err());
}
#[test]
fn deserializing_rejects_bytes_that_are_not_a_key() {
let key = PublicKeyDer::new(vectors::P256.to_vec()).unwrap();
let json = serde_json::to_string(&key).unwrap();
assert_eq!(serde_json::from_str::<PublicKeyDer>(&json).unwrap(), key);
let certificate_json = serde_json::to_string(vectors::CERTIFICATE).unwrap();
assert!(serde_json::from_str::<PublicKeyDer>(&certificate_json).is_err());
}
fn armour(label: &str, der: &[u8]) -> Vec<u8> {
x509_cert::der::pem::encode_string(label, x509_cert::der::pem::LineEnding::LF, der)
.expect("armouring is infallible for a valid label")
.into_bytes()
}
#[test]
fn reads_a_key_from_der_and_pem_and_certificates() {
let expected = PublicKeyDer::new(vectors::P256.to_vec()).unwrap();
let sources = [
vectors::P256.to_vec(),
armour("PUBLIC KEY", vectors::P256),
vectors::CERTIFICATE.to_vec(),
armour("CERTIFICATE", vectors::CERTIFICATE),
];
for source in sources {
assert_eq!(PublicKeyDer::from_key_material(&source).unwrap(), expected);
}
}
#[test]
fn refuses_a_pem_block_that_is_not_a_key_or_certificate() {
let private = armour("PRIVATE KEY", vectors::P256);
let err = PublicKeyDer::from_key_material(&private).unwrap_err();
assert!(err.to_string().contains("PRIVATE KEY"), "{err}");
}
#[test]
fn refuses_key_material_that_is_neither() {
let err = PublicKeyDer::from_key_material(b"just some bytes").unwrap_err();
assert!(
err.to_string().contains("public key or certificate"),
"{err}"
);
}
#[test]
fn reads_a_certificate_from_der_and_pem() {
let expected = CertificateDer::new(vectors::CERTIFICATE.to_vec()).unwrap();
for source in [
vectors::CERTIFICATE.to_vec(),
armour("CERTIFICATE", vectors::CERTIFICATE),
] {
assert_eq!(
CertificateDer::from_key_material(&source).unwrap(),
expected
);
}
}
#[test]
fn refuses_a_pem_block_that_is_not_a_certificate() {
let key = armour("PUBLIC KEY", vectors::P256);
let err = CertificateDer::from_key_material(&key).unwrap_err();
assert!(err.to_string().contains("PUBLIC KEY"), "{err}");
}
#[test]
fn bit_length_counts_from_the_top_set_bit() {
assert_eq!(bit_length(&[]), 0);
assert_eq!(bit_length(&[0x01]), 1);
assert_eq!(bit_length(&[0x80]), 8);
assert_eq!(bit_length(&[0x80, 0x00]), 16);
assert_eq!(bit_length(&[0x0f, 0xff]), 12);
}
}