use std::{fmt, ops};
use std::collections::HashSet;
use std::str::FromStr;
use bcder::{decode, encode};
use bcder::{Captured, Mode, OctetString, Oid, Tag};
use bcder::decode::{ContentError, DecodeError, IntoSource, Source};
use bcder::encode::PrimitiveContent;
use bytes::Bytes;
use crate::{oid, uri};
use crate::crypto::{
KeyIdentifier, PublicKey, RpkiSignatureAlgorithm, SignatureAlgorithm,
Signer, SigningError,
};
#[cfg(feature = "serde")] use crate::util::base64;
use super::error::VerificationError;
use super::x509::{
Name, RepresentationError, Serial, SignedData, Time, encode_extension,
};
#[derive(Clone, Debug)]
pub struct Crl {
signed_data: SignedData,
tbs: TbsCertList<RevokedCertificates>,
serials: Option<HashSet<Serial>>,
}
impl Crl {
pub fn signed_data(&self) -> &SignedData {
&self.signed_data
}
pub fn as_cert_list(&self) -> &TbsCertList<RevokedCertificates> {
&self.tbs
}
pub fn cache_serials(&mut self) {
self.serials = Some(
self.tbs.revoked_certs.iter().map(|entry| entry.user_certificate)
.collect()
);
}
pub fn contains(&self, serial: Serial) -> bool {
match self.serials {
Some(ref set) => set.contains(&serial),
None => self.tbs.revoked_certs.contains(serial)
}
}
}
impl Crl {
pub fn decode<S: IntoSource>(
source: S
) -> Result<Self, DecodeError<<S::Source as Source>::Error>> {
Mode::Der.decode(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 take_opt_from<S: decode::Source>(
cons: &mut decode::Constructed<S>
) -> Result<Option<Self>, DecodeError<S::Error>> {
cons.take_opt_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(
TbsCertList::take_from
).map_err(DecodeError::convert)?;
if tbs.signature != *signed_data.signature().algorithm() {
return Err(cons.content_err(
"CRL signature algorithm mismatch"
))
}
Ok(Self { signed_data, tbs, serials: None })
}
pub fn verify_signature(
&self,
public_key: &PublicKey
) -> Result<(), VerificationError> {
self.signed_data.verify_signature(
public_key
).map_err(VerificationError::new)
}
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())
}
}
impl ops::Deref for Crl {
type Target = TbsCertList<RevokedCertificates>;
fn deref(&self) -> &Self::Target {
&self.tbs
}
}
impl AsRef<TbsCertList<RevokedCertificates>> for Crl {
fn as_ref(&self) -> &TbsCertList<RevokedCertificates> {
&self.tbs
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for Crl {
fn serialize<S: serde::Serializer>(
&self, serializer: S
) -> Result<S::Ok, S::Error> {
let bytes = self.to_captured().into_bytes();
let b64 = base64::Serde.encode(&bytes);
b64.serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Crl {
fn deserialize<D: serde::Deserializer<'de>>(
deserializer: D
) -> Result<Self, D::Error> {
use serde::de;
let s = String::deserialize(deserializer)?;
let decoded = base64::Serde.decode(&s).map_err(de::Error::custom)?;
let bytes = bytes::Bytes::from(decoded);
Crl::decode(bytes).map_err(de::Error::custom)
}
}
#[derive(Clone, Debug)]
pub struct TbsCertList<C> {
signature: RpkiSignatureAlgorithm,
issuer: Name,
this_update: Time,
next_update: Time,
revoked_certs: C,
authority_key_id: KeyIdentifier,
crl_number: Serial,
}
impl<C> TbsCertList<C> {
pub fn new(
signature: RpkiSignatureAlgorithm,
issuer: Name,
this_update: Time,
next_update: Time,
revoked_certs: C,
authority_key_id: KeyIdentifier,
crl_number: Serial
) -> Self {
Self {
signature,
issuer,
this_update,
next_update,
revoked_certs,
authority_key_id,
crl_number
}
}
pub fn into_crl<S: Signer>(
self,
signer: &S,
key: &S::KeyId
) -> Result<Crl, SigningError<S::Error>>
where
C: IntoIterator<Item=CrlEntry>,
<C as IntoIterator>::IntoIter: Clone
{
let tbs: TbsCertList<RevokedCertificates> = self.into();
let data = Captured::from_values(Mode::Der, tbs.encode_ref());
let signature = signer.sign(key, tbs.signature, &data)?;
Ok(Crl {
signed_data: SignedData::new(data, signature),
tbs,
serials: None,
})
}
}
impl<C> TbsCertList<C> {
pub fn signature(&self) -> RpkiSignatureAlgorithm {
self.signature
}
pub fn set_signature(&mut self, signature: RpkiSignatureAlgorithm) {
self.signature = signature
}
pub fn issuer(&self) -> &Name {
&self.issuer
}
pub fn set_issuer(&mut self, issuer: Name) {
self.issuer = issuer
}
pub fn this_update(&self) -> Time {
self.this_update
}
pub fn set_this_update(&mut self, this_update: Time) {
self.this_update = this_update
}
pub fn next_update(&self) -> Time {
self.next_update
}
pub fn is_stale(&self) -> bool {
self.next_update < Time::now()
}
pub fn set_next_update(&mut self, next_update: Time) {
self.next_update = next_update
}
pub fn revoked_certs(&self) -> &C {
&self.revoked_certs
}
pub fn revoked_certs_mut(&mut self) -> &mut C {
&mut self.revoked_certs
}
pub fn set_revoked_certs(&mut self, revoked_certs: C) {
self.revoked_certs = revoked_certs
}
pub fn authority_key_identifier(&self) -> &KeyIdentifier {
&self.authority_key_id
}
pub fn set_authority_key_identifier(&mut self, id: KeyIdentifier) {
self.authority_key_id = id
}
pub fn crl_number(&self) -> Serial {
self.crl_number
}
pub fn set_crl_number(&mut self, crl_number: Serial) {
self.crl_number = crl_number
}
}
impl TbsCertList<RevokedCertificates> {
pub fn take_from<S: decode::Source>(
cons: &mut decode::Constructed<S>
) -> Result<Self, DecodeError<S::Error>> {
cons.take_sequence(|cons| {
cons.skip_u8_if(1)?;
let signature = RpkiSignatureAlgorithm::x509_take_from(cons)?;
let issuer = Name::take_from(cons)?;
let this_update = Time::take_from(cons)?;
let next_update = Time::take_from(cons)?;
let revoked_certs = RevokedCertificates::take_from(cons)?;
let mut authority_key_id = None;
let mut crl_number = None;
cons.take_constructed_if(Tag::CTX_0, |cons| {
cons.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, |content| {
if id == oid::CE_AUTHORITY_KEY_IDENTIFIER {
Self::take_authority_key_identifier(
content, &mut authority_key_id
)
}
else if id == oid::CE_CRL_NUMBER {
Self::take_crl_number(
content, &mut crl_number
)
}
else {
Err(content.content_err(
InvalidExtension::new(id)
))
}
}).map_err(DecodeError::convert)
})? { }
Ok(())
})
})?;
let authority_key_id = authority_key_id.ok_or_else(|| {
cons.content_err(
"missing Authority Key Identifier extension"
)
})?;
let crl_number = crl_number.ok_or_else(|| {
cons.content_err("missing CRL Number extension")
})?;
Ok(Self {
signature,
issuer,
this_update,
next_update,
revoked_certs,
authority_key_id,
crl_number
})
})
}
fn take_authority_key_identifier<S: decode::Source>(
cons: &mut decode::Constructed<S>,
authority_key_id: &mut Option<KeyIdentifier>,
) -> Result<(), DecodeError<S::Error>> {
if authority_key_id.is_some() {
Err(cons.content_err(
"duplicate Authority Key Identifier extension"
))
}
else {
*authority_key_id = Some(
cons.take_sequence(|cons| {
cons.take_value_if(Tag::CTX_0, KeyIdentifier::from_content)
})?
);
Ok(())
}
}
fn take_crl_number<S: decode::Source>(
cons: &mut decode::Constructed<S>,
crl_number: &mut Option<Serial>,
) -> Result<(), DecodeError<S::Error>> {
if crl_number.is_some() {
Err(cons.content_err("duplicate CRL Number extension"))
}
else {
*crl_number = Some(Serial::take_from(cons)?);
Ok(())
}
}
pub fn encode_ref(&self) -> impl encode::Values + '_ {
encode::sequence((
1.encode(), self.signature.x509_encode(),
self.issuer.encode_ref(),
self.this_update.encode_varied(),
self.next_update.encode_varied(),
self.revoked_certs.encode_ref(),
encode::sequence_as(Tag::CTX_0,
encode::sequence((
encode_extension(
&oid::CE_AUTHORITY_KEY_IDENTIFIER, false,
encode::sequence(
self.authority_key_id.encode_ref_as(Tag::CTX_0)
)
),
encode_extension(
&oid::CE_CRL_NUMBER, false,
self.crl_number.encode()
),
))
)
))
}
}
impl<C> From<TbsCertList<C>> for TbsCertList<RevokedCertificates>
where
C: IntoIterator<Item = CrlEntry>,
<C as IntoIterator>::IntoIter: Clone
{
fn from(list: TbsCertList<C>) -> Self {
Self {
signature: list.signature,
issuer: list.issuer,
this_update: list.this_update,
next_update: list.next_update,
revoked_certs: RevokedCertificates::from_iter(list.revoked_certs),
authority_key_id: list.authority_key_id,
crl_number: list.crl_number,
}
}
}
#[derive(Clone, Debug)]
pub struct RevokedCertificates(Captured);
impl RevokedCertificates {
pub fn empty() -> Self {
let entries: Vec<CrlEntry> = vec![];
Self::from_iter(entries)
}
pub fn take_from<S: decode::Source>(
cons: &mut decode::Constructed<S>
) -> Result<Self, DecodeError<S::Error>> {
let res = cons.take_opt_sequence(|cons| {
cons.capture(|cons| {
while CrlEntry::take_opt_from(cons)?.is_some() { }
Ok(())
})
})?;
Ok(RevokedCertificates(match res {
Some(res) => res,
None => Captured::empty(Mode::Der)
}))
}
pub fn contains(&self, serial: Serial) -> bool {
Mode::Der.decode(self.0.as_ref(), |cons| {
while let Some(entry) = CrlEntry::take_opt_from(cons).unwrap() {
if entry.user_certificate == serial {
return Ok(true)
}
}
Ok(false)
}).unwrap()
}
pub fn iter(&self) -> RevokedCertificatesIter {
RevokedCertificatesIter(self.0.clone())
}
pub fn encode_ref(&self) -> impl encode::Values + '_ {
(!self.0.is_empty()).then(|| {
encode::sequence(&self.0)
})
}
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = CrlEntry>,
<I as IntoIterator>::IntoIter: Clone
{
RevokedCertificates(Captured::from_values(
Mode::Der, encode::iter(
iter.into_iter().map(CrlEntry::encode)
)
))
}
}
#[derive(Clone, Debug)]
pub struct RevokedCertificatesIter(Captured);
impl Iterator for RevokedCertificatesIter {
type Item = CrlEntry;
#[allow(clippy::redundant_closure)]
fn next(&mut self) -> Option<Self::Item> {
self.0.decode_partial(|cons| CrlEntry::take_opt_from(cons)).unwrap()
}
}
#[derive(Clone, Copy, Debug)]
pub struct CrlEntry {
user_certificate: Serial,
revocation_date: Time,
}
impl CrlEntry {
pub fn new(user_certificate: Serial, revocation_date: Time) -> Self {
CrlEntry { user_certificate, revocation_date }
}
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 take_opt_from<S: decode::Source>(
cons: &mut decode::Constructed<S>
) -> Result<Option<Self>, DecodeError<S::Error>> {
cons.take_opt_sequence(Self::from_constructed)
}
pub fn from_constructed<S: decode::Source>(
cons: &mut decode::Constructed<S>
) -> Result<Self, DecodeError<S::Error>> {
Ok(CrlEntry {
user_certificate: Serial::take_from(cons)?,
revocation_date: Time::take_from(cons)?,
})
}
pub fn encode(self) -> impl encode::Values {
encode::sequence((
self.user_certificate.encode(),
self.revocation_date.encode_varied(),
))
}
}
impl FromStr for CrlEntry {
type Err = RepresentationError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Some(pos) = s.find('@') {
Ok(CrlEntry::new(
Serial::from_str(&s[..pos])?,
Time::from_str(&s[pos + 1..]).map_err(|_| RepresentationError)?
))
}
else {
Serial::from_str(s).map(|serial| {
CrlEntry::new(serial, Time::now())
})
}
}
}
#[deprecated(since = "0.10.0", note = "new manifest rules only allow one CRL")]
#[allow(deprecated)]
#[derive(Clone, Debug)]
pub struct CrlStore {
crls: Vec<(uri::Rsync, Crl)>,
cache_serials: bool,
}
#[allow(deprecated)]
impl CrlStore {
pub fn new() -> Self {
CrlStore {
crls: Vec::new(),
cache_serials: false,
}
}
pub fn enable_serial_caching(&mut self) {
self.cache_serials = true
}
pub fn push(&mut self, uri: uri::Rsync, mut crl: Crl) {
if self.cache_serials {
crl.cache_serials()
}
self.crls.push((uri, crl))
}
pub fn get(&self, uri: &uri::Rsync) -> Option<&Crl> {
for (stored_uri, crl) in &self.crls {
if *stored_uri == *uri {
return Some(crl)
}
}
None
}
}
#[allow(deprecated)]
impl Default for CrlStore {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug)]
pub struct InvalidExtension {
oid: Oid<Bytes>,
}
impl InvalidExtension {
pub(crate) fn new(oid: Oid<Bytes>) -> Self {
InvalidExtension { oid }
}
}
impl From<InvalidExtension> for ContentError {
fn from(err: InvalidExtension) -> Self {
ContentError::from_boxed(Box::new(err))
}
}
impl fmt::Display for InvalidExtension {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "invalid extension {}", self.oid)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn decode_certs() {
Crl::decode(
include_bytes!("../../test-data/repository/ta.crl").as_ref()
).unwrap();
Crl::decode(
include_bytes!("../../test-data/repository/ca1.crl").as_ref()
).unwrap();
}
#[test]
#[cfg(feature = "serde")]
fn serde_crl() {
let der = include_bytes!("../../test-data/repository/ta.crl");
let crl = Crl::decode(bytes::Bytes::from_static(der)).unwrap();
let serialized = serde_json::to_string(&crl).unwrap();
let deser_crl: Crl = serde_json::from_str(&serialized).unwrap();
assert_eq!(
crl.to_captured().into_bytes(),
deser_crl.to_captured().into_bytes()
);
}
#[test]
#[cfg(feature = "serde")]
fn compat_de_crl() {
serde_json::from_slice::<Crl>(include_bytes!(
"../../test-data/repository/serde-compat/crl.json"
)).unwrap();
}
}
#[cfg(all(test, feature="softkeys"))]
mod signer_test {
use super::*;
use crate::crypto::PublicKeyFormat;
use crate::crypto::softsigner::OpenSslSigner;
#[test]
fn build_ta_cert() {
let signer = OpenSslSigner::new();
let key = signer.create_key(PublicKeyFormat::Rsa).unwrap();
let pubkey = signer.get_key_info(&key).unwrap();
let crl = TbsCertList::new(
Default::default(),
pubkey.to_subject_name(),
Time::now(),
Time::tomorrow(),
vec![
CrlEntry::new(12u64.into(), Time::now()),
CrlEntry::new(42u64.into(), Time::now())
],
pubkey.key_identifier(),
12u64.into()
);
let crl = crl.into_crl(&signer, &key).unwrap().to_captured();
let crl = Crl::decode(crl.as_slice()).unwrap();
assert_eq!(
crl.revoked_certs().iter().collect::<Vec<_>>().len(),
2
);
let crl = TbsCertList::new(
Default::default(),
pubkey.to_subject_name(),
Time::now(),
Time::tomorrow(),
vec![],
pubkey.key_identifier(),
12u64.into()
);
let crl = crl.into_crl(&signer, &key).unwrap().to_captured();
let crl = Crl::decode(crl.as_slice()).unwrap();
assert!(crl.revoked_certs().iter().next().is_none());
}
}