use std::{borrow, fmt, ops};
use std::sync::Arc;
use bcder::{decode, encode};
use bcder::{
BitString, Captured, ConstOid, Ia5String, Mode, OctetString, Oid, Tag
};
use bcder::decode::{ContentError, DecodeError, IntoSource, Source};
use bcder::encode::{PrimitiveContent, Values};
use bytes::Bytes;
use crate::{oid, uri};
use crate::crypto::{
KeyIdentifier, PublicKey, RpkiSignatureAlgorithm, SignatureAlgorithm,
SignatureVerificationError, Signer, SigningError,
};
#[cfg(feature = "serde")] use crate::util::base64;
use super::error::{InspectionError, ValidationError, VerificationError};
use super::resources::{
AsBlock, AsBlocks, AsBlocksBuilder, AsResources, AsResourcesBuilder,
IpBlock, IpBlocks, IpBlocksBuilder, IpResources, IpResourcesBuilder
};
use super::tal::TalInfo;
use super::x509::{
Name, SignedData, Serial, Time, Validity, encode_extension, update_first,
};
#[derive(Clone, Debug)]
pub struct Cert {
signed_data: SignedData,
tbs: TbsCert,
}
impl Cert {
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(
TbsCert::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())
}
}
impl Cert {
pub fn validate_ta(
self,
tal: Arc<TalInfo>,
strict: bool
) -> Result<ResourceCert, ValidationError> {
self.validate_ta_at(tal, strict, Time::now())
}
pub fn validate_ta_at(
self,
tal: Arc<TalInfo>,
strict: bool,
now: Time,
) -> Result<ResourceCert, ValidationError> {
self.inspect_ta(strict)?;
self.verify_ta_at(tal, strict, now).map_err(Into::into)
}
pub fn validate_ca(
self,
issuer: &ResourceCert,
strict: bool
) -> Result<ResourceCert, ValidationError> {
self.validate_ca_at(issuer, strict, Time::now())
}
pub fn validate_ca_at(
self,
issuer: &ResourceCert,
strict: bool,
now: Time,
) -> Result<ResourceCert, ValidationError> {
self.inspect_ca(strict)?;
self.verify_ca_at(issuer, strict, now).map_err(Into::into)
}
pub fn validate_ee(
self,
issuer: &ResourceCert,
strict: bool
) -> Result<ResourceCert, ValidationError> {
self.validate_ee_at(issuer, strict, Time::now())
}
pub fn validate_ee_at(
self,
issuer: &ResourceCert,
strict: bool,
now: Time,
) -> Result<ResourceCert, ValidationError> {
self.inspect_ee(strict)?;
self.verify_ee_at(issuer, strict, now).map_err(Into::into)
}
pub fn validate_detached_ee(
self,
issuer: &ResourceCert,
strict: bool
) -> Result<ResourceCert, ValidationError> {
self.validate_detached_ee_at(issuer, strict, Time::now())
}
pub fn validate_detached_ee_at(
self,
issuer: &ResourceCert,
strict: bool,
now: Time,
) -> Result<ResourceCert, ValidationError> {
self.inspect_detached_ee(strict)?;
self.verify_ee_at(issuer, strict, now).map_err(Into::into)
}
pub fn validate_router(
&self,
issuer: &ResourceCert,
strict: bool
) -> Result<(), ValidationError> {
self.validate_router_at(issuer, strict, Time::now())
}
pub fn validate_router_at(
&self,
issuer: &ResourceCert,
strict: bool,
now: Time,
) -> Result<(), ValidationError> {
self.inspect_router(strict)?;
self.verify_router_at(issuer, strict, now).map_err(Into::into)
}
pub fn inspect_ta(
&self, strict: bool,
) -> Result<(), InspectionError> {
self.inspect_basics(strict)?;
self.inspect_ca_basics(strict)?;
if let Some(ref aki) = self.authority_key_identifier {
if *aki != self.subject_key_identifier {
return Err(InspectionError::new(
"Authority Key Identifier doesn't match \
Subject Key Identifier"
));
}
}
if self.crl_uri.is_some() {
return Err(InspectionError::new(
"CRL Distribution Points extension \
not allowed in trust anchor certificate"
))
}
if self.ca_issuer.is_some() {
return Err(InspectionError::new(
"Authority Information Access extension \
not allowed in trust anchor certificate"
))
}
Ok(())
}
pub fn inspect_ca(&self, strict: bool) -> Result<(), InspectionError> {
self.inspect_basics(strict)?;
self.inspect_ca_basics(strict)?;
self.inspect_issued(strict)
}
pub fn inspect_ee(&self, strict: bool) -> Result<(), InspectionError> {
self.inspect_basics(strict)?;
self.inspect_issued(strict)?;
if self.basic_ca.is_some(){
return Err(InspectionError::new(
"Basic Constraints extension \
not allowed in end entity certificate"
))
}
if self.key_usage != KeyUsage::Ee {
return Err(InspectionError::new(
"invalid Key Usage extension \
for end entity certificate"
))
}
if self.ca_repository.is_some() {
return Err(InspectionError::new(
"id-ad-caRepository SIA instance \
not allowed in end entity certificate"
))
}
if self.rpki_manifest.is_some() {
return Err(InspectionError::new(
"id-ad-rpkiManifest SIA instance \
not allowed in end entity certificate"
))
}
if self.signed_object.is_none() {
return Err(InspectionError::new(
"missing id-ad-signedObject SIA instance \
in signed object end entity certificate"
))
}
Ok(())
}
pub fn inspect_detached_ee(
&self, strict: bool
) -> Result<(), InspectionError> {
self.inspect_basics(strict)?;
self.inspect_issued(strict)?;
if self.basic_ca.is_some(){
return Err(InspectionError::new(
"Basic Constraints extension \
not allowed in end entity certificate"
))
}
if self.key_usage != KeyUsage::Ee {
return Err(InspectionError::new(
"invalid Key Usage extension \
for end entity certificate"
))
}
if self.ca_repository.is_some() {
return Err(InspectionError::new(
"id-ad-caRepository SIA instance \
not allowed in end entity certificate"
))
}
if self.rpki_manifest.is_some() {
return Err(InspectionError::new(
"id-ad-rpkiManifest SIA instance \
not allowed in end entity certificate"
))
}
Ok(())
}
pub fn inspect_router(
&self, strict: bool
) -> Result<(), InspectionError> {
if self.signature != *self.signed_data.signature().algorithm() {
return Err(InspectionError::new(
"signature algorithm mismatch"
))
}
Name::inspect_rpki(&self.issuer, strict).map_err(IssuerError)?;
Name::inspect_router(&self.subject, strict).map_err(SubjectError)?;
if !self.subject_public_key_info().allow_router_cert() {
return Err(InspectionError::new(
"invalid public key algorithm for router certificate"
))
}
if self.basic_ca.is_some(){
return Err(InspectionError::new(
"Basic Constraints extension \
not allowed in end entity certificate"
))
}
if self.subject_key_identifier() !=
self.subject_public_key_info().key_identifier()
{
return Err(InspectionError::new(
"Subject Key Identifier extension doesn't match \
the public key"
))
}
if self.key_usage != KeyUsage::Ee {
return Err(InspectionError::new(
"invalid Key Usage extension \
for end entity certificate"
))
}
match self.extended_key_usage().as_ref() {
Some(eku) => eku.inspect_router()?,
None => {
return Err(InspectionError::new(
"missing Extended Key Usage extension \
in router certificate"
))
}
}
if self.crl_uri().is_none() {
return Err(InspectionError::new(
"missing CRL Distribution Points extension \
in router certificate"
))
}
if self.ca_repository().is_some() || self.rpki_manifest().is_some()
|| self.signed_object().is_some() || self.rpki_notify().is_some()
{
return Err(InspectionError::new(
"Subject Information Access extension \
not allowed in router certificate"
))
}
if self.v4_resources().is_present() || self.v6_resources().is_present()
{
return Err(InspectionError::new(
"IP Resources extension \
not allowed in router certificate"
))
}
if !self.as_resources().is_present() {
return Err(InspectionError::new(
"missing AS Resources extension \
in router certificate"
))
}
if self.as_resources().is_inherited() {
return Err(InspectionError::new(
"inherited AS Resources in router certificate"
))
}
Ok(())
}
pub fn verify_ta(
self, tal: Arc<TalInfo>, strict: bool,
) -> Result<ResourceCert, VerificationError> {
self.verify_ta_at(tal, strict, Time::now())
}
pub fn verify_ta_at(
self, tal: Arc<TalInfo>, _strict: bool, now: Time,
) -> Result<ResourceCert, VerificationError> {
self.verify_validity(now)?;
let v4_resources = IpBlocks::from_resources(
self.v4_resources.clone()
).map_err(|_| {
VerificationError::new(
"inherited IPv4 resources not allowed \
in trust anchor certificate"
)
})?;
let v6_resources = IpBlocks::from_resources(
self.v6_resources.clone()
).map_err(|_| {
VerificationError::new(
"inherited IPv6 resources not allowed \
in trust anchor certificate"
)
})?;
let as_resources = AsBlocks::from_resources(
self.as_resources.clone()
).map_err(|_| {
VerificationError::new(
"inherited AS resources not allowed \
in trust anchor certificate"
)
})?;
self.signed_data.verify_signature(
&self.subject_public_key_info
)?;
Ok(ResourceCert {
cert: self,
v4_resources,
v6_resources,
as_resources,
tal
})
}
pub fn verify_ta_ref(
&self, strict: bool
) -> Result<(), VerificationError> {
self.verify_ta_ref_at(strict, Time::now())
}
pub fn verify_ta_ref_at(
&self, _strict: bool, now: Time,
) -> Result<(), VerificationError> {
self.verify_validity(now)?;
if self.v4_resources.is_inherited() {
return Err(VerificationError::new(
"inherited IPv4 resources not allowed \
in trust anchor certificate"
))
}
if self.v6_resources.is_inherited() {
return Err(VerificationError::new(
"inherited IPv6 resources not allowed \
in trust anchor certificate"
))
}
if self.as_resources.is_inherited() {
return Err(VerificationError::new(
"inherited AS resources not allowed \
in trust anchor certificate"
))
}
self.signed_data.verify_signature(
&self.subject_public_key_info
)?;
Ok(())
}
pub fn verify_ca(
self, issuer: &ResourceCert, strict: bool
) -> Result<ResourceCert, VerificationError> {
self.verify_ca_at(issuer, strict, Time::now())
}
pub fn verify_ca_at(
self, issuer: &ResourceCert, strict: bool, now: Time,
) -> Result<ResourceCert, VerificationError> {
self.verify_validity(now)?;
self.verify_issuer_claim(issuer, strict)?;
self.verify_signature(issuer, strict)?;
self.verify_resources(issuer, strict)
}
pub fn verify_ee(
self, issuer: &ResourceCert, strict: bool,
) -> Result<ResourceCert, VerificationError> {
self.verify_ee_at(issuer, strict, Time::now())
}
pub fn verify_ee_at(
self, issuer: &ResourceCert, strict: bool, now: Time,
) -> Result<ResourceCert, VerificationError> {
self.verify_validity(now)?;
self.verify_issuer_claim(issuer, strict)?;
self.verify_signature(issuer, strict)?;
self.verify_resources(issuer, strict)
}
pub fn verify_router(
&self, issuer: &ResourceCert, strict: bool,
) -> Result<(), VerificationError> {
self.verify_router_at(issuer, strict, Time::now())
}
pub fn verify_router_at(
&self, issuer: &ResourceCert, strict: bool, now: Time,
) -> Result<(), VerificationError> {
self.verify_validity(now)?;
self.verify_issuer_claim(issuer, strict)?;
self.verify_signature(issuer, strict)?;
self.verify_as_resources(issuer, strict)
}
fn inspect_basics(
&self,
strict: bool,
) -> Result<(), InspectionError> {
if self.signature != *self.signed_data.signature().algorithm() {
return Err(InspectionError::new(
"signature algorithm mismatch in certificate"
))
}
Name::inspect_rpki(&self.issuer, strict).map_err(IssuerError)?;
Name::inspect_rpki(&self.subject, strict).map_err(SubjectError)?;
if !self.subject_public_key_info().allow_rpki_cert() {
return Err(InspectionError::new(
"public key algorithm not allowed for RPKI certificates"
))
}
if self.subject_key_identifier()
!= self.subject_public_key_info().key_identifier()
{
return Err(InspectionError::new(
"Subject Key Identifier extension \
doesn't match public key"
))
}
if self.extended_key_usage().is_some() {
return Err(InspectionError::new(
"Extended Key Usage extension \
not allowed in RPKI certificates"
))
}
Ok(())
}
fn inspect_issued(&self, _strict: bool) -> Result<(), InspectionError> {
if self.crl_uri().is_none() {
return Err(InspectionError::new(
"missing CRL Distribution Points extension in certificate"
))
}
Ok(())
}
fn inspect_ca_basics(
&self,
_strict: bool
) -> Result<(), InspectionError> {
match self.basic_ca() {
Some(true) => { }
Some(false) => {
return Err(InspectionError::new(
"cA flag in Basic Constraints extension set to false"
))
}
None => {
return Err(InspectionError::new(
"missing Basic Constraints extension \
in CA certificate"
))
}
}
if self.key_usage() != KeyUsage::Ca {
return Err(InspectionError::new(
"invalid Key Usage in CA certificate"
))
}
if self.ca_repository().is_none() {
return Err(InspectionError::new(
"missing id-ad-caRepository SIA instance in CA certificate"
))
}
if self.rpki_manifest().is_none() {
return Err(InspectionError::new(
"missing id-ad-rpkiManifest SIA instance in CA certificate"
))
}
if self.signed_object().is_some() {
return Err(InspectionError::new(
"id-ad-signedObject SIA instance not allowed \
in CA certificate"
))
}
Ok(())
}
pub fn verify_validity(
&self, now: Time,
) -> Result<(), VerificationError> {
self.validity.verify_at(now).map_err(Into::into)
}
pub fn verify_issuer_claim(
&self,
issuer: &ResourceCert,
_strict: bool,
) -> Result<(), VerificationError> {
match self.authority_key_identifier() {
Some(aki) => {
if aki != issuer.cert.subject_key_identifier() {
return Err(VerificationError::new(
"certificate's Authority Key Identifier doesn't \
match issuer's Subject Key Identifier"
))
}
}
None => {
return Err(VerificationError::new(
"missing Authority Key Identifier extension \
on certificate"
))
}
}
if self.ca_issuer().is_none() {
return Err(VerificationError::new(
"missing Authority Information Access extension \
on certificate"
))
}
Ok(())
}
pub fn verify_signature(
&self,
issuer: &Cert,
_strict: bool
) -> Result<(), SignatureVerificationError> {
self.signed_data.verify_signature(
issuer.subject_public_key_info()
)
}
fn verify_resources(
self,
issuer: &ResourceCert,
_strict: bool
) -> Result<ResourceCert, VerificationError> {
Ok(ResourceCert {
v4_resources: issuer.v4_resources.verify_issued(
self.v4_resources(), self.overclaim
).map_err(|_| {
VerificationError::new(
"certificate is overclaiming IPv4 resources"
)
})?,
v6_resources: issuer.v6_resources.verify_issued(
self.v6_resources(), self.overclaim
).map_err(|_| {
VerificationError::new(
"certificate is overclaiming IPv6 resources"
)
})?,
as_resources: issuer.as_resources.verify_issued(
self.as_resources(), self.overclaim()
).map_err(|_| {
VerificationError::new(
"certificate is overclaiming AS resources"
)
})?,
cert: self,
tal: issuer.tal.clone(),
})
}
fn verify_as_resources(
&self,
issuer: &ResourceCert,
_strict: bool
) -> Result<(), VerificationError> {
let _ = issuer.as_resources.verify_issued(
self.as_resources(), self.overclaim()
).map_err(|_| {
VerificationError::new(
"certificate is overclaiming AS resources"
)
})?;
Ok(())
}
}
impl ops::Deref for Cert {
type Target = TbsCert;
fn deref(&self) -> &Self::Target {
&self.tbs
}
}
impl AsRef<Cert> for Cert {
fn as_ref(&self) -> &Self {
self
}
}
impl AsRef<TbsCert> for Cert {
fn as_ref(&self) -> &TbsCert {
&self.tbs
}
}
impl borrow::Borrow<TbsCert> for Cert {
fn borrow(&self) -> &TbsCert {
&self.tbs
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for Cert {
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 Cert {
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::from(decoded);
Cert::decode(bytes).map_err(de::Error::custom)
}
}
#[derive(Clone, Debug)]
pub struct TbsCert {
serial_number: Serial,
signature: RpkiSignatureAlgorithm,
issuer: Name,
validity: Validity,
subject: Name,
subject_public_key_info: PublicKey,
basic_ca: Option<bool>,
subject_key_identifier: KeyIdentifier,
authority_key_identifier: Option<KeyIdentifier>,
key_usage: KeyUsage,
extended_key_usage: Option<ExtendedKeyUsage>,
crl_uri: Option<uri::Rsync>,
ca_issuer: Option<uri::Rsync>,
ca_repository: Option<uri::Rsync>,
rpki_manifest: Option<uri::Rsync>,
signed_object: Option<uri::Rsync>,
rpki_notify: Option<uri::Https>,
overclaim: Overclaim,
v4_resources: IpResources,
v6_resources: IpResources,
as_resources: AsResources,
}
impl TbsCert {
#[allow(clippy::too_many_arguments)]
pub fn new(
serial_number: Serial,
issuer: Name,
validity: Validity,
subject: Option<Name>,
subject_public_key_info: PublicKey,
key_usage: KeyUsage,
overclaim: Overclaim,
) -> Self {
Self {
serial_number,
signature: RpkiSignatureAlgorithm::default(),
issuer,
validity,
subject: {
subject.unwrap_or_else(||
subject_public_key_info.to_subject_name()
)
},
subject_key_identifier: subject_public_key_info.key_identifier(),
subject_public_key_info,
basic_ca: None,
authority_key_identifier: None,
key_usage,
extended_key_usage: None,
crl_uri: None,
ca_issuer: None,
ca_repository: None,
rpki_manifest: None,
signed_object: None,
rpki_notify: None,
overclaim,
v4_resources: IpResources::missing(),
v6_resources: IpResources::missing(),
as_resources: AsResources::missing(),
}
}
pub fn into_cert<S: Signer>(
self,
signer: &S,
key: &S::KeyId,
) -> Result<Cert, SigningError<S::Error>> {
let data = Captured::from_values(Mode::Der, self.encode_ref());
let signature = signer.sign(key, self.signature, &data)?;
Ok(Cert {
signed_data: SignedData::new(data, signature),
tbs: self
})
}
}
impl TbsCert {
pub fn serial_number(&self) -> Serial {
self.serial_number
}
pub fn set_serial_number<S: Into<Serial>>(&mut self, serial: S) {
self.serial_number = serial.into()
}
pub fn issuer(&self) -> &Name {
&self.issuer
}
pub fn set_issuer(&mut self, name: Name) {
self.issuer = name
}
pub fn validity(&self) -> Validity {
self.validity
}
pub fn set_validity(&mut self, validity: Validity) {
self.validity = validity
}
pub fn subject(&self) -> &Name {
&self.subject
}
pub fn set_subject(&mut self, subject: Name) {
self.subject = subject
}
pub fn subject_public_key_info(&self) -> &PublicKey {
&self.subject_public_key_info
}
pub fn set_subject_public_key(&mut self, key: PublicKey) {
self.subject_key_identifier = key.key_identifier();
self.subject_public_key_info = key;
}
pub fn basic_ca(&self) -> Option<bool> {
self.basic_ca
}
pub fn set_basic_ca(&mut self, value: Option<bool>) {
self.basic_ca = value
}
pub fn subject_key_identifier(&self) -> KeyIdentifier {
self.subject_key_identifier
}
pub fn authority_key_identifier(&self) -> Option<KeyIdentifier> {
self.authority_key_identifier
}
pub fn set_authority_key_identifier(
&mut self,
id: Option<KeyIdentifier>
) {
self.authority_key_identifier = id
}
pub fn key_usage(&self) -> KeyUsage {
self.key_usage
}
pub fn set_key_usage(&mut self, key_usage: KeyUsage) {
self.key_usage = key_usage
}
pub fn extended_key_usage(&self) -> Option<&ExtendedKeyUsage> {
self.extended_key_usage.as_ref()
}
pub fn set_extended_key_usage(&mut self, eku: Option<ExtendedKeyUsage>) {
self.extended_key_usage = eku
}
pub fn crl_uri(&self) -> Option<&uri::Rsync> {
self.crl_uri.as_ref()
}
pub fn set_crl_uri(&mut self, uri: Option<uri::Rsync>) {
self.crl_uri = uri
}
pub fn ca_issuer(&self) -> Option<&uri::Rsync> {
self.ca_issuer.as_ref()
}
pub fn set_ca_issuer(&mut self, uri: Option<uri::Rsync>) {
self.ca_issuer= uri
}
pub fn ca_repository(&self) -> Option<&uri::Rsync> {
self.ca_repository.as_ref()
}
pub fn set_ca_repository(&mut self, uri: Option<uri::Rsync>) {
self.ca_repository = uri
}
pub fn rpki_manifest(&self) -> Option<&uri::Rsync> {
self.rpki_manifest.as_ref()
}
pub fn set_rpki_manifest(&mut self, uri: Option<uri::Rsync>) {
self.rpki_manifest = uri
}
pub fn signed_object(&self) -> Option<&uri::Rsync> {
self.signed_object.as_ref()
}
pub fn set_signed_object(&mut self, uri: Option<uri::Rsync>) {
self.signed_object = uri
}
pub fn rpki_notify(&self) -> Option<&uri::Https> {
self.rpki_notify.as_ref()
}
pub fn set_rpki_notify(&mut self, uri: Option<uri::Https>) {
self.rpki_notify = uri
}
pub fn overclaim(&self) -> Overclaim {
self.overclaim
}
pub fn set_overclaim(&mut self, overclaim: Overclaim) {
self.overclaim = overclaim
}
pub fn v4_resources(&self) -> &IpResources {
&self.v4_resources
}
pub fn set_v4_resources(&mut self, resources: IpResources) {
self.v4_resources = resources
}
pub fn set_v4_resources_inherit(&mut self) {
self.set_v4_resources(IpResources::inherit())
}
pub fn build_v4_resource_blocks<F>(&mut self, op: F)
where F: FnOnce(&mut IpBlocksBuilder) {
let mut builder = IpResourcesBuilder::new();
builder.blocks(op);
self.set_v4_resources(builder.finalize())
}
pub fn v4_resources_from_iter<I>(&mut self, iter: I)
where I: IntoIterator<Item=IpBlock> {
self.v4_resources = IpResources::blocks(IpBlocks::from_iter(iter))
}
pub fn v6_resources(&self) -> &IpResources {
&self.v6_resources
}
pub fn set_v6_resources(&mut self, resources: IpResources) {
self.v6_resources = resources
}
pub fn set_v6_resources_inherit(&mut self) {
self.set_v6_resources(IpResources::inherit())
}
pub fn build_v6_resource_blocks<F>(&mut self, op: F)
where F: FnOnce(&mut IpBlocksBuilder) {
let mut builder = IpResourcesBuilder::new();
builder.blocks(op);
self.set_v6_resources(builder.finalize())
}
pub fn v6_resources_from_iter<I>(&mut self, iter: I)
where I: IntoIterator<Item=IpBlock> {
self.v6_resources = IpResources::blocks(IpBlocks::from_iter(iter))
}
pub fn has_ip_resources(&self) -> bool {
self.v4_resources.is_present() || self.v6_resources().is_present()
}
pub fn as_resources(&self) -> &AsResources {
&self.as_resources
}
pub fn set_as_resources(&mut self, resources: AsResources) {
self.as_resources = resources
}
pub fn set_as_resources_inherit(&mut self) {
self.set_as_resources(AsResources::inherit())
}
pub fn build_as_resource_blocks<F>(&mut self, op: F)
where F: FnOnce(&mut AsBlocksBuilder) {
let mut builder = AsResourcesBuilder::new();
builder.blocks(op);
self.set_as_resources(builder.finalize())
}
pub fn as_resources_from_iter<I>(&mut self, iter: I)
where I: IntoIterator<Item = AsBlock> {
self.as_resources = AsResources::blocks(AsBlocks::from_iter(iter))
}
pub fn is_ca(&self) -> bool {
self.basic_ca.unwrap_or(false)
}
pub fn is_self_signed(&self) -> bool {
match self.authority_key_identifier {
Some(aki) => aki == self.subject_key_identifier,
None => true
}
}
}
impl TbsCert {
pub 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 signature = 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;
let mut key_usage = None;
let mut extended_key_usage = None;
let mut crl_uri = None;
let mut ca_issuer = None;
let mut sia = None;
let mut overclaim = None;
let mut ip_resources = None;
let mut ip_overclaim = None;
let mut as_resources = None;
let mut as_overclaim = None;
cons.take_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, |content| {
if id == oid::CE_BASIC_CONSTRAINTS {
Self::take_basic_constraints_critical(
content, critical, &mut basic_ca
)
} else if id == oid::CE_SUBJECT_KEY_IDENTIFIER {
Self::take_subject_key_identifier_critical(
content, critical, &mut subject_key_id
)
} else if id == oid::CE_AUTHORITY_KEY_IDENTIFIER {
Self::take_authority_key_identifier_critical(
content, critical, &mut authority_key_id
)
} else if id == oid::CE_KEY_USAGE {
Self::take_key_usage_critical(
content, critical, &mut key_usage
)
} else if id == oid::CE_EXTENDED_KEY_USAGE {
Self::take_extended_key_usage_critical(
content, critical, &mut extended_key_usage
)
} else if id == oid::CE_CRL_DISTRIBUTION_POINTS {
Self::take_crl_distribution_points(
content, critical, &mut crl_uri
)
} else if id == oid::PE_AUTHORITY_INFO_ACCESS {
Self::take_authority_info_access(
content, critical, &mut ca_issuer
)
} else if id == oid::PE_SUBJECT_INFO_ACCESS {
Self::take_subject_info_access_critical(
content, critical, &mut sia
)
} else if id == oid::CE_CERTIFICATE_POLICIES {
Self::take_certificate_policies(
content, critical, &mut overclaim
)
} else if let Some(m) = Overclaim::from_ip_res(&id) {
ip_overclaim = Some(m);
Self::take_ip_resources(content, &mut ip_resources)
} else if let Some(m) = Overclaim::from_as_res(&id) {
as_overclaim = Some(m);
Self::take_as_resources(content, &mut as_resources)
} else if critical {
Err(content.content_err(
UnexpectedCriticalExtension::new(id)
))
} else {
Ok(())
}
}).map_err(DecodeError::convert)?;
Ok(())
})? { }
Ok(())
}))?;
if ip_resources.is_none() && as_resources.is_none() {
return Err(cons.content_err(
"both AS and IP resources extensions are missing"
))
}
if ip_resources.is_some() && ip_overclaim != overclaim {
return Err(cons.content_err(
"wrong IP resources extension for certificate policy"
))
}
if as_resources.is_some() && as_overclaim != overclaim {
return Err(cons.content_err(
"wrong AS resources extension for certificate policy"
))
}
let (v4_resources, v6_resources) = ip_resources.unwrap_or_default();
let (ca_repository, rpki_manifest, signed_object, rpki_notify) = {
match sia {
Some(sia) => (
sia.ca_repository, sia.rpki_manifest,
sia.signed_object, sia.rpki_notify
),
None => (None, None, None, None)
}
};
Ok(Self {
serial_number,
signature,
issuer,
validity,
subject,
subject_public_key_info,
basic_ca,
subject_key_identifier: subject_key_id.ok_or_else(|| {
cons.content_err(
"missing Subject Key Identifier extension"
)
})?,
authority_key_identifier: authority_key_id,
key_usage: key_usage.ok_or_else(|| {
cons.content_err(
"missing Key Usage extension"
)
})?,
extended_key_usage,
crl_uri,
ca_issuer,
ca_repository,
rpki_manifest,
signed_object,
rpki_notify,
overclaim: overclaim.ok_or_else(|| {
cons.content_err(
"missing Certificate Policies extension"
)
})?,
v4_resources: v4_resources.unwrap_or_else(
IpResources::missing
),
v6_resources: v6_resources.unwrap_or_else(
IpResources::missing
),
as_resources: as_resources.unwrap_or_else(
AsResources::missing
),
})
})
}
pub(crate) 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));
if cons.take_opt_u64()?.is_some() {
Err(cons.content_err(
"pathLenConstraint in Basic Constraints extension"
))
} else {
Ok(())
}
})
}
}
pub(crate) fn take_basic_constraints_critical<S: decode::Source>(
cons: &mut decode::Constructed<S>,
critical: bool,
basic_ca: &mut Option<bool>,
) -> Result<(), DecodeError<S::Error>> {
if !critical {
Err(cons.content_err("non-critical Basic Constraints extension"))
}
else {
Self::take_basic_constraints(cons, basic_ca)
}
}
pub(crate) fn take_subject_key_identifier<S: decode::Source>(
cons: &mut decode::Constructed<S>,
subject_key_id: &mut Option<KeyIdentifier>,
) -> Result<(), DecodeError<S::Error>> {
if subject_key_id.is_some() {
Err(cons.content_err(
"duplicate Subject Key Identifier extension"
))
}
else {
*subject_key_id = Some(KeyIdentifier::take_from(cons)?);
Ok(())
}
}
fn take_subject_key_identifier_critical<S: decode::Source>(
cons: &mut decode::Constructed<S>,
critical: bool,
basic_ca: &mut Option<KeyIdentifier>,
) -> Result<(), DecodeError<S::Error>> {
if critical {
Err(cons.content_err(
"critical Subject Key Identifier extension"
))
}
else {
Self::take_subject_key_identifier(cons, basic_ca)
}
}
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_authority_key_identifier_critical<S: decode::Source>(
cons: &mut decode::Constructed<S>,
critical: bool,
authority_key_id: &mut Option<KeyIdentifier>,
) -> Result<(), DecodeError<S::Error>> {
if critical {
Err(cons.content_err(
"critical Authority Key Identifier extension"
))
}
else {
Self::take_authority_key_identifier(cons, authority_key_id)
}
}
pub(crate) fn take_key_usage<S: decode::Source>(
cons: &mut decode::Constructed<S>,
key_usage: &mut Option<KeyUsage>
) -> Result<(), DecodeError<S::Error>> {
if key_usage.is_some() {
Err(cons.content_err("duplicate Key Usage extension"))
}
else {
*key_usage = Some({
let bits = BitString::take_from(cons)?;
if bits.bit_len() == 7 && bits.octet(0) == 0b0000_0110 {
Ok(KeyUsage::Ca)
}
else if bits.bit_len() == 1 && bits.bit(0) {
Ok(KeyUsage::Ee)
}
else {
Err(cons.content_err("invalid Key Usage"))
}
}?);
Ok(())
}
}
fn take_key_usage_critical<S: decode::Source>(
cons: &mut decode::Constructed<S>,
critical: bool,
key_usage: &mut Option<KeyUsage>
) -> Result<(), DecodeError<S::Error>> {
if !critical {
Err(cons.content_err("non-critical Key Usage extension"))
}
else {
Self::take_key_usage(cons, key_usage)
}
}
pub(crate) fn take_extended_key_usage<S: decode::Source>(
cons: &mut decode::Constructed<S>,
extended_key_usage: &mut Option<ExtendedKeyUsage>
) -> Result<(), DecodeError<S::Error>> {
if extended_key_usage.is_some() {
Err(cons.content_err("duplicate Extended Key Usage extension"))
}
else {
*extended_key_usage = Some(ExtendedKeyUsage::take_from(cons)?);
Ok(())
}
}
fn take_extended_key_usage_critical<S: decode::Source>(
cons: &mut decode::Constructed<S>,
critical: bool,
extended_key_usage: &mut Option<ExtendedKeyUsage>
) -> Result<(), DecodeError<S::Error>> {
if critical {
Err(cons.content_err("critical Extended Key Usage extension"))
}
else {
Self::take_extended_key_usage(cons, extended_key_usage)
}
}
fn take_crl_distribution_points<S: decode::Source>(
cons: &mut decode::Constructed<S>,
critical: bool,
crl_uri: &mut Option<uri::Rsync>
) -> Result<(), DecodeError<S::Error>> {
if crl_uri.is_some() {
Err(cons.content_err(
"duplicate CRL Distribution Points extension"
))
}
else if critical {
Err(cons.content_err(
"critical CRL Distribution Points extension"
))
}
else {
*crl_uri = Some(
cons.take_sequence(|cons| {
cons.take_sequence(|cons| {
cons.take_constructed_if(Tag::CTX_0, |cons| {
cons.take_constructed_if(Tag::CTX_0, |cons| {
take_general_names_content(
cons,
"invalid CRL Distribution Points \
extension",
uri::Rsync::from_bytes,
)
})
})
})
})?
);
Ok(())
}
}
fn take_authority_info_access<S: decode::Source>(
cons: &mut decode::Constructed<S>,
critical: bool,
ca_issuer: &mut Option<uri::Rsync>
) -> Result<(), DecodeError<S::Error>> {
if ca_issuer.is_some() {
Err(cons.content_err(
"duplicate Authority Information Access extension"
))
}
else if critical {
Err(cons.content_err(
"critical Authority Information Access extension"
))
}
else {
*ca_issuer = Some(
cons.take_sequence(|cons| {
cons.take_sequence(|cons| {
oid::AD_CA_ISSUERS.skip_if(cons)?;
take_general_names_content(
cons,
"invalid Authority Information Access extension",
uri::Rsync::from_bytes,
)
})
})?
);
Ok(())
}
}
pub(crate) fn take_subject_info_access<S: decode::Source>(
cons: &mut decode::Constructed<S>,
sia: &mut Option<Sia>,
) -> Result<(), DecodeError<S::Error>> {
if sia.is_some() {
Err(cons.content_err(
"duplicate Subject Key Identifier extension"
))
}
else {
*sia = Some(Sia::take_from(cons)?);
Ok(())
}
}
fn take_subject_info_access_critical<S: decode::Source>(
cons: &mut decode::Constructed<S>,
critical: bool,
sia: &mut Option<Sia>,
) -> Result<(), DecodeError<S::Error>> {
if critical {
Err(cons.content_err(
"critical Subject Key Identifier extension"
))
}
else {
Self::take_subject_info_access(cons, sia)
}
}
fn take_certificate_policies<S: decode::Source>(
cons: &mut decode::Constructed<S>,
critical: bool,
overclaim: &mut Option<Overclaim>,
) -> Result<(), DecodeError<S::Error>> {
if overclaim.is_some() {
Err(cons.content_err("duplicate Certificate Policies extension"))
}
else if !critical {
Err(cons.content_err(
"non-critical Certificate Policies extension"
))
}
else {
*overclaim = Some(
cons.take_sequence(|cons| {
cons.take_sequence(|cons| {
let res = Overclaim::from_policy(
&Oid::take_from(cons)?
).map_err(|err| cons.content_err(err))?;
cons.skip_all()?;
Ok(res)
})
})?
);
Ok(())
}
}
fn take_ip_resources<S: decode::Source>(
cons: &mut decode::Constructed<S>,
ip_resources: &mut Option<(Option<IpResources>, Option<IpResources>)>
) -> Result<(), DecodeError<S::Error>> {
if ip_resources.is_some() {
Err(cons.content_err("duplicate IP Resources extension"))
}
else {
*ip_resources = Some(IpResources::take_families_from(cons)?);
Ok(())
}
}
fn take_as_resources<S: decode::Source>(
cons: &mut decode::Constructed<S>,
as_resources: &mut Option<AsResources>
) -> Result<(), DecodeError<S::Error>> {
if as_resources.is_some() {
Err(cons.content_err("duplicate AS Resources extension"))
}
else {
*as_resources = Some(AsResources::take_from(cons)?);
Ok(())
}
}
pub fn encode_ref(&self) -> impl encode::Values + '_ {
encode::sequence((
encode::sequence_as(Tag::CTX_0, 2.encode()), self.serial_number.encode(),
self.signature.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_identifier.encode_ref(),
),
self.authority_key_identifier.as_ref().map(|id| {
encode_extension(
&oid::CE_AUTHORITY_KEY_IDENTIFIER, false,
encode::sequence(id.encode_ref_as(Tag::CTX_0))
)
}),
encode_extension(
&oid::CE_KEY_USAGE, true,
self.key_usage.encode()
),
self.extended_key_usage.as_ref().map(|eku| {
encode_extension(
&oid::CE_EXTENDED_KEY_USAGE, false,
encode::sequence(eku.encode_ref())
)
}),
self.crl_uri.as_ref().map(|uri| {
encode_extension(
&oid::CE_CRL_DISTRIBUTION_POINTS, false,
encode::sequence( encode::sequence( encode::sequence_as(Tag::CTX_0, encode::sequence_as(Tag::CTX_0, uri.encode_general_name()
)
)
)
)
)
}),
self.ca_issuer.as_ref().map(|uri| {
encode_extension(
&oid::PE_AUTHORITY_INFO_ACCESS, false,
encode::sequence(
encode::sequence((
oid::AD_CA_ISSUERS.encode(),
uri.encode_general_name()
))
)
)
}),
if self.ca_repository.is_some()
|| self.rpki_manifest.is_some()
|| self.signed_object.is_some()
|| self.rpki_notify.is_some()
{
Some(encode_extension(
&oid::PE_SUBJECT_INFO_ACCESS, false,
encode::sequence((
self.ca_repository.as_ref().map(|uri| {
encode::sequence((
oid::AD_CA_REPOSITORY.encode(),
uri.encode_general_name()
))
}),
self.rpki_manifest.as_ref().map(|uri| {
encode::sequence((
oid::AD_RPKI_MANIFEST.encode(),
uri.encode_general_name()
))
}),
self.signed_object.as_ref().map(|uri| {
encode::sequence((
oid::AD_SIGNED_OBJECT.encode(),
uri.encode_general_name()
))
}),
self.rpki_notify.as_ref().map(|uri| {
encode::sequence((
oid::AD_RPKI_NOTIFY.encode(),
uri.encode_general_name()
))
})
))
))
}
else {
None
},
encode_extension(
&oid::CE_CERTIFICATE_POLICIES, true,
encode::sequence(
encode::sequence(
self.overclaim.policy_id().encode()
)
)
),
IpResources::encode_extension(
self.overclaim(),
self.v4_resources(),
self.v6_resources()
),
self.as_resources.encode_extension(self.overclaim)
)))
))
}
}
fn take_general_names_content<S: decode::Source, F, T, E>(
cons: &mut decode::Constructed<S>,
error_msg: &'static str,
mut op: F
) -> Result<T, DecodeError<S::Error>>
where F: FnMut(Bytes) -> Result<T, E> {
let mut res = None;
while let Some(()) = cons.take_opt_value_if(Tag::CTX_6, |content| {
let uri = Ia5String::from_content(content)?;
if let Ok(uri) = op(uri.into_bytes()) {
if res.is_some() {
return Err(content.content_err(error_msg))
}
res = Some(uri)
}
Ok(())
})? {}
match res {
Some(res) => Ok(res),
None => Err(cons.content_err(error_msg))
}
}
fn take_general_name<S: decode::Source, F, T, E>(
cons: &mut decode::Constructed<S>,
mut op: F
) -> Result<Option<T>, DecodeError<S::Error>>
where F: FnMut(Bytes) -> Result<T, E> {
cons.take_value_if(Tag::CTX_6, |content| {
Ia5String::from_content(content).map(|uri| {
op(uri.into_bytes()).ok()
})
})
}
#[derive(Clone, Debug, Default)]
pub(crate) struct Sia {
ca_repository: Option<uri::Rsync>,
rpki_manifest: Option<uri::Rsync>,
signed_object: Option<uri::Rsync>,
rpki_notify: Option<uri::Https>,
}
#[cfg(feature = "ca")]
impl Sia {
pub(crate) fn ca_repository(&self) -> Option<&uri::Rsync> {
self.ca_repository.as_ref()
}
pub(crate) fn rpki_manifest(&self) -> Option<&uri::Rsync> {
self.rpki_manifest.as_ref()
}
pub(crate) fn rpki_notify(&self) -> Option<&uri::Https> {
self.rpki_notify.as_ref()
}
}
impl Sia {
pub fn take_from<S: decode::Source>(
cons: &mut decode::Constructed<S>
) -> Result<Self, DecodeError<S::Error>> {
let mut sia = Sia::default();
let mut any_seen = false;
cons.take_sequence(|cons| {
while let Some(()) = cons.take_opt_sequence(|cons| {
let oid = Oid::take_from(cons)?;
any_seen = true;
if oid == oid::AD_CA_REPOSITORY {
update_first(&mut sia.ca_repository, || {
take_general_name(
cons, uri::Rsync::from_bytes
)
})
}
else if oid == oid::AD_RPKI_MANIFEST {
update_first(&mut sia.rpki_manifest, || {
take_general_name(
cons, uri::Rsync::from_bytes
)
})
}
else if oid == oid::AD_SIGNED_OBJECT {
update_first(&mut sia.signed_object, || {
take_general_name(
cons, uri::Rsync::from_bytes
)
})
}
else if oid == oid::AD_RPKI_NOTIFY {
update_first(&mut sia.rpki_notify, || {
take_general_name(
cons, uri::Https::from_bytes
)
})
}
else {
cons.skip_all()
}
})? { }
Ok(())
})?;
if any_seen {
Ok(sia)
}
else {
Err(cons.content_err(
"empty Subject Information Access extension"
))
}
}
}
#[derive(Clone, Debug)]
pub struct ResourceCert {
cert: Cert,
v4_resources: IpBlocks,
v6_resources: IpBlocks,
as_resources: AsBlocks,
tal: Arc<TalInfo>,
}
impl ResourceCert {
pub fn as_cert(&self) -> &Cert {
&self.cert
}
pub fn v4_resources(&self) -> &IpBlocks {
&self.v4_resources
}
pub fn v6_resources(&self) -> &IpBlocks {
&self.v6_resources
}
pub fn as_resources(&self) -> &AsBlocks {
&self.as_resources
}
pub fn tal(&self) -> &Arc<TalInfo> {
&self.tal
}
pub fn into_tal(self) -> Arc<TalInfo> {
self.tal
}
}
impl ops::Deref for ResourceCert {
type Target = Cert;
fn deref(&self) -> &Cert {
self.as_cert()
}
}
impl AsRef<Cert> for ResourceCert {
fn as_ref(&self) -> &Cert {
self.as_cert()
}
}
impl AsRef<TbsCert> for ResourceCert {
fn as_ref(&self) -> &TbsCert {
self.as_cert().as_ref()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum KeyUsage {
Ca,
Ee,
}
impl KeyUsage {
pub fn encode(self) -> impl encode::Values {
let s = match self {
KeyUsage::Ca => b"\x01\x06", KeyUsage::Ee => b"\x07\x80", };
s.encode_as(Tag::BIT_STRING)
}
}
#[derive(Clone, Debug)]
pub struct ExtendedKeyUsage {
content: Captured,
has_bgpsec_router: bool,
}
impl ExtendedKeyUsage {
fn take_from<S: decode::Source>(
cons: &mut decode::Constructed<S>
) -> Result<Self, DecodeError<S::Error>> {
let mut has_bgpsec_router = false;
let content = cons.take_sequence(|cons| cons.capture(|cons| {
let mut empty = true;
while let Some(oid) = Oid::take_opt_from(cons)? {
if oid == oid::KP_BGPSEC_ROUTER {
has_bgpsec_router = true;
}
empty = false;
}
if empty {
Err(cons.content_err(
"empty Extended key Usage extension"
))
}
else {
Ok(())
}
}))?;
Ok(ExtendedKeyUsage { content, has_bgpsec_router })
}
fn encode_ref(&self) -> impl encode::Values + '_ {
&self.content
}
pub fn inspect_router(&self) -> Result<(), InspectionError> {
if self.has_bgpsec_router {
Ok(())
}
else {
Err(InspectionError::new(
"Extended Key Usage extension is missing \
id-kp-bgpsec-router usage in router certificate"
))
}
}
pub fn create_router() -> Self {
ExtendedKeyUsage {
content: oid::KP_BGPSEC_ROUTER.encode().to_captured(Mode::Der),
has_bgpsec_router: true
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Overclaim {
Refuse,
Trim,
}
impl Overclaim {
fn from_policy(
oid: &Oid
) -> Result<Self, ContentError> {
if oid == &oid::CP_IPADDR_ASNUMBER {
Ok(Overclaim::Refuse)
}
else if oid == &oid::CP_IPADDR_ASNUMBER_V2 {
Ok(Overclaim::Trim)
}
else {
Err("invalid Certificate Policy identifier".into())
}
}
fn from_ip_res(oid: &Oid) -> Option<Self> {
if oid == &oid::PE_IP_ADDR_BLOCK {
Some(Overclaim::Refuse)
}
else if oid == &oid::PE_IP_ADDR_BLOCK_V2 {
Some(Overclaim::Trim)
}
else {
None
}
}
fn from_as_res(oid: &Oid) -> Option<Self> {
if oid == &oid::PE_AUTONOMOUS_SYS_IDS {
Some(Overclaim::Refuse)
}
else if oid == &oid::PE_AUTONOMOUS_SYS_IDS_V2 {
Some(Overclaim::Trim)
}
else {
None
}
}
pub fn policy_id(self) -> &'static ConstOid {
match self {
Overclaim::Refuse => &oid::CP_IPADDR_ASNUMBER,
Overclaim::Trim => &oid::CP_IPADDR_ASNUMBER_V2
}
}
pub fn ip_res_id(self) -> &'static ConstOid {
match self {
Overclaim::Refuse => &oid::PE_IP_ADDR_BLOCK,
Overclaim::Trim => &oid::PE_IP_ADDR_BLOCK_V2
}
}
pub fn as_res_id(self) -> &'static ConstOid {
match self {
Overclaim::Refuse => &oid::PE_AUTONOMOUS_SYS_IDS,
Overclaim::Trim => &oid::PE_AUTONOMOUS_SYS_IDS_V2
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct InvalidExtension {
oid: Oid<Bytes>,
}
impl InvalidExtension {
#[cfg(feature = "ca")]
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)
}
}
#[derive(Clone, Debug)]
struct UnexpectedCriticalExtension {
oid: Oid<Bytes>,
}
impl UnexpectedCriticalExtension {
fn new(oid: Oid<Bytes>) -> Self {
UnexpectedCriticalExtension { oid }
}
}
impl From<UnexpectedCriticalExtension> for ContentError {
fn from(err: UnexpectedCriticalExtension) -> Self {
ContentError::from_boxed(Box::new(err))
}
}
impl fmt::Display for UnexpectedCriticalExtension {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "unexpected critical extension {}", self.oid)
}
}
#[derive(Debug)]
struct IssuerError(InspectionError);
impl From<IssuerError> for InspectionError {
fn from(err: IssuerError) -> Self {
ContentError::from_boxed(Box::new(err)).into()
}
}
impl fmt::Display for IssuerError{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "invalid subject: {}", self.0)
}
}
#[derive(Debug)]
struct SubjectError(InspectionError);
impl From<SubjectError> for InspectionError {
fn from(err: SubjectError) -> Self {
ContentError::from_boxed(Box::new(err)).into()
}
}
impl fmt::Display for SubjectError{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "invalid subject: {}", self.0)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn decode_and_inspect_certs() {
Cert::decode(
include_bytes!("../../test-data/repository/ta.cer").as_ref()
).unwrap().inspect_ta(true).unwrap();
Cert::decode(
include_bytes!("../../test-data/repository/ca1.cer").as_ref()
).unwrap().inspect_ca(true).unwrap();
Cert::decode(
include_bytes!("../../test-data/repository/router.cer").as_ref()
).unwrap().inspect_router(true).unwrap();
}
#[test]
fn signature_algorithm_mismatch() {
let roa = crate::repository::roa::Roa::decode(
include_bytes!(
"../../test-data/repository/example-ripe.roa"
).as_ref(),
false
).unwrap();
assert!(roa.cert().inspect_ee(true).is_ok());
let mft = crate::repository::manifest::Manifest::decode(
include_bytes!(
"../../test-data/repository/signature-alg-mismatch.mft"
).as_ref(),
false
).unwrap();
assert!(mft.cert().inspect_ee(true).is_err());
}
#[test]
#[cfg(feature = "serde")]
fn serde_cert() {
let der = include_bytes!("../../test-data/repository/ta.cer");
let cert = Cert::decode(Bytes::from_static(der)).unwrap();
let serialize = serde_json::to_string(&cert).unwrap();
let des_cert: Cert = serde_json::from_str(&serialize).unwrap();
assert_eq!(
cert.to_captured().into_bytes(),
des_cert.to_captured().into_bytes()
);
}
#[test]
#[cfg(feature = "serde")]
fn compat_de_cert() {
serde_json::from_slice::<Cert>(include_bytes!(
"../../test-data/repository/serde-compat/cert.json"
)).unwrap();
}
}
#[cfg(all(test, feature="softkeys"))]
mod signer_test {
use std::str::FromStr;
use crate::crypto::PublicKeyFormat;
use crate::crypto::softsigner::OpenSslSigner;
use crate::repository::resources::{Asn, Prefix};
use super::*;
#[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 uri = uri::Rsync::from_str("rsync://example.com/m/p").unwrap();
let mut cert = TbsCert::new(
12u64.into(), pubkey.to_subject_name(),
Validity::from_secs(86400), None, pubkey, KeyUsage::Ca,
Overclaim::Trim
);
cert.set_basic_ca(Some(true));
cert.set_ca_repository(Some(uri.clone()));
cert.set_rpki_manifest(Some(uri));
cert.build_v4_resource_blocks(|b| b.push(Prefix::new(0, 0)));
cert.build_v6_resource_blocks(|b| b.push(Prefix::new(0, 0)));
cert.build_as_resource_blocks(|b| b.push((Asn::MIN, Asn::MAX)));
let cert = cert.into_cert(&signer, &key).unwrap().to_captured();
let cert = Cert::decode(cert.as_slice()).unwrap();
let talinfo = TalInfo::from_name("foo".into()).into_arc();
cert.validate_ta(talinfo, true).unwrap();
}
}