mirror of
https://github.com/NLnetLabs/krill.git
synced 2026-09-27 20:04:52 +02:00
Manually finish key-roll by activating the new key. Tested with apnic. Fixes #23
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
+76
-28
@@ -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<IssuanceRequest>,
|
||||
}
|
||||
|
||||
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<IssuanceRequest>) {
|
||||
pub fn unwrap(self) -> (KeyId, Option<IssuanceRequest>) {
|
||||
(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<IssuanceRequest>,
|
||||
}
|
||||
|
||||
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<CrlEntry> {
|
||||
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<Revocation> {
|
||||
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 {
|
||||
|
||||
@@ -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<RevocationRequest> for RevocationResponse {
|
||||
fn from(req: RevocationRequest) -> Self {
|
||||
RevocationResponse {
|
||||
class_name: req.class_name,
|
||||
key: req.key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Publish> {
|
||||
&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)]
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<Self, Error> {
|
||||
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<W>,
|
||||
) -> 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<W: io::Write>(
|
||||
res: &RevocationResponse,
|
||||
w: &mut XmlWriter<W>,
|
||||
) -> 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);
|
||||
|
||||
@@ -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<str> for SignerKeyId {
|
||||
impl AsRef<str> for KeyId {
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for SignerKeyId {
|
||||
impl Serialize for KeyId {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
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<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
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<OpenSslKeyPair, SignerError> {
|
||||
fn load_key(&self, id: &KeyId) -> Result<OpenSslKeyPair, SignerError> {
|
||||
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<Self::KeyId, Self::Error> {
|
||||
@@ -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)?;
|
||||
|
||||
+4
-15
@@ -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<R: io::Read> XmlReader<R> {
|
||||
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<Bytes, XmlReaderErr> {
|
||||
self.take_bytes(base64::URL_SAFE_NO_PAD)
|
||||
}
|
||||
|
||||
fn take_bytes(&mut self, config: base64::Config) -> Result<Bytes, XmlReaderErr> {
|
||||
let chars = self.take_chars()?;
|
||||
// strip whitespace and padding (we are liberal in what we accept here)
|
||||
@@ -486,13 +482,6 @@ impl<W: io::Write> XmlWriter<W> {
|
||||
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(())
|
||||
|
||||
+173
-38
@@ -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<S: Signer> Aggregate for CertAuth<S> {
|
||||
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<S: Signer> Aggregate for CertAuth<S> {
|
||||
//-----------------------------------------------------------------------
|
||||
// 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<S: Signer> Aggregate for CertAuth<S> {
|
||||
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<S: Signer> Aggregate for CertAuth<S> {
|
||||
|
||||
// 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<S: Signer> CertAuth<S> {
|
||||
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<S: Signer> CertAuth<S> {
|
||||
.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<S: Signer> CertAuth<S> {
|
||||
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<S: Signer> CertAuth<S> {
|
||||
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<S: Signer> CertAuth<S> {
|
||||
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<S: Signer> CertAuth<S> {
|
||||
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<RwLock<S>>,
|
||||
) -> ca::Result<Vec<Evt>> {
|
||||
// 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<S: Signer> CertAuth<S> {
|
||||
}
|
||||
}
|
||||
|
||||
fn parent(&self, parent: &Handle) -> Result<&ParentCa> {
|
||||
pub fn parent(&self, parent: &Handle) -> Result<&ParentCa> {
|
||||
self.parents.get(parent)
|
||||
}
|
||||
|
||||
@@ -833,6 +911,19 @@ impl<S: Signer> CertAuth<S> {
|
||||
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<S: Signer> CertAuth<S> {
|
||||
rcvd_cert: RcvdCert,
|
||||
signer: Arc<RwLock<S>>,
|
||||
) -> ca::Result<Vec<Evt>> {
|
||||
info!(
|
||||
debug!(
|
||||
"CA {}: Updating received cert for class: {}",
|
||||
self.handle, class_name
|
||||
);
|
||||
@@ -977,19 +1068,17 @@ impl<S: Signer> CertAuth<S> {
|
||||
|
||||
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<S: Signer> CertAuth<S> {
|
||||
}
|
||||
}
|
||||
|
||||
fn keyroll_activate(&self, _duration: Duration) -> ca::Result<Vec<Evt>> {
|
||||
unimplemented!()
|
||||
fn keyroll_activate(&self, staging: Duration, signer: Arc<RwLock<S>>) -> ca::Result<Vec<Evt>> {
|
||||
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<Vec<Evt>> {
|
||||
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<S: Signer> CertAuth<S> {
|
||||
) -> Result<PublicationDelta> {
|
||||
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<S: Signer> CertAuth<S> {
|
||||
res.push(EvtDet::published_ta(&self.handle, self.version, delta))
|
||||
}
|
||||
}
|
||||
CaParents::Parents(_map) => unimplemented!(),
|
||||
CaParents::Parents(_map) => error!("Republishing CAs not implemented"),
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
+45
-14
@@ -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<S: Signer> {
|
||||
UpdateChild(ChildHandle, UpdateChildRequest),
|
||||
// Process an issuance request by an existing child.
|
||||
CertifyChild(ChildHandle, IssuanceRequest, Token, Arc<RwLock<S>>),
|
||||
// Process a revoke request by an existing child.
|
||||
RevokeKeyForChild(ChildHandle, RevocationRequest, Arc<RwLock<S>>),
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Being a child (only allowed if this CA is not self-signed)
|
||||
@@ -59,11 +61,11 @@ pub enum CmdDet<S: Signer> {
|
||||
//
|
||||
// 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<RwLock<S>>),
|
||||
|
||||
// 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<S: Signer> CmdDet<S> {
|
||||
/// 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<RwLock<S>>,
|
||||
@@ -117,42 +119,71 @@ impl<S: Signer> CmdDet<S> {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn add_parent(handle: &Handle, name: &str, info: ParentCaContact) -> Cmd<S> {
|
||||
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<RwLock<S>>,
|
||||
) -> Cmd<S> {
|
||||
eventsourcing::SentCommand::new(
|
||||
handle,
|
||||
None,
|
||||
CmdDet::RevokeKeyForChild(child_handle, request, signer),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn add_parent(handle: &Handle, parent: ParentHandle, info: ParentCaContact) -> Cmd<S> {
|
||||
eventsourcing::SentCommand::new(handle, None, CmdDet::AddParent(parent, info))
|
||||
}
|
||||
|
||||
pub fn upd_entitlements(
|
||||
handle: &Handle,
|
||||
parent: &ParentHandle,
|
||||
parent: ParentHandle,
|
||||
entitlements: Entitlements,
|
||||
signer: Arc<RwLock<S>>,
|
||||
) -> Cmd<S> {
|
||||
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<RwLock<S>>,
|
||||
) -> Cmd<S> {
|
||||
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<RwLock<S>>) -> Cmd<S> {
|
||||
//-------------------------------------------------------------------------------
|
||||
// Key Rolls
|
||||
//-------------------------------------------------------------------------------
|
||||
|
||||
pub fn key_roll_init(handle: &Handle, duration: Duration, signer: Arc<RwLock<S>>) -> Cmd<S> {
|
||||
eventsourcing::SentCommand::new(handle, None, CmdDet::KeyRollInitiate(duration, signer))
|
||||
}
|
||||
|
||||
pub fn key_roll_activate(handle: &Handle, staging: Duration, signer: Arc<RwLock<S>>) -> Cmd<S> {
|
||||
eventsourcing::SentCommand::new(handle, None, CmdDet::KeyRollActivate(staging, signer))
|
||||
}
|
||||
|
||||
pub fn key_roll_finish(
|
||||
handle: &Handle,
|
||||
parent: ParentHandle,
|
||||
res: RevocationResponse,
|
||||
) -> Cmd<S> {
|
||||
eventsourcing::SentCommand::new(handle, None, CmdDet::KeyRollFinish(parent, res))
|
||||
}
|
||||
|
||||
pub fn publish(handle: &Handle, signer: Arc<RwLock<S>>) -> Cmd<S> {
|
||||
eventsourcing::SentCommand::new(handle, None, CmdDet::Republish(signer))
|
||||
}
|
||||
|
||||
+28
-10
@@ -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<EvtDet>;
|
||||
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<KeyId, PublicationDelta>,
|
||||
),
|
||||
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 {
|
||||
|
||||
+129
-33
@@ -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<IssuanceRequest> {
|
||||
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<S: Signer>(
|
||||
&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<S: Signer>(
|
||||
&self,
|
||||
parent: ParentHandle,
|
||||
class_name: ResourceClassName,
|
||||
staging: Duration,
|
||||
signer: &S,
|
||||
) -> ca::Result<Vec<EvtDet>> {
|
||||
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<EvtDet> {
|
||||
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<S: Signer>(
|
||||
&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<IssuanceRequest> {
|
||||
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<S: Signer>(
|
||||
&self,
|
||||
parent: ParentHandle,
|
||||
class_name: ResourceClassName,
|
||||
signer: &S,
|
||||
) -> ca::Result<Vec<EvtDet>> {
|
||||
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![]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+218
-109
@@ -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<S: Signer> CaServer<S> {
|
||||
/// 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<Bytes, S> {
|
||||
info!("RFC6492 Request: will check");
|
||||
pub fn rfc6492(&self, ca_handle: &Handle, msg: SignedMessage) -> ServerResult<Bytes, S> {
|
||||
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<S: Signer> CaServer<S> {
|
||||
pub fn issue(
|
||||
&self,
|
||||
parent: &Handle,
|
||||
child: &Handle,
|
||||
child: &ChildHandle,
|
||||
issue_req: IssuanceRequest,
|
||||
token: Token,
|
||||
) -> ServerResult<IssuanceResponse, S> {
|
||||
@@ -311,6 +313,25 @@ impl<S: Signer> CaServer<S> {
|
||||
}
|
||||
}
|
||||
|
||||
/// See: https://tools.ietf.org/html/rfc6492#section3.5.1-2
|
||||
pub fn revoke(
|
||||
&self,
|
||||
ca_handle: &Handle,
|
||||
child: ChildHandle,
|
||||
revoke_request: RevocationRequest,
|
||||
) -> 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 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<S: Signer> CaServer<S> {
|
||||
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<S: Signer> CaServer<S> {
|
||||
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<S: Signer> CaServer<S> {
|
||||
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<S: Signer> CaServer<S> {
|
||||
&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<Vec<RevocationResponse>, 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<Vec<RevocationResponse>, 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<IssuanceRequest>,
|
||||
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<Vec<(ResourceClassName, IssuedCert)>, S> {
|
||||
let mut parent = self.ca_store.get_latest(parent_h)?;
|
||||
|
||||
let mut issued_certs: Vec<(String, IssuedCert)> = vec![];
|
||||
@@ -453,83 +588,51 @@ impl<S: Signer> CaServer<S> {
|
||||
|
||||
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<IssuanceRequest>,
|
||||
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<Vec<(ResourceClassName, IssuedCert)>, 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<bool, S> {
|
||||
let child = self.ca_store.get_latest(handle)?;
|
||||
@@ -549,9 +652,9 @@ impl<S: Signer> CaServer<S> {
|
||||
fn get_entitlements_from_parent(
|
||||
&self,
|
||||
handle: &Handle,
|
||||
contact: &ParentCaContact,
|
||||
parent: &ParentHandle,
|
||||
) -> ServerResult<api::Entitlements, S> {
|
||||
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<S: Signer> CaServer<S> {
|
||||
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<S: Signer> CaServer<S> {
|
||||
|
||||
fn send_rfc6492_and_validate_response(
|
||||
&self,
|
||||
signing_key: &SignerKeyId,
|
||||
signing_key: &KeyId,
|
||||
parent_res: &rfc8183::ParentResponse,
|
||||
msg: Bytes,
|
||||
) -> ServerResult<rfc6492::Res, S> {
|
||||
@@ -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(),
|
||||
);
|
||||
|
||||
@@ -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<KeyId = SignerKeyId> + Clone + Debug + Serialize + Sized + Sync + Send + 'static
|
||||
{
|
||||
}
|
||||
impl<
|
||||
T: crypto::Signer<KeyId = SignerKeyId>
|
||||
+ Clone
|
||||
+ Debug
|
||||
+ Serialize
|
||||
+ Sized
|
||||
+ Sync
|
||||
+ Send
|
||||
+ 'static,
|
||||
> Signer for T
|
||||
{
|
||||
}
|
||||
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 {}
|
||||
|
||||
//------------ CaSignSupport -------------------------------------------------
|
||||
|
||||
@@ -57,6 +40,7 @@ impl SignSupport {
|
||||
repo_info: &RepoInfo,
|
||||
name_space: &str,
|
||||
mut objects_delta: ObjectsDelta,
|
||||
new_revocations: Vec<Revocation>,
|
||||
) -> Result<PublicationDelta, SignError<S>> {
|
||||
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);
|
||||
|
||||
+13
-4
@@ -380,12 +380,21 @@ pub fn ca_add_parent(
|
||||
pub fn ca_keyroll_init(
|
||||
server: web::Data<AppServer>,
|
||||
auth: Auth,
|
||||
handle: Path<Handle>
|
||||
handle: Path<Handle>,
|
||||
) -> 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<AppServer>,
|
||||
auth: Auth,
|
||||
handle: Path<Handle>,
|
||||
) -> HttpResponse {
|
||||
if_api_allowed(&server, &auth, || {
|
||||
render_empty_res(server.read().ca_keyroll_activate(handle.into_inner()))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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<Entitlements> {
|
||||
|
||||
+25
-6
@@ -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<S: Signer> eventsourcing::EventListener<CertAuth<S>> 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<S: Signer> eventsourcing::EventListener<CertAuth<S>> 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);
|
||||
}
|
||||
_ => {}
|
||||
|
||||
+11
-5
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+53
-19
@@ -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<O>(tries: u64, error_msg: &'static str, op: O) where O: Copy + FnOnce() -> bool {
|
||||
fn wait_for<O>(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<O>(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);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user