Clean up CA event json to ensure backward compatibility in future releases (#53)

This commit is contained in:
Tim Bruijnzeels
2019-11-22 12:55:17 +08:00
parent 2098f0116d
commit 532f83817d
13 changed files with 172 additions and 130 deletions
+2
View File
@@ -314,6 +314,7 @@ impl RepositoryUpdate {
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[allow(clippy::large_enum_variant)]
#[serde(tag = "t", content = "c")]
pub enum RepositoryContact {
Embedded(RepoInfo),
Rfc8181(rfc8183::RepositoryResponse),
@@ -432,6 +433,7 @@ impl Eq for TaCertDetails {}
/// for resource provisioning requests (RFC6492).
#[derive(Clone, Debug, Deserialize, Display, Eq, PartialEq, Serialize)]
#[allow(clippy::large_enum_variant)]
#[serde(tag = "t", content = "c")]
pub enum ParentCaContact {
#[display(fmt = "This CA is a TA")]
Ta(TaCertDetails),
+4 -4
View File
@@ -216,10 +216,10 @@ impl From<&IssuedCert> for ReplacedObject {
}
}
impl From<&Roa> for ReplacedObject {
fn from(r: &Roa) -> Self {
let revocation = Revocation::from(r);
let hash = HexEncodedHash::from_content(r.to_captured().as_slice());
impl From<&CurrentObject> for ReplacedObject {
fn from(current: &CurrentObject) -> Self {
let revocation = Revocation::from(current);
let hash = current.to_hex_hash();
ReplacedObject { revocation, hash }
}
}
+4
View File
@@ -59,6 +59,10 @@ impl Base64 {
pub fn size(&self) -> usize {
self.0.len()
}
pub fn as_bytes(&self) -> &[u8] {
self.0.as_ref()
}
}
impl AsRef<str> for Base64 {
+4
View File
@@ -368,6 +368,10 @@ impl IssuanceResponse {
/// See: https://tools.ietf.org/html/rfc6492#section-3.4.1
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct RequestResourceLimit {
#[serde(
deserialize_with = "ext_serde::de_as_blocks_opt",
serialize_with = "ext_serde::ser_as_blocks_opt"
)]
asn: Option<AsBlocks>,
#[serde(
+22 -3
View File
@@ -11,6 +11,7 @@ use std::{fmt, io};
use base64::DecodeError;
use bcder::decode;
use bytes::Bytes;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use rpki::uri;
use rpki::x509;
@@ -628,7 +629,7 @@ impl RepositoryResponse {
//------------ ServiceUri ----------------------------------------------------
/// The service URI where a child or publisher needs to send its
#[derive(Clone, Debug, Deserialize, Eq, Serialize, PartialEq)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ServiceUri {
Https(uri::Https),
Http(String),
@@ -638,8 +639,7 @@ impl TryFrom<String> for ServiceUri {
type Error = Error;
fn try_from(value: String) -> Result<Self, Self::Error> {
if value.starts_with("http://") {
// TODO: Check a bit better? It will blow up when the uri is used..
if value.to_lowercase().starts_with("http://") {
Ok(ServiceUri::Http(value))
} else {
Ok(ServiceUri::Https(uri::Https::from_str(&value)?))
@@ -656,6 +656,25 @@ impl fmt::Display for ServiceUri {
}
}
impl Serialize for ServiceUri {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.to_string().serialize(serializer)
}
}
impl<'de> Deserialize<'de> for ServiceUri {
fn deserialize<D>(deserializer: D) -> Result<ServiceUri, D::Error>
where
D: Deserializer<'de>,
{
let string = String::deserialize(deserializer)?;
ServiceUri::try_from(string).map_err(de::Error::custom)
}
}
//------------ Error ---------------------------------------------------------
#[derive(Debug, Display)]
+26 -1
View File
@@ -2,7 +2,7 @@
use base64;
use bytes::Bytes;
use log::LevelFilter;
use rpki::resources::IpBlocks;
use rpki::resources::{AsBlocks, IpBlocks};
use serde::de;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::str::FromStr;
@@ -26,6 +26,31 @@ where
base64::encode(b).serialize(s)
}
//------------ AsBlocks ------------------------------------------------------
pub fn ser_as_blocks_opt<S>(blocks: &Option<AsBlocks>, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match blocks {
None => "none".serialize(s),
Some(blocks) => blocks.to_string().serialize(s),
}
}
pub fn de_as_blocks_opt<'de, D>(d: D) -> Result<Option<AsBlocks>, D::Error>
where
D: Deserializer<'de>,
{
let string = String::deserialize(d)?;
if string.as_str() == "none" {
return Ok(None);
}
let blocks = AsBlocks::from_str(string.as_str()).map_err(de::Error::custom)?;
Ok(Some(blocks))
}
//------------ IpBlocks ------------------------------------------------------
pub fn de_ip_blocks_4<'de, D>(d: D) -> Result<IpBlocks, D::Error>
+2 -2
View File
@@ -188,11 +188,11 @@ impl<S: Signer> Aggregate for CertAuth<S> {
self.children.get_mut(&child).unwrap().set_id_cert(cert)
}
EvtDet::ChildUpdatedResources(child, resources, grace) => self
EvtDet::ChildUpdatedResources(child, resources) => self
.children
.get_mut(&child)
.unwrap()
.set_resources(resources, grace),
.set_resources(resources),
EvtDet::ChildRemoved(child) => {
self.children.remove(&child);
+1 -7
View File
@@ -29,7 +29,6 @@ impl LastResponse {}
pub struct ChildDetails {
id_cert: Option<IdCert>,
resources: ResourceSet,
shrink_pending: Option<Time>,
used_keys: HashMap<KeyIdentifier, LastResponse>,
}
@@ -38,7 +37,6 @@ impl ChildDetails {
ChildDetails {
id_cert,
resources,
shrink_pending: None,
used_keys: HashMap::new(),
}
}
@@ -55,11 +53,7 @@ impl ChildDetails {
&self.resources
}
pub fn set_resources(&mut self, resources: ResourceSet, grace: Time) {
if !resources.contains(&self.resources) {
self.shrink_pending = Some(grace);
}
pub fn set_resources(&mut self, resources: ResourceSet) {
self.resources = resources;
}
+14 -15
View File
@@ -9,10 +9,10 @@ use rpki::uri;
use rpki::x509::{Serial, Time, Validity};
use crate::commons::api::{
AddedObject, ChildHandle, CurrentObject, Handle, IssuanceRequest, IssuedCert, ObjectName,
ObjectsDelta, ParentCaContact, ParentHandle, RcvdCert, RepoInfo, RepositoryContact,
ResourceClassName, ResourceSet, Revocation, RevocationRequest, RevokedObject, TaCertDetails,
TrustAnchorLocator, UpdatedObject, WithdrawnObject,
AddedObject, ChildHandle, Handle, IssuanceRequest, IssuedCert, ObjectName, ObjectsDelta,
ParentCaContact, ParentHandle, RcvdCert, RepoInfo, RepositoryContact, ResourceClassName,
ResourceSet, Revocation, RevocationRequest, RevokedObject, TaCertDetails, TrustAnchorLocator,
UpdatedObject, WithdrawnObject,
};
use crate::commons::eventsourcing::StoredEvent;
use crate::commons::remote::id::IdCert;
@@ -190,10 +190,10 @@ impl RoaUpdates {
pub fn added(&self) -> Vec<AddedObject> {
let mut res = vec![];
for (auth, info) in self.updated.iter() {
for (_auth, info) in self.updated.iter() {
if info.replaces().is_none() {
let object = CurrentObject::from(info.roa());
let name = ObjectName::from(auth);
let object = info.object().clone();
let name = info.name().clone();
res.push(AddedObject::new(name, object));
}
}
@@ -202,10 +202,10 @@ impl RoaUpdates {
pub fn updated(&self) -> Vec<UpdatedObject> {
let mut res = vec![];
for (auth, info) in self.updated.iter() {
for (_auth, info) in self.updated.iter() {
if let Some(replaced) = info.replaces() {
let object = CurrentObject::from(info.roa());
let name = ObjectName::from(auth);
let object = info.object().clone();
let name = info.name().clone();
res.push(UpdatedObject::new(name, object, replaced.hash().clone()));
}
}
@@ -286,6 +286,7 @@ pub type Evt = StoredEvent<EvtDet>;
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[allow(clippy::large_enum_variant)]
#[serde(tag = "t", content = "c")]
pub enum EvtDet {
// Being a parent Events
ChildAdded(ChildHandle, ChildDetails),
@@ -293,7 +294,7 @@ pub enum EvtDet {
ChildKeyRevoked(ChildHandle, ResourceClassName, KeyIdentifier),
ChildCertificatesUpdated(ResourceClassName, ChildCertificateUpdates),
ChildUpdatedIdCert(ChildHandle, IdCert),
ChildUpdatedResources(ChildHandle, ResourceSet, Time),
ChildUpdatedResources(ChildHandle, ResourceSet),
ChildRemoved(ChildHandle),
// Being a child Events
@@ -427,12 +428,10 @@ impl EvtDet {
child: ChildHandle,
resources: ResourceSet,
) -> Evt {
let time = Time::now();
StoredEvent::new(
handle,
version,
EvtDet::ChildUpdatedResources(child, resources, time),
EvtDet::ChildUpdatedResources(child, resources),
)
}
@@ -545,7 +544,7 @@ impl fmt::Display for EvtDet {
child,
id_crt.ski_hex()
),
EvtDet::ChildUpdatedResources(child, resources, _) => {
EvtDet::ChildUpdatedResources(child, resources) => {
write!(f, "updated child '{}' resources to '{}'", child, resources)
}
EvtDet::ChildRemoved(child) => {
+47 -56
View File
@@ -25,25 +25,42 @@ pub enum AddedOrUpdated {
//------------ ManifestInfo ------------------------------------------------
#[derive(Clone, Debug, Deserialize, Serialize)]
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct ManifestInfo {
name: ObjectName,
manifest: Manifest,
current: CurrentObject,
next_update: Time,
old: Option<HexEncodedHash>,
}
impl ManifestInfo {
pub fn new(mft: &Manifest, old: Option<HexEncodedHash>) -> Self {
let name = ObjectName::from(mft);
let current = CurrentObject::from(mft);
let next_update = mft.next_update();
ManifestInfo {
name,
current,
next_update,
old,
}
}
pub fn name(&self) -> &ObjectName {
&self.name
}
pub fn manifest(&self) -> &Manifest {
&self.manifest
pub fn current(&self) -> &CurrentObject {
&self.current
}
pub fn next_update(&self) -> Time {
self.next_update
}
pub fn added_or_updated(&self) -> AddedOrUpdated {
let name = self.name.clone();
let object = CurrentObject::from(&self.manifest);
let object = self.current.clone();
match self.old.clone() {
None => AddedOrUpdated::Added(AddedObject::new(name, object)),
Some(old) => AddedOrUpdated::Updated(UpdatedObject::new(name, object, old)),
@@ -52,41 +69,38 @@ impl ManifestInfo {
pub fn withdraw(&self) -> WithdrawnObject {
let name = self.name.clone();
let hash = HexEncodedHash::from(&self.manifest);
let hash = self.current.to_hex_hash();
WithdrawnObject::new(name, hash)
}
}
impl PartialEq for ManifestInfo {
fn eq(&self, other: &Self) -> bool {
self.manifest.to_captured().as_slice() == other.manifest.to_captured().as_slice()
&& self.old == other.old
}
}
impl Eq for ManifestInfo {}
//------------ CrlInfo -----------------------------------------------------
#[derive(Clone, Debug, Deserialize, Serialize)]
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct CrlInfo {
name: ObjectName, // can be derived from CRL, but keeping in mem saves cpu
crl: Crl,
current: CurrentObject,
old: Option<HexEncodedHash>,
}
impl CrlInfo {
pub fn new(crl: &Crl, old: Option<HexEncodedHash>) -> Self {
let name = ObjectName::from(crl);
let current = CurrentObject::from(crl);
CrlInfo { name, current, old }
}
pub fn name(&self) -> &ObjectName {
&self.name
}
pub fn crl(&self) -> &Crl {
&self.crl
pub fn current(&self) -> &CurrentObject {
&self.current
}
pub fn added_or_updated(&self) -> AddedOrUpdated {
let name = self.name.clone();
let object = CurrentObject::from(&self.crl);
let object = self.current.clone();
match self.old.clone() {
None => AddedOrUpdated::Added(AddedObject::new(name, object)),
Some(old) => AddedOrUpdated::Updated(UpdatedObject::new(name, object, old)),
@@ -95,20 +109,11 @@ impl CrlInfo {
pub fn withdraw(&self) -> WithdrawnObject {
let name = self.name.clone();
let hash = HexEncodedHash::from(&self.crl);
let hash = self.current.to_hex_hash();
WithdrawnObject::new(name, hash)
}
}
impl PartialEq for CrlInfo {
fn eq(&self, other: &Self) -> bool {
self.crl.to_captured().as_slice() == other.crl.to_captured().as_slice()
&& self.old == other.old
}
}
impl Eq for CrlInfo {}
//------------ PublicationDelta ----------------------------------------------
/// This type describes a set up of objects published for a CA key.
@@ -191,20 +196,12 @@ impl CurrentObjectSet {
&self.manifest_info
}
pub fn manifest(&self) -> &Manifest {
&self.manifest_info.manifest
}
pub fn crl_info(&self) -> &CrlInfo {
&self.crl_info
}
pub fn crl(&self) -> &Crl {
&self.crl_info.crl
}
pub fn next_update(&self) -> Time {
self.manifest().next_update()
self.manifest_info().next_update()
}
pub fn apply_delta(&mut self, delta: CurrentObjectSetDelta) {
@@ -231,7 +228,6 @@ impl CrlBuilder {
let signing_key = signing_cert.cert().subject_public_key_info();
let aki = KeyIdentifier::from_public_key(signing_key);
let name = ObjectName::new(&aki, "crl");
let mut revocations_delta = RevocationsDelta::default();
for revocation in new_revocations.into_iter() {
@@ -243,14 +239,14 @@ impl CrlBuilder {
revocations_delta.drop(expired);
}
let now = Time::five_minutes_ago();
let just_now = Time::five_minutes_ago();
let tomorrow = Time::tomorrow();
let serial_number = Serial::from(number);
let mut crl = TbsCertList::new(
Default::default(),
signing_key.to_subject_name(),
now,
just_now,
tomorrow,
revocations.to_crl_entries(),
aki,
@@ -260,7 +256,9 @@ impl CrlBuilder {
let crl = crl.into_crl(signer, &aki).map_err(ca::Error::signer)?;
Ok((CrlInfo { name, crl, old }, revocations_delta))
let crl_info = CrlInfo::new(&crl, old);
Ok((crl_info, revocations_delta))
}
}
@@ -274,7 +272,7 @@ impl ManifestBuilder {
entries.insert(
crl_info.name.clone().into(),
Self::mft_hash(crl_info.crl.to_captured().as_slice()),
Self::mft_hash(crl_info.current().content().as_bytes()),
);
ManifestBuilder { entries }
@@ -291,7 +289,7 @@ impl ManifestBuilder {
// Add the *new* CRL
entries.insert(
crl_info.name.clone().into(),
Self::mft_hash(crl_info.crl.to_captured().as_slice()),
Self::mft_hash(crl_info.current().content().as_bytes()),
);
// Add all *current* issued certs
@@ -304,10 +302,9 @@ impl ManifestBuilder {
}
// Add all *current* ROAs
for (auth, roa_info) in roas {
let roa = roa_info.roa();
let name = ObjectName::from(auth);
let hash = Self::mft_hash(roa.to_captured().as_slice());
for (_auth, roa_info) in roas {
let name = roa_info.name().clone();
let hash = Self::mft_hash(roa_info.object().content().as_bytes());
entries.insert(name.into(), hash);
}
@@ -381,13 +378,7 @@ impl ManifestBuilder {
.map_err(ca::Error::signer)?
};
let name = ObjectName::from(&manifest);
Ok(ManifestInfo {
name,
manifest,
old,
})
Ok(ManifestInfo::new(&manifest, old))
}
fn mft_hash(bytes: &[u8]) -> Bytes {
+22 -22
View File
@@ -148,9 +148,8 @@ impl ResourceClass {
pub fn current_objects(&self) -> CurrentObjects {
let mut current_objects = CurrentObjects::default();
for (auth, roa_info) in self.roas.iter() {
let roa = roa_info.roa();
current_objects.insert(ObjectName::from(auth), CurrentObject::from(roa));
for roa_info in self.roas.current() {
current_objects.insert(roa_info.name().clone(), roa_info.object().clone());
}
for issued in self.certificates.current() {
@@ -159,11 +158,11 @@ impl ResourceClass {
}
fn add_mft_and_crl(objects: &mut CurrentObjects, key: &CertifiedKey) {
let mft = key.current_set().manifest();
objects.insert(ObjectName::from(mft), CurrentObject::from(mft));
let mft = key.current_set().manifest_info();
objects.insert(mft.name().clone(), mft.current().clone());
let crl = key.current_set().crl();
objects.insert(ObjectName::from(crl), CurrentObject::from(crl));
let crl = key.current_set().crl_info();
objects.insert(crl.name().clone(), crl.current().clone());
}
match &self.key_state {
@@ -540,12 +539,12 @@ impl ResourceClass {
let current_revocations = current_set.revocations().clone();
let number = current_set.number() + 1;
let current_mft = current_set.manifest();
let current_mft_hash = HexEncodedHash::from(current_mft);
let current_crl = current_set.crl();
let current_crl_hash = HexEncodedHash::from(current_crl);
let current_mft = current_set.manifest_info();
let current_mft_hash = current_mft.current().to_hex_hash();
let current_crl = current_set.crl_info();
let current_crl_hash = current_crl.current().to_hex_hash();
new_revocations.push(Revocation::from(current_mft));
new_revocations.push(Revocation::from(current_mft.current()));
// Create a new CRL
let (crl_info, revocations_delta) = CrlBuilder::build(
@@ -598,9 +597,9 @@ impl ResourceClass {
let ns = self.name_space();
// ROAs
for (authorization, info) in self.roas.iter() {
let base64 = Base64::from_content(info.roa().to_captured().as_slice());
let object_name = ObjectName::from(authorization);
for info in self.roas.current() {
let base64 = info.object().content().clone();
let object_name = info.name().clone();
let uri = base_repo.resolve(ns, object_name.as_str());
res.push(PublishElement::new(base64, uri));
}
@@ -622,12 +621,12 @@ impl ResourceClass {
for set in sets {
let crl_info = set.crl_info();
let crl_base64 = Base64::from_content(crl_info.crl().to_captured().as_slice());
let crl_base64 = crl_info.current().content().clone();
let crl_uri = base_repo.resolve(ns, crl_info.name());
res.push(PublishElement::new(crl_base64, crl_uri));
let mft_info = set.manifest_info();
let mft_base64 = Base64::from_content(mft_info.manifest().to_captured().as_slice());
let mft_base64 = mft_info.current().content().clone();
let mft_uri = base_repo.resolve(ns, mft_info.name());
res.push(PublishElement::new(mft_base64, mft_uri));
}
@@ -984,7 +983,7 @@ impl ResourceClass {
// Remove any ROAs no longer in auths, or no longer in resources.
for (current_auth, roa_info) in self.roas.iter() {
if !auths.contains(current_auth) || !resources.contains(&current_auth.prefix().into()) {
updates.remove(*current_auth, RevokedObject::from(roa_info.roa()));
updates.remove(*current_auth, RevokedObject::from(roa_info.object()));
}
}
@@ -998,18 +997,19 @@ impl ResourceClass {
None => {
// NO ROA yet, so create one.
let roa = Roas::make_roa(auth, key, new_repo.as_ref(), signer)?;
updates.update(*auth, RoaInfo::new_roa(roa));
let name = ObjectName::from(auth);
updates.update(*auth, RoaInfo::new_roa(&roa, name));
}
Some(roa) => {
// Re-issue if the ROA is getting close to its expiration time, or if we are
// activating the new key.
let expiring =
roa.roa().cert().validity().not_after() < Time::now() + Duration::weeks(4);
let expiring = roa.object().expires() < Time::now() + Duration::weeks(4);
let activating = mode == &PublishMode::KeyRollActivation;
if expiring || activating || new_repo.is_some() {
let new_roa = Roas::make_roa(auth, key, new_repo.as_ref(), signer)?;
updates.update(*auth, RoaInfo::updated_roa(roa, new_roa));
let name = ObjectName::from(auth);
updates.update(*auth, RoaInfo::updated_roa(roa, &new_roa, name));
}
}
}
+23 -19
View File
@@ -13,6 +13,7 @@ use rpki::x509::{Serial, Time};
use crate::commons::api::{ObjectName, ReplacedObject, RoaDefinition, RoaDefinitionUpdates};
use crate::daemon::ca::events::RoaUpdates;
use crate::daemon::ca::{self, CertifiedKey, SignSupport, Signer};
use commons::api::CurrentObject;
//------------ RouteAuthorization ------------------------------------------
@@ -41,6 +42,8 @@ impl Deref for RouteAuthorization {
}
}
/// We use RouteAuthorization as (json) map keys and therefore we need it
/// to be serializable to a single simple string.
impl Serialize for RouteAuthorization {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
@@ -50,6 +53,8 @@ impl Serialize for RouteAuthorization {
}
}
/// We use RouteAuthorization as (json) map keys and therefore we need it
/// to be deserializable from a single simple string.
impl<'de> Deserialize<'de> for RouteAuthorization {
fn deserialize<D>(d: D) -> Result<RouteAuthorization, D::Error>
where
@@ -166,33 +171,42 @@ impl Default for RouteInfo {
//------------ RoaInfo -----------------------------------------------------
#[derive(Clone, Debug, Deserialize, Serialize)]
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct RoaInfo {
roa: Roa, // actual ROA
object: CurrentObject, // actual ROA
name: ObjectName, // Name for object in repo
since: Time, // first ROA in RC created
replaces: Option<ReplacedObject>, // for revoking when re-newing
}
impl RoaInfo {
pub fn new_roa(roa: Roa) -> Self {
pub fn new_roa(roa: &Roa, name: ObjectName) -> Self {
let object = CurrentObject::from(roa);
RoaInfo {
roa,
object,
name,
since: Time::now(),
replaces: None,
}
}
pub fn updated_roa(old: &RoaInfo, roa: Roa) -> Self {
let replaces = Some(ReplacedObject::from(&old.roa));
pub fn updated_roa(old: &RoaInfo, roa: &Roa, name: ObjectName) -> Self {
let object = CurrentObject::from(roa);
let replaces = Some(ReplacedObject::from(old.object()));
RoaInfo {
roa,
object,
name,
since: old.since,
replaces,
}
}
pub fn roa(&self) -> &Roa {
&self.roa
pub fn object(&self) -> &CurrentObject {
&self.object
}
pub fn name(&self) -> &ObjectName {
&self.name
}
pub fn since(&self) -> Time {
@@ -204,16 +218,6 @@ impl RoaInfo {
}
}
impl PartialEq for RoaInfo {
fn eq(&self, other: &RoaInfo) -> bool {
self.roa.to_captured().as_slice() == other.roa.to_captured().as_slice()
&& self.since == other.since
&& self.replaces == other.replaces
}
}
impl Eq for RoaInfo {}
//------------ Roas --------------------------------------------------------
/// ROAs held by a resource class in a CA.
+1 -1
View File
@@ -334,7 +334,7 @@ impl<S: Signer> CaServer<S> {
let ca = self.ca_store.update(parent, ca, events)?;
// The updated CA will now include the newly issued certificate.
let response = ca.issuance_response(child, &class_name, &pub_key)?;
let response = ca.issuance_response(child, &class_name, pub_key)?;
Ok(response)
}