Simplify: let TA and CA use same codebase 'CertAuth'

This commit is contained in:
Tim Bruijnzeels
2019-07-09 12:17:40 +02:00
parent fd1df3bb82
commit 0280e6eaec
10 changed files with 862 additions and 861 deletions
+745 -34
View File
@@ -4,20 +4,48 @@ use std::marker::PhantomData;
use std::ops::Deref;
use std::sync::{Arc, RwLock};
use chrono::Duration;
use rand::Rng;
use rpki::cert::{Cert, TbsCert, KeyUsage, Overclaim};
use rpki::crypto::{PublicKey, PublicKeyFormat};
use rpki::csr::Csr;
use rpki::uri;
use rpki::x509::{Serial, Validity, Time, Name};
use krill_commons::api::{EntitlementClass, Entitlements, IssuanceRequest};
use krill_commons::api::admin::{Handle, ParentCaContact, Token};
use krill_commons::api::{
self,
DFLT_CLASS,
EncodedHash,
EntitlementClass,
Entitlements,
IssuanceRequest,
SigningCert,
};
use krill_commons::api::admin::{
Handle,
ParentCaContact,
Token
};
use krill_commons::api::ca::{
AddedObject,
AllCurrentObjects,
CertifiedKey,
ChildCa,
ChildCaDetails,
CurrentObject,
CurrentObjects,
IssuedCert,
KeyRef,
ObjectName,
ObjectsDelta,
PublicationDelta,
RcvdCert,
RepoInfo,
ResourceSet,
TrustAnchorInfo,
TrustAnchorLocator,
UpdatedObject,
};
use krill_commons::eventsourcing::{
Aggregate,
@@ -27,17 +55,29 @@ use krill_commons::eventsourcing::{
};
use krill_commons::util::softsigner::SignerKeyId;
use crate::trustanchor::CaSigner;
use crate::signing::CaSignSupport;
use crate::signing::{CaSigner, CaSignSupport};
pub const CA_NS: &str = "cas";
const TA_NAME: &str = "ta"; // reserved for TA
pub fn ta_handle() -> Handle {
Handle::from(TA_NAME)
}
//------------ CertAuthInit --------------------------------------------------
pub type CaIni = StoredEvent<CaIniDet>;
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[allow(clippy::large_enum_variant)]
pub enum CaType {
Child,
Ta(CertifiedKey, TrustAnchorLocator)
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct CaIniDet(Token, RepoInfo);
pub struct CaIniDet(Token, RepoInfo, CaType);
impl CaIniDet {
pub fn init(
@@ -48,9 +88,71 @@ impl CaIniDet {
CaIni::new(
handle,
0,
CaIniDet(token, info)
CaIniDet(token, info, CaType::Child)
)
}
pub fn init_ta<S: CaSigner>(
handle: &Handle,
token: Token,
info: RepoInfo,
ta_aia: uri::Rsync,
ta_uris: Vec<uri::Https>,
key: SignerKeyId,
signer: Arc<RwLock<S>>,
) -> CaRes<CaIni> {
let resources = ResourceSet::all_resources();
let ta_cert = Self::mk_ta_cer(&info, &resources, &key, signer)?;
let tal = TrustAnchorLocator::new(ta_uris, &ta_cert);
let key = CertifiedKey::new(key, RcvdCert::new(ta_cert, ta_aia));
Ok(CaIni::new(
handle,
0,
CaIniDet(token, info, CaType::Ta(key, tal))
))
}
fn mk_ta_cer<S: CaSigner>(
repo_info: &RepoInfo,
resources: &ResourceSet,
key: &S::KeyId,
signer: Arc<RwLock<S>>
) -> CaRes<Cert> {
let serial: Serial = rand::thread_rng().gen::<u128>().into();
let signer = signer.read().unwrap();
let pub_key = signer.get_key_info(&key).map_err(Error::signer)?;
let name = pub_key.to_subject_name();
let mut cert = TbsCert::new(
serial,
name.clone(),
Validity::new(Time::now(), Time::years_from_now(100)),
Some(name),
pub_key.clone(),
KeyUsage::Ca,
Overclaim::Refuse
);
cert.set_basic_ca(Some(true));
cert.set_ca_repository(Some(repo_info.ca_repository("")));
cert.set_rpki_manifest(Some(repo_info.rpki_manifest("", &pub_key.key_identifier())));
cert.set_rpki_notify(Some(repo_info.rpki_notify()));
cert.set_as_resources(Some(resources.asn().clone()));
cert.set_v4_resources(Some(resources.v4().deref().clone()));
cert.set_v6_resources(Some(resources.v6().deref().clone()));
cert.into_cert(
signer.deref(),
key
).map_err(Error::signer)
}
}
@@ -112,7 +214,11 @@ pub struct CertReceived {
#[derive(Clone, Debug, Deserialize, Serialize)]
#[allow(clippy::large_enum_variant)]
pub enum CaEvtDet {
// Parent Events
// Being a parent Events
ChildAdded(ChildCa),
CertificateIssued(Handle, String, IssuedCert),
// Being a child Events
ParentAdded(ParentHandle, ParentCaContact),
ResourceClassAdded(ParentHandle, ResourceClassName, ResourceClass),
@@ -124,12 +230,13 @@ pub enum CaEvtDet {
PendingKeyActivated(ParentHandle, ResourceClassName, RcvdCert),
// Publishing
Published(ParentHandle, ResourceClassName, KeyStatus, PublicationDelta)
Published(ParentHandle, ResourceClassName, KeyStatus, PublicationDelta),
TaPublished(PublicationDelta)
}
impl CaEvtDet {
/// This marks a parent as added to the CA.
pub fn parent_added(
fn parent_added(
handle: &Handle,
version: u64,
parent_handle: ParentHandle,
@@ -143,7 +250,7 @@ impl CaEvtDet {
}
/// This marks a resource class as added under a parent for the CA.
pub fn resource_class_added(
fn resource_class_added(
handle: &Handle,
version: u64,
parent_handle: ParentHandle,
@@ -165,7 +272,7 @@ impl CaEvtDet {
/// then gets a new certificate, it will send a command to the CA with
/// the new certificate to mark it as received, and take other
/// appriopiate actions (key life cycle, publication).
pub fn certificate_requested(
fn certificate_requested(
handle: &Handle,
version: u64,
cert_issue_req: CertRequested
@@ -179,7 +286,7 @@ impl CaEvtDet {
/// This marks a certificate as received for the key of the given status
/// in a given resource class under a parent.
pub fn certificate_received(
fn certificate_received(
handle: &Handle,
version: u64,
received: CertReceived
@@ -198,7 +305,7 @@ impl CaEvtDet {
/// Note that key roll management is going to be implemented in the near
/// future and then there will also be appropriate events for all the
/// stages in a key roll.
pub fn pending_activated(
fn pending_activated(
handle: &Handle,
version: u64,
parent: ParentHandle,
@@ -214,7 +321,7 @@ impl CaEvtDet {
/// This marks a delta as published for a key under a resource class
/// under a parent CA.
pub fn published(
fn published(
handle: &Handle,
version: u64,
parent: ParentHandle,
@@ -228,6 +335,48 @@ impl CaEvtDet {
CaEvtDet::Published(parent, class_name, key_status, delta)
)
}
fn child_added(
handle: &Handle,
version: u64,
child: ChildCa
) -> CaEvt {
StoredEvent::new(
handle,
version,
CaEvtDet::ChildAdded(child)
)
}
fn certificate_issued(
handle: &Handle,
version: u64,
child_handle: Handle,
class_name: &str,
cert: IssuedCert
) -> CaEvt {
StoredEvent::new(
handle,
version,
CaEvtDet::CertificateIssued(
child_handle,
class_name.to_string(),
cert
)
)
}
fn published_ta(
handle: &Handle,
version: u64,
delta: PublicationDelta
) -> CaEvt {
StoredEvent::new(
handle,
version,
CaEvtDet::TaPublished(delta)
)
}
}
@@ -241,6 +390,11 @@ type ResourceClassName = String;
#[derive(Clone, Debug)]
#[allow(clippy::large_enum_variant)]
pub enum CaCmdDet<S: CaSigner> {
// Being a parent
AddChild(Handle, Token, ResourceSet),
CertifyChild(Handle, Csr, Option<ResourceSet>, Token, Arc<RwLock<S>>),
// Being a child
AddParent(ParentHandle, ParentCaContact),
UpdateEntitlements(ParentHandle, Entitlements, Arc<RwLock<S>>),
UpdateRcvdCert(
@@ -248,7 +402,10 @@ pub enum CaCmdDet<S: CaSigner> {
ResourceClassName,
RcvdCert,
Arc<RwLock<S>>
)
),
// General
Republish(Arc<RwLock<S>>)
}
impl<S: CaSigner> CommandDetails for CaCmdDet<S> {
@@ -256,6 +413,42 @@ impl<S: CaSigner> CommandDetails for CaCmdDet<S> {
}
impl<S: CaSigner> CaCmdDet<S> {
/// Adds a child to this CA. Will return an error in case you try
/// to give the child resources not held by the CA. And until issue
/// #25 is implemented, returns an error when the CA is not a TA.
pub fn add_child(
handle: &Handle,
child_handle: Handle,
child_token: Token,
child_resources: ResourceSet,
) -> CaCmd<S> {
SentCommand::new(
handle,
None,
CaCmdDet::AddChild(child_handle, child_token, child_resources)
)
}
/// 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: &Handle,
child_handle: Handle,
csr: Csr,
limit: Option<ResourceSet>,
token: Token,
signer: Arc<RwLock<S>>
) -> CaCmd<S> {
SentCommand::new(
handle,
None,
CaCmdDet::CertifyChild(child_handle, csr, limit, token, signer)
)
}
pub fn add_parent(
handle: &Handle,
name: &str,
@@ -303,6 +496,80 @@ impl<S: CaSigner> CaCmdDet<S> {
)
)
}
pub fn publish(
handle: &Handle,
signer: Arc<RwLock<S>>
) -> CaCmd<S> {
SentCommand::new(
handle,
None,
CaCmdDet::Republish(signer)
)
}
}
//------------ CaParents ---------------------------------------------------
#[derive(Clone, Debug, Deserialize, Serialize)]
#[allow(clippy::large_enum_variant)]
pub enum CaParents {
SelfSigned(CertifiedKey, TrustAnchorLocator),
Parents(HashMap<Handle, ParentCa>)
}
impl CaParents {
fn is_self_signed(&self) -> bool {
match self {
CaParents::SelfSigned(_,_) => true,
_ => false
}
}
fn assert_parent_new(&self, parent: &Handle) -> CaRes<()> {
match self {
CaParents::SelfSigned(_,_) => Err(Error::NotAllowedForTa),
CaParents::Parents(map) => {
if map.contains_key(parent) {
Err(Error::DuplicateParent(parent.clone()))
} else {
Ok(())
}
}
}
}
fn insert(&mut self, handle: Handle, parent: ParentCa) -> CaRes<()> {
match self {
CaParents::SelfSigned(_,_) => Err(Error::NotAllowedForTa),
CaParents::Parents(map) => {
map.insert(handle, parent);
Ok(())
}
}
}
fn get(&self, handle: &Handle) -> CaRes<&ParentCa> {
match self {
CaParents::SelfSigned(_,_) => Err(Error::NotAllowedForTa),
CaParents::Parents(map) => Ok(
map.get(handle)
.ok_or_else(|| Error::UnknownParent(handle.clone()))?
)
}
}
fn get_mut(&mut self, handle: &Handle) -> CaRes<&mut ParentCa> {
match self {
CaParents::SelfSigned(_,_) => Err(Error::NotAllowedForTa),
CaParents::Parents(map) => Ok(
map.get_mut(handle)
.ok_or_else(|| Error::UnknownParent(handle.clone()))?
)
}
}
}
@@ -316,7 +583,9 @@ pub struct CertAuth<S: CaSigner> {
token: Token, // The admin token for this CertAuth
base_repo: RepoInfo,
parents: HashMap<Handle, ParentCa>,
parents: CaParents,
children: HashMap<Handle, ChildCaDetails>,
phantom_signer: PhantomData<S>
}
@@ -336,16 +605,30 @@ impl<S: CaSigner> Aggregate for CertAuth<S> {
let token = details.0;
let base_repo = details.1;
let ca_type = details.2;
let parents = HashMap::new();
if ca_type == CaType::Child && handle == Handle::from(TA_NAME) {
return Err(Error::NameReservedTa)
}
let parents = match ca_type {
CaType::Child => CaParents::Parents(HashMap::new()),
CaType::Ta(key, tal) => CaParents::SelfSigned(key, tal)
};
let children = HashMap::new();
Ok(CertAuth {
handle,
version: 1,
token,
base_repo,
parents,
children,
phantom_signer: PhantomData
})
}
@@ -357,9 +640,20 @@ impl<S: CaSigner> Aggregate for CertAuth<S> {
fn apply(&mut self, event: CaEvt) {
self.version += 1;
match event.into_details() {
// Being a parent
CaEvtDet::ChildAdded(child) => {
let (handle, details) = child.unwrap();
self.children.insert(handle, details);
},
CaEvtDet::CertificateIssued(child, class_name, issued_cert) => {
let child = self.children.get_mut(&child).unwrap();
child.add_cert(&class_name, issued_cert);
},
// Being a child
CaEvtDet::ParentAdded(handle, info) => {
let parent = ParentCa::without_resource(info);
self.parents.insert(handle, parent);
self.parents.insert(handle, parent).unwrap();
},
CaEvtDet::ResourceClassAdded(parent, name, rc) => {
// Evt cannot occur without parent existing
@@ -383,11 +677,16 @@ impl<S: CaSigner> Aggregate for CertAuth<S> {
CaEvtDet::CertificateReceived(_rcvd) => {
unimplemented!()
},
// General functions
CaEvtDet::Published(parent, class_name, status, delta) => {
let mut parent = self.parent_mut(parent).unwrap();
let mut rc = parent.class_mut(&class_name).unwrap();
let mut ck = rc.get_key_mut(&status).unwrap();
ck.apply_delta(delta);
},
CaEvtDet::TaPublished(_delta) => {
}
}
@@ -395,6 +694,15 @@ impl<S: CaSigner> Aggregate for CertAuth<S> {
fn process_command(&self, command: CaCmd<S>) -> CaEvtsRes {
match command.into_details() {
// being a parent
CaCmdDet::AddChild(child, token, resources) => {
self.add_child(child, token, resources)
},
CaCmdDet::CertifyChild(child, csr, limit, token, signer) => {
self.certify_child(child, csr, limit, token, signer)
}
// being a child
CaCmdDet::AddParent(parent, info) => {
self.add_parent(parent,info)
},
@@ -403,49 +711,410 @@ impl<S: CaSigner> Aggregate for CertAuth<S> {
},
CaCmdDet::UpdateRcvdCert(parent, class_name, rcvd_cert, signer) => {
self.update_received_cert(parent, class_name, rcvd_cert, signer)
},
// general CA functions
CaCmdDet::Republish(signer) => {
self.republish(signer)
}
}
}
}
/// # Data presentation
///
impl<S: CaSigner> CertAuth<S> {
pub fn as_ta_info(&self) -> CaRes<TrustAnchorInfo> {
if let CaParents::SelfSigned(key, tal) = &self.parents {
let resources = key.incoming_cert().resources().clone();
let repo_info = self.base_repo.clone();
let children = self.children.clone();
let cert = key.incoming_cert().clone();
let tal = tal.clone();
Ok(TrustAnchorInfo::new(
resources,
repo_info,
children,
cert,
tal
))
} else {
unimplemented!()
}
}
}
/// # Publishing
///
impl<S: CaSigner> CertAuth<S> {
/// Returns all current objects for all parents, resource classes and keys
pub fn current_objects(&self) -> AllCurrentObjects {
AllCurrentObjects::empty()
let mut objects = AllCurrentObjects::empty();
match &self.parents {
CaParents::SelfSigned(key, _tal) => {
objects.add_name_space(DFLT_CLASS, key.current_set().objects())
},
CaParents::Parents(parents) => {
for parent in parents.values() {
for rc in parent.resources.values() {
let ns = rc.name_space();
if let Some(new_objects) = rc.new_objects() {
objects.add_name_space(ns, new_objects);
}
if let Some(current_objects) = rc.current_objects() {
objects.add_name_space(ns, current_objects);
}
if let Some(revoke_objects) = rc.revoke_objects() {
objects.add_name_space(ns, revoke_objects);
}
}
}
}
}
objects
}
fn republish_delta_for_key(
key: &CertifiedKey,
repo_info: &RepoInfo,
name_space: &str,
signer: Arc<RwLock<S>>
) -> CaRes<PublicationDelta> {
let ca_repo = repo_info.ca_repository(name_space);
let objects_delta = ObjectsDelta::new(ca_repo);
CaSignSupport::publish(
signer,
key,
repo_info,
name_space,
objects_delta
).map_err(Error::signer)
}
/// Republish objects for this CA
pub fn republish(&self, signer: Arc<RwLock<S>>) -> CaEvtsRes {
let mut res = vec![];
match &self.parents {
CaParents::SelfSigned(key, _tal) => {
if key.needs_publication() {
let delta = Self::republish_delta_for_key(
key,
&self.base_repo,
"",
signer.clone()
)?;
res.push(CaEvtDet::published_ta(
&self.handle,
self.version,
delta
))
}
},
CaParents::Parents(_map) => {
unimplemented!()
}
}
Ok(res)
}
}
/// # Manage parents & resources under parents
/// # Being a parent
///
impl<S: CaSigner> CertAuth<S> {
/// List entitlements (section 3.3.2 of RFC6492). Return an error if
/// the child is not authorized -- or unknown etc.
///
/// Only supported in TAs until issue #25 is implemented.
pub fn list(
&self,
child_handle: &Handle,
token: &Token
) -> CaRes<api::Entitlements> {
let child = self.get_authorised_child(child_handle, token)?;
let child_resources = child.resources(DFLT_CLASS)
.ok_or_else(|| Error::MissingResources)?;
let until = child_resources.not_after();
let issued = child_resources.certs().cloned().collect();
let cert = match &self.parents {
CaParents::SelfSigned(key, _tal) => key.incoming_cert(),
CaParents::Parents(_) => unimplemented!("Issue #25")
};
let resources = cert.resources().clone();
let cert = SigningCert::new(cert.uri().clone(), cert.cert().clone());
Ok(Entitlements::with_default_class(
cert, resources, until, issued
))
}
/// Returns an authorized child, or an error if the child is not
/// authorized or unknown.
pub fn get_authorised_child(
&self,
child_handle: &Handle,
token: &Token
) -> CaRes<&ChildCaDetails> {
let child = self.get_child(child_handle)?;
if token != child.token() {
Err(Error::Unauthorized(child_handle.clone()))
} else {
Ok(child)
}
}
/// Returns a child, or an error if the child is unknown.
pub fn get_child(&self, child: &Handle) -> CaRes<&ChildCaDetails> {
match self.children.get(child) {
None => Err(Error::UnknownChild(child.clone())),
Some(child) => Ok(child)
}
}
/// Adds the child, returns an error if the child is a duplicate,
/// or if the resources are not held by this CA, or (until #25) if
/// this CA is not a TA.
fn add_child(
&self,
handle: Handle,
token: Token,
resources: ResourceSet
) -> CaEvtsRes {
// check that
// 1) the resources are held by me
match &self.parents {
CaParents::SelfSigned(key, _tal) => {
if ! key.incoming_cert().resources().contains(&resources) {
return Err(Error::MissingResources)
}
},
CaParents::Parents(_map) => {
unimplemented!("Issue #25");
}
}
// 2) there is no existing child by this name
if self.has_child(&handle) {
return Err(Error::DuplicateChild(handle))
}
// TODO: Handle add child to normal CA (issue #25)
let mut child = ChildCa::without_resources(handle, token);
child.add_resources(DFLT_CLASS, resources);
Ok(vec![CaEvtDet::child_added(
&self.handle,
self.version,
child
)])
}
/// Certifies a child, unless:
/// = the child is unknown,
/// = the child is not authorised,
/// = the csr is invalid,
/// = the limit exceeds the child allocation,
/// = the signer throws up..
///
/// This CA is not a TA (until #25)
fn certify_child(
&self,
child: Handle,
csr: Csr,
limit: Option<ResourceSet>,
token: Token,
signer: Arc<RwLock<S>>
) -> CaEvtsRes {
let issuing_key = match &self.parents {
CaParents::SelfSigned(key, _tal) => key,
CaParents::Parents(_) => unimplemented!("Issue #25")
};
let issuing_cert = issuing_key.incoming_cert();
// verify child and resources
let child_resources = self.get_authorised_child(&child, &token)?
.resources(DFLT_CLASS)
.ok_or_else(|| Error::MissingResources)?;
let resources = match limit.as_ref() {
Some(limit) => {
if child_resources.resources().contains(limit) {
limit
} else {
return Err(Error::MissingResources)
}
},
None => child_resources.resources()
};
csr.validate()
.map_err(|_| Error::invalid_csr(&child, "invalid signature"))?;
// TODO: Check for key-re-use, ultimately return 1204 (RFC6492 3.4.1)
let current_cert = child_resources.cert(csr.public_key());
// create new cert
let issued_cert = {
let serial = {
Serial::random(signer.read().unwrap().deref())
.map_err(Error::signer)?
};
let issuer = issuing_cert.cert().subject().clone();
let validity = Validity::new(
Time::now() - Duration::minutes(3),
child_resources.not_after()
);
let subject = Some(Name::from_pub_key(csr.public_key()));
let pub_key = csr.public_key().clone();
let key_usage = KeyUsage::Ca;
let overclaim = Overclaim::Refuse;
let mut cert = TbsCert::new(
serial, issuer, validity, subject, pub_key, key_usage, overclaim
);
cert.set_basic_ca(Some(true));
// Note! The issuing CA is not authoritative over *where* the child CA
// may publish. I.e. it will sign over any claimed URIs by the child,
// and assume that they will not be able to do anything malicious,
// because the publication server for those URIs should verify the
// identity of the publisher, and that RPs will not invalidate the
// content of another CA's repo, if they it is wrongfully claimed.
let ca_repository = csr.ca_repository()
.ok_or_else(|| Error::invalid_csr(&child, "missing ca repo"))?;
let rpki_manifest = csr.rpki_manifest()
.ok_or_else(|| Error::invalid_csr(&child, "missing mft uri"))?;
let rpki_notify = csr.rpki_notify();
cert.set_ca_issuer(Some(issuing_cert.uri().clone()));
cert.set_crl_uri(Some(issuing_cert.crl_uri()));
cert.set_ca_repository(Some(ca_repository.clone()));
cert.set_rpki_manifest(Some(rpki_manifest.clone()));
cert.set_rpki_notify(rpki_notify.cloned());
cert.set_as_resources(Some(resources.asn().clone()));
cert.set_v4_resources(Some(resources.v4().deref().clone()));
cert.set_v6_resources(Some(resources.v6().deref().clone()));
cert.set_authority_key_identifier(
Some(issuing_cert.cert().subject_key_identifier())
);
let cert = {
cert.into_cert(
signer.read().unwrap().deref(),
issuing_key.key_id()
).map_err(Error::signer)?
};
let cert_uri = issuing_cert.uri_for_object(&cert);
IssuedCert::new(cert_uri, resources.clone(), cert)
};
let version = self.version;
let cert_object = CurrentObject::from(issued_cert.cert());
let issued_event = CaEvtDet::certificate_issued(
&self.handle,
version,
child,
DFLT_CLASS,
issued_cert.clone()
);
let delta = {
let ca_repo = self.base_repo.ca_repository("");
let mut delta = ObjectsDelta::new(ca_repo);
let cert_name = ObjectName::from(issued_cert.cert());
match current_cert {
None => {
delta.add(AddedObject::new(cert_name,cert_object))
},
Some(old) => {
let old_hash = EncodedHash::from_content(
old.cert().to_captured().as_slice()
);
delta.update(UpdatedObject::new(
cert_name, cert_object, old_hash
))
}
}
delta
};
let publish_event = CaEvtDet::published_ta(
&self.handle,
version + 1,
CaSignSupport::publish(
signer,
issuing_key,
&self.base_repo,
"",
delta
).map_err(Error::signer)?
);
Ok(vec![issued_event, publish_event])
}
/// 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)
}
}
/// # Being a child
///
impl<S: CaSigner> CertAuth<S> {
/// Returns true if this CertAuth is set up as a TA.
pub fn is_ta(&self) -> bool {
self.parents.is_self_signed()
}
/// List all parents
pub fn parents(&self) -> impl Iterator<Item=(&Handle, &ParentCa)>{
self.parents.iter()
pub fn parents(&self) -> CaRes<Vec<(Handle, ParentCa)>> {
match &self.parents {
CaParents::SelfSigned(_,_) => Err(Error::NotAllowedForTa),
CaParents::Parents(map) => {
Ok(map.iter().map(|e| (e.0.clone(), e.1.clone())).collect())
}
}
}
fn parent(&self, parent: Handle) -> CaRes<&ParentCa> {
self.parents.get(&parent).ok_or_else(|| Error::UnknownParent(parent))
self.parents.get(&parent)
}
fn parent_mut(&mut self, parent: Handle) -> CaRes<&mut ParentCa> {
self.parents.get_mut(&parent).ok_or_else(|| Error::UnknownParent(parent))
self.parents.get_mut(&parent)
}
/// Adds a parent. This method will return an error in case a parent
/// by this name (handle) is already known.
fn add_parent(&self, parent: Handle, info: ParentCaContact) -> CaEvtsRes {
if self.parents.contains_key(&parent) {
Err(Error::DuplicateParent(parent))
} else {
Ok(vec![CaEvtDet::parent_added(
&self.handle,
self.version,
parent,
info
)])
}
self.parents.assert_parent_new(&parent)?;
Ok(vec![CaEvtDet::parent_added(
&self.handle,
self.version,
parent,
info
)])
}
/// This processes entitlements from a parent, and updates the known
@@ -795,6 +1464,21 @@ impl ResourceClass {
revoke_key: None
}
}
pub fn name_space(&self) -> &str { &self.name_space }
pub fn new_objects(&self) -> Option<&CurrentObjects> {
self.new_key.as_ref().map(|k| k.current_set().objects())
}
pub fn current_objects(&self) -> Option<&CurrentObjects> {
self.current_key.as_ref().map(|k| k.current_set().objects())
}
pub fn revoke_objects(&self) -> Option<&CurrentObjects> {
self.revoke_key.as_ref().map(|k| k.current_set().objects())
}
}
/// # Request certificates
@@ -981,6 +1665,9 @@ pub enum KeyStatus {
#[derive(Debug, Display)]
pub enum Error {
#[display(fmt = "Functionality not supported for TA.")]
NotAllowedForTa,
#[display(fmt = "Duplicate parent added: {}", _0)]
DuplicateParent(Handle),
@@ -990,6 +1677,25 @@ pub enum Error {
#[display(fmt = "Got response for unknown resource class: {}", _0)]
UnknownResourceClass(String),
// Child related errors
#[display(fmt = "Name reserved for embedded TA.")]
NameReservedTa,
#[display(fmt = "Child {} already exists.", _0)]
DuplicateChild(Handle),
#[display(fmt = "Unknown child {}.", _0)]
UnknownChild(Handle),
#[display(fmt = "Unauthorized child {}", _0)]
Unauthorized(Handle),
#[display(fmt = "Not all child resources are held by TA")]
MissingResources,
#[display(fmt = "Invalidly CSR for child {}: {}.", _0, _1)]
InvalidCsr(Handle, String),
#[display(fmt = "No key held by CA matching issued certificate: {}", _0)]
NoKeyMatch(KeyRef),
@@ -1004,6 +1710,11 @@ impl Error {
pub fn signer(e: impl Display) -> Self {
Error::SignerError(e.to_string())
}
pub fn invalid_csr(handle: &Handle, msg: &str) -> Self {
Error::InvalidCsr(handle.clone(), msg.to_string())
}
}
impl std::error::Error for Error {}
+24 -37
View File
@@ -1,6 +1,7 @@
use std::io;
use std::path::PathBuf;
use std::sync::{Arc, RwLock};
use std::ops::Deref;
use rpki::crypto::PublicKeyFormat;
use rpki::uri;
@@ -32,25 +33,18 @@ use crate::ca::{
CaCmdDet,
CaIniDet,
CaEvtDet,
ParentCa,
};
use crate::trustanchor::{
self,
TA_NS,
use crate::{
ta_handle,
CaSigner,
TrustAnchor,
TaCmdDet,
TaIniDet,
PubClients
};
use crate::PubClients;
//------------ CaServer ------------------------------------------------------
pub struct CaServer<S: CaSigner> {
signer: Arc<RwLock<S>>,
ta_store: Arc<DiskAggregateStore<TrustAnchor<S>>>,
ca_store: Arc<DiskAggregateStore<CertAuth<S>>>
}
@@ -64,22 +58,18 @@ impl<S: CaSigner> CaServer<S> {
pub_clients: Arc<PubClients>,
signer: S
) -> CaResult<Self, S> {
let mut ta_store = DiskAggregateStore::<TrustAnchor<S>>::new(work_dir, TA_NS)?;
ta_store.add_listener(pub_clients.clone());
let mut ca_store = DiskAggregateStore::<CertAuth<S>>::new(work_dir, CA_NS)?;
ca_store.add_listener(pub_clients);
Ok(CaServer {
signer: Arc::new(RwLock::new(signer)),
ta_store: Arc::new(ta_store),
ca_store: Arc::new(ca_store)
})
}
/// Gets the TrustAnchor, if present. Returns an error if the TA is uninitialized.
pub fn get_trust_anchor(&self) -> CaResult<Arc<TrustAnchor<S>>, S> {
self.ta_store
pub fn get_trust_anchor(&self) -> CaResult<Arc<CertAuth<S>>, S> {
self.ca_store
.get_latest(&ta_handle())
.map_err(|_| Error::TrustAnchorNotInitialisedError)
}
@@ -92,7 +82,7 @@ impl<S: CaSigner> CaServer<S> {
ta_uris: Vec<uri::Https>
) -> CaResult<(), S> {
let handle = ta_handle();
if self.ta_store.has(&handle) {
if self.ca_store.has(&handle) {
Err(Error::TrustAnchorInitialisedError)
} else {
let key = {
@@ -101,8 +91,11 @@ impl<S: CaSigner> CaServer<S> {
.map_err(Error::SignerError)?
};
let init = TaIniDet::init_with_all_resources(
let token = Token::random(self.signer.read().unwrap().deref());
let init = CaIniDet::init_ta(
&handle,
token,
info,
ta_aia,
ta_uris,
@@ -110,7 +103,7 @@ impl<S: CaSigner> CaServer<S> {
self.signer.clone()
)?;
self.ta_store.add(init)?;
self.ca_store.add(init)?;
Ok(())
}
@@ -131,21 +124,21 @@ impl<S: CaSigner> CaServer<S> {
// if there is a TA, publish it
let ta_handle = ta_handle();
if ! self.ta_store.has(&ta_handle) {
if ! self.ca_store.has(&ta_handle) {
debug!("No embedded TA present");
return Ok(()) // bail out w/o error in case there is no embedded TA
}
if let Ok(ta) = self.ta_store.get_latest(&ta_handle) {
if let Ok(ta) = self.ca_store.get_latest(&ta_handle) {
debug!("Publishing TA");
let ta_republish = TaCmdDet::republish(
let ta_republish = CaCmdDet::publish(
&ta_handle,
self.signer.clone()
);
let events = ta.process_command(ta_republish)?;
if ! events.is_empty() {
self.ta_store.update(&ta_handle, ta, events)?;
self.ca_store.update(&ta_handle, ta, events)?;
}
} else {
error!("TA present, but could not be loaded");
@@ -166,7 +159,7 @@ impl<S: CaSigner> CaServer<S> {
let ta = self.get_trust_anchor()?;
let ta_handle = ta_handle();
let add_child = TaCmdDet::<S>::add_child(
let add_child = CaCmdDet::<S>::add_child(
&ta_handle,
handle,
token.clone(),
@@ -174,7 +167,7 @@ impl<S: CaSigner> CaServer<S> {
);
let events = ta.process_command(add_child)?;
self.ta_store.update(&ta_handle, ta, events)?;
self.ca_store.update(&ta_handle, ta, events)?;
Ok(())
}
@@ -235,7 +228,7 @@ impl<S: CaSigner> CaServer<S> {
unimplemented!("Issue for multiple classes from CAs, issue #25")
}
let cmd = TaCmdDet::certify_child(
let cmd = CaCmdDet::certify_child(
parent,
child.clone(),
csr,
@@ -245,7 +238,7 @@ impl<S: CaSigner> CaServer<S> {
);
let events = ta.process_command(cmd)?;
let ta = self.ta_store.update(parent, ta, events)?;
let ta = self.ca_store.update(parent, ta, events)?;
// Get the newly issued cert for the child. Unwrap here is safe.
let child = ta.get_child(child).unwrap();
@@ -308,11 +301,12 @@ impl<S: CaSigner> CaServer<S> {
let mut child = self.ca_store.get_latest(handle)?;
let parents: Vec<(Handle, ParentCa)> = child.parents()
.map(|e| (e.0.clone(), e.1.clone()))
.collect();
// If this is a TA, then just return.. there is not updating
if child.is_ta() {
return Ok(())
}
for (parent_handle, parent) in parents {
for (parent_handle, parent) in child.parents()? {
let entitlements = match parent.contact() {
ParentCaContact::RemoteKrill(_uri, _token) => {
@@ -420,9 +414,6 @@ pub enum Error<S: CaSigner> {
#[display(fmt = "{}", _0)]
IoError(io::Error),
#[display(fmt = "{}", _0)]
TrustAnchorError(trustanchor::Error),
#[display(fmt = "TrustAnchor was already initialised")]
TrustAnchorInitialisedError,
@@ -449,10 +440,6 @@ impl<S: CaSigner> From<io::Error> for Error<S> {
fn from(e: io::Error) -> Self { Error::IoError(e) }
}
impl<S: CaSigner> From<trustanchor::Error> for Error<S> {
fn from(e: trustanchor::Error) -> Self { Error::TrustAnchorError(e) }
}
impl<S: CaSigner> From<ca::Error> for Error<S> {
fn from(e: ca::Error) -> Self { Error::CaError(e) }
}
+53 -69
View File
@@ -14,18 +14,19 @@ extern crate rpki;
extern crate krill_commons;
mod ca;
pub use ca::ta_handle;
mod caserver;
pub use caserver::CaServer;
pub use caserver::Error as CaServerError;
pub mod trustanchor;
pub use self::caserver::CaServer;
pub use self::caserver::Error as CaServerError;
mod signing;
pub use self::signing::CaSigner;
pub use self::signing::CaSignSupport;
mod publishing;
pub use publishing::PubClients;
pub use publishing::Error as PubClientError;
pub use self::publishing::PubClients;
pub use self::publishing::Error as PubClientError;
//------------ Tests ---------------------------------------------------------
@@ -62,15 +63,8 @@ mod tests {
test_under_tmp,
};
use crate::trustanchor::{
ta_handle,
TA_NS,
TaCmdDet,
TaEvtDet,
TaIniDet,
TrustAnchor,
};
use crate::ca::{
ta_handle,
CA_NS,
CertAuth,
CaIniDet,
@@ -84,12 +78,11 @@ mod tests {
}
#[test]
fn ca_under_ta() {
fn init_ta() {
test_under_tmp(|d| {
let ta_store = DiskAggregateStore::<TrustAnchor<OpenSslSigner>>::new(
&d, TA_NS
let ca_store = DiskAggregateStore::<CertAuth<OpenSslSigner>>::new(
&d, CA_NS
).unwrap();
let handle = ta_handle();
let ta_repo_info = {
let base_uri = rsync("rsync://localhost/repo/ta/");
@@ -97,6 +90,10 @@ mod tests {
RepoInfo::new(base_uri, rrdp_uri)
};
let ta_handle = ta_handle();
let ta_token = Token::from("ta");
let ta_uri = https("https://localhost/tal/ta.cer");
let ta_aia = rsync("rsync://localhost/repo/ta.cer");
@@ -105,51 +102,23 @@ mod tests {
let signer = Arc::new(RwLock::new(signer));
//
// --- Create TA
//
// Expect:
// - TA initialised
// --- Create TA and publish
//
let init = TaIniDet::init_with_all_resources(
&handle,
let ta_ini = CaIniDet::init_ta(
&ta_handle,
ta_token.clone(),
ta_repo_info,
ta_aia,
vec![ta_uri],
key,
signer.clone()
).unwrap();
ta_store.add(init).unwrap();
let ta = ta_store.get_latest(&handle).unwrap();
let publish_cmd = TaCmdDet::republish(&handle, signer.clone());
let events = ta.process_command(publish_cmd).unwrap();
let ta = ta_store.update(&handle, ta, events).unwrap();
//
// --- Add Child to TA
//
// Expect:
// - Child added to TA
//
let child_handle = Handle::from("child");
let child_token = Token::from("child");
let child_rs = ResourceSet::from_strs("", "10.0.0.0/16", "").unwrap();
let cmd = TaCmdDet::add_child(
&handle,
child_handle.clone(),
child_token.clone(),
child_rs
);
let events = ta.process_command(cmd).unwrap();
let ta = ta_store.update(&handle, ta, events).unwrap();
assert_eq!(1, ta.as_info().children().len());
ca_store.add(ta_ini).unwrap();
let ta = ca_store.get_latest(&ta_handle).unwrap();
//
// --- Create Child CA
@@ -157,6 +126,9 @@ mod tests {
// Expect:
// - Child CA initialised
//
let child_handle = Handle::from("child");
let child_token = Token::from("child");
let child_rs = ResourceSet::from_strs("", "10.0.0.0/16", "").unwrap();
let ca_repo_info = {
let base_uri = rsync("rsync://localhost/repo/ca/");
@@ -164,10 +136,6 @@ mod tests {
RepoInfo::new(base_uri, rrdp_uri)
};
let ca_store = DiskAggregateStore::<CertAuth<OpenSslSigner>>::new(
&d, CA_NS
).unwrap();
let ca_ini = CaIniDet::init(
&child_handle,
child_token.clone(),
@@ -177,6 +145,23 @@ mod tests {
ca_store.add(ca_ini).unwrap();
let child = ca_store.get_latest(&child_handle).unwrap();
//
// --- Add Child to TA
//
// Expect:
// - Child added to TA
//
let cmd = CaCmdDet::add_child(
&ta_handle,
child_handle.clone(),
child_token.clone(),
child_rs
);
let events = ta.process_command(cmd).unwrap();
let ta = ca_store.update(&ta_handle, ta, events).unwrap();
//
// --- Add TA as parent to child CA
//
@@ -184,16 +169,14 @@ mod tests {
// - Parent added
//
let parent_handle = Handle::from("parent");
let parent = ParentCaContact::for_embedded(
parent_handle.clone(),
ta_handle.clone(),
child_token.clone()
);
let add_parent = CaCmdDet::add_parent(
&child_handle,
parent_handle.as_str(),
ta_handle.as_str(),
parent
);
@@ -213,7 +196,7 @@ mod tests {
let upd_ent = CaCmdDet::upd_entitlements(
&child_handle,
&parent_handle,
&ta_handle,
entitlements,
signer.clone()
);
@@ -232,7 +215,7 @@ mod tests {
assert_eq!("all", &class_name);
assert_eq!(None, limit);
if let ParentCaContact::Embedded(handle, token) = parent_info {
assert_eq!(parent_handle, handle);
assert_eq!(ta_handle, handle);
assert_eq!(child_token, token);
} else {
panic!("Expected embedded contact")
@@ -246,8 +229,9 @@ mod tests {
// - Publication
//
let ta_cmd = TaCmdDet::certify_child(
&handle, child_handle.clone(),
let ta_cmd = CaCmdDet::certify_child(
&ta_handle,
child_handle.clone(),
csr,
limit,
child_token.clone(),
@@ -256,10 +240,10 @@ mod tests {
let ta_events = ta.process_command(ta_cmd).unwrap();
let issued_evt = ta_events[0].clone().into_details();
let _ta = ta_store.update(&handle, ta, ta_events).unwrap();
let _ta = ca_store.update(&ta_handle, ta, ta_events).unwrap();
let (h, n, issued) = match issued_evt {
TaEvtDet::CertificateIssued(h, n, c) => (h, n, c),
CaEvtDet::CertificateIssued(h, n, c) => (h, n, c),
_ => panic!("Expected issued certificate.")
};
assert_eq!(child_handle, h);
@@ -275,7 +259,7 @@ mod tests {
let rcvd_cert = RcvdCert::from(issued);
let upd_rcvd = CaCmdDet::upd_received_cert(
&child_handle, &parent_handle, DFLT_CLASS, rcvd_cert, signer.clone()
&child_handle, &ta_handle, DFLT_CLASS, rcvd_cert, signer.clone()
);
let events = child.process_command(upd_rcvd).unwrap();
+9 -23
View File
@@ -24,13 +24,9 @@ use krill_commons::eventsourcing::{
};
use krill_commons::util::httpclient;
use crate::trustanchor::{
CaSigner,
TrustAnchor,
TaEvt,
TaEvtDet
};
use ca::{CertAuth, CaEvt, CaEvtDet};
use crate::ca::{CertAuth, CaEvt, CaEvtDet};
use crate::signing::CaSigner;
//------------ PubClientInit -------------------------------------------------
@@ -185,26 +181,16 @@ impl PubClients {
}
}
/// Implement listening for TrustAnchor Published events.
impl<S: CaSigner> EventListener<TrustAnchor<S>> for PubClients {
fn listen(&self, ta: &TrustAnchor<S>, event: &TaEvt) {
if let TaEvtDet::Published(delta) = event.details() {
debug!("PubClients: Observed delta for publishing");
let current_objects = ta.current_objects();
self.publish(
event.handle(),
current_objects,
delta.objects().clone().into()
);
}
}
}
/// Implement listening for CertAuth Published events.
impl<S: CaSigner> EventListener<CertAuth<S>> for PubClients {
fn listen(&self, ca: &CertAuth<S>, event: &CaEvt) {
if let CaEvtDet::Published(_,_,_,delta) = event.details() {
if let Some(delta) = match event.details() {
CaEvtDet::Published(_,_,_, delta) => Some(delta),
CaEvtDet::TaPublished(delta) => Some(delta),
_ => None
} {
debug!("Pubclients: publishing for {}", event.handle());
let current_objects = ca.current_objects();
+14 -3
View File
@@ -1,13 +1,15 @@
//! Support for signing mft, crl, certificates, roas..
//! Common objects for TAs and CAs
use std::sync::{Arc, RwLock};
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::manifest::{Manifest, ManifestContent, FileAndHash};
use rpki::crypto::{DigestAlgorithm, KeyIdentifier, SigningError};
use rpki::crypto::{DigestAlgorithm, KeyIdentifier, SigningError, Signer};
use rpki::crypto::signer::KeyError;
use rpki::sigobj::SignedObjectBuilder;
use rpki::x509::{Serial, Time, Validity};
@@ -24,9 +26,18 @@ use krill_commons::api::ca::{
UpdatedObject,
};
use crate::trustanchor::CaSigner;
use krill_commons::util::softsigner::SignerKeyId;
//------------ CaSigner ------------------------------------------------------
pub trait CaSigner: Signer<KeyId=SignerKeyId> + Clone + Debug + Serialize + Sized + Sync + Send +'static {}
impl<T: Signer<KeyId=SignerKeyId> + Clone + Debug + Serialize + Sized + Sync + Send + 'static > CaSigner for T {}
//------------ CaSignSupport -------------------------------------------------
/// Support signing by CAs
pub struct CaSignSupport;
impl CaSignSupport {
-687
View File
@@ -1,687 +0,0 @@
use std::collections::HashMap;
use std::fmt::{Debug, Display};
use std::marker::PhantomData;
use std::ops::Deref;
use std::sync::{Arc, RwLock};
use chrono::Duration;
use serde::Serialize;
use rand::Rng;
use rpki::cert::{
Cert,
KeyUsage,
Overclaim,
TbsCert,
};
use rpki::crypto::Signer;
use rpki::csr::Csr;
use rpki::uri;
use rpki::x509::{Serial, Time, Validity, Name};
use krill_commons::api;
use krill_commons::api::{
DFLT_CLASS,
EncodedHash,
Entitlements,
SigningCert,
};
use krill_commons::api::admin::{
Handle,
Token
};
use krill_commons::api::ca::{
AddedObject,
AllCurrentObjects,
CertifiedKey,
ChildCa,
ChildCaDetails,
CurrentObject,
IssuedCert,
ObjectName,
ObjectsDelta,
PublicationDelta,
RcvdCert,
RepoInfo,
ResourceSet,
TrustAnchorInfo,
TrustAnchorLocator,
UpdatedObject,
};
use krill_commons::eventsourcing::{
Aggregate,
CommandDetails,
SentCommand,
StoredEvent,
};
use krill_commons::util::softsigner::SignerKeyId;
use crate::signing::CaSignSupport;
pub const TA_NS: &str = "trustanchors";
pub const TA_ID: &str = "ta";
pub fn ta_handle() -> Handle {
Handle::from(TA_ID)
}
//------------ CaSigner ------------------------------------------------------
pub trait CaSigner: Signer<KeyId=SignerKeyId> + Clone + Debug + Serialize + Sized + Sync + Send +'static {}
impl<T: Signer<KeyId=SignerKeyId> + Clone + Debug + Serialize + Sized + Sync + Send + 'static > CaSigner for T {}
//------------ TrustAnchorInit -----------------------------------------------
pub type TaIni = StoredEvent<TaIniDet>;
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct TaIniDet {
repo_info: RepoInfo,
children: Vec<ChildCa>,
current_key: CertifiedKey,
tal: TrustAnchorLocator,
}
impl TaIniDet {
/// Generates all the details for a Trust Anchor with all resources.
pub fn init_with_all_resources<S: CaSigner>(
handle: &Handle,
repo_info: RepoInfo,
ta_aia: uri::Rsync,
ta_uris: Vec<uri::Https>,
key: SignerKeyId,
signer: Arc<RwLock<S>>,
) -> TaResult<TaIni> {
let resources = ResourceSet::all_resources();
let ta_cert = Self::mk_ta_cer(&repo_info, &resources, &key, signer)?;
let tal = TrustAnchorLocator::new(ta_uris, &ta_cert);
let current_key = CertifiedKey::new(key, RcvdCert::new(ta_cert, ta_aia));
Ok(StoredEvent::new(
&handle,
0,
TaIniDet {
repo_info,
children: vec![],
current_key,
tal
}
))
}
fn mk_ta_cer<S: CaSigner>(
repo_info: &RepoInfo,
resources: &ResourceSet,
key: &S::KeyId,
signer: Arc<RwLock<S>>
) -> TaResult<Cert> {
let serial: Serial = rand::thread_rng().gen::<u128>().into();
let signer = signer.read().unwrap();
let pub_key = signer.get_key_info(&key)
.map_err(|_| Error::MissingKey)?;
let name = pub_key.to_subject_name();
let mut cert = TbsCert::new(
serial,
name.clone(),
Validity::new(Time::now(), Time::years_from_now(100)),
Some(name),
pub_key.clone(),
KeyUsage::Ca,
Overclaim::Refuse
);
cert.set_basic_ca(Some(true));
cert.set_ca_repository(Some(repo_info.ca_repository("")));
cert.set_rpki_manifest(Some(repo_info.rpki_manifest("", &pub_key.key_identifier())));
cert.set_rpki_notify(Some(repo_info.rpki_notify()));
cert.set_as_resources(Some(resources.asn().clone()));
cert.set_v4_resources(Some(resources.v4().deref().clone()));
cert.set_v6_resources(Some(resources.v6().deref().clone()));
cert.into_cert(
signer.deref(),
key
).map_err(Error::signer)
}
}
//------------ TrustAnchorEvent ----------------------------------------------
pub type TaEvt = StoredEvent<TaEvtDet>;
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub enum TaEvtDet {
Published(PublicationDelta),
ChildAdded(ChildCa),
CertificateIssued(Handle, String, IssuedCert)
}
impl TaEvtDet {
fn published(
handle: &Handle,
version: u64,
delta: PublicationDelta
) -> TaEvt {
Self::with_details(handle, version, TaEvtDet::Published(delta))
}
fn child_added(handle: &Handle, version: u64, child: ChildCa) -> TaEvt {
Self::with_details(handle, version, TaEvtDet::ChildAdded(child))
}
fn certificate_issued(
handle: &Handle,
version: u64,
child_handle: Handle,
class_name: &str,
cert: IssuedCert
) -> TaEvt {
Self::with_details(
handle,
version,
TaEvtDet::CertificateIssued(
child_handle,
class_name.to_string(),
cert
)
)
}
fn with_details(
handle: &Handle,
version: u64,
details: TaEvtDet
) -> TaEvt {
TaEvt::new(&handle, version, details)
}
}
//------------ TrustAnchorCommand --------------------------------------------
pub type TaCmd<S> = SentCommand<TaCmdDet<S>>;
#[derive(Clone, Debug)]
#[allow(clippy::large_enum_variant)]
pub enum TaCmdDet<S: CaSigner> {
Republish(Arc<RwLock<S>>),
AddChild(ChildCa),
CertifyChild(Handle, Csr, Option<ResourceSet>, Token, Arc<RwLock<S>>)
}
impl<S: CaSigner> CommandDetails for TaCmdDet<S> {
type Event = TaEvt;
}
impl<S: CaSigner> TaCmdDet<S> {
pub fn republish(handle: &Handle, signer: Arc<RwLock<S>>) -> TaCmd<S> {
SentCommand::new(
handle,
None,
TaCmdDet::Republish(signer)
)
}
pub fn add_child(
handle: &Handle,
child_handle: Handle,
child_token: Token,
child_resources: ResourceSet,
) -> TaCmd<S> {
let mut child = ChildCa::without_resources(child_handle, child_token);
child.add_resources(DFLT_CLASS, child_resources);
SentCommand::new(
handle,
None,
TaCmdDet::AddChild(child)
)
}
pub fn certify_child(
handle: &Handle,
child_handle: Handle,
csr: Csr,
limit: Option<ResourceSet>,
token: Token,
signer: Arc<RwLock<S>>
) -> TaCmd<S> {
SentCommand::new(
handle,
None,
TaCmdDet::CertifyChild(child_handle, csr, limit, token, signer)
)
}
}
//------------ TrustResult ---------------------------------------------------
/// Helper type for TrustAnchor results
type TaResult<R> = Result<R, Error>;
type TaEvtsRes = TaResult<Vec<TaEvt>>;
//------------ TrustAnchor ---------------------------------------------------
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct TrustAnchor<S: CaSigner> {
handle: Handle,
version: u64,
repo_info: RepoInfo,
current_key: CertifiedKey,
tal: TrustAnchorLocator,
children: HashMap<Handle, ChildCaDetails>,
phantom_signer: PhantomData<S>
}
impl<S: CaSigner> TrustAnchor<S> {
pub fn as_info(&self) -> TrustAnchorInfo {
TrustAnchorInfo::new(
self.resources().clone(),
self.repo_info.clone(),
self.children.clone(),
self.tal.clone()
)
}
pub fn tal(&self) -> &TrustAnchorLocator {
&self.tal
}
pub fn cert(&self) -> &RcvdCert {
self.current_key.incoming_cert()
}
pub fn resources(&self) -> &ResourceSet {
&self.current_key.incoming_cert().resources()
}
pub fn repo_info(&self) -> &RepoInfo {
&self.repo_info
}
pub fn current_objects(&self) -> AllCurrentObjects {
AllCurrentObjects::for_name_space(
"",
self.current_key.current_set().objects()
)
}
fn republish(&self, signer: Arc<RwLock<S>>) -> TaEvtsRes {
if !self.current_key.needs_publication() {
debug!("TA does not need to be republished");
return Ok(vec![])
}
let ca_repo = self.repo_info.ca_repository("");
let delta = ObjectsDelta::new(ca_repo);
let delta = CaSignSupport::publish(
signer,
&self.current_key,
self.repo_info(),
"",
delta
).map_err(Error::signer)?;
Ok(vec![TaEvtDet::published(&self.handle, self.version, delta)])
}
}
/// # Child CA Support
impl<S: CaSigner> TrustAnchor<S> {
/// Returns an authorized child, or an error if the child is not
/// authorized or unknown.
pub fn get_authorised_child(
&self,
child: &Handle,
token: &Token
) -> TaResult<&ChildCaDetails> {
let child = self.get_child(child)?;
if token != child.token() {
Err(Error::Unauthorized)
} else {
Ok(child)
}
}
pub fn get_child(&self, child: &Handle) -> TaResult<&ChildCaDetails> {
match self.children.get(child) {
None => Err(Error::UnknownChild(child.clone())),
Some(child) => Ok(child)
}
}
fn has_child(&self, handle: &Handle) -> bool {
self.children.contains_key(handle)
}
fn add_child(&self, child: ChildCa) -> TaEvtsRes {
// check that
// 1) the resources are held by the TA
// 2) there is no existing child by this name
let my_res = self.current_key.incoming_cert().resources();
for res in child.details().resource_sets() {
if ! my_res.contains(res) {
return Err(Error::MissingResources)
}
}
if self.has_child(child.handle()) {
return Err(Error::DuplicateChild(child.handle().clone()))
}
Ok(vec![TaEvtDet::child_added(&self.handle, self.version, child)])
}
/// List entitlements (section 3.3.2 of RFC6492). Return an error if
/// the child is not authorized -- or unknown etc.
pub fn list(
&self,
child_handle: &Handle,
token: &Token
) -> TaResult<api::Entitlements> {
let child = self.get_authorised_child(child_handle, token)?;
let child_resources = child.resources(DFLT_CLASS)
.ok_or_else(|| Error::ChildLacksResources(child_handle.clone()))?;
let until = child_resources.not_after();
let issued = child_resources.certs().cloned().collect();
let cert = self.current_key.incoming_cert();
let resources = cert.resources().clone();
let cert = SigningCert::new(cert.uri().clone(), cert.cert().clone());
Ok(Entitlements::with_default_class(
cert, resources, until, issued
))
}
/// Certify a child CA. Returns the events that should be applied to this
/// CA. Meant to be called by issuing a 'CertifyChild' command.
fn certify_child(
&self,
child: Handle,
csr: Csr,
limit: Option<ResourceSet>,
token: Token,
signer: Arc<RwLock<S>>
) -> TaEvtsRes {
// verify child and resources
let child_resources = self.get_authorised_child(&child, &token)?
.resources(DFLT_CLASS)
.ok_or_else(|| Error::ChildLacksResources(child.clone()))?;
let resources = match limit.as_ref() {
Some(limit) => {
if child_resources.resources().contains(limit) {
limit
} else {
return Err(Error::ChildOverclaims(child.clone()))
}
},
None => child_resources.resources()
};
csr.validate()
.map_err(|_| Error::invalid_csr(&child, "invalid signature"))?;
// TODO: Check for key-re-use, ultimately return 1204 (RFC6492 3.4.1)
let current_cert = child_resources.cert(csr.public_key());
// create new cert
let issued_cert = {
let issuing_cert = self.current_key.incoming_cert();
let serial = {
Serial::random(signer.read().unwrap().deref())
.map_err(Error::signer)?
};
let issuer = issuing_cert.cert().subject().clone();
let validity = Validity::new(
Time::now() - Duration::minutes(3),
child_resources.not_after()
);
let subject = Some(Name::from_pub_key(csr.public_key()));
let pub_key = csr.public_key().clone();
let key_usage = KeyUsage::Ca;
let overclaim = Overclaim::Refuse;
let mut cert = TbsCert::new(
serial, issuer, validity, subject, pub_key, key_usage, overclaim
);
cert.set_basic_ca(Some(true));
// Note! The issuing CA is not authoritative over *where* the child CA
// may publish. I.e. it will sign over any claimed URIs by the child,
// and assume that they will not be able to do anything malicious,
// because the publication server for those URIs should verify the
// identity of the publisher, and that RPs will not invalidate the
// content of another CA's repo, if they it is wrongfully claimed.
let ca_repository = csr.ca_repository()
.ok_or_else(|| Error::invalid_csr(&child, "missing ca repo"))?;
let rpki_manifest = csr.rpki_manifest()
.ok_or_else(|| Error::invalid_csr(&child, "missing mft uri"))?;
let rpki_notify = csr.rpki_notify();
cert.set_ca_issuer(Some(issuing_cert.uri().clone()));
cert.set_crl_uri(Some(issuing_cert.crl_uri()));
cert.set_ca_repository(Some(ca_repository.clone()));
cert.set_rpki_manifest(Some(rpki_manifest.clone()));
cert.set_rpki_notify(rpki_notify.cloned());
cert.set_as_resources(Some(resources.asn().clone()));
cert.set_v4_resources(Some(resources.v4().deref().clone()));
cert.set_v6_resources(Some(resources.v6().deref().clone()));
cert.set_authority_key_identifier(
Some(issuing_cert.cert().subject_key_identifier())
);
let cert = {
cert.into_cert(
signer.read().unwrap().deref(),
self.current_key.key_id()
).map_err(Error::signer)?
};
let cert_uri = issuing_cert.uri_for_object(&cert);
IssuedCert::new(cert_uri, resources.clone(), cert)
};
let version = self.version;
let cert_object = CurrentObject::from(issued_cert.cert());
let issued_event = TaEvtDet::certificate_issued(
&self.handle,
version,
child,
DFLT_CLASS,
issued_cert.clone()
);
let delta = {
let ca_repo = self.repo_info.ca_repository("");
let mut delta = ObjectsDelta::new(ca_repo);
let cert_name = ObjectName::from(issued_cert.cert());
match current_cert {
None => {
delta.add(AddedObject::new(cert_name,cert_object))
},
Some(old) => {
let old_hash = EncodedHash::from_content(
old.cert().to_captured().as_slice()
);
delta.update(UpdatedObject::new(
cert_name, cert_object, old_hash
))
}
}
delta
};
let publish_event = TaEvtDet::published(
&self.handle,
version + 1,
CaSignSupport::publish(
signer,
&self.current_key,
&self.repo_info,
"",
delta
).map_err(Error::signer)?
);
Ok(vec![issued_event, publish_event])
}
}
impl<S: CaSigner> Aggregate for TrustAnchor<S> {
type Command = TaCmd<S>;
type Event = TaEvt;
type InitEvent = TaIni;
type Error = Error;
fn init(event: Self::InitEvent) -> Result<Self, Self::Error> {
let (handle, _version, init) = event.unwrap();
let version = 1; // after applying init
let repo_info = init.repo_info;
let current_key = init.current_key;
let tal = init.tal;
let children = HashMap::new();
Ok(
TrustAnchor {
handle,
version,
repo_info,
current_key,
tal,
children,
phantom_signer: PhantomData
}
)
}
fn version(&self) -> u64 {
self.version
}
fn apply(&mut self, event: TaEvt) {
self.version += 1;
match event.into_details() {
TaEvtDet::Published(delta) => {
self.current_key.apply_delta(delta);
},
TaEvtDet::ChildAdded(child) => {
let (handle, details) = child.unwrap();
self.children.insert(handle, details);
},
TaEvtDet::CertificateIssued(child, class_name, issued_cert) => {
let child = self.children.get_mut(&child).unwrap();
child.add_cert(&class_name, issued_cert)
}
}
}
fn process_command(&self, cmd: TaCmd<S>) -> TaEvtsRes {
match cmd.into_details() {
TaCmdDet::Republish(signer) => {
self.republish(signer)
},
TaCmdDet::AddChild(child) => {
self.add_child(child)
},
TaCmdDet::CertifyChild(child, csr, limit, token, signer) => {
self.certify_child(child, csr, limit, token, signer)
}
}
}
}
//------------ Error ---------------------------------------------------------
/// Trust Anchor Errors
#[derive(Debug, Display)]
pub enum Error {
#[display(fmt = "Cannot find key.")]
MissingKey,
#[display(fmt = "Error while signing: {}", _0)]
SignerError(String),
#[display(fmt = "Resource Authority was not initialised.")]
NotInitialised,
#[display(fmt = "Not all child resources are held by TA")]
MissingResources,
#[display(fmt = "Child {} already exists.", _0)]
DuplicateChild(Handle),
#[display(fmt = "Unknown child {}.", _0)]
UnknownChild(Handle),
#[display(fmt = "Child {} has no default resource class.", _0)]
ChildLacksResources(Handle),
#[display(fmt = "Child {} asks resources beyond entitlement.", _0)]
ChildOverclaims(Handle),
#[display(fmt = "Invalidly CSR for child {}: {}.", _0, _1)]
InvalidCsr(Handle, String),
#[display(fmt = "Unauthorized request")]
Unauthorized,
}
impl Error {
pub fn signer(e: impl Display) -> Self {
Error::SignerError(e.to_string())
}
pub fn invalid_csr(handle: &Handle, msg: &str) -> Self {
Error::InvalidCsr(handle.clone(), msg.to_string())
}
}
impl std::error::Error for Error {}
+9 -2
View File
@@ -3,6 +3,7 @@
use std::collections::HashMap;
use std::fmt;
use std::ops::Deref;
use std::str;
use std::str::FromStr;
@@ -44,7 +45,6 @@ use crate::rpki::manifest::{
FileAndHash,
Manifest,
};
use std::ops::Deref;
//------------ ChildCa -------------------------------------------------------
@@ -1080,7 +1080,8 @@ pub struct TrustAnchorInfo {
resources: ResourceSet,
repo_info: RepoInfo,
children: HashMap<Handle, ChildCaDetails>,
tal: TrustAnchorLocator
cert: RcvdCert,
tal: TrustAnchorLocator
}
impl TrustAnchorInfo {
@@ -1088,6 +1089,7 @@ impl TrustAnchorInfo {
resources: ResourceSet,
repo_info: RepoInfo,
children: HashMap<Handle, ChildCaDetails>,
cert: RcvdCert,
tal: TrustAnchorLocator
) -> Self {
@@ -1095,6 +1097,7 @@ impl TrustAnchorInfo {
resources,
repo_info,
children,
cert,
tal
}
}
@@ -1111,6 +1114,10 @@ impl TrustAnchorInfo {
&self.children
}
pub fn cert(&self) -> &RcvdCert {
&self.cert
}
pub fn tal(&self) -> &TrustAnchorLocator {
&self.tal
}
+1 -1
View File
@@ -20,7 +20,7 @@ pub fn test_under_tmp<F>(op: F) where F: FnOnce(PathBuf) -> () {
op(dir);
fs::remove_dir_all(path).unwrap();
let _result = fs::remove_dir_all(path);
}
/// This method sets up a random subdirectory and returns it. It is
+6 -4
View File
@@ -6,8 +6,7 @@ use std::sync::Arc;
use bcder::Captured;
use rpki::uri;
use krill_ca::{CaServer, CaServerError, PubClients, PubClientError};
use krill_ca::trustanchor::{ta_handle};
use krill_ca::{ta_handle, CaServer, CaServerError, PubClients, PubClientError};
use krill_commons::api::{publication, Entitlements, IssuanceRequest};
use krill_commons::api::admin;
use krill_commons::api::admin::{Handle, Token, PubServerInfo, CertAuthInit, CertAuthPubMode, ParentCaContact, AddChildRequest, ParentCaInfo};
@@ -253,11 +252,14 @@ impl KrillServer {
///
impl KrillServer {
pub fn ta_info(&self) -> Option<TrustAnchorInfo> {
self.caserver.get_trust_anchor().map(|ta| ta.as_info()).ok()
match self.caserver.get_trust_anchor() {
Ok(ta) => ta.as_ta_info().ok(),
_ => None
}
}
pub fn trust_anchor_cert(&self) -> Option<RcvdCert> {
self.caserver.get_trust_anchor().map(|ta| ta.cert().clone()).ok()
self.ta_info().map(|ta| ta.cert().clone())
}
pub fn ta_init(&mut self) -> EmptyRes {
+1 -1
View File
@@ -4,7 +4,7 @@ extern crate krill_commons;
extern crate krill_pubc;
extern crate krill_ca;
use krill_ca::trustanchor::ta_handle;
use krill_ca::ta_handle;
use krill_client::options::{
CaCommand,
Command,