use crate::x509;
use crate::xdsa::{PublicKey, SecretKey};
use const_oid::ObjectIdentifier;
use der::Encode;
use std::error::Error;
use x509_cert::Certificate;
impl x509::Subject for PublicKey {
type Bytes = [u8; 1984];
fn to_bytes(&self) -> Self::Bytes {
self.to_bytes()
}
fn algorithm_oid(&self) -> ObjectIdentifier {
ObjectIdentifier::new_unwrap("1.3.6.1.5.5.7.6.48")
}
}
impl PublicKey {
pub fn from_cert_pem(pem: &str, signer: PublicKey) -> Result<(Self, u64, u64), Box<dyn Error>> {
let (_, der) = x509_parser::pem::parse_x509_pem(pem.as_bytes())?;
PublicKey::from_cert_der(der.contents.as_slice(), signer)
}
pub fn from_cert_der(
der: &[u8],
signer: PublicKey,
) -> Result<(Self, u64, u64), Box<dyn Error>> {
let (_, cert) = x509_parser::parse_x509_certificate(der)?;
let tbs = cert.tbs_certificate.as_ref();
let sig_bytes: [u8; 3373] = cert
.signature_value
.data
.as_ref()
.try_into()
.map_err(|_| "invalid signature length")?;
let sig = super::Signature::from_bytes(&sig_bytes);
signer.verify(tbs, &sig)?;
let key = PublicKey::from_bytes(
cert.tbs_certificate
.subject_pki
.subject_public_key
.data
.as_ref()
.try_into()?,
)?;
let start = cert.tbs_certificate.validity.not_before.timestamp() as u64;
let until = cert.tbs_certificate.validity.not_after.timestamp() as u64;
Ok((key, start, until))
}
pub fn to_cert_pem(
&self,
signer: &SecretKey,
params: &x509::Params,
) -> Result<String, Box<dyn Error>> {
let cert = self.to_cert(signer, params)?;
let der = cert.to_der()?;
let pem_str = der::pem::encode_string("CERTIFICATE", der::pem::LineEnding::LF, &der)
.map_err(|e| format!("PEM encoding error: {:?}", e))?;
Ok(pem_str)
}
pub fn to_cert_der(
&self,
signer: &SecretKey,
params: &x509::Params,
) -> Result<Vec<u8>, Box<dyn Error>> {
Ok(self.to_cert(signer, params)?.to_der()?)
}
pub fn to_cert(
&self,
signer: &SecretKey,
params: &x509::Params,
) -> Result<Certificate, Box<dyn Error>> {
x509::new(self, signer, params)
}
}
#[cfg(test)]
mod test {
use crate::x509::Params;
use crate::xdsa::{PublicKey, SecretKey};
use std::time::{SystemTime, UNIX_EPOCH};
#[test]
fn test_cert_parse() {
let alice_secret = SecretKey::generate();
let bobby_secret = SecretKey::generate();
let alice_public = alice_secret.public_key();
let bobby_public = bobby_secret.public_key();
let start = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
let until = start + 3600;
let pem = alice_public
.to_cert_pem(
&bobby_secret,
&Params {
subject_name: "Alice",
issuer_name: "Bobby",
not_before: start,
not_after: until,
is_ca: false,
path_len: None,
},
)
.unwrap();
let (parsed_key, parsed_start, parsed_until) =
PublicKey::from_cert_pem(pem.as_str(), bobby_public.clone()).unwrap();
assert_eq!(parsed_key.to_bytes(), alice_public.to_bytes());
assert_eq!(parsed_start, start);
assert_eq!(parsed_until, until);
let der = alice_public
.to_cert_der(
&bobby_secret,
&Params {
subject_name: "Alice",
issuer_name: "Bobby",
not_before: start,
not_after: until,
is_ca: true,
path_len: Some(0),
},
)
.unwrap();
let (parsed_key, parsed_start, parsed_until) =
PublicKey::from_cert_der(der.as_slice(), bobby_public.clone()).unwrap();
assert_eq!(parsed_key.to_bytes(), alice_public.to_bytes());
assert_eq!(parsed_start, start);
assert_eq!(parsed_until, until);
}
#[test]
fn test_cert_invalid_signer() {
let alice_secret = SecretKey::generate();
let bobby_secret = SecretKey::generate();
let wrong_secret = SecretKey::generate();
let alice_public = alice_secret.public_key();
let start = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
let until = start + 3600;
let pem = alice_public
.to_cert_pem(
&bobby_secret,
&Params {
subject_name: "Alice",
issuer_name: "Bobby",
not_before: start,
not_after: until,
is_ca: false,
path_len: None,
},
)
.unwrap();
let result = PublicKey::from_cert_pem(pem.as_str(), wrong_secret.public_key());
assert!(result.is_err());
}
}