mirror of
https://github.com/NLnetLabs/krill.git
synced 2026-09-20 16:37:43 +02:00
Force update resources for child CA.
This commit is contained in:
+12
-3
@@ -177,8 +177,13 @@ impl Options {
|
||||
.help("Update the delegated IPv6 resources: e.g. 2001:db8::/32")
|
||||
.required(false)
|
||||
)
|
||||
|
||||
|
||||
.arg(Arg::with_name("force")
|
||||
.short("f")
|
||||
.long("force")
|
||||
.takes_value(false)
|
||||
.help("Force resource shrink now.")
|
||||
.required(false)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -441,7 +446,11 @@ impl Options {
|
||||
Some(resources)
|
||||
};
|
||||
|
||||
let req = UpdateChildRequest::new(cert, resources);
|
||||
let req = if m.is_present("force") {
|
||||
UpdateChildRequest::force(cert, resources)
|
||||
} else {
|
||||
UpdateChildRequest::graceful(cert, resources)
|
||||
};
|
||||
|
||||
command = Command::TrustAnchor(TrustAnchorCommand::UpdateChild(handle, req))
|
||||
}
|
||||
|
||||
@@ -405,14 +405,31 @@ pub enum ChildAuthRequest {
|
||||
pub struct UpdateChildRequest {
|
||||
id_cert: Option<IdCert>,
|
||||
resources: Option<ResourceSet>,
|
||||
force: bool,
|
||||
}
|
||||
|
||||
impl UpdateChildRequest {
|
||||
pub fn new(id_cert: Option<IdCert>, resources: Option<ResourceSet>) -> Self {
|
||||
UpdateChildRequest { id_cert, resources }
|
||||
pub fn graceful(id_cert: Option<IdCert>, resources: Option<ResourceSet>) -> Self {
|
||||
UpdateChildRequest {
|
||||
id_cert,
|
||||
resources,
|
||||
force: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn force(id_cert: Option<IdCert>, resources: Option<ResourceSet>) -> Self {
|
||||
UpdateChildRequest {
|
||||
id_cert,
|
||||
resources,
|
||||
force: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unpack(self) -> (Option<IdCert>, Option<ResourceSet>) {
|
||||
(self.id_cert, self.resources)
|
||||
}
|
||||
|
||||
pub fn is_force(&self) -> bool {
|
||||
self.force
|
||||
}
|
||||
}
|
||||
|
||||
+149
-18
@@ -10,6 +10,8 @@ use std::{fmt, ops};
|
||||
|
||||
use bytes::Bytes;
|
||||
use chrono::Duration;
|
||||
use serde::de;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use rpki::cert::Cert;
|
||||
use rpki::crypto::KeyIdentifier;
|
||||
@@ -92,6 +94,10 @@ impl ChildCaDetails {
|
||||
&self.resources
|
||||
}
|
||||
|
||||
pub fn remove_resource(&mut self, class_name: &str) {
|
||||
self.resources.remove(class_name);
|
||||
}
|
||||
|
||||
/// This function will update the resource entitlements for an existing class
|
||||
/// or create a new class if needed
|
||||
pub fn set_resources_for_class(&mut self, class: &str, resources: ResourceSet) {
|
||||
@@ -127,20 +133,56 @@ impl ChildCaDetails {
|
||||
}
|
||||
|
||||
/// This type defines a reference to PublicKey for easy storage and lookup.
|
||||
#[derive(Clone, Debug, Deserialize, Display, Eq, Hash, PartialEq, Serialize)]
|
||||
pub struct KeyRef(String);
|
||||
#[derive(Clone, Debug, Display, Eq, Hash, PartialEq)]
|
||||
pub struct KeyRef(KeyIdentifier);
|
||||
|
||||
impl From<&KeyIdentifier> for KeyRef {
|
||||
fn from(ki: &KeyIdentifier) -> Self {
|
||||
let hex = ki.into_hex();
|
||||
let s = unsafe { str::from_utf8_unchecked(&hex) };
|
||||
KeyRef(s.to_string())
|
||||
KeyRef(ki.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<KeyIdentifier> for KeyRef {
|
||||
fn from(ki: KeyIdentifier) -> Self {
|
||||
KeyRef(ki)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&KeyRef> for KeyIdentifier {
|
||||
fn from(kr: &KeyRef) -> Self {
|
||||
kr.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<KeyRef> for KeyIdentifier {
|
||||
fn from(kr: KeyRef) -> Self {
|
||||
kr.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Cert> for KeyRef {
|
||||
fn from(c: &Cert) -> Self {
|
||||
Self::from(&c.subject_key_identifier())
|
||||
KeyRef(c.subject_key_identifier())
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for KeyRef {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.0.to_string().serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for KeyRef {
|
||||
fn deserialize<D>(deserializer: D) -> Result<KeyRef, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let string = String::deserialize(deserializer)?;
|
||||
let ki = KeyIdentifier::from_str(&string).map_err(de::Error::custom)?;
|
||||
Ok(KeyRef(ki))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,7 +196,7 @@ impl From<&Cert> for KeyRef {
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct ChildResources {
|
||||
resources: ResourceSet,
|
||||
since: Time,
|
||||
shrink_pending: Option<Time>,
|
||||
not_after: Time,
|
||||
certs: HashMap<KeyRef, IssuedCert>,
|
||||
}
|
||||
@@ -163,7 +205,7 @@ impl ChildResources {
|
||||
pub fn new(resources: ResourceSet) -> Self {
|
||||
ChildResources {
|
||||
resources,
|
||||
since: Time::now(),
|
||||
shrink_pending: None,
|
||||
not_after: Time::next_year(),
|
||||
certs: HashMap::new(),
|
||||
}
|
||||
@@ -174,8 +216,10 @@ impl ChildResources {
|
||||
}
|
||||
|
||||
pub fn set_resources(&mut self, resources: ResourceSet) {
|
||||
if !resources.contains(&self.resources) {
|
||||
self.shrink_pending = Some(Time::now());
|
||||
}
|
||||
self.resources = resources;
|
||||
self.since = Time::now();
|
||||
}
|
||||
|
||||
/// Give back the not_after time that would be used on newly
|
||||
@@ -195,21 +239,43 @@ impl ChildResources {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn certs(&self) -> impl Iterator<Item = &IssuedCert> {
|
||||
pub fn certs_iter(&self) -> impl Iterator<Item = &IssuedCert> {
|
||||
self.certs.values()
|
||||
}
|
||||
|
||||
pub fn certs(&self) -> &HashMap<KeyRef, IssuedCert> {
|
||||
&self.certs
|
||||
}
|
||||
|
||||
pub fn cert(&self, key_id: &KeyIdentifier) -> Option<&IssuedCert> {
|
||||
let key_ref = KeyRef::from(key_id);
|
||||
self.certs.get(&key_ref)
|
||||
}
|
||||
|
||||
pub fn add_cert(&mut self, cert: IssuedCert) {
|
||||
let key_ref = KeyRef::from(cert.cert());
|
||||
|
||||
// We can assume that the correct not_after date was used when the certificate
|
||||
// was issued - because the `not_after` function was called. Therefore we can update
|
||||
// the not_after value for this child resource class for future use.
|
||||
self.not_after = cert.cert().validity().not_after();
|
||||
self.resources = ResourceSet::try_from(cert.cert()).unwrap();
|
||||
|
||||
// Update the certificate for this key, or insert it for a new key.
|
||||
let key_ref = KeyRef::from(cert.cert());
|
||||
self.certs.insert(key_ref, cert);
|
||||
|
||||
// If a shrink was pending, check that it's still applicable.
|
||||
if self.shrink_pending.is_some()
|
||||
&& self
|
||||
.certs
|
||||
.values()
|
||||
.find(|c| !self.resources.contains(c.resource_set()))
|
||||
.is_none()
|
||||
{
|
||||
self.shrink_pending = None;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shrink_pending(&self) -> Option<Time> {
|
||||
self.shrink_pending
|
||||
}
|
||||
|
||||
pub fn revoke(&mut self, key_id: &KeyIdentifier) {
|
||||
@@ -218,6 +284,42 @@ impl ChildResources {
|
||||
}
|
||||
}
|
||||
|
||||
//------------ ReplacedObject ------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct ReplacedObject {
|
||||
revocation: Revocation,
|
||||
hash: EncodedHash,
|
||||
}
|
||||
|
||||
impl ReplacedObject {
|
||||
pub fn new(revocation: Revocation, hash: EncodedHash) -> Self {
|
||||
ReplacedObject { revocation, hash }
|
||||
}
|
||||
|
||||
pub fn revocation(&self) -> Revocation {
|
||||
self.revocation
|
||||
}
|
||||
|
||||
pub fn hash(&self) -> &EncodedHash {
|
||||
&self.hash
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Cert> for ReplacedObject {
|
||||
fn from(c: &Cert) -> Self {
|
||||
let revocation = Revocation::from(c);
|
||||
let hash = EncodedHash::from_content(c.to_captured().as_slice());
|
||||
ReplacedObject { revocation, hash }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&IssuedCert> for ReplacedObject {
|
||||
fn from(issued: &IssuedCert) -> Self {
|
||||
Self::from(issued.cert())
|
||||
}
|
||||
}
|
||||
|
||||
//------------ IssuedCert ----------------------------------------------------
|
||||
|
||||
/// This type defines an issued certificate, including its publication
|
||||
@@ -236,6 +338,7 @@ pub struct IssuedCert {
|
||||
limit: RequestResourceLimit, // the limit on the request
|
||||
resource_set: ResourceSet,
|
||||
cert: Cert,
|
||||
replaces: Option<ReplacedObject>,
|
||||
}
|
||||
|
||||
impl IssuedCert {
|
||||
@@ -244,12 +347,14 @@ impl IssuedCert {
|
||||
limit: RequestResourceLimit,
|
||||
resource_set: ResourceSet,
|
||||
cert: Cert,
|
||||
replaces: Option<ReplacedObject>,
|
||||
) -> Self {
|
||||
IssuedCert {
|
||||
uri,
|
||||
limit,
|
||||
resource_set,
|
||||
cert,
|
||||
replaces,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,6 +374,9 @@ impl IssuedCert {
|
||||
pub fn cert(&self) -> &Cert {
|
||||
&self.cert
|
||||
}
|
||||
pub fn replaces(&self) -> Option<&ReplacedObject> {
|
||||
self.replaces.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for IssuedCert {
|
||||
@@ -769,7 +877,8 @@ impl CurrentObjects {
|
||||
/// Returns publish's for all objects in this set.
|
||||
pub fn publish(&self, base_uri: &RepoInfo, name_space: &str) -> Vec<Publish> {
|
||||
let ca_repo = base_uri.ca_repository(name_space);
|
||||
self.0.iter()
|
||||
self.0
|
||||
.iter()
|
||||
.map(|(name, object)| {
|
||||
Publish::new(None, ca_repo.join(name.as_bytes()), object.content.clone())
|
||||
})
|
||||
@@ -1065,6 +1174,14 @@ impl AddedObject {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Cert> for AddedObject {
|
||||
fn from(cert: &Cert) -> Self {
|
||||
let name = ObjectName::from(cert);
|
||||
let object = CurrentObject::from(cert);
|
||||
AddedObject { name, object }
|
||||
}
|
||||
}
|
||||
|
||||
//------------ UpdatedObject -------------------------------------------------
|
||||
|
||||
/// A new object that replaces an earlier version by this name.
|
||||
@@ -1079,6 +1196,12 @@ impl UpdatedObject {
|
||||
pub fn new(name: ObjectName, object: CurrentObject, old: EncodedHash) -> Self {
|
||||
UpdatedObject { name, object, old }
|
||||
}
|
||||
|
||||
pub fn for_cert(new: &Cert, old: EncodedHash) -> Self {
|
||||
let name = ObjectName::from(new);
|
||||
let object = CurrentObject::from(new);
|
||||
UpdatedObject { name, object, old }
|
||||
}
|
||||
}
|
||||
|
||||
//------------ WithdrawnObject -----------------------------------------------
|
||||
@@ -1099,6 +1222,14 @@ impl WithdrawnObject {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Cert> for WithdrawnObject {
|
||||
fn from(c: &Cert) -> Self {
|
||||
let name = ObjectName::from(c);
|
||||
let hash = EncodedHash::from_content(c.to_captured().as_slice());
|
||||
WithdrawnObject { name, hash }
|
||||
}
|
||||
}
|
||||
|
||||
//------------ ResourceSet ---------------------------------------------------
|
||||
|
||||
/// This type defines a set of Internet Number Resources.
|
||||
@@ -1423,7 +1554,7 @@ impl CertAuthInfo {
|
||||
match &self.parents {
|
||||
CaParentsInfo::SelfSigned(key, _tal) => {
|
||||
res.append(&mut key.current_set.objects().publish(self.base_repo(), ""));
|
||||
},
|
||||
}
|
||||
CaParentsInfo::Parents(map) => {
|
||||
for (_parent, parent_info) in map.iter() {
|
||||
for rc in parent_info.resources().values() {
|
||||
@@ -1436,7 +1567,6 @@ impl CertAuthInfo {
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// This type contains public data about parents of a CA
|
||||
@@ -1501,8 +1631,9 @@ impl ResourceClassInfo {
|
||||
pub fn objects(&self) -> CurrentObjects {
|
||||
let mut res = CurrentObjects::default();
|
||||
match &self.keys {
|
||||
ResourceClassKeysInfo::Pending(_) => {},
|
||||
ResourceClassKeysInfo::Active(current) | ResourceClassKeysInfo::RollPending(_, current)=> {
|
||||
ResourceClassKeysInfo::Pending(_) => {}
|
||||
ResourceClassKeysInfo::Active(current)
|
||||
| ResourceClassKeysInfo::RollPending(_, current) => {
|
||||
res = res + current.current_set().objects().clone();
|
||||
}
|
||||
ResourceClassKeysInfo::RollNew(new, current) => {
|
||||
|
||||
@@ -303,7 +303,8 @@ impl Qry {
|
||||
|
||||
a.exhausted()?;
|
||||
|
||||
let ski = KeyIdentifier::try_from(ski_bytes.as_ref()).map_err(|_| Error::InvalidSki)?;
|
||||
let ski =
|
||||
KeyIdentifier::try_from(ski_bytes.as_slice()).map_err(|_| Error::InvalidSki)?;
|
||||
Ok(RevocationRequest::new(class_name.to_string(), ski))
|
||||
})
|
||||
}
|
||||
@@ -579,7 +580,7 @@ impl Res {
|
||||
let cert = Self::decode_cert(r)?;
|
||||
let resource_set = ResourceSet::try_from(&cert)?;
|
||||
|
||||
Ok(IssuedCert::new(cert_url, limit, resource_set, cert))
|
||||
Ok(IssuedCert::new(cert_url, limit, resource_set, cert, None))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+254
-71
@@ -6,7 +6,7 @@ use std::sync::{Arc, RwLock};
|
||||
use bytes::Bytes;
|
||||
use chrono::Duration;
|
||||
|
||||
use rpki::cert::{KeyUsage, Overclaim, TbsCert};
|
||||
use rpki::cert::{Cert, KeyUsage, Overclaim, TbsCert};
|
||||
use rpki::crypto::{PublicKey, PublicKeyFormat};
|
||||
use rpki::x509::{Name, Serial, Time, Validity};
|
||||
|
||||
@@ -15,11 +15,12 @@ use krill_commons::api::admin::{
|
||||
};
|
||||
use krill_commons::api::ca::{
|
||||
AddedObject, CaParentsInfo, CertAuthInfo, CertifiedKey, ChildCaDetails, CurrentObject,
|
||||
IssuedCert, ObjectName, ObjectsDelta, ParentCaInfo, PublicationDelta, RcvdCert, RepoInfo,
|
||||
ResourceSet, Revocation, TrustAnchorInfo, TrustAnchorLocator, UpdatedObject, WithdrawnObject,
|
||||
IssuedCert, ObjectName, ObjectsDelta, ParentCaInfo, PublicationDelta, RcvdCert, ReplacedObject,
|
||||
RepoInfo, ResourceSet, Revocation, TrustAnchorInfo, TrustAnchorLocator, UpdatedObject,
|
||||
WithdrawnObject,
|
||||
};
|
||||
use krill_commons::api::{
|
||||
self, EncodedHash, EntitlementClass, Entitlements, IssuanceRequest, IssuanceResponse,
|
||||
self, EntitlementClass, Entitlements, IssuanceRequest, IssuanceResponse, RequestResourceLimit,
|
||||
RevocationRequest, RevocationResponse, SigningCert, DFLT_CLASS,
|
||||
};
|
||||
use krill_commons::eventsourcing::{Aggregate, StoredEvent};
|
||||
@@ -34,6 +35,7 @@ use crate::ca::{
|
||||
self, ChildHandle, Cmd, CmdDet, Error, Evt, EvtDet, Ini, ParentHandle, ResourceClass,
|
||||
ResourceClassName, Result, SignSupport, Signer,
|
||||
};
|
||||
use crate::ca::signing::CertSiaInfo;
|
||||
|
||||
//------------ Rfc8183Id ---------------------------------------------------
|
||||
|
||||
@@ -233,7 +235,10 @@ impl<S: Signer> Aggregate for CertAuth<S> {
|
||||
let child = self.children.get_mut(&child).unwrap();
|
||||
child.set_resources_for_class(&class, resources)
|
||||
}
|
||||
EvtDet::ChildRemovedResourceClass(_child, _name) => unimplemented!(),
|
||||
EvtDet::ChildRemovedResourceClass(child, name) => {
|
||||
let child = self.children.get_mut(&child).unwrap();
|
||||
child.remove_resource(name.as_str());
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// Being a child
|
||||
@@ -312,16 +317,17 @@ impl<S: Signer> Aggregate for CertAuth<S> {
|
||||
fn process_command(&self, command: Cmd<S>) -> ca::Result<Vec<Evt>> {
|
||||
match command.into_details() {
|
||||
// being a parent
|
||||
CmdDet::AddChild(child, id_cert_opt, resources) => {
|
||||
CmdDet::ChildAdd(child, id_cert_opt, resources) => {
|
||||
self.add_child(child, id_cert_opt, resources)
|
||||
}
|
||||
CmdDet::UpdateChild(child, req) => self.update_child(&child, req),
|
||||
CmdDet::CertifyChild(child, request, signer) => {
|
||||
CmdDet::ChildUpdate(child, req) => self.update_child(&child, req),
|
||||
CmdDet::ChildCertify(child, request, signer) => {
|
||||
self.certify_child(child, request, signer)
|
||||
}
|
||||
CmdDet::RevokeKeyForChild(child, request, signer) => {
|
||||
CmdDet::ChildRevokeKey(child, request, signer) => {
|
||||
self.revoke_child_key(child, request, signer)
|
||||
}
|
||||
CmdDet::ChildShrink(child, grace, signer) => self.shrink_child(&child, grace, signer),
|
||||
|
||||
// being a child
|
||||
CmdDet::AddParent(parent, info) => self.add_parent(parent, info),
|
||||
@@ -463,7 +469,7 @@ impl<S: Signer> CertAuth<S> {
|
||||
}
|
||||
|
||||
let until = child_resources.not_after();
|
||||
let issued = child_resources.certs().cloned().collect();
|
||||
let issued = child_resources.certs_iter().cloned().collect();
|
||||
|
||||
let cert = match &self.parents {
|
||||
CaParents::SelfSigned(key, _tal) => key.incoming_cert(),
|
||||
@@ -548,6 +554,59 @@ impl<S: Signer> CertAuth<S> {
|
||||
) -> ca::Result<Vec<Evt>> {
|
||||
let (class_name, limit, csr) = request.unwrap();
|
||||
|
||||
csr.validate()
|
||||
.map_err(|_| Error::invalid_csr(&child, "invalid signature"))?;
|
||||
|
||||
let sia_info = {
|
||||
let ca_repository = csr
|
||||
.ca_repository()
|
||||
.cloned()
|
||||
.ok_or_else(|| Error::invalid_csr(&child, "Missing CA repository uri"))?;
|
||||
let rpki_manifest = csr
|
||||
.rpki_manifest()
|
||||
.cloned()
|
||||
.ok_or_else(|| Error::invalid_csr(&child, "Missing rpki manifest uri"))?;
|
||||
let rpki_notify = csr.rpki_notify().cloned();
|
||||
|
||||
CertSiaInfo::new(ca_repository, rpki_manifest, rpki_notify)
|
||||
};
|
||||
|
||||
let pub_key = csr.public_key().clone();
|
||||
|
||||
let issue_response = self.issue_child_certificate(
|
||||
&child,
|
||||
&class_name,
|
||||
pub_key,
|
||||
sia_info,
|
||||
limit,
|
||||
signer.read().unwrap().deref(),
|
||||
)?;
|
||||
|
||||
let publication_delta = self.update_published_child_certificates(
|
||||
&class_name,
|
||||
vec![issue_response.issued()],
|
||||
vec![],
|
||||
signer,
|
||||
)?;
|
||||
|
||||
let issued_event =
|
||||
EvtDet::child_certificate_issued(&self.handle, self.version, child, issue_response);
|
||||
|
||||
let publish_event = EvtDet::published_ta(&self.handle, self.version + 1, publication_delta);
|
||||
|
||||
Ok(vec![issued_event, publish_event])
|
||||
}
|
||||
|
||||
/// Issue a new child certificate.
|
||||
fn issue_child_certificate(
|
||||
&self,
|
||||
child: &ChildHandle,
|
||||
class_name: &ResourceClassName,
|
||||
pub_key: PublicKey,
|
||||
sia_info: CertSiaInfo,
|
||||
limit: RequestResourceLimit,
|
||||
signer: &S,
|
||||
) -> Result<IssuanceResponse> {
|
||||
let issuing_key = match &self.parents {
|
||||
CaParents::SelfSigned(key, _tal) => key,
|
||||
CaParents::Parents(_) => unimplemented!("Issue #25 (delegate from CA)"),
|
||||
@@ -565,26 +624,17 @@ impl<S: Signer> CertAuth<S> {
|
||||
return Err(Error::MissingResources);
|
||||
}
|
||||
|
||||
let current_cert = child_resources.cert(&pub_key.key_identifier());
|
||||
let replaces = current_cert.map(ReplacedObject::from);
|
||||
|
||||
let resources = child_resources
|
||||
.resources()
|
||||
.apply_limit(&limit)
|
||||
.map_err(|_| Error::MissingResources)?;
|
||||
|
||||
csr.validate()
|
||||
.map_err(|_| Error::invalid_csr(&child, "invalid signature"))?;
|
||||
|
||||
// TODO: Check for key-re-use, ultimately return 1204 (RFC6492 3.4.1)
|
||||
let current_cert = child_resources.cert(&csr.public_key().key_identifier());
|
||||
|
||||
// Check if we need to revoke
|
||||
let mut revocations = vec![];
|
||||
if let Some(issued) = current_cert {
|
||||
revocations.push(Revocation::from(issued.cert()))
|
||||
}
|
||||
|
||||
// create new cert
|
||||
let issued_cert = {
|
||||
let serial = { Serial::random(signer.read().unwrap().deref()).map_err(Error::signer)? };
|
||||
let serial = { Serial::random(signer).map_err(Error::signer)? };
|
||||
let issuer = issuing_cert.cert().subject().clone();
|
||||
|
||||
let validity = Validity::new(
|
||||
@@ -592,8 +642,7 @@ impl<S: Signer> CertAuth<S> {
|
||||
child_resources.not_after(),
|
||||
);
|
||||
|
||||
let subject = Some(Name::from_pub_key(csr.public_key()));
|
||||
let pub_key = csr.public_key().clone();
|
||||
let subject = Some(Name::from_pub_key(&pub_key));
|
||||
|
||||
let key_usage = KeyUsage::Ca;
|
||||
let overclaim = Overclaim::Refuse;
|
||||
@@ -609,20 +658,13 @@ impl<S: Signer> CertAuth<S> {
|
||||
// because the publication server for those URIs should verify the
|
||||
// identity of the publisher, and that RPs will not invalidate the
|
||||
// content of another CA's repo, if they it is wrongfully claimed.
|
||||
let ca_repository = csr
|
||||
.ca_repository()
|
||||
.ok_or_else(|| Error::invalid_csr(&child, "missing ca repo"))?;
|
||||
let rpki_manifest = csr
|
||||
.rpki_manifest()
|
||||
.ok_or_else(|| Error::invalid_csr(&child, "missing mft uri"))?;
|
||||
let rpki_notify = csr.rpki_notify();
|
||||
let (ca_repository, rpki_manifest, rpki_notify) = sia_info.unpack();
|
||||
|
||||
cert.set_ca_issuer(Some(issuing_cert.uri().clone()));
|
||||
cert.set_crl_uri(Some(issuing_cert.crl_uri()));
|
||||
|
||||
cert.set_ca_repository(Some(ca_repository.clone()));
|
||||
cert.set_rpki_manifest(Some(rpki_manifest.clone()));
|
||||
cert.set_rpki_notify(rpki_notify.cloned());
|
||||
cert.set_ca_repository(Some(ca_repository));
|
||||
cert.set_rpki_manifest(Some(rpki_manifest));
|
||||
cert.set_rpki_notify(rpki_notify);
|
||||
|
||||
cert.set_as_resources(Some(resources.to_as_resources()));
|
||||
cert.set_v4_resources(Some(resources.to_ip_resources_v4()));
|
||||
@@ -631,55 +673,195 @@ impl<S: Signer> CertAuth<S> {
|
||||
cert.set_authority_key_identifier(Some(issuing_cert.cert().subject_key_identifier()));
|
||||
|
||||
let cert = {
|
||||
cert.into_cert(signer.read().unwrap().deref(), issuing_key.key_id())
|
||||
cert.into_cert(signer, issuing_key.key_id())
|
||||
.map_err(Error::signer)?
|
||||
};
|
||||
|
||||
let cert_uri = issuing_cert.uri_for_object(&cert);
|
||||
|
||||
IssuedCert::new(cert_uri, limit, resources.clone(), cert)
|
||||
IssuedCert::new(cert_uri, limit, resources.clone(), cert, replaces)
|
||||
};
|
||||
|
||||
let version = self.version;
|
||||
let cert_object = CurrentObject::from(issued_cert.cert());
|
||||
|
||||
let signing_cert = SigningCert::from(issuing_cert);
|
||||
|
||||
let response = IssuanceResponse::new(
|
||||
DFLT_CLASS.to_string(),
|
||||
Ok(IssuanceResponse::new(
|
||||
class_name.clone(),
|
||||
signing_cert,
|
||||
resources,
|
||||
issued_cert.cert().validity().not_after(),
|
||||
issued_cert.clone(),
|
||||
);
|
||||
|
||||
let issued_event = EvtDet::child_certificate_issued(&self.handle, version, child, response);
|
||||
|
||||
let delta = {
|
||||
let ca_repo = self.base_repo.ca_repository("");
|
||||
let mut delta = ObjectsDelta::new(ca_repo);
|
||||
let cert_name = ObjectName::from(issued_cert.cert());
|
||||
|
||||
match current_cert {
|
||||
None => delta.add(AddedObject::new(cert_name, cert_object)),
|
||||
Some(old) => {
|
||||
let old_hash = EncodedHash::from_content(old.cert().to_captured().as_slice());
|
||||
delta.update(UpdatedObject::new(cert_name, cert_object, old_hash));
|
||||
}
|
||||
}
|
||||
delta
|
||||
};
|
||||
|
||||
let publish_event = EvtDet::published_ta(
|
||||
&self.handle,
|
||||
version + 1,
|
||||
SignSupport::publish(signer, issuing_key, &self.base_repo, "", delta, revocations)
|
||||
.map_err(Error::signer)?,
|
||||
);
|
||||
|
||||
Ok(vec![issued_event, publish_event])
|
||||
issued_cert,
|
||||
))
|
||||
}
|
||||
|
||||
/// Create a publish event details including the revocations, update, withdrawals needed
|
||||
/// for updating child certificates.
|
||||
pub fn update_published_child_certificates(
|
||||
&self,
|
||||
_class_name: &ResourceClassName, // Issue #25
|
||||
issued_certs: Vec<&IssuedCert>,
|
||||
removed_certs: Vec<&Cert>,
|
||||
signer: Arc<RwLock<S>>,
|
||||
) -> Result<PublicationDelta> {
|
||||
let issuing_key = match &self.parents {
|
||||
CaParents::SelfSigned(key, _tal) => key,
|
||||
CaParents::Parents(_) => unimplemented!("Issue #25 (delegate from CA)"),
|
||||
};
|
||||
|
||||
let name_space = ""; // TODO: #25 use name space for RC
|
||||
|
||||
let mut revocations = vec![];
|
||||
for cert in removed_certs.iter() {
|
||||
revocations.push(Revocation::from(*cert));
|
||||
}
|
||||
for issued in issued_certs.iter() {
|
||||
if let Some(replaced) = issued.replaces() {
|
||||
revocations.push(replaced.revocation());
|
||||
}
|
||||
}
|
||||
|
||||
let ca_repo = self.base_repo.ca_repository(name_space);
|
||||
let mut objects_delta = ObjectsDelta::new(ca_repo);
|
||||
|
||||
for removed in removed_certs.into_iter() {
|
||||
objects_delta.withdraw(WithdrawnObject::from(removed));
|
||||
}
|
||||
for issued in issued_certs.into_iter() {
|
||||
match issued.replaces() {
|
||||
None => objects_delta.add(AddedObject::from(issued.cert())),
|
||||
Some(replaced) => objects_delta.update(UpdatedObject::for_cert(
|
||||
issued.cert(),
|
||||
replaced.hash().clone(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
SignSupport::publish(
|
||||
signer,
|
||||
issuing_key,
|
||||
&self.base_repo,
|
||||
name_space,
|
||||
objects_delta,
|
||||
revocations,
|
||||
)
|
||||
.map_err(Error::signer)
|
||||
}
|
||||
|
||||
/// Shrink a child if it has any overclaiming certificates and the grace period has passed.
|
||||
///
|
||||
/// When shrinking the parent will remove and completely revoke any resource classes for
|
||||
/// which there are no more resources. And it will shrink certificates where resources are
|
||||
/// lost, i.e. it will revoke the current certificate and issue a new certificate with the
|
||||
/// new resource set on it.
|
||||
///
|
||||
/// Note: We could also go for the intersection of the currently entitled resources and the
|
||||
/// resources on the old certificate, but.. this is useful only really if the child CA
|
||||
/// deliberately asked for a certificate with a sub-set of resources (which is allowed, but
|
||||
/// very uncommon, and unclear why it would be beneficial), and - more importantly - it
|
||||
/// creates a corner case where there are new entitled resources but there is no intersection
|
||||
/// with the old resource set.
|
||||
fn shrink_child(
|
||||
&self,
|
||||
child_handle: &ChildHandle,
|
||||
grace: Duration,
|
||||
signer: Arc<RwLock<S>>,
|
||||
) -> ca::Result<Vec<Evt>> {
|
||||
let child = self.get_child(child_handle)?;
|
||||
let mut events = vec![];
|
||||
|
||||
debug!("Checking if child {} needs shrinking", child_handle);
|
||||
|
||||
for (class_name, child_resources) in child.resources().iter() {
|
||||
if let Some(pending_time) = child_resources.shrink_pending() {
|
||||
if pending_time + grace <= Time::now() {
|
||||
let mut issuance_responses = vec![];
|
||||
let mut removed = vec![];
|
||||
|
||||
let new_resources = child_resources.resources();
|
||||
|
||||
if new_resources.is_empty() {
|
||||
info!(
|
||||
"Removing resource class '{}' for child '{}'",
|
||||
class_name, child_handle
|
||||
);
|
||||
// Remove resource set and revoke all certs
|
||||
for (keyref, issued) in child_resources.certs().iter() {
|
||||
let revocation =
|
||||
RevocationResponse::new(class_name.clone(), keyref.into());
|
||||
events.push(EvtDet::ChildKeyRevoked(child_handle.clone(), revocation));
|
||||
removed.push(issued.cert())
|
||||
}
|
||||
events.push(EvtDet::ChildRemovedResourceClass(
|
||||
child_handle.clone(),
|
||||
class_name.clone(),
|
||||
));
|
||||
} else {
|
||||
// Re-issue all certs that are overclaiming.
|
||||
for issued_cert in child_resources.certs_iter() {
|
||||
if !new_resources.contains(issued_cert.resource_set()) {
|
||||
info!(
|
||||
"Shrinking cert in resource class '{}' for child '{}' to '{}'",
|
||||
class_name, child_handle, new_resources
|
||||
);
|
||||
|
||||
let sia_info = {
|
||||
let ca_repo =
|
||||
issued_cert.cert().ca_repository().cloned().unwrap();
|
||||
let mft_uri =
|
||||
issued_cert.cert().rpki_manifest().cloned().unwrap();
|
||||
let not_opt = issued_cert.cert().rpki_notify().cloned();
|
||||
CertSiaInfo::new(ca_repo, mft_uri, not_opt)
|
||||
};
|
||||
|
||||
let pub_key = issued_cert.cert().subject_public_key_info().clone();
|
||||
issuance_responses.push(self.issue_child_certificate(
|
||||
child_handle,
|
||||
class_name,
|
||||
pub_key,
|
||||
sia_info,
|
||||
RequestResourceLimit::default(),
|
||||
signer.read().unwrap().deref(),
|
||||
)?);
|
||||
}
|
||||
}
|
||||
}
|
||||
let issued = issuance_responses.iter().map(|res| res.issued()).collect();
|
||||
|
||||
let publication_delta = self.update_published_child_certificates(
|
||||
class_name,
|
||||
issued,
|
||||
removed,
|
||||
signer.clone(),
|
||||
)?;
|
||||
|
||||
for response in issuance_responses.into_iter() {
|
||||
events.push(EvtDet::ChildCertificateIssued(
|
||||
child_handle.clone(),
|
||||
response,
|
||||
));
|
||||
}
|
||||
|
||||
events.push(EvtDet::TaPublished(publication_delta));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut version = self.version;
|
||||
let events = events
|
||||
.into_iter()
|
||||
.map(|details| {
|
||||
version += 1;
|
||||
StoredEvent::new(self.handle(), version - 1, details)
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(events)
|
||||
}
|
||||
|
||||
/// Updates child IdCert and/or Resource entitlements.
|
||||
///
|
||||
/// Note: this does not yet revoke / reissue / republish anything. If the 'force' option was
|
||||
/// used in the update request, then shrink_child should be called with a grace period that
|
||||
/// is effective immediately.
|
||||
fn update_child(&self, child_handle: &Handle, req: UpdateChildRequest) -> ca::Result<Vec<Evt>> {
|
||||
let (cert_opt, resources_opt) = req.unpack();
|
||||
|
||||
@@ -730,6 +912,7 @@ impl<S: Signer> CertAuth<S> {
|
||||
}
|
||||
|
||||
// Determine for each whether the entitlement is changed, added, or removed
|
||||
|
||||
for (class_name, entitled_resource_set) in child_entitlements.into_iter() {
|
||||
if match child_resources.remove(&class_name) {
|
||||
None => true,
|
||||
|
||||
+27
-12
@@ -24,13 +24,15 @@ pub enum CmdDet<S: Signer> {
|
||||
// ------------------------------------------------------------
|
||||
|
||||
// Add a new child under this parent CA
|
||||
AddChild(ChildHandle, Option<IdCert>, ResourceSet),
|
||||
ChildAdd(ChildHandle, Option<IdCert>, ResourceSet),
|
||||
// Update some details for an existing child, e.g. resources.
|
||||
UpdateChild(ChildHandle, UpdateChildRequest),
|
||||
ChildUpdate(ChildHandle, UpdateChildRequest),
|
||||
// Process an issuance request by an existing child.
|
||||
CertifyChild(ChildHandle, IssuanceRequest, Arc<RwLock<S>>),
|
||||
ChildCertify(ChildHandle, IssuanceRequest, Arc<RwLock<S>>),
|
||||
// Process a revoke request by an existing child.
|
||||
RevokeKeyForChild(ChildHandle, RevocationRequest, Arc<RwLock<S>>),
|
||||
ChildRevokeKey(ChildHandle, RevocationRequest, Arc<RwLock<S>>),
|
||||
// Shrink child (only has events in case child is overclaiming)
|
||||
ChildShrink(ChildHandle, Duration, Arc<RwLock<S>>),
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Being a child (only allowed if this CA is not self-signed)
|
||||
@@ -81,7 +83,7 @@ impl<S: Signer> CmdDet<S> {
|
||||
/// Adds a child to this CA. Will return an error in case you try
|
||||
/// to give the child resources not held by the CA. And until issue
|
||||
/// #25 is implemented, returns an error when the CA is not a TA.
|
||||
pub fn add_child(
|
||||
pub fn child_add(
|
||||
handle: &Handle,
|
||||
child_handle: Handle,
|
||||
child_id_cert: Option<IdCert>,
|
||||
@@ -90,21 +92,21 @@ impl<S: Signer> CmdDet<S> {
|
||||
eventsourcing::SentCommand::new(
|
||||
handle,
|
||||
None,
|
||||
CmdDet::AddChild(child_handle, child_id_cert, child_resources),
|
||||
CmdDet::ChildAdd(child_handle, child_id_cert, child_resources),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn update_child(
|
||||
pub fn child_update(
|
||||
handle: &Handle,
|
||||
child_handle: ChildHandle,
|
||||
req: UpdateChildRequest,
|
||||
) -> Cmd<S> {
|
||||
eventsourcing::SentCommand::new(handle, None, CmdDet::UpdateChild(child_handle, req))
|
||||
eventsourcing::SentCommand::new(handle, None, CmdDet::ChildUpdate(child_handle, req))
|
||||
}
|
||||
|
||||
/// Certify a child. Will return an error in case the child is
|
||||
/// unknown, or in case resources are not held by the child.
|
||||
pub fn certify_child(
|
||||
pub fn child_certify(
|
||||
handle: &Handle,
|
||||
child_handle: ChildHandle,
|
||||
request: IssuanceRequest,
|
||||
@@ -113,12 +115,12 @@ impl<S: Signer> CmdDet<S> {
|
||||
eventsourcing::SentCommand::new(
|
||||
handle,
|
||||
None,
|
||||
CmdDet::CertifyChild(child_handle, request, signer),
|
||||
CmdDet::ChildCertify(child_handle, request, signer),
|
||||
)
|
||||
}
|
||||
|
||||
/// Revoke a key for a child.
|
||||
pub fn revoke_key_for_child(
|
||||
pub fn child_revoke_key(
|
||||
handle: &Handle,
|
||||
child_handle: ChildHandle,
|
||||
request: RevocationRequest,
|
||||
@@ -127,7 +129,20 @@ impl<S: Signer> CmdDet<S> {
|
||||
eventsourcing::SentCommand::new(
|
||||
handle,
|
||||
None,
|
||||
CmdDet::RevokeKeyForChild(child_handle, request, signer),
|
||||
CmdDet::ChildRevokeKey(child_handle, request, signer),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn child_shrink(
|
||||
handle: &Handle,
|
||||
child_handle: ChildHandle,
|
||||
grace: Duration,
|
||||
signer: Arc<RwLock<S>>,
|
||||
) -> Cmd<S> {
|
||||
eventsourcing::SentCommand::new(
|
||||
handle,
|
||||
None,
|
||||
CmdDet::ChildShrink(child_handle, grace, signer),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+37
-18
@@ -129,7 +129,7 @@ impl<S: Signer> CaServer<S> {
|
||||
ChildAuthRequest::Rfc8183(req) => Some(req.id_cert().clone()),
|
||||
};
|
||||
|
||||
let add_child = CmdDet::add_child(&ta_handle, handle.clone(), id_cert, resources);
|
||||
let add_child = CmdDet::child_add(&ta_handle, handle.clone(), id_cert, resources);
|
||||
|
||||
let events = ta.process_command(add_child)?;
|
||||
let ta = self.ca_store.update(&ta_handle, ta, events)?;
|
||||
@@ -172,13 +172,27 @@ impl<S: Signer> CaServer<S> {
|
||||
child: ChildHandle,
|
||||
req: UpdateChildRequest,
|
||||
) -> ServerResult<(), S> {
|
||||
debug!("Finding details for {} under TA", child);
|
||||
let ta = self.get_trust_anchor()?;
|
||||
debug!("Updating details for {} under TA", child);
|
||||
let mut ta = self.get_trust_anchor()?;
|
||||
let ta_handle = ca::ta_handle();
|
||||
|
||||
let events = ta.process_command(CmdDet::update_child(&ta_handle, child, req))?;
|
||||
let force = req.is_force();
|
||||
|
||||
let events = ta.process_command(CmdDet::child_update(&ta_handle, child.clone(), req))?;
|
||||
if !events.is_empty() {
|
||||
self.ca_store.update(&ta_handle, ta, events)?;
|
||||
ta = self.ca_store.update(&ta_handle, ta, events)?;
|
||||
|
||||
if force {
|
||||
let events = ta.process_command(CmdDet::child_shrink(
|
||||
&ta_handle,
|
||||
child,
|
||||
Duration::seconds(0),
|
||||
self.signer.clone(),
|
||||
))?;
|
||||
if !events.is_empty() {
|
||||
self.ca_store.update(&ta_handle, ta, events)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -276,7 +290,7 @@ impl<S: Signer> CaServer<S> {
|
||||
unimplemented!("Issue for multiple classes from CAs, issue #25")
|
||||
}
|
||||
|
||||
let cmd = CmdDet::certify_child(
|
||||
let cmd = CmdDet::child_certify(
|
||||
parent,
|
||||
child.clone(),
|
||||
issue_req.clone(),
|
||||
@@ -303,8 +317,7 @@ impl<S: Signer> CaServer<S> {
|
||||
) -> ServerResult<RevocationResponse, S> {
|
||||
let res = (&revoke_request).into(); // response provided that no errors are returned earlier
|
||||
|
||||
let cmd =
|
||||
CmdDet::revoke_key_for_child(ca_handle, child, revoke_request, self.signer.clone());
|
||||
let cmd = CmdDet::child_revoke_key(ca_handle, child, revoke_request, self.signer.clone());
|
||||
|
||||
let ca = self.get_ca(ca_handle)?;
|
||||
let events = ca.process_command(cmd)?;
|
||||
@@ -425,13 +438,15 @@ impl<S: Signer> CaServer<S> {
|
||||
self.send_cert_requests_handle_responses(handle, parent)
|
||||
}
|
||||
|
||||
fn send_revoke_requests_handle_responses(&self, handle: &Handle, parent: &ParentHandle) -> ServerResult<(), S> {
|
||||
fn send_revoke_requests_handle_responses(
|
||||
&self,
|
||||
handle: &Handle,
|
||||
parent: &ParentHandle,
|
||||
) -> ServerResult<(), S> {
|
||||
let mut child = self.ca_store.get_latest(handle)?;
|
||||
let requests = child.revoke_requests(parent);
|
||||
|
||||
let revoke_responses = self.send_revoke_requests(
|
||||
handle, parent, requests
|
||||
)?;
|
||||
let revoke_responses = self.send_revoke_requests(handle, parent, requests)?;
|
||||
|
||||
for response in revoke_responses.into_iter() {
|
||||
let cmd = CmdDet::key_roll_finish(handle, parent.clone(), response);
|
||||
@@ -446,7 +461,7 @@ impl<S: Signer> CaServer<S> {
|
||||
&self,
|
||||
handle: &Handle,
|
||||
parent: &ParentHandle,
|
||||
requests: Vec<&RevocationRequest>
|
||||
requests: Vec<&RevocationRequest>,
|
||||
) -> ServerResult<Vec<RevocationResponse>, S> {
|
||||
let child = self.ca_store.get_latest(handle)?;
|
||||
match child.parent(parent)?.contact() {
|
||||
@@ -469,7 +484,7 @@ impl<S: Signer> CaServer<S> {
|
||||
let mut revocation_responses = vec![];
|
||||
|
||||
for req in revoke_requests.into_iter() {
|
||||
let cmd = CmdDet::revoke_key_for_child(
|
||||
let cmd = CmdDet::child_revoke_key(
|
||||
parent_h,
|
||||
handle.clone(),
|
||||
req.clone(),
|
||||
@@ -516,7 +531,11 @@ impl<S: Signer> CaServer<S> {
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn send_cert_requests_handle_responses(&self, handle: &Handle, parent: &ParentHandle) -> ServerResult<(), S> {
|
||||
fn send_cert_requests_handle_responses(
|
||||
&self,
|
||||
handle: &Handle,
|
||||
parent: &ParentHandle,
|
||||
) -> ServerResult<(), S> {
|
||||
let mut child = self.ca_store.get_latest(handle)?;
|
||||
let cert_requests = child.cert_requests(parent);
|
||||
|
||||
@@ -561,7 +580,7 @@ impl<S: Signer> CaServer<S> {
|
||||
let class_name = req.class_name().to_string();
|
||||
let pub_key = req.csr().public_key().clone();
|
||||
|
||||
let cmd = CmdDet::certify_child(parent_h, handle.clone(), req, self.signer.clone());
|
||||
let cmd = CmdDet::child_certify(parent_h, handle.clone(), req, self.signer.clone());
|
||||
|
||||
let events = parent.process_command(cmd)?;
|
||||
parent = self.ca_store.update(parent_h, parent, events)?;
|
||||
@@ -844,7 +863,7 @@ mod tests {
|
||||
// - Child added to TA
|
||||
//
|
||||
|
||||
let cmd = CmdDet::add_child(&ta_handle, child_handle.clone(), None, child_rs);
|
||||
let cmd = CmdDet::child_add(&ta_handle, child_handle.clone(), None, child_rs);
|
||||
|
||||
let events = ta.process_command(cmd).unwrap();
|
||||
let ta = ca_store.update(&ta_handle, ta, events).unwrap();
|
||||
@@ -906,7 +925,7 @@ mod tests {
|
||||
let request = IssuanceRequest::new(DFLT_CLASS.to_string(), limit, csr);
|
||||
|
||||
let ta_cmd =
|
||||
CmdDet::certify_child(&ta_handle, child_handle.clone(), request, signer.clone());
|
||||
CmdDet::child_certify(&ta_handle, child_handle.clone(), request, signer.clone());
|
||||
|
||||
let ta_events = ta.process_command(ta_cmd).unwrap();
|
||||
let issued_evt = ta_events[0].clone().into_details();
|
||||
|
||||
@@ -10,6 +10,7 @@ use rpki::crypto::signer::KeyError;
|
||||
use rpki::crypto::{self, DigestAlgorithm, KeyIdentifier, SigningError};
|
||||
use rpki::manifest::{FileAndHash, Manifest, ManifestContent};
|
||||
use rpki::sigobj::SignedObjectBuilder;
|
||||
use rpki::uri;
|
||||
use rpki::x509::{Serial, Time, Validity};
|
||||
|
||||
use krill_commons::api::ca::{
|
||||
@@ -23,6 +24,36 @@ use krill_commons::util::softsigner::KeyId;
|
||||
pub trait Signer: crypto::Signer<KeyId = KeyId> + Clone + Sized + Sync + Send + 'static {}
|
||||
impl<T: crypto::Signer<KeyId = KeyId> + Clone + Sized + Sync + Send + 'static> Signer for T {}
|
||||
|
||||
//------------ CertSiaInfo ---------------------------------------------------
|
||||
|
||||
pub type CaRepository = uri::Rsync;
|
||||
pub type RpkiManifest = uri::Rsync;
|
||||
pub type RpkiNotify = uri::Https;
|
||||
|
||||
pub struct CertSiaInfo {
|
||||
ca_repository: CaRepository,
|
||||
rpki_manifest: RpkiManifest,
|
||||
rpki_notify: Option<RpkiNotify>,
|
||||
}
|
||||
|
||||
impl CertSiaInfo {
|
||||
pub fn new(
|
||||
ca_repository: CaRepository,
|
||||
rpki_manifest: RpkiManifest,
|
||||
rpki_notify: Option<RpkiNotify>,
|
||||
) -> Self {
|
||||
CertSiaInfo {
|
||||
ca_repository,
|
||||
rpki_manifest,
|
||||
rpki_notify,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unpack(self) -> (CaRepository, RpkiManifest, Option<RpkiNotify>) {
|
||||
(self.ca_repository, self.rpki_manifest, self.rpki_notify)
|
||||
}
|
||||
}
|
||||
|
||||
//------------ CaSignSupport -------------------------------------------------
|
||||
|
||||
/// Support signing by CAs
|
||||
|
||||
+1
-1
@@ -7,9 +7,9 @@ use std::collections::VecDeque;
|
||||
use std::fmt;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use krill_commons::api::RevocationRequest;
|
||||
use krill_commons::api::admin::Handle;
|
||||
use krill_commons::api::publication::PublishDelta;
|
||||
use krill_commons::api::RevocationRequest;
|
||||
use krill_commons::eventsourcing;
|
||||
|
||||
use crate::ca::{CertAuth, Evt, EvtDet, ParentHandle, Signer};
|
||||
|
||||
@@ -58,12 +58,14 @@ fn make_event_sh(
|
||||
}
|
||||
QueueEvent::ResourceClassRemoved(handle, parent, revocations) => {
|
||||
let revocations = revocations.iter().collect();
|
||||
if caserver.send_revoke_requests(&handle, &parent, revocations).is_err() {
|
||||
if caserver
|
||||
.send_revoke_requests(&handle, &parent, revocations)
|
||||
.is_err()
|
||||
{
|
||||
info!("Could not revoke key for removed resource class. This is not \
|
||||
an issue, because typically the parent will revoke our keys pro-actively, \
|
||||
just before removing the resource class entitlements.");
|
||||
}
|
||||
|
||||
}
|
||||
QueueEvent::ParentAdded(handle, parent) => {
|
||||
if let Err(e) = caserver.get_updates_from_parent(&handle, &parent) {
|
||||
|
||||
+61
-19
@@ -57,7 +57,18 @@ fn add_child_to_ta_rfc6492(
|
||||
}
|
||||
|
||||
fn update_child(handle: &Handle, resources: &ResourceSet) {
|
||||
let req = UpdateChildRequest::new(None, Some(resources.clone()));
|
||||
let req = UpdateChildRequest::graceful(None, Some(resources.clone()));
|
||||
match krill_admin(Command::TrustAnchor(TrustAnchorCommand::UpdateChild(
|
||||
handle.clone(),
|
||||
req,
|
||||
))) {
|
||||
ApiResponse::Empty => {}
|
||||
_ => panic!("Expected empty ok response"),
|
||||
}
|
||||
}
|
||||
|
||||
fn force_update_child(handle: &Handle, resources: &ResourceSet) {
|
||||
let req = UpdateChildRequest::force(None, Some(resources.clone()));
|
||||
match krill_admin(Command::TrustAnchor(TrustAnchorCommand::UpdateChild(
|
||||
handle.clone(),
|
||||
req,
|
||||
@@ -108,22 +119,7 @@ fn wait_for_resources_on_current_key(handle: &Handle, resources: &ResourceSet) {
|
||||
wait_for(
|
||||
30,
|
||||
"cms child did not get its resource certificate",
|
||||
move || {
|
||||
let cms_ca_info = ca_details(handle);
|
||||
|
||||
if let CaParentsInfo::Parents(parents) = cms_ca_info.parents() {
|
||||
if let Some(parent) = parents.get(&ta_handle()) {
|
||||
if let Some(rc) = parent.resources().get("all") {
|
||||
if let Some(current_resources) = rc.current_resources() {
|
||||
if resources == current_resources {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
},
|
||||
move || &ca_current_resources(handle) == resources,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -180,11 +176,47 @@ fn wait_for_resource_class_to_disappear(handle: &Handle) {
|
||||
|
||||
fn wait_for_ta_to_have_number_of_issued_certs(number: usize) {
|
||||
wait_for(30, "TA has wrong amount of issued certs", || {
|
||||
let ta = ca_details(&ta_handle());
|
||||
ta.published_objects().len() == 2 + number
|
||||
ta_issued_certs() == number
|
||||
})
|
||||
}
|
||||
|
||||
fn ta_issued_certs() -> usize {
|
||||
let ta = ca_details(&ta_handle());
|
||||
ta.published_objects().len() - 2
|
||||
}
|
||||
|
||||
fn ta_issued_resources(child: &Handle) -> ResourceSet {
|
||||
let ta = ca_details(&ta_handle());
|
||||
let child = ta.children().get(child).unwrap();
|
||||
if let Some(resources) = child.resources().get("all") {
|
||||
for cert in resources.certs_iter() {
|
||||
return cert.resource_set().clone();
|
||||
}
|
||||
}
|
||||
ResourceSet::default()
|
||||
}
|
||||
|
||||
fn ca_current_resources(handle: &Handle) -> ResourceSet {
|
||||
let ca = ca_details(handle);
|
||||
|
||||
if let CaParentsInfo::Parents(parents) = ca.parents() {
|
||||
if let Some(parent) = parents.get(&ta_handle()) {
|
||||
if let Some(rc) = parent.resources().get("all") {
|
||||
match rc.keys() {
|
||||
ResourceClassKeysInfo::Active(current)
|
||||
| ResourceClassKeysInfo::RollPending(_, current)
|
||||
| ResourceClassKeysInfo::RollNew(_, current)
|
||||
| ResourceClassKeysInfo::RollOld(current, _) => {
|
||||
return current.incoming_cert().resources().clone()
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ResourceSet::default()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ca_under_ta() {
|
||||
test_with_krill_server(|_d| {
|
||||
@@ -242,5 +274,15 @@ fn ca_under_ta() {
|
||||
wait_for_resource_class_to_disappear(&cms_child_handle);
|
||||
|
||||
wait_for_ta_to_have_number_of_issued_certs(1);
|
||||
|
||||
let emb_child_resources = ResourceSet::from_strs("", "192.168.0.0/24", "").unwrap();
|
||||
force_update_child(&emb_child_handle, &emb_child_resources);
|
||||
assert_eq!(ta_issued_resources(&emb_child_handle), emb_child_resources);
|
||||
wait_for_resources_on_current_key(&emb_child_handle, &emb_child_resources);
|
||||
|
||||
let emb_child_resources = ResourceSet::default();
|
||||
force_update_child(&emb_child_handle, &emb_child_resources);
|
||||
assert_eq!(0, ta_issued_certs());
|
||||
wait_for_resource_class_to_disappear(&emb_child_handle);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user