use bytes::Bytes;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use bcder::encode::{Constructed, PrimitiveContent, Values};
use bcder::{decode, encode, Unsigned};
use bcder::{BitString, Mode, OctetString, Oid, Tag};
use rpki::crypto::{DigestAlgorithm, KeyIdentifier, PublicKey, Signature, SignatureAlgorithm};
use rpki::oid;
use rpki::x509::{encode_extension, update_once, Name, SignedData, Time, ValidationError, Validity};
use crate::commons::crypto::{CryptoResult, KrillSigner};
use crate::constants::ID_CERTIFICATE_VALIDITY_YEARS;
struct IdTbsCertificate {
serial_number: u32,
issuer: Name,
validity: Validity,
subject: Name,
subject_public_key_info: PublicKey,
extensions: IdExtensions,
}
impl IdTbsCertificate {
#[allow(clippy::needless_lifetimes)]
fn encode<'a>(&'a self) -> impl encode::Values + 'a {
encode::sequence((
(
Constructed::new(
Tag::CTX_0,
2.encode(), ),
self.serial_number.encode(),
SignatureAlgorithm::default().x509_encode(),
self.issuer.encode_ref(),
),
(
self.validity.encode(),
self.subject.encode_ref(),
self.subject_public_key_info.clone().encode(),
self.extensions.encode(),
),
))
}
}
pub struct IdCertBuilder;
impl IdCertBuilder {
fn make_tbs_certificate_request(
serial_number: u32,
issuing_key: &PublicKey,
subject_key: &PublicKey,
extensions: IdExtensions,
) -> IdTbsCertificate {
let issuer = Name::from_pub_key(issuing_key);
let validity = Validity::new(
Time::five_minutes_ago(),
Time::years_from_now(ID_CERTIFICATE_VALIDITY_YEARS),
);
let subject = Name::from_pub_key(subject_key);
IdTbsCertificate {
serial_number,
issuer,
validity,
subject,
subject_public_key_info: subject_key.clone(),
extensions,
}
}
fn create_signed_cert(
issuing_key: &KeyIdentifier,
subject_key: &PublicKey,
ext: IdExtensions,
signer: &KrillSigner,
) -> CryptoResult<IdCert> {
let issuing_key_info = signer.get_key_info(issuing_key)?;
let tbs = Self::make_tbs_certificate_request(1, &issuing_key_info, &subject_key, ext);
let enc_cert = tbs.encode();
let enc_cert_c = enc_cert.to_captured(Mode::Der);
let enc_cert_b: &Bytes = enc_cert_c.as_ref();
let signature = BitString::new(0, signer.sign(issuing_key, enc_cert_b)?.value().clone());
let captured_cert = encode::sequence((
enc_cert,
SignatureAlgorithm::default().x509_encode(),
signature.encode(),
))
.to_captured(Mode::Der);
let id_cert = IdCert::decode(captured_cert.as_ref()).unwrap();
Ok(id_cert)
}
pub fn new_ta_id_cert(issuing_key: &KeyIdentifier, signer: &KrillSigner) -> CryptoResult<IdCert> {
let issuing_key_info = signer.get_key_info(issuing_key)?;
let ext = IdExtensions::for_id_ta_cert(&issuing_key_info);
let cert = IdCertBuilder::create_signed_cert(issuing_key, &issuing_key_info, ext, signer)?;
Ok(cert)
}
pub fn new_ee_cert(
issuing_key: &KeyIdentifier,
subject_key: &PublicKey,
signer: &KrillSigner,
) -> CryptoResult<IdCert> {
let issuing_key_info = signer.get_key_info(issuing_key)?;
let ext = IdExtensions::for_id_ee_cert(subject_key, &issuing_key_info);
let cert = IdCertBuilder::create_signed_cert(issuing_key, subject_key, ext, signer)?;
Ok(cert)
}
}
pub struct SignedAttributes {
content_type: &'static Oid<&'static [u8]>,
digest: OctetString,
signing_time: Time,
}
impl SignedAttributes {
pub fn new(content_type: &'static Oid<&'static [u8]>, content: &Bytes) -> Self {
let content_digest = DigestAlgorithm::default().digest(content);
let digest = Bytes::copy_from_slice(content_digest.as_ref());
let digest = OctetString::new(digest);
Self {
content_type,
digest,
signing_time: Time::now(),
}
}
#[allow(clippy::needless_lifetimes)]
pub fn encode<'a>(&'a self) -> impl encode::Values + 'a {
(
encode::sequence((oid::CONTENT_TYPE.encode(), encode::set(self.content_type.encode()))),
encode::sequence((
oid::SIGNING_TIME.encode(),
encode::set(self.signing_time.encode_varied()),
)),
encode::sequence((oid::MESSAGE_DIGEST.encode(), encode::set(self.digest.clone().encode()))),
)
}
pub fn sign(&self, signer: &KrillSigner) -> CryptoResult<(Signature, PublicKey)> {
let encode_in_set = encode::set(self.encode()).to_captured(Mode::Der);
signer.sign_one_off(encode_in_set.as_slice())
}
}
#[derive(Clone, Debug)]
pub struct IdCert {
signed_data: SignedData,
serial_number: Unsigned,
signature: SignatureAlgorithm,
issuer: Name,
validity: Validity,
subject: Name,
subject_public_key_info: PublicKey,
extensions: IdExtensions,
}
impl IdCert {
pub fn public_key(&self) -> &[u8] {
self.subject_public_key_info.bits()
}
pub fn ski_hex(&self) -> String {
self.subject_public_key_info.key_identifier().to_string()
}
pub fn subject_public_key_info(&self) -> &PublicKey {
&self.subject_public_key_info
}
pub fn serial_number(&self) -> &Unsigned {
&self.serial_number
}
}
impl IdCert {
pub fn decode<S: decode::Source>(source: S) -> Result<Self, S::Err> {
Mode::Der.decode(source, Self::take_from)
}
pub fn take_from<S: decode::Source>(cons: &mut decode::Constructed<S>) -> Result<Self, S::Err> {
cons.take_sequence(Self::from_constructed)
}
pub fn from_constructed<S: decode::Source>(cons: &mut decode::Constructed<S>) -> Result<Self, S::Err> {
let signed_data = SignedData::from_constructed(cons)?;
signed_data
.data()
.clone()
.decode(|cons| {
cons.take_sequence(|cons| {
cons.take_constructed_if(Tag::CTX_0, |c| c.skip_u8_if(2))?;
Ok(IdCert {
signed_data,
serial_number: Unsigned::take_from(cons)?,
signature: SignatureAlgorithm::x509_take_from(cons)?,
issuer: Name::take_from(cons)?,
validity: Validity::take_from(cons)?,
subject: Name::take_from(cons)?,
subject_public_key_info: PublicKey::take_from(cons)?,
extensions: cons.take_constructed_if(Tag::CTX_3, IdExtensions::take_from)?,
})
})
})
.map_err(Into::into)
}
#[allow(clippy::needless_lifetimes)]
pub fn encode<'a>(&'a self) -> impl encode::Values + 'a {
self.signed_data.encode_ref()
}
pub fn to_bytes(&self) -> Bytes {
self.encode().to_captured(Mode::Der).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.validate_basics(now)?;
self.validate_ca_basics()?;
if let Some(aki) = self.extensions.authority_key_id() {
if aki != self.extensions.subject_key_id() {
debug!("ID certificate is not self-signed.")
}
}
if let Err(_e) = self.signed_data.verify_signature(&self.subject_public_key_info) {
debug!("ID certificate is not self-signed.")
}
Ok(())
}
pub fn validate_ee(&self, issuer: &IdCert) -> Result<(), ValidationError> {
self.validate_ee_at(issuer, Time::now())
}
pub fn validate_ee_at(&self, issuer: &IdCert, now: Time) -> Result<(), ValidationError> {
self.validate_basics(now)?;
self.validate_issued(issuer)?;
if let Some(basic_ca) = self.extensions.basic_ca {
if basic_ca {
return Err(ValidationError);
}
}
self.validate_signature(issuer)?;
Ok(())
}
fn validate_basics(&self, now: Time) -> Result<(), ValidationError> {
self.validity.validate_at(now)?;
if *self.extensions.subject_key_id() != self.subject_public_key_info().key_identifier() {
return Err(ValidationError);
}
Ok(())
}
fn validate_issued(&self, issuer: &IdCert) -> Result<(), ValidationError> {
if let Some(aki) = self.extensions.authority_key_id() {
if aki != issuer.extensions.subject_key_id() {
return Err(ValidationError);
}
} else {
return Err(ValidationError);
}
Ok(())
}
fn validate_ca_basics(&self) -> Result<(), ValidationError> {
if let Some(ca) = self.extensions.basic_ca {
if ca {
return Ok(());
}
}
Err(ValidationError)
}
fn validate_signature(&self, issuer: &IdCert) -> Result<(), ValidationError> {
self.signed_data.verify_signature(issuer.subject_public_key_info())
}
}
impl AsRef<IdCert> for IdCert {
fn as_ref(&self) -> &Self {
self
}
}
impl Serialize for IdCert {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let bytes = self.to_bytes();
let str = base64::encode(&bytes);
str.serialize(serializer)
}
}
impl PartialEq for IdCert {
fn eq(&self, other: &Self) -> bool {
self.to_bytes().eq(&other.to_bytes())
}
}
impl Eq for IdCert {}
impl<'de> Deserialize<'de> for IdCert {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
use serde::de;
let some = String::deserialize(deserializer)?;
let dec = base64::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 IdExtensions {
basic_ca: Option<bool>,
subject_key_id: KeyIdentifier,
authority_key_id: Option<KeyIdentifier>,
}
impl IdExtensions {
pub fn take_from<S: decode::Source>(cons: &mut decode::Constructed<S>) -> Result<Self, S::Err> {
cons.take_sequence(|cons| {
let mut basic_ca = None;
let mut subject_key_id = None;
let mut authority_key_id = None;
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.to_source(), |content| {
if id == oid::CE_BASIC_CONSTRAINTS {
Self::take_basic_constraints(content, &mut basic_ca)
} else if id == oid::CE_SUBJECT_KEY_IDENTIFIER {
Self::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(())
}
})?;
Ok(())
})? {}
Ok(IdExtensions {
basic_ca,
subject_key_id: subject_key_id.ok_or(decode::Malformed)?,
authority_key_id,
})
})
}
pub(crate) fn take_basic_constraints<S: decode::Source>(
cons: &mut decode::Constructed<S>,
basic_ca: &mut Option<bool>,
) -> Result<(), S::Err> {
update_once(basic_ca, || {
cons.take_sequence(|cons| cons.take_opt_bool())
.map(|ca| ca.unwrap_or(false))
})
}
fn take_subject_key_identifier<S: decode::Source>(
cons: &mut decode::Constructed<S>,
subject_key_id: &mut Option<KeyIdentifier>,
) -> Result<(), S::Err> {
update_once(subject_key_id, || KeyIdentifier::take_from(cons))
}
fn take_authority_key_identifier<S: decode::Source>(
cons: &mut decode::Constructed<S>,
authority_key_id: &mut Option<KeyIdentifier>,
) -> Result<(), S::Err> {
update_once(authority_key_id, || {
cons.take_sequence(|cons| cons.take_value_if(Tag::CTX_0, KeyIdentifier::from_content))
})
}
}
impl IdExtensions {
#[allow(clippy::needless_lifetimes)]
pub fn encode<'a>(&'a self) -> impl encode::Values + 'a {
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 IdExtensions {
pub fn for_id_ta_cert(key: &PublicKey) -> Self {
let ki = KeyIdentifier::from_public_key(key);
IdExtensions {
basic_ca: Some(true),
subject_key_id: ki,
authority_key_id: Some(ki),
}
}
pub fn for_id_ee_cert(subject_key: &PublicKey, issuing_key: &PublicKey) -> Self {
IdExtensions {
basic_ca: None,
subject_key_id: KeyIdentifier::from_public_key(subject_key),
authority_key_id: Some(KeyIdentifier::from_public_key(issuing_key)),
}
}
}
impl IdExtensions {
pub fn subject_key_id(&self) -> &KeyIdentifier {
&self.subject_key_id
}
pub fn authority_key_id(&self) -> Option<&KeyIdentifier> {
self.authority_key_id.as_ref()
}
}
impl From<&PublicKey> for IdExtensions {
fn from(pk: &PublicKey) -> Self {
let basic_ca = Some(true);
let ki = KeyIdentifier::from_public_key(pk);
let subject_key_id = ki;
let authority_key_id = Some(ki);
IdExtensions {
basic_ca,
subject_key_id,
authority_key_id,
}
}
}
#[cfg(test)]
pub mod tests {
use crate::test::*;
use super::*;
#[test]
fn parse_id_publisher_ta_cert() {
test_id_certificate()
.validate_ta_at(Time::utc(2012, 1, 1, 0, 0, 0))
.unwrap();
}
#[test]
fn should_create_self_signed_ta_id_cert() {
test_under_tmp(|d| {
let s = KrillSigner::build(&d).unwrap();
let key_id = s.create_key().unwrap();
let id_cert = IdCertBuilder::new_ta_id_cert(&key_id, &s).unwrap();
id_cert.validate_ta().unwrap();
});
}
}