use std::{collections::HashMap, fmt, sync::Arc};
use chrono::Duration;
use log::{log_enabled, trace};
use rpki::{
ca::{
idcert::IdCert,
idexchange::{self, CaHandle, ChildHandle, MyHandle},
provisioning::{ResourceClassEntitlements, SigningCert},
},
crypto::KeyIdentifier,
repository::x509::Time,
};
use serde::{Deserialize, Serialize};
use crate::{
commons::{
actor::Actor,
crypto::{CsrInfo, KrillSigner},
error::Error,
eventsourcing::{
self, Event, InitCommandDetails, InitEvent, WithStorableDetails,
},
KrillResult,
},
};
use crate::api::admin::{AddChildRequest, RepositoryContact};
use crate::api::ca::IdCertInfo;
use crate::api::ta::{
Nonce, ProvisioningRequest, ProvisioningResponse, TaCertDetails,
TrustAnchorChild, TrustAnchorChildRequests, TrustAnchorObjects,
ApiTrustAnchorSignedRequest, TrustAnchorSignedResponse,
TrustAnchorSignerInfo, TrustAnchorSignerRequest,
};
use crate::constants::ta_resource_class_name;
use crate::server::ca::UsedKeyState;
use crate::tasigner::TaTimingConfig;
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct TrustAnchorProxy {
handle: CaHandle,
version: u64,
id: IdCertInfo,
signer: Option<TrustAnchorSignerInfo>,
repository: Option<RepositoryContact>,
child_details: HashMap<ChildHandle, TrustAnchorChild>,
open_signer_request: Option<Nonce>,
}
impl eventsourcing::Aggregate for TrustAnchorProxy {
type Command = TrustAnchorProxyCommand;
type StorableCommandDetails = TrustAnchorProxyCommandDetails;
type Event = TrustAnchorProxyEvent;
type InitCommand = TrustAnchorProxyInitCommand;
type InitEvent = TrustAnchorProxyInitEvent;
type Error = Error;
fn init(
handle: &CaHandle, event: TrustAnchorProxyInitEvent,
) -> Self {
TrustAnchorProxy {
handle: handle.clone(),
version: 1,
id: event.id,
repository: None,
signer: None,
child_details: HashMap::new(),
open_signer_request: None,
}
}
fn process_init_command(
command: TrustAnchorProxyInitCommand,
) -> Result<TrustAnchorProxyInitEvent, Error> {
Ok(TrustAnchorProxyInitEvent {
id: {
command.into_details().signer.create_self_signed_id_cert()?
.into()
}
})
}
fn version(&self) -> u64 {
self.version
}
fn increment_version(&mut self) {
self.version += 1;
}
fn apply(&mut self, event: Self::Event) {
if log_enabled!(log::Level::Trace) {
trace!(
"Applying event to Trust Anchor Proxy '{}', version: {}: {}",
self.handle,
self.version,
event
);
}
match event {
TrustAnchorProxyEvent::RepositoryAdded(repository) => {
self.repository = Some(repository)
}
TrustAnchorProxyEvent::SignerAdded(signer) => {
self.signer = Some(signer)
}
TrustAnchorProxyEvent::SignerUpdated(signer) => {
self.signer = Some(signer)
}
TrustAnchorProxyEvent::SignerRequestMade(nonce) => {
self.open_signer_request = Some(nonce)
}
TrustAnchorProxyEvent::SignerResponseReceived(response) => {
let content = response.into_content();
for (child_handle, child_responses) in content.child_responses
{
if let Some(child_details) =
self.child_details.get_mut(&child_handle)
{
for (key_id, response) in child_responses {
match &response {
ProvisioningResponse::Issuance(_) => {
child_details.used_keys.insert(
key_id,
UsedKeyState::InUse("default".into()),
);
}
ProvisioningResponse::Revocation(_) => {
child_details.used_keys.insert(
key_id,
UsedKeyState::Revoked,
);
}
_ => {}
}
child_details.open_requests.remove(&key_id);
child_details
.open_responses
.insert(key_id, response);
}
}
}
self.signer.as_mut().unwrap().objects = content.objects;
self.open_signer_request = None;
}
TrustAnchorProxyEvent::ChildAdded(child) => {
self.child_details.insert(child.handle.clone(), child);
}
TrustAnchorProxyEvent::ChildRequestAdded(
child_handle,
request,
) => {
self.child_details
.get_mut(&child_handle)
.unwrap() .open_requests
.insert(request.key_identifier(), request);
}
TrustAnchorProxyEvent::ChildResponseGiven(child_handle, key) => {
self.child_details
.get_mut(&child_handle)
.unwrap() .open_responses
.remove(&key);
}
}
}
fn process_command(
&self,
command: Self::Command,
) -> Result<Vec<Self::Event>, Self::Error> {
if log_enabled!(log::Level::Trace) {
trace!(
"Sending command to Trust Anchor Proxy '{}', version: {}: {}",
self.handle,
self.version,
command
);
}
match command.into_details() {
TrustAnchorProxyCommandDetails::Init => {
Err(Error::custom("Trust Anchor Proxy already created"))
}
TrustAnchorProxyCommandDetails::AddRepository(repository) => {
self.process_add_repository(repository)
}
TrustAnchorProxyCommandDetails::AddSigner(signer) => {
self.process_add_signer(signer)
}
TrustAnchorProxyCommandDetails::UpdateSigner(signer) => {
self.process_update_signer(signer)
}
TrustAnchorProxyCommandDetails::MakeSignerRequest => {
self.process_make_signer_request()
}
TrustAnchorProxyCommandDetails::ProcessSignerResponse(
response,
) => self.process_signer_response(response),
TrustAnchorProxyCommandDetails::AddChild(child) => {
self.process_add_child(child)
}
TrustAnchorProxyCommandDetails::AddChildRequest(
child_handle,
request,
) => self.process_add_child_request(child_handle, request),
TrustAnchorProxyCommandDetails::GiveChildResponse(
child_handle,
key,
) => self.process_give_child_response(child_handle, key),
}
}
}
impl TrustAnchorProxy {
fn process_add_repository(
&self,
repository: RepositoryContact,
) -> KrillResult<Vec<TrustAnchorProxyEvent>> {
if self.repository.is_none() {
Ok(vec![TrustAnchorProxyEvent::RepositoryAdded(repository)])
} else {
Err(Error::TaProxyAlreadyHasRepository)
}
}
fn process_add_signer(
&self,
signer: TrustAnchorSignerInfo,
) -> KrillResult<Vec<TrustAnchorProxyEvent>> {
if self.signer.is_none() {
Ok(vec![TrustAnchorProxyEvent::SignerAdded(signer)])
} else {
Err(Error::TaProxyAlreadyHasSigner)
}
}
fn process_update_signer(
&self,
signer: TrustAnchorSignerInfo,
) -> KrillResult<Vec<TrustAnchorProxyEvent>> {
if
let Some(s) = &self.signer
&& s.ta_cert_details.cert.key_identifier() ==
signer.ta_cert_details.cert.key_identifier()
{
return Ok(vec![
TrustAnchorProxyEvent::SignerUpdated(signer)
]);
}
Err(Error::TaProxyHasDifferentSigner)
}
fn process_make_signer_request(
&self,
) -> KrillResult<Vec<TrustAnchorProxyEvent>> {
if self.open_signer_request.is_some() {
Err(Error::TaProxyHasRequest)
} else {
Ok(vec![TrustAnchorProxyEvent::SignerRequestMade(Nonce::new())])
}
}
fn process_signer_response(
&self,
response: TrustAnchorSignedResponse,
) -> KrillResult<Vec<TrustAnchorProxyEvent>> {
let open_request_nonce = self
.open_signer_request
.as_ref()
.ok_or(Error::TaProxyHasNoRequest)?;
if &response.content().nonce != open_request_nonce {
Err(Error::TaProxyRequestNonceMismatch(
response.into_content().nonce,
open_request_nonce.clone(),
))
} else if let Some(signer) = &self.signer {
response.validate(&signer.id)?;
Ok(vec![TrustAnchorProxyEvent::SignerResponseReceived(
response,
)])
} else {
Err(Error::TaProxyHasNoSigner)
}
}
fn process_add_child(
&self,
child: AddChildRequest,
) -> KrillResult<Vec<TrustAnchorProxyEvent>> {
if self.child_details.contains_key(&child.handle) {
Err(Error::CaChildDuplicate(
self.handle.clone(),
child.handle,
))
}
else {
Ok(vec![TrustAnchorProxyEvent::ChildAdded(
TrustAnchorChild::new(
child.handle, child.id_cert.into(), child.resources
),
)])
}
}
fn process_add_child_request(
&self,
child_handle: ChildHandle,
request: ProvisioningRequest,
) -> KrillResult<Vec<TrustAnchorProxyEvent>> {
let child = self.get_child_details(&child_handle)?;
let ta_resource_class_name = ta_resource_class_name();
match &request {
ProvisioningRequest::Issuance(issuance) => {
if issuance.class_name() != &ta_resource_class_name {
return Err(Error::Custom(format!(
"TA child certificate sign request uses unknown resource class name '{}'",
issuance.class_name()
)));
}
issuance.limit().apply_to(&child.resources)?; CsrInfo::try_from(issuance.csr())?; }
ProvisioningRequest::Revocation(revocation) => {
if revocation.class_name() != &ta_resource_class_name {
return Err(Error::Custom(format!(
"TA child revocation request uses unknown resource class name '{}'",
revocation.class_name()
)));
}
if !child.used_keys.contains_key(&revocation.key()) {
return Err(Error::Custom(format!(
"TA child revocation requested for unknown key: {}",
revocation.key()
)));
}
}
}
Ok(vec![TrustAnchorProxyEvent::ChildRequestAdded(
child_handle,
request,
)])
}
fn process_give_child_response(
&self,
child_handle: ChildHandle,
key: KeyIdentifier,
) -> KrillResult<Vec<TrustAnchorProxyEvent>> {
let child = self.get_child_details(&child_handle)?;
if child.open_responses.contains_key(&key) {
Ok(vec![TrustAnchorProxyEvent::ChildResponseGiven(
child_handle,
key,
)])
} else {
Err(Error::Custom(format!(
"No response found for child {child_handle} and key {key}"
)))
}
}
}
impl TrustAnchorProxy {
pub fn has_open_request(&self) -> bool {
self.open_signer_request.is_some()
}
pub fn get_signer_request(
&self,
timing: TaTimingConfig,
signer: &KrillSigner,
) -> KrillResult<ApiTrustAnchorSignedRequest> {
if let Some(nonce) = self.open_signer_request.as_ref().cloned() {
let mut child_requests = vec![];
let mut renew_times = Vec::new();
for (child, details) in &self.child_details {
if !details.open_requests.is_empty() {
child_requests.push(TrustAnchorChildRequests {
child: child.clone(),
resources: details.resources.clone(),
requests: details.open_requests.clone(),
});
}
if let Ok(cert) = IdCert::try_from(&details.id) {
let v = cert.validity();
renew_times.push((cert.public_key().key_identifier(), v.not_after()));
}
}
let request = TrustAnchorSignerRequest {
nonce,
child_requests,
}
.sign(
self.id.public_key.key_identifier(),
timing.signed_message_validity_days,
signer,
)?;
Ok(ApiTrustAnchorSignedRequest {
request: request.request,
signed: request.signed,
issued_certificate_reissue_weeks_before:
timing.issued_certificate_reissue_weeks_before,
renew_times,
ta_renew_time: self.signer.as_ref().map(|info| {
info.ta_cert_details.cert.validity.not_after()
}),
})
} else {
Err(Error::TaProxyHasNoRequest)
}
}
pub fn get_ta_details(&self) -> KrillResult<&TaCertDetails> {
self.signer
.as_ref()
.ok_or(Error::TaNotInitialized)
.map(|signer| &signer.ta_cert_details)
}
pub fn get_trust_anchor_objects(
&self,
) -> KrillResult<&TrustAnchorObjects> {
self.signer
.as_ref()
.ok_or(Error::TaNotInitialized)
.map(|signer| &signer.objects)
}
pub fn id(&self) -> &IdCertInfo {
&self.id
}
pub fn repository(&self) -> Option<&RepositoryContact> {
self.repository.as_ref()
}
pub fn get_child(
&self,
child: &ChildHandle,
) -> KrillResult<&TrustAnchorChild> {
match self.child_details.get(child) {
None => {
Err(Error::CaChildUnknown(self.handle.clone(), child.clone()))
}
Some(child) => Ok(child),
}
}
}
impl TrustAnchorProxy {
pub fn publisher_request(&self) -> idexchange::PublisherRequest {
idexchange::PublisherRequest::new(
self.id.base64.clone(),
self.handle.convert(),
None,
)
}
}
impl TrustAnchorProxy {
pub fn entitlements(
&self,
child_handle: &ChildHandle,
ta_timing: &TaTimingConfig,
) -> KrillResult<ResourceClassEntitlements> {
let signer = self.signer.as_ref().ok_or(Error::TaNotInitialized)?;
let child = self.get_child_details(child_handle)?;
let signing_cert = {
let received_cert = &signer.ta_cert_details.cert;
let my_cert = received_cert.to_cert().map_err(|e| {
Error::Custom(format!(
"Issue with certificate held by TA: {e} "
))
})?;
SigningCert::new(received_cert.uri.clone(), my_cert)
};
let mut issued_certs = vec![];
let mut not_after = Time::now()
+ Duration::weeks(ta_timing.issued_certificate_validity_weeks);
let threshold = Time::now()
+ Duration::weeks(
ta_timing.issued_certificate_reissue_weeks_before,
);
for ki in child.used_keys.keys() {
if let Some(issued) = signer.objects.get_issued(ki) {
issued_certs.push(issued.to_rfc6492_issued_cert().map_err(
|e| {
Error::Custom(format!(
"Issue with issued certificate held by TA: {e} "
))
},
)?);
let expires = issued.validity.not_after();
if expires > threshold {
not_after = expires;
}
}
}
Ok(ResourceClassEntitlements::new(
ta_resource_class_name(),
child.resources.clone(),
not_after,
issued_certs,
signing_cert,
))
}
pub fn response_for_child(
&self,
child_handle: &ChildHandle,
request: &ProvisioningRequest,
) -> KrillResult<Option<&ProvisioningResponse>> {
let child = self.get_child_details(child_handle)?;
if let Some(response) =
child.open_responses.get(&request.key_identifier())
{
if request.matches_response(response) {
Ok(Some(response))
} else {
Err(Error::Custom(format!(
"Response for {child_handle} does not match request type."
)))
}
} else {
Ok(None)
}
}
pub fn matching_open_request(
&self,
child_handle: &ChildHandle,
request: &ProvisioningRequest,
) -> KrillResult<bool> {
let child = self.get_child_details(child_handle)?;
if let Some(existing) =
child.open_requests.get(&request.key_identifier())
{
match (existing, request) {
(
ProvisioningRequest::Issuance(existing),
ProvisioningRequest::Issuance(request),
) => {
Ok(
existing.class_name() == request.class_name() && existing.limit() == request.limit()
&& CsrInfo::try_from(existing.csr())? == CsrInfo::try_from(request.csr())?,
)
}
(
ProvisioningRequest::Revocation(existing),
ProvisioningRequest::Revocation(request),
) => {
Ok(existing.class_name() == request.class_name()) }
_ => Ok(false),
}
} else {
Ok(false)
}
}
fn get_child_details(
&self,
child_handle: &ChildHandle,
) -> KrillResult<&TrustAnchorChild> {
self.child_details.get(child_handle).ok_or_else(|| {
Error::CaChildUnknown(self.handle.clone(), child_handle.clone())
})
}
}
pub type TrustAnchorProxyInitCommand =
eventsourcing::SentInitCommand<TrustAnchorProxyInitCommandDetails>;
impl TrustAnchorProxyInitCommand {
pub fn make(
id: MyHandle,
signer: Arc<KrillSigner>,
actor: &Actor,
) -> Self {
TrustAnchorProxyInitCommand::new(
id,
TrustAnchorProxyInitCommandDetails { signer },
actor,
)
}
}
#[derive(Clone, Debug)]
pub struct TrustAnchorProxyInitCommandDetails {
signer: Arc<KrillSigner>,
}
impl fmt::Display for TrustAnchorProxyInitCommandDetails {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.store().fmt(f)
}
}
impl InitCommandDetails for TrustAnchorProxyInitCommandDetails {
type StorableDetails = TrustAnchorProxyCommandDetails;
fn store(&self) -> Self::StorableDetails {
TrustAnchorProxyCommandDetails::make_init()
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct TrustAnchorProxyInitEvent {
pub id: IdCertInfo,
}
impl InitEvent for TrustAnchorProxyInitEvent {}
impl fmt::Display for TrustAnchorProxyInitEvent {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Trust Anchor Proxy was initialised.")
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[allow(clippy::large_enum_variant)]
pub enum TrustAnchorProxyEvent {
RepositoryAdded(RepositoryContact),
SignerAdded(TrustAnchorSignerInfo),
SignerUpdated(TrustAnchorSignerInfo),
SignerRequestMade(Nonce),
SignerResponseReceived(TrustAnchorSignedResponse),
ChildAdded(TrustAnchorChild),
ChildRequestAdded(ChildHandle, ProvisioningRequest),
ChildResponseGiven(ChildHandle, KeyIdentifier),
}
impl Event for TrustAnchorProxyEvent {}
impl fmt::Display for TrustAnchorProxyEvent {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TrustAnchorProxyEvent::RepositoryAdded(repository) => {
write!(
f,
"Added repository with service uri: {}",
repository.server_info.service_uri
)
}
TrustAnchorProxyEvent::SignerAdded(signer) => {
write!(
f,
"Added signer with ID certificate hash: {}",
signer.id.hash
)
}
TrustAnchorProxyEvent::SignerUpdated(signer) => {
write!(
f,
"Updated signer with ID certificate hash: {}",
signer.id.hash
)
}
TrustAnchorProxyEvent::SignerRequestMade(nonce) => {
write!(f, "Created signer request with nonce '{nonce}'")
}
TrustAnchorProxyEvent::SignerResponseReceived(response) => {
write!(
f,
"Received signer response with nonce '{}'",
response.content().nonce
)
}
TrustAnchorProxyEvent::ChildAdded(child) => {
write!(
f,
"Added child: {}, with resources: {}",
child.handle, child.resources
)
}
TrustAnchorProxyEvent::ChildRequestAdded(
child_handle,
request,
) => {
write!(
f,
"Added request for child {child_handle}: {request}"
)
}
TrustAnchorProxyEvent::ChildResponseGiven(child_handle, key) => {
write!(
f,
"Given response to child {child_handle} for key: {key}"
)
}
}
}
}
pub type TrustAnchorProxyCommand =
eventsourcing::SentCommand<TrustAnchorProxyCommandDetails>;
impl TrustAnchorProxyCommand {
pub fn add_repo(
id: &CaHandle,
repository: RepositoryContact,
actor: &Actor,
) -> Self {
TrustAnchorProxyCommand::new(
id.clone(),
None,
TrustAnchorProxyCommandDetails::AddRepository(repository),
actor,
)
}
pub fn add_signer(
id: &CaHandle,
signer: TrustAnchorSignerInfo,
actor: &Actor,
) -> Self {
TrustAnchorProxyCommand::new(
id.clone(),
None,
TrustAnchorProxyCommandDetails::AddSigner(signer),
actor,
)
}
pub fn update_signer(
id: &CaHandle,
signer: TrustAnchorSignerInfo,
actor: &Actor,
) -> Self {
TrustAnchorProxyCommand::new(
id.clone(),
None,
TrustAnchorProxyCommandDetails::UpdateSigner(signer),
actor,
)
}
pub fn make_signer_request(
id: &CaHandle,
actor: &Actor,
) -> Self {
TrustAnchorProxyCommand::new(
id.clone(),
None,
TrustAnchorProxyCommandDetails::MakeSignerRequest,
actor,
)
}
pub fn process_signer_response(
id: &CaHandle,
response: TrustAnchorSignedResponse,
actor: &Actor,
) -> Self {
TrustAnchorProxyCommand::new(
id.clone(),
None,
TrustAnchorProxyCommandDetails::ProcessSignerResponse(response),
actor,
)
}
pub fn add_child(
id: &CaHandle,
child: AddChildRequest,
actor: &Actor,
) -> Self {
TrustAnchorProxyCommand::new(
id.clone(),
None,
TrustAnchorProxyCommandDetails::AddChild(child),
actor,
)
}
pub fn add_child_request(
id: &CaHandle,
child: ChildHandle,
request: ProvisioningRequest,
actor: &Actor,
) -> Self {
TrustAnchorProxyCommand::new(
id.clone(),
None,
TrustAnchorProxyCommandDetails::AddChildRequest(child, request),
actor,
)
}
pub fn give_child_response(
id: &CaHandle,
child: ChildHandle,
key: KeyIdentifier,
actor: &Actor,
) -> Self {
TrustAnchorProxyCommand::new(
id.clone(),
None,
TrustAnchorProxyCommandDetails::GiveChildResponse(child, key),
actor,
)
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[allow(clippy::large_enum_variant)]
pub enum TrustAnchorProxyCommandDetails {
Init,
AddRepository(RepositoryContact),
AddSigner(TrustAnchorSignerInfo),
UpdateSigner(TrustAnchorSignerInfo),
MakeSignerRequest,
ProcessSignerResponse(TrustAnchorSignedResponse),
AddChild(AddChildRequest),
AddChildRequest(ChildHandle, ProvisioningRequest),
GiveChildResponse(ChildHandle, KeyIdentifier),
}
impl fmt::Display for TrustAnchorProxyCommandDetails {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TrustAnchorProxyCommandDetails::Init => {
write!(f, "Initialise TA proxy")
}
TrustAnchorProxyCommandDetails::AddRepository(repository) => {
write!(
f,
"Add repository at: {}",
repository.server_info.service_uri
)
}
TrustAnchorProxyCommandDetails::AddSigner(signer) => {
write!(
f,
"Add signer with id certificate hash: {}",
signer.id.hash
)
}
TrustAnchorProxyCommandDetails::UpdateSigner(signer) => {
write!(
f,
"Update signer with id certificate hash: {}",
signer.id.hash
)
}
TrustAnchorProxyCommandDetails::MakeSignerRequest => {
write!(f, "Create new publish request for signer")
}
TrustAnchorProxyCommandDetails::ProcessSignerResponse(
response,
) => {
write!(
f,
"Process signer response. Nonce: {}. Next Update (before): {}",
response.content().nonce,
response.content().objects.revision().next_update().to_rfc3339()
)
}
TrustAnchorProxyCommandDetails::AddChild(child) => {
write!(f, "Add child: {child}")
}
TrustAnchorProxyCommandDetails::AddChildRequest(
child_handle,
request,
) => {
write!(
f,
"Add request for child {child_handle}: {request}"
)
}
TrustAnchorProxyCommandDetails::GiveChildResponse(
child_handle,
key,
) => {
write!(
f,
"Give (and remove) response to child {child_handle} for key {key}"
)
}
}
}
}
impl eventsourcing::WithStorableDetails for TrustAnchorProxyCommandDetails {
fn summary(&self) -> crate::api::history::CommandSummary {
match self {
TrustAnchorProxyCommandDetails::Init => {
crate::api::history::CommandSummary::new(
"cmd-ta-proxy-init",
self,
)
}
TrustAnchorProxyCommandDetails::AddRepository(repository) => {
crate::api::history::CommandSummary::new(
"cmd-ta-proxy-repo-add",
self,
)
.service_uri(&repository.server_info.service_uri)
}
TrustAnchorProxyCommandDetails::AddSigner(signer) => {
crate::api::history::CommandSummary::new(
"cmd-ta-proxy-signer-add",
self,
)
.id_cert_hash(&signer.id.hash)
}
TrustAnchorProxyCommandDetails::UpdateSigner(signer) => {
crate::api::history::CommandSummary::new(
"cmd-ta-proxy-signer-update",
self,
)
.id_cert_hash(&signer.id.hash)
}
TrustAnchorProxyCommandDetails::MakeSignerRequest => {
crate::api::history::CommandSummary::new(
"cmd-ta-proxy-pub-req",
self,
)
}
TrustAnchorProxyCommandDetails::ProcessSignerResponse(
response,
) => crate::api::history::CommandSummary::new(
"cmd-ta-proxy-pub-res",
self,
)
.arg("nonce", &response.content().nonce)
.arg(
"manifest number",
response.content().objects.revision().number(),
)
.arg(
"this update",
response
.content()
.objects
.revision()
.this_update()
.to_rfc3339(),
)
.arg(
"next update",
response
.content()
.objects
.revision()
.next_update()
.to_rfc3339(),
),
TrustAnchorProxyCommandDetails::AddChild(child) => {
crate::api::history::CommandSummary::new(
"cmd-ta-proxy-child-add", self,
).child(&child.handle)
}
TrustAnchorProxyCommandDetails::AddChildRequest(
child_handle,
_request,
) => {
crate::api::history::CommandSummary::new(
"cmd-ta-proxy-child-req",
self,
).child(child_handle)
}
TrustAnchorProxyCommandDetails::GiveChildResponse(
child_handle,
_response,
) => {
crate::api::history::CommandSummary::new(
"cmd-ta-proxy-child-res",
self,
).child(child_handle)
}
}
}
fn make_init() -> Self {
Self::Init
}
}
impl eventsourcing::CommandDetails for TrustAnchorProxyCommandDetails {
type Event = TrustAnchorProxyEvent;
type StorableDetails = Self;
fn store(&self) -> Self::StorableDetails {
self.clone()
}
}
#[cfg(test)]
mod tests {
use rpki::ca::idexchange::{RepoInfo, ServiceUri};
use super::*;
use std::{sync::Arc, time::Duration};
use crate::{
api::admin::{PublicationServerInfo, RepositoryContact},
commons::{
crypto::KrillSignerBuilder,
eventsourcing::AggregateStore,
storage::Ident,
test,
},
config::ConfigDefaults,
};
use crate::tasigner::{
TrustAnchorSigner, TrustAnchorSignerInitCommand,
TrustAnchorSignerInitCommandDetails, TrustAnchorSignerCommand,
};
#[test]
fn init_ta() {
test::test_in_memory(|storage_uri| {
let _ = stderrlog::new().verbosity(
log::LevelFilter::Trace
).init();
let ta_signer_store: AggregateStore<TrustAnchorSigner> =
AggregateStore::create(
storage_uri,
const { Ident::make("ta_signer") },
false,
)
.unwrap();
let ta_proxy_store: AggregateStore<TrustAnchorProxy> =
AggregateStore::create(
storage_uri,
const { Ident::make("ta_proxy") },
false,
)
.unwrap();
let signers = ConfigDefaults::openssl_signer_only();
let signer = Arc::new(
KrillSignerBuilder::new(
storage_uri,
Duration::from_secs(1),
&signers,
)
.build()
.unwrap(),
);
let timing = TaTimingConfig::default();
let actor = crate::constants::ACTOR_DEF_KRILL;
let proxy_handle = CaHandle::new("proxy".into());
let proxy_init = TrustAnchorProxyInitCommand::make(
proxy_handle.clone(),
signer.clone(),
&actor,
);
ta_proxy_store.add(proxy_init).unwrap();
let repository = {
let repo_info = RepoInfo::new(
test::rsync("rsync://example.krill.cloud/repo/"),
Some(test::https(
"https://exmple.krill.cloud/repo/notification.xml",
)),
);
let repo_key_id = signer.create_key().unwrap();
let public_key = signer.get_key_info(&repo_key_id).unwrap();
let service_uri = ServiceUri::Https(test::https(
"https://example.krill.cloud/rfc8181/ta",
));
let server_info = PublicationServerInfo {
public_key, service_uri
};
RepositoryContact { repo_info, server_info }
};
let add_repo_cmd = TrustAnchorProxyCommand::add_repo(
&proxy_handle,
repository,
&actor,
);
let mut proxy = ta_proxy_store.command(add_repo_cmd).unwrap();
let signer_handle = CaHandle::new("signer".into());
let tal_https =
vec![test::https("https://example.krill.cloud/ta/ta.cer")];
let tal_rsync =
test::rsync("rsync://example.krill.cloud/ta/ta.cer");
let import_key_pem =
include_str!("../../test-resources/ta/example-pkcs1.pem");
let signer_init_cmd = TrustAnchorSignerInitCommand::new(
signer_handle.clone(),
TrustAnchorSignerInitCommandDetails {
proxy_id: proxy.id().clone(),
repo_info: proxy
.repository()
.unwrap()
.repo_info
.clone(),
tal_https: tal_https.clone(),
tal_rsync: tal_rsync.clone(),
private_key_pem: Some(import_key_pem.to_string()),
ta_mft_nr_override: Some(42),
timing,
signer: signer.clone(),
},
&actor,
);
let mut ta_signer = ta_signer_store.add(signer_init_cmd).unwrap();
let signer_info = ta_signer.get_signer_info();
let add_signer_cmd = TrustAnchorProxyCommand::add_signer(
&proxy_handle,
signer_info,
&actor,
);
proxy = ta_proxy_store.command(add_signer_cmd).unwrap();
let ta_objects = proxy.get_trust_anchor_objects().unwrap();
assert_eq!(ta_objects.revision().number(), 42);
let ta_cert_details = proxy.get_ta_details().unwrap();
assert_eq!(ta_cert_details.tal.uris(), &tal_https);
assert_eq!(ta_cert_details.tal.rsync_uri(), &tal_rsync);
let make_publish_request_cmd =
TrustAnchorProxyCommand::make_signer_request(
&proxy_handle,
&actor,
);
proxy = ta_proxy_store.command(make_publish_request_cmd).unwrap();
let signed_request =
proxy.get_signer_request(timing, &signer).unwrap();
let request_nonce = signed_request.request.nonce.clone();
let ta_signer_process_request_command =
TrustAnchorSignerCommand::make_process_request_command(
&signer_handle,
signed_request.into(),
timing,
Some(55), signer,
&actor,
);
ta_signer = ta_signer_store
.command(ta_signer_process_request_command)
.unwrap();
let exchange = ta_signer.get_exchange(&request_nonce).unwrap();
let ta_proxy_process_signer_response_command =
TrustAnchorProxyCommand::process_signer_response(
&proxy_handle,
exchange.response.clone(),
&actor,
);
proxy = ta_proxy_store
.command(ta_proxy_process_signer_response_command)
.unwrap();
let ta_objects = proxy.get_trust_anchor_objects().unwrap();
assert_eq!(ta_objects.revision().number(), 55);
})
}
}