diff --git a/client/src/client.rs b/client/src/client.rs index 25e739a0..fe5585f6 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -119,7 +119,13 @@ impl KrillClient { Ok(ApiResponse::CertAuths(cas)) } CaCommand::KeyRollInit(handle) => { - let uri = format!("api/v1/cas/{}/keys/init_roll", handle); + let uri = format!("api/v1/cas/{}/keys/roll_init", handle); + let uri = self.resolve_uri(&uri); + self.post_empty(&uri)?; + Ok(ApiResponse::Empty) + } + CaCommand::KeyRollActivate(handle) => { + let uri = format!("api/v1/cas/{}/keys/roll_activate", handle); let uri = self.resolve_uri(&uri); self.post_empty(&uri)?; Ok(ApiResponse::Empty) diff --git a/client/src/options.rs b/client/src/options.rs index ec55b047..2a6e1725 100644 --- a/client/src/options.rs +++ b/client/src/options.rs @@ -1,9 +1,10 @@ -use clap::{App, Arg, SubCommand}; -use rpki::uri; use std::io; use std::path::PathBuf; use std::str::FromStr; +use clap::{App, Arg, SubCommand}; +use rpki::uri; + use krill_commons::api::admin::{ AddChildRequest, AddParentRequest, CertAuthInit, CertAuthPubMode, ChildAuthRequest, Handle, ParentCaContact, Token, UpdateChildRequest, @@ -241,6 +242,9 @@ impl Options { .subcommand(SubCommand::with_name("init") .about("Initialise a key roll for all active keys") ) + .subcommand(SubCommand::with_name("activate") + .about("Activate all new keys now (RFC say to do this >24H after init)") + ) ) .subcommand(SubCommand::with_name("add") @@ -496,6 +500,8 @@ impl Options { let handle = Handle::from(m.value_of("handle").unwrap()); if let Some(_m) = m.subcommand_matches("init") { command = Command::CertAuth(CaCommand::KeyRollInit(handle)); + } else if let Some(_m) = m.subcommand_matches("activate") { + command = Command::CertAuth(CaCommand::KeyRollActivate(handle)); } } @@ -622,6 +628,7 @@ pub enum CaCommand { ChildRequest(Handle), Init(CertAuthInit), KeyRollInit(Handle), + KeyRollActivate(Handle), List, Show(Handle), } diff --git a/commons/src/api/ca.rs b/commons/src/api/ca.rs index ab90ca58..b0cf0eed 100644 --- a/commons/src/api/ca.rs +++ b/commons/src/api/ca.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use std::convert::TryFrom; -use std::ops::Deref; +use std::ops::{Deref, DerefMut}; use std::str; use std::str::FromStr; use std::{fmt, ops}; @@ -12,19 +12,22 @@ use bytes::Bytes; use chrono::Duration; use rpki::cert::Cert; -use rpki::crypto::{KeyIdentifier, PublicKey}; +use rpki::crypto::KeyIdentifier; use rpki::resources::{AsBlocks, AsResources, IpBlocks, IpBlocksForFamily, IpResources}; use rpki::uri; use rpki::x509::{Serial, Time}; use crate::api::admin::{Handle, ParentCaContact, Token}; use crate::api::publication; -use crate::api::{Base64, EncodedHash, IssuanceRequest, RequestResourceLimit}; +use crate::api::{ + Base64, EncodedHash, IssuanceRequest, RequestResourceLimit, RevocationRequest, + RevocationResponse, +}; use crate::remote::id::IdCert; use crate::rpki::crl::{Crl, CrlEntry}; use crate::rpki::manifest::{FileAndHash, Manifest}; use crate::util::ext_serde; -use crate::util::softsigner::SignerKeyId; +use crate::util::softsigner::KeyId; //------------ ChildCaInfo --------------------------------------------------- @@ -125,6 +128,11 @@ impl ChildCaDetails { // been issued to it. So, it's safe to unwrap here. self.resources.get_mut(class_name).unwrap().add_cert(cert) } + + pub fn revoke_key(&mut self, revocation: RevocationResponse) { + let (class_name, key_id) = revocation.unpack(); + self.resources.get_mut(&class_name).unwrap().revoke(&key_id) + } } /// This type defines a reference to PublicKey for easy storage and lookup. @@ -200,8 +208,8 @@ impl ChildResources { self.certs.values() } - pub fn cert(&self, pub_key: &PublicKey) -> Option<&IssuedCert> { - let key_ref = KeyRef::from(&pub_key.key_identifier()); + pub fn cert(&self, key_id: &KeyIdentifier) -> Option<&IssuedCert> { + let key_ref = KeyRef::from(key_id); self.certs.get(&key_ref) } @@ -212,6 +220,11 @@ impl ChildResources { self.resources = ResourceSet::try_from(cert.cert()).unwrap(); self.certs.insert(key_ref, cert); } + + pub fn revoke(&mut self, key_id: &KeyIdentifier) { + let key_ref = KeyRef::from(key_id); + self.certs.remove(&key_ref); + } } //------------ IssuedCert ---------------------------------------------------- @@ -465,22 +478,22 @@ impl Eq for RepoInfo {} /// when a certificate is received. #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct PendingKey { - key_id: SignerKeyId, + key_id: KeyId, request: Option, } impl PendingKey { - pub fn new(key_id: SignerKeyId) -> Self { + pub fn new(key_id: KeyId) -> Self { PendingKey { key_id, request: None, } } - pub fn unwrap(self) -> (SignerKeyId, Option) { + pub fn unwrap(self) -> (KeyId, Option) { (self.key_id, self.request) } - pub fn key_id(&self) -> &SignerKeyId { + pub fn key_id(&self) -> &KeyId { &self.key_id } pub fn request(&self) -> Option<&IssuanceRequest> { @@ -494,20 +507,55 @@ impl PendingKey { } } +//------------ OldKey -------------------------------------------------------- + +#[derive(Clone, Debug, Deserialize, Eq, Serialize, PartialEq)] +pub struct OldKey { + key: CertifiedKey, + revoke_req: RevocationRequest, +} + +impl OldKey { + pub fn new(key: CertifiedKey, revoke_req: RevocationRequest) -> Self { + OldKey { key, revoke_req } + } + + pub fn key(&self) -> &CertifiedKey { + &self.key + } + pub fn revoke_req(&self) -> &RevocationRequest { + &self.revoke_req + } +} + +impl Deref for OldKey { + type Target = CertifiedKey; + + fn deref(&self) -> &Self::Target { + &self.key + } +} + +impl DerefMut for OldKey { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.key + } +} + //------------ CertifiedKey -------------------------------------------------- #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] /// Describes a Key that is certified. I.e. it received an incoming certificate /// and has at least a MFT and CRL. pub struct CertifiedKey { - key_id: SignerKeyId, + key_id: KeyId, incoming_cert: RcvdCert, current_set: CurrentObjectSet, request: Option, } impl CertifiedKey { - pub fn new(key_id: SignerKeyId, incoming_cert: RcvdCert) -> Self { + pub fn new(key_id: KeyId, incoming_cert: RcvdCert) -> Self { let current_set = CurrentObjectSet::default(); CertifiedKey { @@ -518,7 +566,7 @@ impl CertifiedKey { } } - pub fn key_id(&self) -> &SignerKeyId { + pub fn key_id(&self) -> &KeyId { &self.key_id } pub fn incoming_cert(&self) -> &RcvdCert { @@ -760,26 +808,30 @@ impl ops::Add for CurrentObjects { #[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct Revocation { serial: Serial, - revocation_date: Time, + expires: Time, } impl From<&CurrentObject> for Revocation { fn from(co: &CurrentObject) -> Self { Revocation { - serial: co.serial, - revocation_date: Time::now(), + serial: co.serial(), + expires: co.expires(), + } + } +} + +impl From<&Cert> for Revocation { + fn from(cer: &Cert) -> Self { + Revocation { + serial: cer.serial_number(), + expires: cer.validity().not_after(), } } } impl From<&Manifest> for Revocation { fn from(m: &Manifest) -> Self { - let serial = m.cert().serial_number(); - let revocation_date = Time::now(); - Revocation { - serial, - revocation_date, - } + Self::from(m.cert()) } } @@ -792,16 +844,13 @@ impl Revocations { pub fn to_crl_entries(&self) -> Vec { self.0 .iter() - .map(|r| CrlEntry::new(r.serial, r.revocation_date)) + .map(|r| CrlEntry::new(r.serial, r.expires)) .collect() } /// Purges all expired revocations, and returns them. pub fn purge(&mut self) -> Vec { - let (relevant, expired) = self - .0 - .iter() - .partition(|r| r.revocation_date.validate_not_after(Time::now()).is_ok()); + let (relevant, expired) = self.0.iter().partition(|r| r.expires > Time::now()); self.0 = relevant; expired } @@ -1444,7 +1493,6 @@ pub enum ResourceClassKeysInfo { type NewKey = CertifiedKey; type CurrentKey = CertifiedKey; -type OldKey = CertifiedKey; impl fmt::Display for ResourceClassKeysInfo { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { diff --git a/commons/src/api/provisioning.rs b/commons/src/api/provisioning.rs index 9b3c2de0..7c1789f4 100644 --- a/commons/src/api/provisioning.rs +++ b/commons/src/api/provisioning.rs @@ -370,3 +370,48 @@ impl RevocationRequest { &self.key } } + +//------------ RevocationResponse -------------------------------------------- + +/// This type represents a Certificate Revocation Response as +/// defined in section 3.5.2 of RFC6492. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct RevocationResponse { + class_name: String, + key: KeyIdentifier, +} + +impl RevocationResponse { + pub fn new(class_name: String, key: KeyIdentifier) -> Self { + RevocationResponse { class_name, key } + } + + pub fn unpack(self) -> (String, KeyIdentifier) { + (self.class_name, self.key) + } + + pub fn class_name(&self) -> &str { + &self.class_name + } + pub fn key(&self) -> &KeyIdentifier { + &self.key + } +} + +impl From<&RevocationRequest> for RevocationResponse { + fn from(req: &RevocationRequest) -> Self { + RevocationResponse { + class_name: req.class_name.clone(), + key: req.key, + } + } +} + +impl From for RevocationResponse { + fn from(req: RevocationRequest) -> Self { + RevocationResponse { + class_name: req.class_name, + key: req.key, + } + } +} diff --git a/commons/src/api/publication.rs b/commons/src/api/publication.rs index e010ec0a..1aad8636 100644 --- a/commons/src/api/publication.rs +++ b/commons/src/api/publication.rs @@ -1,7 +1,10 @@ //! Support for requests sent to the Json API +use std::ops; + +use rpki::uri; + use crate::api::{Base64, EncodedHash}; use crate::util::file::CurrentFile; -use rpki::uri; //------------ PublishRequest ------------------------------------------------ @@ -33,6 +36,10 @@ impl PublishDelta { } } + pub fn empty() -> Self { + Self::default() + } + pub fn publishes(&self) -> &Vec { &self.publishes } @@ -56,6 +63,27 @@ impl PublishDelta { } } +impl Default for PublishDelta { + fn default() -> Self { + PublishDelta { + publishes: vec![], + updates: vec![], + withdraws: vec![], + } + } +} + +impl ops::Add for PublishDelta { + type Output = PublishDelta; + + fn add(mut self, mut other: Self) -> Self::Output { + self.publishes.append(&mut other.publishes); + self.updates.append(&mut other.updates); + self.withdraws.append(&mut other.withdraws); + self + } +} + //------------ PublishDeltaBuilder ------------------------------------------- #[derive(Default)] diff --git a/commons/src/remote/api.rs b/commons/src/remote/api.rs index 88740447..e6fff4f1 100644 --- a/commons/src/remote/api.rs +++ b/commons/src/remote/api.rs @@ -1,7 +1,7 @@ use rpki::uri; use crate::api::admin::Handle; -use crate::util::softsigner::SignerKeyId; +use crate::util::softsigner::KeyId; use crate::remote::id::IdCert; @@ -55,17 +55,12 @@ impl ClientInfo { pub struct CmsClientInfo { handle: Handle, server_cert: IdCert, - key_id: SignerKeyId, + key_id: KeyId, publication_uri: uri::Https, } impl CmsClientInfo { - pub fn new( - handle: Handle, - cert: IdCert, - key_id: SignerKeyId, - publication_uri: uri::Https, - ) -> Self { + pub fn new(handle: Handle, cert: IdCert, key_id: KeyId, publication_uri: uri::Https) -> Self { CmsClientInfo { handle, server_cert: cert, @@ -86,10 +81,10 @@ impl CmsClientInfo { pub fn set_server_cert(&mut self, cert: IdCert) { self.server_cert = cert; } - pub fn key_id(&self) -> &SignerKeyId { + pub fn key_id(&self) -> &KeyId { &self.key_id } - pub fn set_key_id(&mut self, key_id: SignerKeyId) { + pub fn set_key_id(&mut self, key_id: KeyId) { self.key_id = key_id; } pub fn publication_uri(&self) -> &uri::Https { diff --git a/commons/src/remote/id.rs b/commons/src/remote/id.rs index 8cd3f992..6a12c754 100644 --- a/commons/src/remote/id.rs +++ b/commons/src/remote/id.rs @@ -11,7 +11,7 @@ use rpki::uri; use rpki::x509::{Name, SignedData, Time, ValidationError, Validity}; use crate::remote::rfc8183::ServiceUri; -use crate::util::softsigner::SignerKeyId; +use crate::util::softsigner::KeyId; //------------ MyIdentity ---------------------------------------------------- @@ -23,11 +23,11 @@ pub struct MyIdentity { id_cert: IdCert, - key_id: SignerKeyId, + key_id: KeyId, } impl MyIdentity { - pub fn new(name: &str, id_cert: IdCert, key_id: SignerKeyId) -> Self { + pub fn new(name: &str, id_cert: IdCert, key_id: KeyId) -> Self { MyIdentity { name: name.to_string(), id_cert, @@ -47,7 +47,7 @@ impl MyIdentity { /// The identifier that the Signer needs to use the key for the identity /// certificate. - pub fn key_id(&self) -> &SignerKeyId { + pub fn key_id(&self) -> &KeyId { &self.key_id } } diff --git a/commons/src/remote/proxy.rs b/commons/src/remote/proxy.rs index 1d3be8d0..5b88d7b5 100644 --- a/commons/src/remote/proxy.rs +++ b/commons/src/remote/proxy.rs @@ -5,6 +5,7 @@ use std::sync::Arc; use bcder::encode::Values; use bcder::{Captured, Mode}; +use rpki::crypto::{PublicKeyFormat, Signer}; use rpki::uri; use rpki::x509::ValidationError; @@ -25,7 +26,6 @@ use crate::remote::rfc8183::ServiceUri; use crate::remote::sigmsg::SignedMessage; use crate::util::httpclient; use crate::util::softsigner::{OpenSslSigner, SignerError}; -use rpki::crypto::{PublicKeyFormat, Signer}; #[derive(Clone)] pub struct ProxyServer { diff --git a/commons/src/remote/rfc6492.rs b/commons/src/remote/rfc6492.rs index 25c407f7..4b59f227 100644 --- a/commons/src/remote/rfc6492.rs +++ b/commons/src/remote/rfc6492.rs @@ -1,6 +1,6 @@ use std::convert::TryFrom; -use std::io; use std::str::FromStr; +use std::{fmt, io}; use bytes::Bytes; use chrono::{DateTime, SecondsFormat, Utc}; @@ -16,7 +16,7 @@ use crate::api::admin::Handle; use crate::api::ca::{IssuedCert, ResSetErr, ResourceSet}; use crate::api::{ EntitlementClass, Entitlements, IssuanceRequest, IssuanceResponse, RequestResourceLimit, - RevocationRequest, SigningCert, + RevocationRequest, RevocationResponse, SigningCert, }; use crate::remote::sigmsg::SignedMessage; use crate::rpki::resources::{AsBlocks, IpBlocks}; @@ -141,7 +141,7 @@ impl Message { pub fn revoke_response( sender: String, recipient: String, - revocation: RevocationRequest, + revocation: RevocationResponse, ) -> Self { let content = Content::Res(Res::Revoke(revocation)); Message { @@ -151,12 +151,12 @@ impl Message { } } - pub fn error_response( + pub fn not_performed_response( sender: String, recipient: String, err: NotPerformedResponse, ) -> Result { - let content = Content::Res(Res::Error(err)); + let content = Content::Res(Res::NotPerformed(err)); Ok(Message { sender, recipient, @@ -295,14 +295,15 @@ impl Qry { where R: io::Read, { - r.take_named_element("key", |mut a, r| { + r.take_named_element("key", |mut a, _r| { let class_name = a.take_req("class_name")?; + let ski = a.take_req("ski")?; + let ski_bytes = base64::decode_config(&ski, base64::URL_SAFE_NO_PAD) + .map_err(|_| Error::InvalidSki)?; + a.exhausted()?; - let ski_bytes = r.take_bytes_url_safe_pad()?; - let ski = KeyIdentifier::try_from(ski_bytes.as_ref()).map_err(|_| Error::InvalidSki)?; - Ok(RevocationRequest::new(class_name.to_string(), ski)) }) } @@ -386,9 +387,10 @@ impl Qry { rev: &RevocationRequest, w: &mut XmlWriter, ) -> Result<(), io::Error> { - let att = [("class_name", rev.class_name())]; let bytes = rev.key().as_slice(); - w.put_element("key", Some(&att), |w| w.put_base64_url_safe(bytes)) + let encoded = base64::encode_config(bytes, base64::URL_SAFE_NO_PAD); + let att = [("class_name", rev.class_name()), ("ski", encoded.as_str())]; + w.put_element("key", Some(&att), |w| w.empty()) } } @@ -400,8 +402,8 @@ impl Qry { pub enum Res { List(Entitlements), Issue(IssuanceResponse), - Revoke(RevocationRequest), - Error(NotPerformedResponse), + Revoke(RevocationResponse), + NotPerformed(NotPerformedResponse), } /// # Data Access @@ -412,7 +414,7 @@ impl Res { Res::List(_) => TYPE_LIST_RES, Res::Issue(_) => TYPE_ISSUE_RES, Res::Revoke(_) => TYPE_REVOKE_RES, - Res::Error(_) => TYPE_ERROR_RES, + Res::NotPerformed(_) => TYPE_ERROR_RES, } } } @@ -435,11 +437,11 @@ impl Res { } TYPE_REVOKE_RES => { let request = Qry::decode_revoke(r)?; - Ok(Res::Revoke(request)) + Ok(Res::Revoke(request.into())) } TYPE_ERROR_RES => { let err = Self::decode_error_response(r)?; - Ok(Res::Error(err)) + Ok(Res::NotPerformed(err)) } _ => Err(Error::UnknownMessageType), } @@ -595,9 +597,18 @@ impl Res { { let code = r.take_named_element("status", |_a, r| r.take_chars())?; - let _desc = r.take_named_element("description", |_a, r| r.take_chars())?; + let desc = r.take_named_element("description", |_a, r| r.take_chars())?; - NotPerformedResponse::from_code(&code) + match NotPerformedResponse::from_code(&code) { + Ok(res) => Ok(res), + Err(e) => { + error!( + "Strange error response with code: {}, description: {}", + code, desc + ); + Err(e) + } + } } } @@ -608,8 +619,8 @@ impl Res { match self { Res::List(ents) => Self::encode_entitlements(ents, w), Res::Issue(response) => Self::encode_issuance_response(response, w), - Res::Revoke(request) => Qry::encode_revoke(request, w), - Res::Error(err) => Self::encode_error_response(err, w), + Res::Revoke(response) => Self::encode_revoke_reponse(response, w), + Res::NotPerformed(err) => Self::encode_error_response(err, w), } } @@ -732,6 +743,16 @@ impl Res { w.put_text(&error.description) }) } + + fn encode_revoke_reponse( + res: &RevocationResponse, + w: &mut XmlWriter, + ) -> Result<(), io::Error> { + let bytes = res.key().as_slice(); + let encoded = base64::encode_config(bytes, base64::URL_SAFE_NO_PAD); + let att = [("class_name", res.class_name()), ("ski", encoded.as_str())]; + w.put_element("key", Some(&att), |w| w.empty()) + } } //------------ NotPerformedResponse ------------------------------------------ @@ -838,6 +859,16 @@ impl NotPerformedResponse { } } +impl fmt::Display for NotPerformedResponse { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "status: {}, description: {}", + self.status, &self.description + ) + } +} + //------------ Error --------------------------------------------------------- #[derive(Debug, Display)] @@ -1034,7 +1065,7 @@ mod tests { let class = "all".to_string(); let ski = cert.subject_public_key_info().key_identifier(); - let revocation = RevocationRequest::new(class, ski); + let revocation = RevocationResponse::new(class, ski); let rev = Message::revoke_response(sender, rcpt, revocation); let decoded_rev = Message::decode(rev.encode_vec().as_slice()).unwrap(); @@ -1050,7 +1081,7 @@ mod tests { let rcpt = "parent".to_string(); let err = NotPerformedResponse::_1101(); - let err = Message::error_response(sender, rcpt, err).unwrap(); + let err = Message::not_performed_response(sender, rcpt, err).unwrap(); let decoded = Message::decode(err.encode_vec().as_slice()).unwrap(); assert_eq!(err, decoded); diff --git a/commons/src/util/softsigner.rs b/commons/src/util/softsigner.rs index a5debacf..58c06773 100644 --- a/commons/src/util/softsigner.rs +++ b/commons/src/util/softsigner.rs @@ -1,39 +1,41 @@ //! Support for signing things using software keys (through openssl) and //! storing them unencrypted on disk. +use std::{fs, io}; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + use bytes::Bytes; use openssl::error::ErrorStack; use openssl::hash::MessageDigest; use openssl::pkey::{PKey, PKeyRef, Private}; use openssl::rsa::Rsa; +use serde::{de, ser}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use rpki::crypto::signer::KeyError; use rpki::crypto::{ PublicKey, PublicKeyFormat, Signature, SignatureAlgorithm, Signer, SigningError, }; -use serde::{de, ser}; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::fs::File; -use std::io::Write; -use std::path::PathBuf; -use std::{fs, io}; -//------------ SignerKeyId --------------------------------------------------- +//------------ KeyId --------------------------------------------------------- -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct SignerKeyId(String); +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct KeyId(String); -impl SignerKeyId { +impl KeyId { pub fn new(s: &str) -> Self { - SignerKeyId(s.to_string()) + KeyId(s.to_string()) } } -impl AsRef for SignerKeyId { +impl AsRef for KeyId { fn as_ref(&self) -> &str { &self.0 } } -impl Serialize for SignerKeyId { +impl Serialize for KeyId { fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -42,13 +44,13 @@ impl Serialize for SignerKeyId { } } -impl<'de> Deserialize<'de> for SignerKeyId { +impl<'de> Deserialize<'de> for KeyId { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { let s = String::deserialize(deserializer)?; - Ok(SignerKeyId::new(&s)) + Ok(KeyId::new(&s)) } } @@ -95,7 +97,7 @@ impl OpenSslSigner { Ok(signature) } - fn load_key(&self, id: &SignerKeyId) -> Result { + fn load_key(&self, id: &KeyId) -> Result { let path = self.key_path(id); if path.exists() { let f = File::open(path)?; @@ -106,7 +108,7 @@ impl OpenSslSigner { } } - fn key_path(&self, key_id: &SignerKeyId) -> PathBuf { + fn key_path(&self, key_id: &KeyId) -> PathBuf { let mut path = self.keys_dir.clone(); path.push(key_id.as_ref()); path @@ -114,7 +116,7 @@ impl OpenSslSigner { } impl Signer for OpenSslSigner { - type KeyId = SignerKeyId; + type KeyId = KeyId; type Error = SignerError; fn create_key(&mut self, _algorithm: PublicKeyFormat) -> Result { @@ -122,7 +124,7 @@ impl Signer for OpenSslSigner { let pk = &kp.subject_public_key_info()?; let hex_hash = hex::encode(pk.key_identifier().as_ref()); - let key_id = SignerKeyId(hex_hash); + let key_id = KeyId(hex_hash); let path = self.key_path(&key_id); let json = serde_json::to_string(&kp)?; diff --git a/commons/src/util/xml.rs b/commons/src/util/xml.rs index bcd9c178..97760e47 100644 --- a/commons/src/util/xml.rs +++ b/commons/src/util/xml.rs @@ -1,12 +1,13 @@ //! Support for RPKI XML structures. +use std::{fs, io}; +use std::fs::File; +use std::path::Path; + use base64; use base64::DecodeError; use bytes::Bytes; use hex; use hex::FromHexError; -use std::fs::File; -use std::path::Path; -use std::{fs, io}; use xmlrs::attribute::OwnedAttribute; use xmlrs::reader::XmlEvent; use xmlrs::{reader, writer}; @@ -220,11 +221,6 @@ impl XmlReader { self.take_bytes(base64::STANDARD_NO_PAD) } - /// Takes base64 encoded bytes from the next 'characters' event. - pub fn take_bytes_url_safe_pad(&mut self) -> Result { - self.take_bytes(base64::URL_SAFE_NO_PAD) - } - fn take_bytes(&mut self, config: base64::Config) -> Result { let chars = self.take_chars()?; // strip whitespace and padding (we are liberal in what we accept here) @@ -486,13 +482,6 @@ impl XmlWriter { self.put_text(b64.as_ref()) } - /// Converts bytes to base64 encoded Characters as the content, using the - /// URL safe character set and padding. - pub fn put_base64_url_safe(&mut self, bytes: &[u8]) -> Result<(), io::Error> { - let b64 = base64::encode_config(bytes, base64::URL_SAFE); - self.put_text(b64.as_ref()) - } - /// Use this for convenience where empty content is required pub fn empty(&mut self) -> Result<(), io::Error> { Ok(()) diff --git a/daemon/src/ca/certauth.rs b/daemon/src/ca/certauth.rs index cdbdcf82..440b1a08 100644 --- a/daemon/src/ca/certauth.rs +++ b/daemon/src/ca/certauth.rs @@ -16,11 +16,11 @@ use krill_commons::api::admin::{ use krill_commons::api::ca::{ AddedObject, CaParentsInfo, CertAuthInfo, CertifiedKey, ChildCaDetails, CurrentObject, IssuedCert, ObjectName, ObjectsDelta, ParentCaInfo, PublicationDelta, RcvdCert, RepoInfo, - ResourceSet, TrustAnchorInfo, TrustAnchorLocator, UpdatedObject, + ResourceSet, Revocation, TrustAnchorInfo, TrustAnchorLocator, UpdatedObject, WithdrawnObject, }; use krill_commons::api::{ self, EncodedHash, EntitlementClass, Entitlements, IssuanceRequest, IssuanceResponse, - SigningCert, DFLT_CLASS, + RevocationRequest, RevocationResponse, SigningCert, DFLT_CLASS, }; use krill_commons::eventsourcing::{Aggregate, StoredEvent}; use krill_commons::remote::builder::{IdCertBuilder, SignedMessageBuilder}; @@ -28,7 +28,7 @@ use krill_commons::remote::id::IdCert; use krill_commons::remote::rfc6492; use krill_commons::remote::rfc8183::ChildRequest; use krill_commons::remote::sigmsg::SignedMessage; -use krill_commons::util::softsigner::SignerKeyId; +use krill_commons::util::softsigner::KeyId; use crate::ca::{ self, ChildHandle, Cmd, CmdDet, Error, Evt, EvtDet, Ini, ParentHandle, ResourceClass, @@ -39,7 +39,7 @@ use crate::ca::{ #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct Rfc8183Id { - key: SignerKeyId, + key: KeyId, cert: IdCert, } @@ -210,18 +210,21 @@ impl Aggregate for CertAuth { fn apply(&mut self, event: Evt) { self.version += 1; match event.into_details() { - //----------------------------------------------------------------------- // Being a parent //----------------------------------------------------------------------- EvtDet::ChildAdded(child, details) => { self.children.insert(child, details); } - EvtDet::CertificateIssued(child, response) => { + EvtDet::ChildCertificateIssued(child, response) => { let (class_name, _, _, issued) = response.unwrap(); let child = self.children.get_mut(&child).unwrap(); child.add_cert(&class_name, issued); } + EvtDet::ChildKeyRevoked(child, response) => { + let child = self.children.get_mut(&child).unwrap(); + child.revoke_key(response); + } EvtDet::ChildUpdatedToken(child, token) => { let child = self.children.get_mut(&child).unwrap(); child.set_token(token); @@ -271,19 +274,31 @@ impl Aggregate for CertAuth { //----------------------------------------------------------------------- // Key Roll //----------------------------------------------------------------------- - EvtDet::KeyrollPendingKeyAdded(parent, class_name, key_id) => { + EvtDet::KeyRollPendingKeyAdded(parent, class_name, key_id) => { let parent = self.parent_mut(&parent).unwrap(); let rc = parent.class_mut(&class_name).unwrap(); rc.pending_key_added(key_id); } + EvtDet::KeyRollActivated(parent, class_name, revoke_req) => { + let parent = self.parent_mut(&parent).unwrap(); + let rc = parent.class_mut(&class_name).unwrap(); + rc.new_key_activated(revoke_req); + } + EvtDet::KeyRollFinished(parent, class_name, _delta) => { + let parent = self.parent_mut(&parent).unwrap(); + let rc = parent.class_mut(&class_name).unwrap(); + rc.old_key_removed(); + } //----------------------------------------------------------------------- // General functions //----------------------------------------------------------------------- - EvtDet::Published(parent, class_name, key_id, delta) => { + EvtDet::Published(parent, class_name, delta_map) => { let parent = self.parent_mut(&parent).unwrap(); let rc = parent.class_mut(&class_name).unwrap(); - rc.apply_delta(delta, key_id); + for (key_id, delta) in delta_map.into_iter() { + rc.apply_delta(delta, key_id); + } } EvtDet::TaPublished(delta) => { let ta_key = self.ta_key_mut().unwrap(); @@ -302,6 +317,9 @@ impl Aggregate for CertAuth { CmdDet::CertifyChild(child, request, token, signer) => { self.certify_child(child, request, token, signer) } + CmdDet::RevokeKeyForChild(child, request, signer) => { + self.revoke_child_key(child, request, signer) + } // being a child CmdDet::AddParent(parent, info) => self.add_parent(parent, info), @@ -314,8 +332,8 @@ impl Aggregate for CertAuth { // Key rolls CmdDet::KeyRollInitiate(duration, signer) => self.keyroll_initiate(duration, signer), - CmdDet::KeyRollActivate(duration) => self.keyroll_activate(duration), - CmdDet::KeyRollFinish(parent, class_name) => self.keyroll_finish(parent, class_name), + CmdDet::KeyRollActivate(duration, signer) => self.keyroll_activate(duration, signer), + CmdDet::KeyRollFinish(parent, response) => self.keyroll_finish(parent, response), // Republish CmdDet::Republish(signer) => self.republish(signer), @@ -358,7 +376,7 @@ impl CertAuth { pub fn id_cert(&self) -> &IdCert { &self.id.cert } - pub fn id_key(&self) -> &SignerKeyId { + pub fn id_key(&self) -> &KeyId { &self.id.key } pub fn handle(&self) -> &Handle { @@ -563,7 +581,13 @@ impl CertAuth { .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()); + 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 = { @@ -636,7 +660,7 @@ impl CertAuth { issued_cert.clone(), ); - let issued_event = EvtDet::certificate_issued(&self.handle, version, child, response); + let issued_event = EvtDet::child_certificate_issued(&self.handle, version, child, response); let delta = { let ca_repo = self.base_repo.ca_repository(""); @@ -647,7 +671,7 @@ impl CertAuth { 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.update(UpdatedObject::new(cert_name, cert_object, old_hash)); } } delta @@ -656,7 +680,7 @@ impl CertAuth { let publish_event = EvtDet::published_ta( &self.handle, version + 1, - SignSupport::publish(signer, issuing_key, &self.base_repo, "", delta) + SignSupport::publish(signer, issuing_key, &self.base_repo, "", delta, revocations) .map_err(Error::signer)?, ); @@ -754,6 +778,60 @@ impl CertAuth { Ok(res) } + /// Revokes a key for a child. So, add all certs for the key to the CRL, and withdraw + /// the .cer file for it. + fn revoke_child_key( + &self, + child: ChildHandle, + request: RevocationRequest, + signer: Arc>, + ) -> ca::Result> { + // verify child and resources + let class_name = request.class_name(); + let child_resources = self + .get_child(&child)? + .resources_for_class(class_name) + .ok_or_else(|| Error::MissingResourceClass)?; + + let ca_key = match &self.parents { + CaParents::SelfSigned(key, _) => key, + CaParents::Parents(_map) => unimplemented!("Issue #25"), + }; + + // TODO For #25 find correct namespace for matching RC + let name_space = ""; + + if let Some(last_cert) = child_resources.cert(request.key()) { + let response = request.into(); + + let name = ObjectName::from(last_cert.cert()); + let current_object = CurrentObject::from(last_cert.cert()); + let withdrawn = WithdrawnObject::for_current(name, ¤t_object); + + let revocations = vec![Revocation::from(last_cert.cert())]; + + let mut objects_delta = ObjectsDelta::new(self.base_repo.ca_repository(name_space)); + objects_delta.withdraw(withdrawn); + + let pub_delta = SignSupport::publish( + signer, + ca_key, + &self.base_repo, + name_space, + objects_delta, + revocations, + ) + .map_err(Error::signer)?; + + let revoked = EvtDet::child_revoke_key(&self.handle, self.version, child, response); + let published = EvtDet::published_ta(&self.handle, self.version + 1, pub_delta); + + Ok(vec![revoked, published]) + } else { + Err(Error::NoIssuedCert) + } + } + /// Returns `true` if the child is known, `false` otherwise. No errors. fn has_child(&self, child_handle: &Handle) -> bool { self.children.contains_key(child_handle) @@ -775,7 +853,7 @@ impl CertAuth { } } - fn parent(&self, parent: &Handle) -> Result<&ParentCa> { + pub fn parent(&self, parent: &Handle) -> Result<&ParentCa> { self.parents.get(parent) } @@ -833,6 +911,19 @@ impl CertAuth { Ok(res) } + /// Returns the open revocation requests for the given parent. + pub fn revoke_requests(&self, parent: &ParentHandle) -> Vec<&RevocationRequest> { + let mut res = vec![]; + if let Ok(parent) = self.parent(parent) { + for (_class_name, rc) in parent.resources.iter() { + if let Some(req) = rc.revoke_request() { + res.push(req) + } + } + } + res + } + /// This processes entitlements from a parent, and updates the known /// entitlement(s) and/or requests certificate(s) as needed. In case /// there are no changes in entitlements and certificates, this method @@ -938,7 +1029,7 @@ impl CertAuth { rcvd_cert: RcvdCert, signer: Arc>, ) -> ca::Result> { - info!( + debug!( "CA {}: Updating received cert for class: {}", self.handle, class_name ); @@ -977,19 +1068,17 @@ impl CertAuth { for (parent_handle, parent) in map.iter() { for (class_name, class) in parent.resources().iter() { - - for details in class.keyroll_initiate( - parent_handle.clone(), - class_name.clone(), - &self.base_repo, - duration, - signer.deref_mut() - )?.into_iter() { - res.push(StoredEvent::new( - self.handle(), - version, - details - )); + for details in class + .keyroll_initiate( + parent_handle.clone(), + class_name.clone(), + &self.base_repo, + duration, + signer.deref_mut(), + )? + .into_iter() + { + res.push(StoredEvent::new(self.handle(), version, details)); version += 1; } } @@ -1000,16 +1089,62 @@ impl CertAuth { } } - fn keyroll_activate(&self, _duration: Duration) -> ca::Result> { - unimplemented!() + fn keyroll_activate(&self, staging: Duration, signer: Arc>) -> ca::Result> { + match &self.parents { + CaParents::SelfSigned(_, _) => Ok(vec![]), + CaParents::Parents(map) => { + let signer = signer.read().unwrap(); + let mut version = self.version; + let mut res = vec![]; + + for (parent_handle, parent) in map.iter() { + for (class_name, class) in parent.resources().iter() { + for details in class + .keyroll_activate( + parent_handle.clone(), + class_name.clone(), + staging, + signer.deref(), + )? + .into_iter() + { + res.push(StoredEvent::new(self.handle(), version, details)); + version += 1; + } + } + } + Ok(res) + } + } } fn keyroll_finish( &self, - _parent: ParentHandle, - _class_name: ResourceClassName, + parent_h: ParentHandle, + response: RevocationResponse, ) -> ca::Result> { - unimplemented!() + match &self.parents { + CaParents::SelfSigned(_, _) => Ok(vec![]), + CaParents::Parents(map) => { + let (class_name, _key_id) = response.unpack(); + let parent = map + .get(&parent_h) + .ok_or_else(|| Error::UnknownParent(parent_h.clone()))?; + + let rc = parent + .resources() + .get(&class_name) + .ok_or_else(|| Error::UnknownResourceClass(class_name.clone()))?; + + let finish_details = rc.keyroll_finish(parent_h, class_name, &self.base_repo)?; + + Ok(vec![StoredEvent::new( + self.handle(), + self.version, + finish_details, + )]) + } + } } } @@ -1024,7 +1159,7 @@ impl CertAuth { ) -> Result { let ca_repo = repo_info.ca_repository(name_space); let objects_delta = ObjectsDelta::new(ca_repo); - SignSupport::publish(signer, key, repo_info, name_space, objects_delta) + SignSupport::publish(signer, key, repo_info, name_space, objects_delta, vec![]) .map_err(Error::signer) } @@ -1040,7 +1175,7 @@ impl CertAuth { res.push(EvtDet::published_ta(&self.handle, self.version, delta)) } } - CaParents::Parents(_map) => unimplemented!(), + CaParents::Parents(_map) => error!("Republishing CAs not implemented"), } Ok(res) } diff --git a/daemon/src/ca/commands.rs b/daemon/src/ca/commands.rs index 7a31a715..ce05cbb9 100644 --- a/daemon/src/ca/commands.rs +++ b/daemon/src/ca/commands.rs @@ -4,7 +4,7 @@ use chrono::Duration; use krill_commons::api::admin::{Handle, ParentCaContact, Token, UpdateChildRequest}; use krill_commons::api::ca::{RcvdCert, ResourceSet}; -use krill_commons::api::{Entitlements, IssuanceRequest}; +use krill_commons::api::{Entitlements, IssuanceRequest, RevocationRequest, RevocationResponse}; use krill_commons::eventsourcing; use krill_commons::remote::id::IdCert; @@ -29,6 +29,8 @@ pub enum CmdDet { UpdateChild(ChildHandle, UpdateChildRequest), // Process an issuance request by an existing child. CertifyChild(ChildHandle, IssuanceRequest, Token, Arc>), + // Process a revoke request by an existing child. + RevokeKeyForChild(ChildHandle, RevocationRequest, Arc>), // ------------------------------------------------------------ // Being a child (only allowed if this CA is not self-signed) @@ -59,11 +61,11 @@ pub enum CmdDet { // // RFC6489 dictates that 24 hours MUST be observed. However, shorter time frames can // be used for testing, and in case of emergency rolls. - KeyRollActivate(Duration), + KeyRollActivate(Duration, Arc>), // Finish the keyroll after the parent confirmed that a key for a parent and resource // class has been revoked. I.e. remove the old key, and withdraw the crl and mft for it. - KeyRollFinish(ParentHandle, ResourceClassName), + KeyRollFinish(ParentHandle, RevocationResponse), // ------------------------------------------------------------ // Publishing @@ -104,8 +106,8 @@ impl CmdDet { /// 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( - handle: &ParentHandle, - child_handle: Handle, + handle: &Handle, + child_handle: ChildHandle, request: IssuanceRequest, token: Token, signer: Arc>, @@ -117,42 +119,71 @@ impl CmdDet { ) } - pub fn add_parent(handle: &Handle, name: &str, info: ParentCaContact) -> Cmd { - eventsourcing::SentCommand::new(handle, None, CmdDet::AddParent(Handle::from(name), info)) + /// Revoke a key for a child. + pub fn revoke_key_for_child( + handle: &Handle, + child_handle: ChildHandle, + request: RevocationRequest, + signer: Arc>, + ) -> Cmd { + eventsourcing::SentCommand::new( + handle, + None, + CmdDet::RevokeKeyForChild(child_handle, request, signer), + ) + } + + pub fn add_parent(handle: &Handle, parent: ParentHandle, info: ParentCaContact) -> Cmd { + eventsourcing::SentCommand::new(handle, None, CmdDet::AddParent(parent, info)) } pub fn upd_entitlements( handle: &Handle, - parent: &ParentHandle, + parent: ParentHandle, entitlements: Entitlements, signer: Arc>, ) -> Cmd { eventsourcing::SentCommand::new( handle, None, - CmdDet::UpdateEntitlements(parent.clone(), entitlements, signer), + CmdDet::UpdateEntitlements(parent, entitlements, signer), ) } pub fn upd_received_cert( handle: &Handle, - parent: &ParentHandle, - class_name: &str, + parent: ParentHandle, + class_name: ResourceClassName, cert: RcvdCert, signer: Arc>, ) -> Cmd { eventsourcing::SentCommand::new( handle, None, - CmdDet::UpdateRcvdCert(parent.clone(), class_name.to_string(), cert, signer), + CmdDet::UpdateRcvdCert(parent, class_name, cert, signer), ) } - //----- Key Rolls - pub fn init_roll(handle: &Handle, duration: Duration, signer: Arc>) -> Cmd { + //------------------------------------------------------------------------------- + // Key Rolls + //------------------------------------------------------------------------------- + + pub fn key_roll_init(handle: &Handle, duration: Duration, signer: Arc>) -> Cmd { eventsourcing::SentCommand::new(handle, None, CmdDet::KeyRollInitiate(duration, signer)) } + pub fn key_roll_activate(handle: &Handle, staging: Duration, signer: Arc>) -> Cmd { + eventsourcing::SentCommand::new(handle, None, CmdDet::KeyRollActivate(staging, signer)) + } + + pub fn key_roll_finish( + handle: &Handle, + parent: ParentHandle, + res: RevocationResponse, + ) -> Cmd { + eventsourcing::SentCommand::new(handle, None, CmdDet::KeyRollFinish(parent, res)) + } + pub fn publish(handle: &Handle, signer: Arc>) -> Cmd { eventsourcing::SentCommand::new(handle, None, CmdDet::Republish(signer)) } diff --git a/daemon/src/ca/events.rs b/daemon/src/ca/events.rs index 99021e4e..0bf381c7 100644 --- a/daemon/src/ca/events.rs +++ b/daemon/src/ca/events.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::convert::TryFrom; use std::ops::{Deref, DerefMut}; use std::sync::{Arc, RwLock}; @@ -7,15 +8,17 @@ use rpki::crypto::PublicKeyFormat; use rpki::uri; use rpki::x509::{Serial, Time, Validity}; -use krill_commons::api::{IssuanceRequest, IssuanceResponse}; use krill_commons::api::admin::{Handle, ParentCaContact, Token}; use krill_commons::api::ca::{ CertifiedKey, ChildCaDetails, ObjectsDelta, PublicationDelta, RcvdCert, RepoInfo, ResourceSet, TrustAnchorLocator, }; +use krill_commons::api::{ + IssuanceRequest, IssuanceResponse, RevocationRequest, RevocationResponse, +}; use krill_commons::eventsourcing::StoredEvent; use krill_commons::remote::id::IdCert; -use krill_commons::util::softsigner::SignerKeyId; +use krill_commons::util::softsigner::KeyId; use crate::ca::signing::Signer; use crate::ca::{ @@ -130,7 +133,8 @@ pub type Evt = StoredEvent; pub enum EvtDet { // Being a parent Events ChildAdded(ChildHandle, ChildCaDetails), - CertificateIssued(ChildHandle, IssuanceResponse), + ChildCertificateIssued(ChildHandle, IssuanceResponse), + ChildKeyRevoked(ChildHandle, RevocationResponse), ChildUpdatedToken(ChildHandle, Token), ChildUpdatedIdCert(ChildHandle, IdCert), ChildUpdatedResourceClass(ChildHandle, ResourceClassName, ResourceSet), @@ -140,18 +144,19 @@ pub enum EvtDet { ParentAdded(ParentHandle, ParentCaContact), ResourceClassAdded(ParentHandle, ResourceClassName, ResourceClass), ResourceClassRemoved(ParentHandle, ResourceClassName, ObjectsDelta), - CertificateRequested(ParentHandle, IssuanceRequest, SignerKeyId), - CertificateReceived(ParentHandle, ResourceClassName, SignerKeyId, RcvdCert), + CertificateRequested(ParentHandle, IssuanceRequest, KeyId), + CertificateReceived(ParentHandle, ResourceClassName, KeyId, RcvdCert), // Key roll - KeyrollPendingKeyAdded(ParentHandle, ResourceClassName, SignerKeyId), + KeyRollPendingKeyAdded(ParentHandle, ResourceClassName, KeyId), + KeyRollActivated(ParentHandle, ResourceClassName, RevocationRequest), + KeyRollFinished(ParentHandle, ResourceClassName, ObjectsDelta), // Publishing Published( ParentHandle, ResourceClassName, - SignerKeyId, - PublicationDelta, + HashMap, ), TaPublished(PublicationDelta), } @@ -238,13 +243,26 @@ impl EvtDet { ) } - pub(super) fn certificate_issued( + pub(super) fn child_certificate_issued( handle: &Handle, version: u64, child: ChildHandle, response: IssuanceResponse, ) -> Evt { - StoredEvent::new(handle, version, EvtDet::CertificateIssued(child, response)) + StoredEvent::new( + handle, + version, + EvtDet::ChildCertificateIssued(child, response), + ) + } + + pub(super) fn child_revoke_key( + handle: &Handle, + version: u64, + child: ChildHandle, + response: RevocationResponse, + ) -> Evt { + StoredEvent::new(handle, version, EvtDet::ChildKeyRevoked(child, response)) } pub(super) fn published_ta(handle: &Handle, version: u64, delta: PublicationDelta) -> Evt { diff --git a/daemon/src/ca/rc.rs b/daemon/src/ca/rc.rs index 09fc9659..8501d1e3 100644 --- a/daemon/src/ca/rc.rs +++ b/daemon/src/ca/rc.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::ops::Deref; use std::sync::{Arc, RwLock}; @@ -9,11 +10,13 @@ use rpki::uri; use rpki::x509::Time; use krill_commons::api::ca::{ - CertifiedKey, CurrentObjects, KeyRef, ObjectsDelta, PendingKey, PublicationDelta, RcvdCert, - RepoInfo, ResourceClassInfo, ResourceClassKeysInfo, + CertifiedKey, CurrentObjects, KeyRef, ObjectsDelta, OldKey, PendingKey, PublicationDelta, + RcvdCert, RepoInfo, ResourceClassInfo, ResourceClassKeysInfo, }; -use krill_commons::api::{EntitlementClass, IssuanceRequest, RequestResourceLimit}; -use krill_commons::util::softsigner::SignerKeyId; +use krill_commons::api::{ + EntitlementClass, IssuanceRequest, RequestResourceLimit, RevocationRequest, +}; +use krill_commons::util::softsigner::KeyId; use crate::ca::{ self, Error, EvtDet, ParentHandle, ResourceClassName, Result, SignSupport, Signer, @@ -82,7 +85,7 @@ pub struct ResourceClass { impl ResourceClass { /// Creates a new ResourceClass with a single pending key only. - pub fn create(name_space: String, pending_key: SignerKeyId) -> Self { + pub fn create(name_space: String, pending_key: KeyId) -> Self { ResourceClass { name_space, last_key_change: Time::now(), @@ -95,7 +98,7 @@ impl ResourceClass { } /// Adds a request to an existing key for future reference. - pub fn add_request(&mut self, key_id: SignerKeyId, req: IssuanceRequest) { + pub fn add_request(&mut self, key_id: KeyId, req: IssuanceRequest) { self.keys.add_request(key_id, req); } @@ -155,13 +158,18 @@ impl ResourceClass { pub fn cert_requests(&self) -> Vec { self.keys.cert_requests() } + + /// Returns the revocation request for the old key, if it exists. + pub fn revoke_request(&self) -> Option<&RevocationRequest> { + self.keys.revoke_request() + } } /// # Publishing /// impl ResourceClass { /// Applies a publication delta to the appropriate key in this resource class. - pub fn apply_delta(&mut self, delta: PublicationDelta, key_id: SignerKeyId) { + pub fn apply_delta(&mut self, delta: PublicationDelta, key_id: KeyId) { self.keys.apply_delta(delta, key_id); } } @@ -170,7 +178,7 @@ impl ResourceClass { /// impl ResourceClass { /// This function marks a certificate as received. - pub fn received_cert(&mut self, key_id: SignerKeyId, cert: RcvdCert) { + pub fn received_cert(&mut self, key_id: KeyId, cert: RcvdCert) { // if there is a pending key, then we need to do some promotions.. match &mut self.keys { ResourceClassKeys::Pending(pending) => { @@ -208,16 +216,38 @@ impl ResourceClass { } /// Adds a pending key. - pub fn pending_key_added(&mut self, key_id: SignerKeyId) { + pub fn pending_key_added(&mut self, key_id: KeyId) { match &self.keys { ResourceClassKeys::Active(current) => { let pending = PendingKey::new(key_id); self.keys = ResourceClassKeys::RollPending(pending, current.clone()) } - _ => unimplemented!("Should never create event to add key when roll in progress") + _ => unimplemented!("Should never create event to add key when roll in progress"), } } + /// Activates the new key + pub fn new_key_activated(&mut self, revoke_req: RevocationRequest) { + match &self.keys { + ResourceClassKeys::RollNew(new, current) => { + let old_key = OldKey::new(current.clone(), revoke_req); + self.keys = ResourceClassKeys::RollOld(new.clone(), old_key); + } + _ => unimplemented!("Should never create event to add key when roll in progress"), + } + } + + /// Removes the old key, we return the to the state where there is one active key. + pub fn old_key_removed(&mut self) { + match &self.keys { + ResourceClassKeys::RollOld(current, _old) => { + self.keys = ResourceClassKeys::Active(current.clone()); + } + _ => unimplemented!("Should never create event to remove old key, when there is none"), + } + } + + /// Initiate a key roll pub fn keyroll_initiate( &self, parent: ParentHandle, @@ -230,13 +260,46 @@ impl ResourceClass { return Ok(vec![]); } - self.keys.keyroll_initiate( - parent, - class_name, - base_repo, - &self.name_space, - signer - ) + self.keys + .keyroll_initiate(parent, class_name, base_repo, &self.name_space, signer) + } + + /// Activate a new key, if it's been longer than the staging period. + pub fn keyroll_activate( + &self, + parent: ParentHandle, + class_name: ResourceClassName, + staging: Duration, + signer: &S, + ) -> ca::Result> { + if self.last_key_change + staging > Time::now() { + return Ok(vec![]); + } + + self.keys.keyroll_activate(parent, class_name, signer) + } + + /// Finish a key roll, withdraw the old key + pub fn keyroll_finish( + &self, + parent: ParentHandle, + class_name: ResourceClassName, + base_repo: &RepoInfo, + ) -> ca::Result { + let withdraws = match &self.keys { + ResourceClassKeys::RollOld(_current, old) => { + Some(old.current_set().objects().withdraw()) + } + _ => None, + } + .ok_or_else(|| Error::InvalidKeyStatus)?; + + let mut objects_delta = ObjectsDelta::new(base_repo.ca_repository(self.name_space())); + for withdraw in withdraws.into_iter() { + objects_delta.withdraw(withdraw); + } + + Ok(EvtDet::KeyRollFinished(parent, class_name, objects_delta)) } } @@ -256,14 +319,13 @@ pub enum ResourceClassKeys { type NewKey = CertifiedKey; type CurrentKey = CertifiedKey; -type OldKey = CertifiedKey; impl ResourceClassKeys { - fn create(pending_key: SignerKeyId) -> Self { + fn create(pending_key: KeyId) -> Self { ResourceClassKeys::Pending(PendingKey::new(pending_key)) } - fn add_request(&mut self, key_id: SignerKeyId, req: IssuanceRequest) { + fn add_request(&mut self, key_id: KeyId, req: IssuanceRequest) { match self { ResourceClassKeys::Pending(pending) => pending.add_request(req), ResourceClassKeys::Active(current) => current.add_request(req), @@ -358,7 +420,7 @@ impl ResourceClassKeys { Ok(current.clone()) } else { self.matches_key_id(old.key_id(), cert, signer)?; - Ok(old.clone()) + Ok(old.key().clone()) } } } @@ -367,7 +429,7 @@ impl ResourceClassKeys { /// Helper to match a key_id to a pub key. fn matches_key_id( &self, - key_id: &SignerKeyId, + key_id: &KeyId, cert: &RcvdCert, signer: &S, ) -> ca::Result<()> { @@ -407,20 +469,19 @@ impl ResourceClassKeys { let ca_repo = base_repo.ca_repository(name_space); let delta = ObjectsDelta::new(ca_repo); - let delta = SignSupport::publish(signer, &certified_key, base_repo, name_space, delta) - .map_err(Error::signer)?; + let delta = + SignSupport::publish(signer, &certified_key, base_repo, name_space, delta, vec![]) + .map_err(Error::signer)?; - res.push(EvtDet::Published( - parent, - class_name, - certified_key.key_id().clone(), - delta, - )); + let mut delta_map = HashMap::new(); + delta_map.insert(certified_key.key_id().clone(), delta); + + res.push(EvtDet::Published(parent, class_name, delta_map)); Ok(res) } - fn apply_delta(&mut self, delta: PublicationDelta, key_id: SignerKeyId) { + fn apply_delta(&mut self, delta: PublicationDelta, key_id: KeyId) { match self { ResourceClassKeys::Pending(_pending) => unimplemented!("Cannot apply delta to pending"), ResourceClassKeys::Active(current) => current.apply_delta(delta), @@ -558,7 +619,7 @@ impl ResourceClassKeys { base_repo: &RepoInfo, name_space: &str, class_name: &str, - key: &SignerKeyId, + key: &KeyId, signer: &S, ) -> Result { let pub_key = signer.get_key_info(key).map_err(Error::signer)?; @@ -581,6 +642,14 @@ impl ResourceClassKeys { )) } + /// Returns the revoke request if there is an old key. + pub fn revoke_request(&self) -> Option<&RevocationRequest> { + match self { + ResourceClassKeys::RollOld(_current, old) => Some(old.revoke_req()), + _ => None, + } + } + fn as_info(&self) -> ResourceClassKeysInfo { match self.clone() { ResourceClassKeys::Pending(p) => ResourceClassKeysInfo::Pending(p), @@ -617,7 +686,7 @@ impl ResourceClassKeys { self.create_issuance_req(base_repo, name_space, &class_name, &key_id, signer)?; Ok(vec![ - EvtDet::KeyrollPendingKeyAdded( + EvtDet::KeyRollPendingKeyAdded( parent.clone(), class_name.clone(), key_id.clone(), @@ -628,4 +697,31 @@ impl ResourceClassKeys { _ => Ok(vec![]), } } + + /// Marks the new key as current, and the current key as old, and requests revocation of + /// the old key. + // TODO: When ROAs are supported, now is also the time to republish all objects under the + // new current key, and withdraw them from the old key. + fn keyroll_activate( + &self, + parent: ParentHandle, + class_name: ResourceClassName, + signer: &S, + ) -> ca::Result> { + match self { + ResourceClassKeys::RollNew(_new, current) => { + let ki = signer + .get_key_info(current.key_id()) + .map_err(Error::signer)? + .key_identifier(); + + let revoke_req = RevocationRequest::new(class_name.clone(), ki); + + Ok(vec![EvtDet::KeyRollActivated( + parent, class_name, revoke_req, + )]) + } + _ => Ok(vec![]), + } + } } diff --git a/daemon/src/ca/server.rs b/daemon/src/ca/server.rs index 4885f2aa..ce1eeea2 100644 --- a/daemon/src/ca/server.rs +++ b/daemon/src/ca/server.rs @@ -15,16 +15,20 @@ use krill_commons::api::admin::{ use krill_commons::api::ca::{ CertAuthList, CertAuthSummary, ChildCaInfo, IssuedCert, RcvdCert, RepoInfo, }; -use krill_commons::api::{Entitlements, IssuanceRequest, IssuanceResponse, DFLT_CLASS}; +use krill_commons::api::{ + Entitlements, IssuanceRequest, IssuanceResponse, RevocationRequest, RevocationResponse, + DFLT_CLASS, +}; use krill_commons::eventsourcing::{Aggregate, AggregateStore, DiskAggregateStore}; use krill_commons::remote::builder::SignedMessageBuilder; use krill_commons::remote::sigmsg::SignedMessage; use krill_commons::remote::{rfc6492, rfc8183}; use krill_commons::util::httpclient; -use krill_commons::util::softsigner::SignerKeyId; +use krill_commons::util::softsigner::KeyId; use crate::ca::{ - self, CertAuth, ChildHandle, CmdDet, IniDet, ParentHandle, ServerError, ServerResult, Signer, + self, CertAuth, ChildHandle, CmdDet, IniDet, ParentHandle, ResourceClassName, ServerError, + ServerResult, Signer, }; use crate::mq::EventQueueListener; @@ -208,34 +212,32 @@ impl CaServer { /// Verifies an RFC6492 message and returns the child handle, token, /// and content of the request, so that the simple 'list' and 'issue' /// functions can be called. - pub fn rfc6492(&self, parent_handle: &Handle, msg: SignedMessage) -> ServerResult { - info!("RFC6492 Request: will check"); + pub fn rfc6492(&self, ca_handle: &Handle, msg: SignedMessage) -> ServerResult { + debug!("RFC6492 Request: will check"); let (content, token) = { - let parent = self.ca_store.get_latest(parent_handle)?; - parent.verify_rfc6492(msg)? + let ca = self.ca_store.get_latest(ca_handle)?; + ca.verify_rfc6492(msg)? }; - info!("RFC6492 Request: verified"); + debug!("RFC6492 Request: verified"); let (sender, recipient, content) = content.unwrap(); - let sender_handle = Handle::from(sender.as_str()); + let child = Handle::from(sender.as_str()); match content { - rfc6492::Content::Qry(rfc6492::Qry::Revoke(_)) => { - unimplemented!("Revocation not yet supported") + rfc6492::Content::Qry(rfc6492::Qry::Revoke(req)) => { + let res = self.revoke(ca_handle, child, req)?; + let msg = rfc6492::Message::revoke_response(sender, recipient, res); + self.wrap_rfc6492_response(ca_handle, msg) } rfc6492::Content::Qry(rfc6492::Qry::List) => { - let entitlements = self.list(parent_handle, &sender_handle, &token)?; - + let entitlements = self.list(ca_handle, &child, &token)?; let msg = rfc6492::Message::list_response(sender, recipient, entitlements); - - self.wrap_rfc6492_response(parent_handle, msg) + self.wrap_rfc6492_response(ca_handle, msg) } rfc6492::Content::Qry(rfc6492::Qry::Issue(req)) => { - let res = self.issue(parent_handle, &sender_handle, req, token)?; - + let res = self.issue(ca_handle, &child, req, token)?; let msg = rfc6492::Message::issue_response(sender, recipient, res); - - self.wrap_rfc6492_response(parent_handle, msg) + self.wrap_rfc6492_response(ca_handle, msg) } _ => Err(ServerError::custom("Unsupported RFC6492 message")), } @@ -276,7 +278,7 @@ impl CaServer { pub fn issue( &self, parent: &Handle, - child: &Handle, + child: &ChildHandle, issue_req: IssuanceRequest, token: Token, ) -> ServerResult { @@ -311,6 +313,25 @@ impl CaServer { } } + /// See: https://tools.ietf.org/html/rfc6492#section3.5.1-2 + pub fn revoke( + &self, + ca_handle: &Handle, + child: ChildHandle, + revoke_request: RevocationRequest, + ) -> ServerResult { + 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 ca = self.get_ca(ca_handle)?; + let events = ca.process_command(cmd)?; + self.ca_store.update(ca_handle, ca, events)?; + + Ok(res) + } + /// Get the current CAs pub fn cas(&self) -> CertAuthList { CertAuthList::new( @@ -343,7 +364,7 @@ impl CaServer { let ca = self.get_ca(&handle)?; let (parent_handle, parent_contact) = parent.unwrap(); - let add = CmdDet::add_parent(&handle, parent_handle.as_str(), parent_contact); + let add = CmdDet::add_parent(&handle, parent_handle, parent_contact); let events = ca.process_command(add)?; self.ca_store.update(&handle, ca, events)?; @@ -355,10 +376,26 @@ impl CaServer { pub fn ca_keyroll_init(&self, handle: Handle, max_age: Duration) -> ServerResult<(), S> { let ca = self.get_ca(&handle)?; - let init_key_roll = CmdDet::init_roll(&handle, max_age, self.signer.clone()); + let init_key_roll = CmdDet::key_roll_init(&handle, max_age, self.signer.clone()); let events = ca.process_command(init_key_roll)?; - if ! events.is_empty() { + if !events.is_empty() { + self.ca_store.update(&handle, ca, events)?; + } + + Ok(()) + } + + /// Activate a new key, as part of the key roll process (RFC6489). Only new keys that + /// have an age equal to or greater than the staging period are promoted. The RFC mandates + /// a staging period of 24 hours, but we may use a shorter period for testing and/or emergency + /// manual key rolls. + pub fn ca_keyroll_activate(&self, handle: Handle, staging: Duration) -> ServerResult<(), S> { + let ca = self.get_ca(&handle)?; + + let activate_cmd = CmdDet::key_roll_activate(&handle, staging, self.signer.clone()); + let events = ca.process_command(activate_cmd)?; + if !events.is_empty() { self.ca_store.update(&handle, ca, events)?; } @@ -372,9 +409,8 @@ impl CaServer { for handle in self.ca_store.list() { if let Ok(ca) = self.get_ca(&handle) { if let Ok(parents) = ca.parents() { - for (parent, info) in parents.into_iter() { - let contact = info.contact(); - if let Err(e) = self.get_updates_from_parent(&handle, &parent, contact) { + for (parent, _info) in parents.into_iter() { + if let Err(e) = self.get_updates_from_parent(&handle, &parent) { error!( "Failed to refresh CA certificates for {}, error: {}", &handle, e @@ -393,41 +429,140 @@ impl CaServer { &self, handle: &Handle, parent: &ParentHandle, - contact: &ParentCaContact, ) -> ServerResult<(), S> { - let entitlements = self.get_entitlements_from_parent(handle, contact)?; + let entitlements = self.get_entitlements_from_parent(handle, parent)?; - if !self.update_if_need(handle, parent, entitlements)? { + if !self.update_if_needed(handle, parent.clone(), entitlements)? { return Ok(()); // Nothing to do } - self.send_requests(handle, parent, contact) + self.send_requests(handle, parent) } - fn send_requests( - &self, - handle: &Handle, - parent: &ParentHandle, - contact: &ParentCaContact, - ) -> ServerResult<(), S> { - match contact { - ParentCaContact::Embedded(_p, token) => { - self.send_requests_embedded(handle, parent, token) + pub fn send_requests(&self, handle: &Handle, parent: &ParentHandle) -> ServerResult<(), S> { + self.send_revoke_requests(handle, parent)?; + self.send_cert_requests(handle, parent) + } + + fn send_revoke_requests(&self, handle: &Handle, parent: &ParentHandle) -> ServerResult<(), S> { + let mut child = self.ca_store.get_latest(handle)?; + let revoke_request = child.revoke_requests(parent); + + let revoke_responses = match child.parent(parent)?.contact() { + ParentCaContact::Embedded(_parent, _token) => { + self.send_revoke_requests_embedded(revoke_request, handle, parent) } - ParentCaContact::Rfc6492(res) => self.send_requests_rfc6492(handle, parent, res), - _ => unimplemented!(), + ParentCaContact::Rfc6492(parent_res) => { + self.send_revoke_requests_rfc6492(revoke_request, child.id_key(), parent_res) + } + ParentCaContact::RemoteKrill(_, _) => unimplemented!(), + }?; + + for response in revoke_responses.into_iter() { + let cmd = CmdDet::key_roll_finish(handle, parent.clone(), response); + let events = child.process_command(cmd)?; + child = self.ca_store.update(handle, child, events)?; } + + Ok(()) } - fn send_requests_embedded( + fn send_revoke_requests_embedded( &self, + revoke_requests: Vec<&RevocationRequest>, + handle: &Handle, + parent_h: &ParentHandle, + ) -> ServerResult, S> { + let mut parent = self.ca_store.get_latest(parent_h)?; + let mut revocation_responses = vec![]; + + for req in revoke_requests.into_iter() { + let cmd = CmdDet::revoke_key_for_child( + parent_h, + handle.clone(), + req.clone(), + self.signer.clone(), + ); + + let events = parent.process_command(cmd)?; + parent = self.ca_store.update(parent_h, parent, events)?; + + revocation_responses.push(req.into()); + } + + Ok(revocation_responses) + } + + fn send_revoke_requests_rfc6492( + &self, + revoke_requests: Vec<&RevocationRequest>, + signing_key: &KeyId, + parent_res: &rfc8183::ParentResponse, + ) -> ServerResult, S> { + let mut res = vec![]; + + for req in revoke_requests.into_iter() { + let sender = parent_res.child_handle().to_string(); + let recipient = parent_res.parent_handle().to_string(); + let revoke = rfc6492::Message::revoke(sender, recipient, req.clone()); + + match self.send_rfc6492_and_validate_response( + signing_key, + parent_res, + revoke.into_bytes(), + ) { + Err(e) => error!("Could not send/validate revoke: {}", e), + Ok(response) => match response { + rfc6492::Res::Revoke(revoke_response) => res.push(revoke_response), + rfc6492::Res::NotPerformed(e) => error!("We got an error response: {}", e), + rfc6492::Res::List(_) => error!("List response to revoke request??"), + rfc6492::Res::Issue(_) => error!("Issue response to revoke request??"), + }, + } + } + + Ok(res) + } + + fn send_cert_requests(&self, handle: &Handle, parent: &ParentHandle) -> ServerResult<(), S> { + let mut child = self.ca_store.get_latest(handle)?; + let cert_requests = child.cert_requests(parent); + + let issued_certs = match child.parent(parent)?.contact() { + ParentCaContact::Embedded(_parent, token) => { + self.send_cert_requests_embedded(cert_requests, handle, parent, token) + } + ParentCaContact::Rfc6492(parent_res) => { + self.send_cert_requests_rfc6492(cert_requests, child.id_key(), &parent_res) + } + ParentCaContact::RemoteKrill(_, _) => unimplemented!("Deprecate?"), + }?; + + for (class_name, issued) in issued_certs.into_iter() { + let received = RcvdCert::from(issued); + + let upd_rcvd_cmd = CmdDet::upd_received_cert( + handle, + parent.clone(), + class_name, + received, + self.signer.clone(), + ); + + let evts = child.process_command(upd_rcvd_cmd)?; + child = self.ca_store.update(handle, child, evts)?; + } + + Ok(()) + } + + fn send_cert_requests_embedded( + &self, + requests: Vec, handle: &Handle, parent_h: &ParentHandle, token: &Token, - ) -> ServerResult<(), S> { - let mut child = self.ca_store.get_latest(handle)?; - let requests = child.cert_requests(parent_h); - + ) -> ServerResult, S> { let mut parent = self.ca_store.get_latest(parent_h)?; let mut issued_certs: Vec<(String, IssuedCert)> = vec![]; @@ -453,83 +588,51 @@ impl CaServer { issued_certs.push((class_name, issued)); } - - for (class_name, issued) in issued_certs { - let received = RcvdCert::from(issued); - - let upd_rcvd_cmd = CmdDet::upd_received_cert( - handle, - parent_h, - &class_name, - received, - self.signer.clone(), - ); - - let evts = child.process_command(upd_rcvd_cmd)?; - child = self.ca_store.update(handle, child, evts)?; - } - - Ok(()) + Ok(issued_certs) } - fn send_requests_rfc6492( + fn send_cert_requests_rfc6492( &self, - handle: &Handle, - parent_h: &ParentHandle, + requests: Vec, + signing_key: &KeyId, parent_res: &rfc8183::ParentResponse, - ) -> ServerResult<(), S> { - let mut child = self.ca_store.get_latest(handle)?; - let requests = child.cert_requests(parent_h); + ) -> ServerResult, S> { + let mut res = vec![]; for req in requests.into_iter() { let sender = parent_res.child_handle().to_string(); let recipient = parent_res.parent_handle().to_string(); let issue = rfc6492::Message::issue(sender, recipient, req); - let res = self.send_rfc6492_and_validate_response( - child.id_key(), + match self.send_rfc6492_and_validate_response( + signing_key, parent_res, issue.into_bytes(), - )?; - - match res { - rfc6492::Res::Error(_) => unimplemented!("Deal with error"), - rfc6492::Res::Issue(issue_response) => { - let (class_name, _, _, issued) = issue_response.unwrap(); - let received = RcvdCert::from(issued); - - let update_rcvd_cmd = CmdDet::upd_received_cert( - handle, - parent_h, - &class_name, - received, - self.signer.clone(), - ); - - let events = child.process_command(update_rcvd_cmd)?; - child = self.ca_store.update(handle, child, events)?; - } - _ => { - return { - Err(ServerError::custom( - "Got unexpected response to issue query", - )) + ) { + Err(e) => error!("Could not send/validate csr: {}", e), + Ok(response) => match response { + rfc6492::Res::NotPerformed(e) => error!("We got an error response: {}", e), + rfc6492::Res::Issue(issue_response) => { + let (class_name, _, _, issued) = issue_response.unwrap(); + res.push((class_name, issued)); } - } + rfc6492::Res::List(_) => error!("List reply to issue request??"), + rfc6492::Res::Revoke(_) => error!("Revoke reply to issue request??"), + }, } } - Ok(()) + Ok(res) } /// Updates the CA if entitlements are different from what the CA /// currently has under this parent. Returns [`Ok(true)`] in case /// there were any updates. In that case the CA will have been updated /// with open certificate requests which can be retrieved. - fn update_if_need( + fn update_if_needed( &self, handle: &Handle, - parent: &ParentHandle, + parent: ParentHandle, entitlements: Entitlements, ) -> ServerResult { let child = self.ca_store.get_latest(handle)?; @@ -549,9 +652,9 @@ impl CaServer { fn get_entitlements_from_parent( &self, handle: &Handle, - contact: &ParentCaContact, + parent: &ParentHandle, ) -> ServerResult { - match contact { + match self.get_ca(&handle)?.parent(parent)?.contact() { ParentCaContact::Embedded(parent, token) => { self.get_entitlements_embedded(handle, parent, token) } @@ -587,7 +690,9 @@ impl CaServer { self.send_rfc6492_and_validate_response(child.id_key(), parent_res, list.into_bytes())?; match response { - rfc6492::Res::Error(_) => unimplemented!("Deal with error response"), + rfc6492::Res::NotPerformed(np) => { + Err(ServerError::Custom(format!("Not performed: {}", np))) + } rfc6492::Res::List(ent) => Ok(ent), _ => Err(ServerError::custom("Got unexpected response to list query")), } @@ -595,7 +700,7 @@ impl CaServer { fn send_rfc6492_and_validate_response( &self, - signing_key: &SignerKeyId, + signing_key: &KeyId, parent_res: &rfc8183::ParentResponse, msg: Bytes, ) -> ServerResult { @@ -780,7 +885,7 @@ mod tests { let parent = ParentCaContact::for_embedded(ta_handle.clone(), child_token.clone()); - let add_parent = CmdDet::add_parent(&child_handle, ta_handle.as_str(), parent); + let add_parent = CmdDet::add_parent(&child_handle, ta_handle.clone(), parent); let events = child.process_command(add_parent).unwrap(); let child = ca_store.update(&child_handle, child, events).unwrap(); @@ -796,8 +901,12 @@ mod tests { let entitlements = ta.list(&child_handle, &child_token).unwrap(); - let upd_ent = - CmdDet::upd_entitlements(&child_handle, &ta_handle, entitlements, signer.clone()); + let upd_ent = CmdDet::upd_entitlements( + &child_handle, + ta_handle.clone(), + entitlements, + signer.clone(), + ); let events = child.process_command(upd_ent).unwrap(); assert_eq!(2, events.len()); // rc and csr @@ -836,7 +945,7 @@ mod tests { let _ta = ca_store.update(&ta_handle, ta, ta_events).unwrap(); let (handle, issuance_res) = match issued_evt { - EvtDet::CertificateIssued(child, issued) => (child, issued), + EvtDet::ChildCertificateIssued(child, issued) => (child, issued), _ => panic!("Expected issued certificate."), }; let (class_name, _, _, issued) = issuance_res.unwrap(); @@ -855,8 +964,8 @@ mod tests { let upd_rcvd = CmdDet::upd_received_cert( &child_handle, - &ta_handle, - DFLT_CLASS, + ta_handle, + DFLT_CLASS.to_string(), rcvd_cert, signer.clone(), ); diff --git a/daemon/src/ca/signing.rs b/daemon/src/ca/signing.rs index 7b60614a..01baee66 100644 --- a/daemon/src/ca/signing.rs +++ b/daemon/src/ca/signing.rs @@ -1,11 +1,9 @@ //! Support for signing mft, crl, certificates, roas.. //! Common objects for TAs and CAs -use std::fmt::Debug; use std::ops::Deref; use std::sync::{Arc, RwLock}; use bytes::Bytes; -use serde::Serialize; use rpki::crl::{Crl, TbsCertList}; use rpki::crypto::signer::KeyError; @@ -18,27 +16,12 @@ use krill_commons::api::ca::{ AddedObject, CertifiedKey, CurrentObject, ObjectsDelta, PublicationDelta, RepoInfo, Revocation, RevocationsDelta, UpdatedObject, }; - -use krill_commons::util::softsigner::SignerKeyId; +use krill_commons::util::softsigner::KeyId; //------------ Signer -------------------------------------------------------- -pub trait Signer: - crypto::Signer + Clone + Debug + Serialize + Sized + Sync + Send + 'static -{ -} -impl< - T: crypto::Signer - + Clone - + Debug - + Serialize - + Sized - + Sync - + Send - + 'static, - > Signer for T -{ -} +pub trait Signer: crypto::Signer + Clone + Sized + Sync + Send + 'static {} +impl + Clone + Sized + Sync + Send + 'static> Signer for T {} //------------ CaSignSupport ------------------------------------------------- @@ -57,6 +40,7 @@ impl SignSupport { repo_info: &RepoInfo, name_space: &str, mut objects_delta: ObjectsDelta, + new_revocations: Vec, ) -> Result> { let aia = ca_key.incoming_cert().uri(); let key_id = ca_key.key_id(); @@ -81,12 +65,16 @@ impl SignSupport { let mut revocations = current_set.revocations().clone(); let mut revocations_delta = RevocationsDelta::default(); - let mut current_objects = current_set.objects().clone(); + for revocation in new_revocations.into_iter() { + revocations_delta.add(revocation); + } for expired in revocations.purge() { revocations_delta.drop(expired); } + let mut current_objects = current_set.objects().clone(); + let mft_name = RepoInfo::mft_name(&pub_key.key_identifier()); let mft_uri = repo_info.resolve(name_space, &mft_name); let old_mft = current_set.objects().object_for(&mft_name); diff --git a/daemon/src/endpoints.rs b/daemon/src/endpoints.rs index 9714e8a2..245baa10 100644 --- a/daemon/src/endpoints.rs +++ b/daemon/src/endpoints.rs @@ -380,12 +380,21 @@ pub fn ca_add_parent( pub fn ca_keyroll_init( server: web::Data, auth: Auth, - handle: Path + handle: Path, ) -> HttpResponse { if_api_allowed(&server, &auth, || { - render_empty_res( - server.read().ca_keyroll_init(handle.into_inner()) - ) + render_empty_res(server.read().ca_keyroll_init(handle.into_inner())) + }) +} + +/// Force key activation for all new keys, i.e. use a staging period of 0 seconds. +pub fn ca_keyroll_activate( + server: web::Data, + auth: Auth, + handle: Path, +) -> HttpResponse { + if_api_allowed(&server, &auth, || { + render_empty_res(server.read().ca_keyroll_activate(handle.into_inner())) }) } diff --git a/daemon/src/http/server.rs b/daemon/src/http/server.rs index bddf11f9..296c2283 100644 --- a/daemon/src/http/server.rs +++ b/daemon/src/http/server.rs @@ -72,12 +72,10 @@ pub fn start(config: &Config) -> Result<(), Error> { .service( scope("/api/v1") .route("/health", get().to(api_health)) - .route("/publishers", get().to(publishers)) .route("/publishers", post().to(add_publisher)) .route("/publishers/{handle}", get().to(publisher_details)) .route("/publishers/{handle}", delete().to(deactivate_publisher)) - .route("/rfc8181/clients", get().to(rfc8181_clients)) .route("/rfc8181/clients", post().to(add_rfc8181_client)) .data(web::Bytes::configure(|cfg| cfg.limit(256 * 1024 * 1024))) @@ -85,21 +83,22 @@ pub fn start(config: &Config) -> Result<(), Error> { "/rfc8181/{handle}/response.xml", get().to(repository_response), ) - .route("/trustanchor", get().to(ta_info)) .route("/trustanchor", post().to(ta_init)) .route("/trustanchor/children", post().to(ta_add_child)) .route("/trustanchor/children/{handle}", get().to(ta_show_child)) .route("/trustanchor/children/{handle}", post().to(ta_update_child)) - .route("/cas", post().to(ca_init)) .route("/cas", get().to(cas)) .route("/cas/{handle}", get().to(ca_info)) .route("/cas/{handle}/child_request", get().to(ca_child_req)) .route("/cas/{handle}/parents", post().to(ca_add_parent)) - .route("/cas/{handle}/keys/init_roll", post().to(ca_keyroll_init)) - - .route("/republish", post().to(republish_all)) + .route("/cas/{handle}/keys/roll_init", post().to(ca_keyroll_init)) + .route( + "/cas/{handle}/keys/roll_activate", + post().to(ca_keyroll_activate), + ) + .route("/republish", post().to(republish_all)), ) // Public TA related methods .route("/ta/ta.tal", get().to(tal)) diff --git a/daemon/src/krillserver.rs b/daemon/src/krillserver.rs index 4e640a48..7d5f02ae 100644 --- a/daemon/src/krillserver.rs +++ b/daemon/src/krillserver.rs @@ -4,8 +4,8 @@ use std::path::PathBuf; use std::sync::Arc; use bcder::Captured; -use chrono::Duration; use bytes::Bytes; +use chrono::Duration; use rpki::uri; use krill_commons::api::admin; @@ -374,7 +374,15 @@ impl KrillServer { } pub fn ca_keyroll_init(&self, handle: Handle) -> EmptyRes { - Ok(self.caserver.ca_keyroll_init(handle, Duration::seconds(0))?) + Ok(self + .caserver + .ca_keyroll_init(handle, Duration::seconds(0))?) + } + + pub fn ca_keyroll_activate(&self, handle: Handle) -> EmptyRes { + Ok(self + .caserver + .ca_keyroll_activate(handle, Duration::seconds(0))?) } pub fn list(&self, parent: &Handle, child: &Handle, auth: Auth) -> KrillRes { diff --git a/daemon/src/mq.rs b/daemon/src/mq.rs index eb505a6a..27343df5 100644 --- a/daemon/src/mq.rs +++ b/daemon/src/mq.rs @@ -7,7 +7,7 @@ use std::collections::VecDeque; use std::fmt; use std::sync::RwLock; -use krill_commons::api::admin::{Handle, ParentCaContact}; +use krill_commons::api::admin::Handle; use krill_commons::api::publication::PublishDelta; use krill_commons::eventsourcing; @@ -20,8 +20,9 @@ use crate::ca::{CertAuth, Evt, EvtDet, ParentHandle, Signer}; #[derive(Clone, Debug, Eq, PartialEq)] #[allow(clippy::large_enum_variant)] pub enum QueueEvent { - ParentAdded(Handle, ParentHandle, ParentCaContact), Delta(Handle, PublishDelta), + ParentAdded(Handle, ParentHandle), + RequestsPending(Handle, ParentHandle), } #[derive(Debug)] @@ -61,8 +62,12 @@ impl eventsourcing::EventListener> for EventQueueListener let handle = event.handle(); match event.details() { - EvtDet::Published(_, _, _, delta) => { - let evt = QueueEvent::Delta(handle.clone(), delta.objects().clone().into()); + EvtDet::Published(_, _, delta) => { + let publish_delta = delta.values().fold(PublishDelta::empty(), |acc, el| { + acc + el.objects().clone().into() + }); + + let evt = QueueEvent::Delta(handle.clone(), publish_delta); self.push_back(evt); } EvtDet::TaPublished(delta) => { @@ -73,8 +78,22 @@ impl eventsourcing::EventListener> for EventQueueListener let evt = QueueEvent::Delta(handle.clone(), delta.clone().into()); self.push_back(evt); } - EvtDet::ParentAdded(parent, contact) => { - let evt = QueueEvent::ParentAdded(handle.clone(), parent.clone(), contact.clone()); + EvtDet::KeyRollFinished(_parent, _class_name, delta) => { + let evt = QueueEvent::Delta(handle.clone(), delta.clone().into()); + self.push_back(evt); + } + + EvtDet::ParentAdded(parent, _contact) => { + let evt = QueueEvent::ParentAdded(handle.clone(), parent.clone()); + self.push_back(evt); + } + + EvtDet::CertificateRequested(parent, _, _) => { + let evt = QueueEvent::RequestsPending(handle.clone(), parent.clone()); + self.push_back(evt); + } + EvtDet::KeyRollActivated(parent, _, _) => { + let evt = QueueEvent::RequestsPending(handle.clone(), parent.clone()); self.push_back(evt); } _ => {} diff --git a/daemon/src/scheduler.rs b/daemon/src/scheduler.rs index cfb7e8a7..3fe18a3c 100644 --- a/daemon/src/scheduler.rs +++ b/daemon/src/scheduler.rs @@ -56,11 +56,17 @@ fn make_event_sh( QueueEvent::Delta(handle, delta) => { publish(&handle, delta, &pubserver); } - QueueEvent::ParentAdded(handle, parent, contact) => { - if let Err(e) = caserver.get_updates_from_parent(&handle, &parent, &contact) { - error!("Getting updates for {}, error: {}", &handle, e); - } else { - info!("Parent added, updated certificates for CA {}", &handle); + QueueEvent::ParentAdded(handle, parent) => { + if let Err(e) = caserver.get_updates_from_parent(&handle, &parent) { + error!( + "Error getting updates for {}, from parent: {}, error: {}", + &handle, &parent, e + ) + } + } + QueueEvent::RequestsPending(handle, parent) => { + if let Err(e) = caserver.send_requests(&handle, &parent) { + error!("Sending pending requests for {}, error: {}", &handle, e); } } } diff --git a/daemon/tests/ca_under_ta.rs b/daemon/tests/ca_under_ta.rs index 5adeec8f..2b6b4c68 100644 --- a/daemon/tests/ca_under_ta.rs +++ b/daemon/tests/ca_under_ta.rs @@ -9,7 +9,7 @@ use krill_commons::api::admin::{ AddChildRequest, AddParentRequest, CertAuthInit, CertAuthPubMode, ChildAuthRequest, Handle, ParentCaContact, Token, UpdateChildRequest, }; -use krill_commons::api::ca::{CaParentsInfo, CertAuthInfo, ResourceSet, ResourceClassKeysInfo}; +use krill_commons::api::ca::{CaParentsInfo, CertAuthInfo, ResourceClassKeysInfo, ResourceSet}; use krill_commons::remote::rfc8183; use krill_daemon::ca::ta_handle; use krill_daemon::test::{krill_admin, test_with_krill_server, wait_seconds}; @@ -78,10 +78,16 @@ fn add_parent_to_ca(handle: &Handle, parent: AddParentRequest) { ))); } -fn ca_init_roll(handle: &Handle) { +fn ca_roll_init(handle: &Handle) { krill_admin(Command::CertAuth(CaCommand::KeyRollInit(handle.clone()))); } +fn ca_roll_activate(handle: &Handle) { + krill_admin(Command::CertAuth(CaCommand::KeyRollActivate( + handle.clone(), + ))); +} + fn ca_details(handle: &Handle) -> CertAuthInfo { match krill_admin(Command::CertAuth(CaCommand::Show(handle.clone()))) { ApiResponse::CertAuthInfo(inf) => inf, @@ -89,7 +95,10 @@ fn ca_details(handle: &Handle) -> CertAuthInfo { } } -fn wait_for(tries: u64, error_msg: &'static str, op: O) where O: Copy + FnOnce() -> bool { +fn wait_for(tries: u64, error_msg: &'static str, op: O) +where + O: Copy + FnOnce() -> bool, +{ for _counter in 1..tries + 1 { if op() == true { return; @@ -100,22 +109,26 @@ fn wait_for(tries: u64, error_msg: &'static str, op: O) where O: Copy + FnOnc } 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); + 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; + 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 - }) + false + }, + ) } fn wait_for_new_key(handle: &Handle) { @@ -126,8 +139,28 @@ fn wait_for_new_key(handle: &Handle) { if let Some(parent) = parents.get(&ta_handle()) { if let Some(rc) = parent.resources().get("all") { match rc.keys() { - ResourceClassKeysInfo::RollNew(_,_) => return true, - _ => return false + ResourceClassKeysInfo::RollNew(new, _) => { + return new.current_set().number() == 2 + } + _ => return false, + } + } + } + } + false + }) +} + +fn wait_for_key_roll_complete(handle: &Handle) { + wait_for(30, "Key roll did not complete", || { + 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") { + match rc.keys() { + ResourceClassKeysInfo::Active(_) => return true, + _ => return false, } } } @@ -174,15 +207,16 @@ fn ca_under_ta() { }; add_parent_to_ca(&cms_child_handle, parent); - wait_for_resources_on_current_key(&cms_child_handle, &cms_child_resources); let cms_child_resources = ResourceSet::from_strs("AS65000", "10.0.0.0/16", "").unwrap(); update_child(&cms_child_handle, &cms_child_resources); - wait_for_resources_on_current_key(&cms_child_handle, &cms_child_resources); - ca_init_roll(&cms_child_handle); + ca_roll_init(&cms_child_handle); wait_for_new_key(&cms_child_handle); + + ca_roll_activate(&cms_child_handle); + wait_for_key_roll_complete(&cms_child_handle); }); }