use bcder::{decode, encode};
use bcder::{Captured, Mode, OctetString, Oid, Tag};
use bcder::decode::{DecodeError, IntoSource, Source};
use bcder::encode::PrimitiveContent;
use bytes::Bytes;
use log::{debug, error};
use std::ops;
use crate::oid;
use crate::crypto::{
KeyIdentifier, PublicKey, RpkiSignatureAlgorithm, SignatureAlgorithm,
SignatureVerificationError, Signer, SigningError,
};
use crate::repository::cert::TbsCert;
use crate::repository::error::{
InspectionError, ValidationError, VerificationError,
};
use crate::repository::x509::{
encode_extension, Name, Serial, SignedData, Time, Validity,
};
use crate::util::base64;
#[derive(Clone, Debug)]
pub struct IdCert {
signed_data: SignedData,
tbs: TbsIdCert,
}
impl IdCert {
pub fn new_ta<S: Signer>(
validity: Validity,
issuing_key_id: &S::KeyId,
signer: &S,
) -> Result<Self, SigningError<S::Error>> {
let pub_key = signer.get_key_info(issuing_key_id)?;
let serial_number = Serial::from(1_u64);
let issuing_key = &pub_key;
let subject_key = &pub_key;
TbsIdCert::new(serial_number, validity, issuing_key, subject_key)
.into_cert(signer, issuing_key_id)
}
pub fn new_ee<S: Signer>(
ee_key: &PublicKey,
validity: Validity,
issuing_key_id: &S::KeyId,
signer: &S,
) -> Result<Self, SigningError<S::Error>> {
let serial_number = Serial::random(signer)?;
let issuing_key = signer.get_key_info(issuing_key_id)?;
TbsIdCert::new(serial_number, validity, &issuing_key, ee_key)
.into_cert(signer, issuing_key_id)
}
}
impl IdCert {
pub fn decode<S: IntoSource>(
source: S
) -> Result<Self, DecodeError<<S::Source as Source>::Error>> {
Mode::Der.decode(source.into_source(), Self::take_from)
}
pub fn take_from<S: decode::Source>(
cons: &mut decode::Constructed<S>
) -> Result<Self, DecodeError<S::Error>> {
cons.take_sequence(Self::from_constructed)
}
pub fn from_constructed<S: decode::Source>(
cons: &mut decode::Constructed<S>,
) -> Result<Self, DecodeError<S::Error>> {
let signed_data = SignedData::from_constructed(cons)?;
let tbs = signed_data.data().clone().decode(
TbsIdCert::from_constructed
).map_err(DecodeError::convert)?;
Ok(Self { signed_data, tbs })
}
pub fn encode_ref(&self) -> impl encode::Values + '_ {
self.signed_data.encode_ref()
}
pub fn to_captured(&self) -> Captured {
Captured::from_values(Mode::Der, self.encode_ref())
}
pub fn to_bytes(&self) -> Bytes {
self.to_captured().into_bytes()
}
}
impl IdCert {
pub fn validate_ta(&self) -> Result<(), ValidationError> {
self.validate_ta_at(Time::now())
}
pub fn validate_ta_at(&self, now: Time) -> Result<(), ValidationError> {
self.inspect_basics()?;
self.inspect_ca_basics()?;
self.verify_validity(now)?;
if let Some(aki) = self.authority_key_id {
if aki != self.subject_key_id {
debug!("ID TA certificate not self-signed, still accepting");
} else if let Err(e) = self
.signed_data
.verify_signature(&self.subject_public_key_info)
{
error!("ID TA certificate is *invalidly* self-signed");
return Err(VerificationError::new(e).into());
}
}
Ok(())
}
pub fn validate_ee(
&self, issuer_key: &PublicKey,
) -> Result<(), ValidationError> {
self.validate_ee_at(issuer_key, Time::now())
}
pub fn validate_ee_at(
&self, issuer_key: &PublicKey, now: Time,
) -> Result<(), ValidationError> {
self.inspect_basics()?;
self.verify_validity(now)?;
self.verify_issuer_key(issuer_key)?;
if let Some(basic_ca) = self.basic_ca {
if basic_ca {
return Err(VerificationError::new(
"Basic Constraints with cA true not allowed in EE cert"
).into());
}
}
self.verify_signature(issuer_key).map_err(VerificationError::new)?;
Ok(())
}
fn inspect_basics(&self) -> Result<(), InspectionError> {
if self.subject_key_id
!= self.subject_public_key_info.key_identifier()
{
return Err(InspectionError::new(
"Subject Key Identifier mismatch"
));
}
Ok(())
}
fn inspect_ca_basics(&self) -> Result<(), InspectionError> {
if let Some(ca) = self.basic_ca {
if !ca {
return Err(InspectionError::new(
"Basic Constraints with cA flag set to false"
))
}
}
else {
return Err(InspectionError::new(
"missing Basic Constraints extension"
))
}
Ok(())
}
pub fn verify_validity(
&self, now: Time,
) -> Result<(), VerificationError> {
self.validity.verify_at(now).map_err(Into::into)
}
fn verify_issuer_key(
&self, issuer_key: &PublicKey,
) -> Result<(), VerificationError> {
if let Some(aki) = self.authority_key_id {
if aki != issuer_key.key_identifier() {
Err(VerificationError::new(
"Authority Key Identifier doesn’t match issuer key"
))
} else {
Ok(())
}
} else {
Ok(())
}
}
fn verify_signature(
&self, public_key: &PublicKey,
) -> Result<(), SignatureVerificationError> {
self.signed_data.verify_signature(public_key)
}
}
impl ops::Deref for IdCert {
type Target = TbsIdCert;
fn deref(&self) -> &Self::Target {
&self.tbs
}
}
impl AsRef<IdCert> for IdCert {
fn as_ref(&self) -> &Self {
self
}
}
impl AsRef<TbsIdCert> for IdCert {
fn as_ref(&self) -> &TbsIdCert {
&self.tbs
}
}
impl PartialEq for IdCert {
fn eq(&self, other: &Self) -> bool {
self.signed_data == other.signed_data
}
}
impl Eq for IdCert { }
#[cfg(feature = "serde")]
impl serde::Serialize for IdCert {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let bytes = self.to_captured().into_bytes();
let str = base64::Serde.encode(&bytes);
str.serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for IdCert {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
use serde::de;
let some = String::deserialize(deserializer)?;
let dec = base64::Serde.decode(&some).map_err(de::Error::custom)?;
let b = Bytes::from(dec);
IdCert::decode(b).map_err(de::Error::custom)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TbsIdCert {
serial_number: Serial,
#[allow(dead_code)]
issuer: Name,
validity: Validity,
#[allow(dead_code)]
subject: Name,
subject_public_key_info: PublicKey,
basic_ca: Option<bool>,
subject_key_id: KeyIdentifier,
authority_key_id: Option<KeyIdentifier>,
}
impl TbsIdCert {
pub fn public_key(&self) -> &PublicKey {
&self.subject_public_key_info
}
pub fn subject_public_key_info(&self) -> &PublicKey {
&self.subject_public_key_info
}
pub fn subject_key_identifier(&self) -> KeyIdentifier {
self.subject_key_id
}
pub fn serial_number(&self) -> Serial {
self.serial_number
}
pub fn subject_key_id(&self) -> KeyIdentifier {
self.subject_key_id
}
pub fn authority_key_id(&self) -> Option<KeyIdentifier> {
self.authority_key_id
}
pub fn subject(&self) -> &Name {
&self.subject
}
pub fn validity(&self) -> &Validity {
&self.validity
}
}
impl TbsIdCert {
fn from_constructed<S: decode::Source>(
cons: &mut decode::Constructed<S>,
) -> Result<Self, DecodeError<S::Error>> {
cons.take_sequence(|cons| {
cons.take_constructed_if(Tag::CTX_0, |c| c.skip_u8_if(2))?;
let serial_number = Serial::take_from(cons)?;
let _sig = RpkiSignatureAlgorithm::x509_take_from(cons)?;
let issuer = Name::take_from(cons)?;
let validity = Validity::take_from(cons)?;
let subject = Name::take_from(cons)?;
let subject_public_key_info = PublicKey::take_from(cons)?;
let mut basic_ca = None;
let mut subject_key_id = None;
let mut authority_key_id = None;
cons.take_opt_constructed_if(Tag::CTX_3, |c| {
c.take_sequence(|cons| {
while let Some(()) = cons.take_opt_sequence(|cons| {
let id = Oid::take_from(cons)?;
let _critical = cons.take_opt_bool()?.unwrap_or(false);
let value = OctetString::take_from(cons)?;
Mode::Der.decode(value.into_source(), |content| {
if id == oid::CE_BASIC_CONSTRAINTS {
Self::take_basic_constraints(
content, &mut basic_ca
)
} else if id == oid::CE_SUBJECT_KEY_IDENTIFIER {
TbsCert::take_subject_key_identifier(
content, &mut subject_key_id
)
} else if id == oid::CE_AUTHORITY_KEY_IDENTIFIER {
Self::take_authority_key_identifier(
content,
&mut authority_key_id,
)
} else {
Ok(())
}
}).map_err(DecodeError::convert)?;
Ok(())
})? {}
Ok(())
})
})?;
Ok(TbsIdCert {
serial_number,
issuer,
validity,
subject,
subject_public_key_info,
basic_ca,
subject_key_id: subject_key_id.ok_or_else(|| {
cons.content_err(
"missing Subject Key Identifier extension"
)
})?,
authority_key_id,
})
})
}
fn take_basic_constraints<S: decode::Source>(
cons: &mut decode::Constructed<S>,
basic_ca: &mut Option<bool>,
) -> Result<(), DecodeError<S::Error>> {
if basic_ca.is_some() {
Err(cons.content_err("duplicate Basic Constraints extension"))
}
else {
cons.take_sequence(|cons| {
*basic_ca = Some(cons.take_opt_bool()?.unwrap_or(false));
let _path_len_constraint = cons.take_opt_u64()?;
Ok(())
})
}
}
fn take_authority_key_identifier<S: decode::Source>(
cons: &mut decode::Constructed<S>,
authority_key_id: &mut Option<KeyIdentifier>,
) -> Result<(), DecodeError<S::Error>> {
cons.take_sequence(|cons| {
*authority_key_id = cons
.take_opt_value_if(Tag::CTX_0, KeyIdentifier::from_content)?;
cons.skip_all()?;
Ok(())
})?;
Ok(())
}
pub fn encode_ref(&self) -> impl encode::Values + '_ {
encode::sequence((
encode::sequence_as(Tag::CTX_0, 2.encode()), self.serial_number.encode(),
RpkiSignatureAlgorithm::default().x509_encode(),
self.issuer.encode_ref(),
self.validity.encode(),
self.subject.encode_ref(),
self.subject_public_key_info.encode_ref(),
encode::sequence_as(
Tag::CTX_3,
encode::sequence((
self.basic_ca.map(|ca| {
encode_extension(
&oid::CE_BASIC_CONSTRAINTS,
true,
encode::sequence(
if ca { Some(ca.encode()) } else { None }
),
)
}),
encode_extension(
&oid::CE_SUBJECT_KEY_IDENTIFIER,
false,
self.subject_key_id.encode_ref(),
),
self.authority_key_id.as_ref().map(|id| {
encode_extension(
&oid::CE_AUTHORITY_KEY_IDENTIFIER,
false,
encode::sequence(id.encode_ref_as(Tag::CTX_0)),
)
}),
)),
),
))
}
}
impl TbsIdCert {
fn new(
serial_number: Serial,
validity: Validity,
issuing_key: &PublicKey,
subject_key: &PublicKey,
) -> TbsIdCert {
let issuer = Name::from_pub_key(issuing_key);
let subject = Name::from_pub_key(subject_key);
let basic_ca = if issuing_key == subject_key {
Some(true)
} else {
None
};
let subject_key_id = subject_key.key_identifier();
let authority_key_id = if issuing_key == subject_key {
None
} else {
Some(issuing_key.key_identifier())
};
TbsIdCert {
serial_number,
issuer,
validity,
subject,
subject_public_key_info: subject_key.clone(),
basic_ca,
subject_key_id,
authority_key_id,
}
}
fn into_cert<S: Signer>(
self,
signer: &S,
key: &S::KeyId,
) -> Result<IdCert, SigningError<S::Error>> {
let data = Captured::from_values(Mode::Der, self.encode_ref());
let signature = signer.sign(
key, RpkiSignatureAlgorithm::default(), &data
)?;
Ok(IdCert {
signed_data: SignedData::new(data, signature),
tbs: self,
})
}
}
#[cfg(test)]
pub mod tests {
use super::*;
#[test]
fn parse_id_publisher_ta_cert() {
let data = include_bytes!("../../test-data/ca/id_ta.cer");
let idcert = IdCert::decode(Bytes::from_static(data)).unwrap();
let idcert_moment = Time::utc(2012, 1, 1, 0, 0, 0);
idcert.validate_ta_at(idcert_moment).unwrap();
}
#[test]
fn parse_afrinic_ta_id_cert() {
let data = include_bytes!("../../test-data/ca/id_afrinic.cer");
let idcert = IdCert::decode(Bytes::from_static(data)).unwrap();
let idcert_moment = Time::utc(2022, 10, 25, 15, 0, 0);
idcert.validate_ta_at(idcert_moment).unwrap();
}
}
#[cfg(all(test, feature = "softkeys"))]
mod signer_test {
use crate::crypto::softsigner::OpenSslSigner;
use crate::crypto::PublicKeyFormat;
use super::*;
#[test]
fn build_id_ta_cert() {
let signer = OpenSslSigner::new();
let ta_key = signer.create_key(PublicKeyFormat::Rsa).unwrap();
let ta_cert = IdCert::new_ta(
Validity::from_secs(60), &ta_key, &signer
).unwrap();
ta_cert.validate_ta().unwrap();
}
}