use std::{
collections::HashMap,
fmt::{self, Debug},
sync::Arc,
};
use bytes::Bytes;
use chrono::TimeDelta;
use rpki::{
ca::{
idexchange::{ChildHandle, RecipientHandle, SenderHandle},
provisioning,
publication::Base64,
sigmsg::SignedMessage,
},
crypto::{KeyIdentifier, PublicKey},
repository::{resources::ResourceSet, x509::Time},
uri,
};
use serde::{Deserialize, Serialize};
use crate::{
commons::{
crypto::KrillSigner,
error::Error,
KrillResult,
},
};
use crate::api::admin::PublishedFile;
use crate::api::ca::{
IdCertInfo, IssuedCertificate, ObjectName, ReceivedCert, Revocations,
};
use crate::server::ca::UsedKeyState;
use crate::server::ca::publishing::{
ManifestBuilder, ObjectSetRevision, PublishedCrl,
PublishedManifest, PublishedObject,
};
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct TrustAnchorObjects {
revision: ObjectSetRevision,
key_identifier: KeyIdentifier,
base_uri: uri::Rsync,
revocations: Revocations,
crl: PublishedCrl,
manifest: PublishedManifest,
issued: HashMap<KeyIdentifier, IssuedCertificate>,
}
impl TrustAnchorObjects {
pub fn create(
signing_cert: &ReceivedCert,
initial_number: u64,
next_update_weeks: i64,
signer: &KrillSigner,
) -> KrillResult<Self> {
let revision = ObjectSetRevision::new(
initial_number,
Self::this_update(),
Self::next_update(next_update_weeks),
);
let key_identifier = signing_cert.key_identifier();
let base_uri = signing_cert.ca_repository().clone();
let revocations = Revocations::default();
let signing_key = signing_cert.key_identifier();
let issuer = signing_cert.subject.clone();
let crl = PublishedCrl::build(
signing_key,
issuer,
&revocations,
revision,
signer,
)?;
let manifest = ManifestBuilder::new(revision)
.with_objects(&crl, &HashMap::new())
.build_new_mft(signing_cert, signer)
.map(|m| m.into())?;
Ok(TrustAnchorObjects {
revision,
key_identifier,
base_uri,
revocations,
crl,
manifest,
issued: HashMap::new(),
})
}
pub fn republish(
&mut self,
signing_cert: &ReceivedCert,
next_update_weeks: i64,
mft_number_override: Option<u64>,
signer: &KrillSigner,
) -> KrillResult<()> {
self.revision
.next(Self::next_update(next_update_weeks), mft_number_override);
let signing_key = signing_cert.key_identifier();
if signing_key != self.key_identifier {
Err(Error::custom("TA key changed when republishing"))
} else {
let issuer = signing_cert.subject.clone();
self.crl = PublishedCrl::build(
signing_key,
issuer,
&self.revocations,
self.revision,
signer,
)?;
self.manifest = ManifestBuilder::new(self.revision)
.with_objects(&self.crl, &self.issued_certs_objects())
.build_new_mft(signing_cert, signer)
.map(|m| m.into())?;
Ok(())
}
}
pub fn publish_elements(&self) -> KrillResult<Vec<PublishedFile>> {
let mut res = vec![];
let mft_uri = self
.base_uri
.join(ObjectName::mft_from_ca_key(&self.key_identifier).as_ref())
.map_err(|e| Error::Custom(format!("Cannot make uri: {e}")))?;
res.push(self.manifest.published_file(mft_uri));
let crl_uri = self
.base_uri
.join(ObjectName::crl_from_ca_key(&self.key_identifier).as_ref())
.map_err(|e| Error::Custom(format!("Cannot make uri: {e}")))?;
res.push(self.crl.published_file(crl_uri));
for (name, object) in self.issued_certs_objects() {
let cert_uri =
self.base_uri.join(name.as_ref()).map_err(|e| {
Error::Custom(format!("Cannot make uri: {e}"))
})?;
res.push(object.published_file(cert_uri));
}
Ok(res)
}
fn issued_certs_objects(&self) -> HashMap<ObjectName, PublishedObject> {
self.issued
.iter()
.map(|(ki, cert)| {
let object = PublishedObject::for_cert_info(cert);
let name = ObjectName::cer_from_key(ki);
(name, object)
})
.collect()
}
pub fn manifest(&self) -> &PublishedManifest {
&self.manifest
}
pub fn revision(&self) -> &ObjectSetRevision {
&self.revision
}
pub fn this_update() -> Time {
Time::five_minutes_ago()
}
pub fn next_update(weeks: i64) -> Time {
Time::now() + chrono::Duration::weeks(weeks)
}
pub fn add_issued(&mut self, issued: IssuedCertificate) {
if let Some(previous) =
self.issued.insert(issued.key_identifier(), issued)
{
self.revocations.add(previous.revocation());
self.revocations.remove_expired();
}
}
pub fn get_issued(
&self,
ki: &KeyIdentifier,
) -> Option<&IssuedCertificate> {
self.issued.get(ki)
}
pub fn revoke_issued(&mut self, key: &KeyIdentifier) -> bool {
if let Some(issued) = self.issued.remove(key) {
self.revocations.add(issued.revocation());
self.revocations.remove_expired();
true
} else {
false
}
}
}
impl fmt::Display for TrustAnchorObjects {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(
f,
"-------------------------------------------------------"
)?;
writeln!(f, " Trust Anchor Objects")?;
writeln!(
f,
"-------------------------------------------------------"
)?;
writeln!(f)?;
writeln!(f, "Revision: {}", self.revision.number())?;
writeln!(
f,
"Next Update: {}",
self.revision.next_update().to_rfc3339()
)?;
writeln!(f)?;
writeln!(f, "Objects:",)?;
for publish in self.publish_elements().map_err(|_| fmt::Error)? {
writeln!(f, "{}", publish.uri)?;
writeln!(f, " ({})", publish.base64.to_hash())?;
}
writeln!(f)?;
writeln!(f, "-------------------------------------------------------")
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct TaCertDetails {
pub cert: ReceivedCert,
pub tal: TrustAnchorLocator,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct TrustAnchorLocator {
uris: Vec<uri::Https>,
rsync_uri: uri::Rsync,
encoded_ski: Base64,
}
impl TrustAnchorLocator {
pub fn new(
uris: Vec<uri::Https>,
rsync_uri: uri::Rsync,
public_key: &PublicKey,
) -> Self {
let encoded_ski = Base64::from_content(&public_key.to_info_bytes());
TrustAnchorLocator {
uris,
rsync_uri,
encoded_ski,
}
}
pub fn uris(&self) -> &Vec<uri::Https> {
&self.uris
}
pub fn rsync_uri(&self) -> &uri::Rsync {
&self.rsync_uri
}
}
impl std::fmt::Display for TrustAnchorLocator {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let base64_string = self.encoded_ski.to_string();
for uri in self.uris.iter() {
writeln!(f, "{uri}")?;
}
writeln!(f, "{}", self.rsync_uri)?;
writeln!(f)?;
let len = base64_string.len();
let wrap = 64;
for i in 0..=(len / wrap) {
if (i * wrap + wrap) < len {
writeln!(f, "{}", &base64_string[i * wrap..i * wrap + wrap])?;
} else {
write!(f, "{}", &base64_string[i * wrap..])?;
}
}
Ok(())
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct TrustAnchorSignerInfo {
pub id: IdCertInfo,
pub objects: TrustAnchorObjects,
pub ta_cert_details: TaCertDetails,
}
impl fmt::Display for TrustAnchorSignerInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(
f,
"-------------------------------------------------------"
)?;
writeln!(f, " ID Certificate")?;
writeln!(
f,
"-------------------------------------------------------"
)?;
writeln!(f)?;
writeln!(f, "{}", self.id)?;
writeln!(f)?;
writeln!(
f,
"-------------------------------------------------------"
)?;
writeln!(f)?;
writeln!(f, "{}", self.objects)?;
writeln!(f)?;
writeln!(
f,
"-------------------------------------------------------"
)?;
writeln!(f, " TAL")?;
writeln!(
f,
"-------------------------------------------------------"
)?;
writeln!(f)?;
writeln!(f, "{}", self.ta_cert_details.tal)?;
writeln!(f)?;
writeln!(
f,
"-------------------------------------------------------"
)?;
Ok(())
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct Nonce(Arc<str>);
impl Nonce {
pub fn new() -> Self {
Nonce(uuid::Uuid::new_v4().to_string().into())
}
}
impl Default for Nonce {
fn default() -> Self {
Nonce::new()
}
}
impl std::fmt::Display for Nonce {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct TrustAnchorProxySignerExchange {
pub time: Time,
pub request: TrustAnchorSignedRequest,
pub response: TrustAnchorSignedResponse,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct TrustAnchorSignedMessage {
message: Base64,
}
impl TrustAnchorSignedMessage {
pub fn validate(
&self,
issuer_key: &PublicKey,
) -> KrillResult<SignedMessage> {
self.validate_at(issuer_key, Time::now())
}
pub fn validate_at(
&self,
issuer_key: &PublicKey,
time: Time,
) -> KrillResult<SignedMessage> {
let bytes = self.message.to_bytes();
let signed_message =
SignedMessage::decode(bytes, true).map_err(|e| {
Error::Custom(format!("Cannot decode signed message: {e}"))
})?;
signed_message.validate_at(issuer_key, time).map_err(|e| {
Error::Custom(format!("Invalid signed message: {e}"))
})?;
Ok(signed_message)
}
}
impl From<SignedMessage> for TrustAnchorSignedMessage {
fn from(signed_msg: SignedMessage) -> Self {
let message =
Base64::from_content(&signed_msg.to_captured().into_bytes());
TrustAnchorSignedMessage { message }
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ApiTrustAnchorSignedRequest {
pub request: TrustAnchorSignerRequest,
pub signed: TrustAnchorSignedMessage,
pub issued_certificate_reissue_weeks_before: i64,
pub renew_times: Vec<(KeyIdentifier, Time)>,
pub ta_renew_time: Option<Time>,
}
impl fmt::Display for ApiTrustAnchorSignedRequest {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "-------------------------------")?;
writeln!(f, "nonce: {}", self.request.nonce)?;
writeln!(f, "-------------------------------")?;
writeln!(f)?;
match &self.request.child_requests.len() {
0 => writeln!(f, "There are no child requests")?,
1 => writeln!(f, "There is one child request")?,
n => writeln!(f, "There are {n} child requests")?,
};
writeln!(f)?;
for request in &self.request.child_requests {
writeln!(f, "-------------------------------")?;
writeln!(f, " child request")?;
writeln!(f, "-------------------------------")?;
writeln!(f, "child: {}", request.child)?;
writeln!(f, "entitlements: {}", request.resources)?;
for (key, child_req) in &request.requests {
match child_req {
ProvisioningRequest::Issuance(_) => {
writeln!(f, "key: {key} (re-)issue")?
}
ProvisioningRequest::Revocation(_) => {
writeln!(f, "key: {key} revoke")?
}
}
}
writeln!(f)?;
}
if self.request.child_requests.is_empty() {
writeln!(
f, "Certificates will be reissued {} weeks before expiry.",
self.issued_certificate_reissue_weeks_before
)?;
for &(key, time) in &self.renew_times {
writeln!(f, "The certificate for key {}\n expires on {},",
key,
time.to_rfc3339()
)?;
if let Some(weeks) = TimeDelta::try_weeks(
self.issued_certificate_reissue_weeks_before
) {
let t = time - weeks;
writeln!(
f, " eligible for renewal on {}.",
t.to_rfc3339()
)?;
}
}
if let Some(ta_renew_time) = self.ta_renew_time {
writeln!(f, "The TA certificate expires on {}.",
ta_renew_time.to_rfc3339(),
)?;
}
}
writeln!(f)?;
writeln!(f, "NOTE: Use the JSON output for the signer.")?;
Ok(())
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct TrustAnchorSignedRequest {
pub signed: TrustAnchorSignedMessage,
pub request: TrustAnchorSignerRequest,
}
impl From<ApiTrustAnchorSignedRequest> for TrustAnchorSignedRequest {
fn from(src: ApiTrustAnchorSignedRequest) -> Self {
Self {
signed: src.signed,
request: src.request,
}
}
}
impl TrustAnchorSignedRequest {
pub fn validate(&self, issuer: &IdCertInfo) -> Result<(), Error> {
let signed_message = self.signed.validate(&issuer.public_key)?;
let signed_bytes = signed_message.content().to_bytes();
let signed_request: TrustAnchorSignerRequest = serde_json::from_slice(&signed_bytes).map_err(|e| {
Error::Custom(format!(
"Cannot deserialize content of signed Trust Anchor request: {e}"
))
})?;
if self.request != signed_request {
Err(Error::custom(
"Clear text request content does not match the contained signed message in Trust Anchor request",
))
} else {
Ok(())
}
}
pub fn content(&self) -> &TrustAnchorSignerRequest {
&self.request
}
}
impl fmt::Display for TrustAnchorSignedRequest {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.request)
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct TrustAnchorSignerRequest {
pub nonce: Nonce, pub child_requests: Vec<TrustAnchorChildRequests>,
}
impl TrustAnchorSignerRequest {
pub fn sign(
&self,
signing_key: KeyIdentifier,
validity_days: i64,
signer: &KrillSigner,
) -> Result<TrustAnchorSignedRequest, Error> {
let data = serde_json::to_string_pretty(&self).unwrap();
let data = Bytes::from(data);
signer
.create_ta_signed_message(data, validity_days, &signing_key)
.map(|msg| TrustAnchorSignedRequest {
request: self.clone(),
signed: msg.into(),
})
.map_err(Error::signer)
}
}
impl fmt::Display for TrustAnchorSignerRequest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "-------------------------------")?;
writeln!(f, "nonce: {}", self.nonce)?;
writeln!(f, "-------------------------------")?;
writeln!(f)?;
for request in &self.child_requests {
writeln!(f, "-------------------------------")?;
writeln!(f, " child request")?;
writeln!(f, "-------------------------------")?;
writeln!(f, "child: {}", request.child)?;
writeln!(f, "entitlements: {}", request.resources)?;
for (key, child_req) in &request.requests {
match child_req {
ProvisioningRequest::Issuance(_) => {
writeln!(f, "key: {key} (re-)issue")?
}
ProvisioningRequest::Revocation(_) => {
writeln!(f, "key: {key} revoke")?
}
}
}
writeln!(f)?;
}
writeln!(f, "NOTE: Use the JSON output for the signer.")?;
Ok(())
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct TrustAnchorChildRequests {
pub child: ChildHandle,
pub resources: ResourceSet,
pub requests: HashMap<KeyIdentifier, ProvisioningRequest>,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct TrustAnchorSignedResponse {
signed: TrustAnchorSignedMessage,
response: TrustAnchorSignerResponse,
}
impl TrustAnchorSignedResponse {
pub fn validate(&self, issuer: &IdCertInfo) -> Result<(), Error> {
let signed_message = self.signed.validate(&issuer.public_key)?;
let signed_bytes = signed_message.content().to_bytes();
let signed_response: TrustAnchorSignerResponse = serde_json::from_slice(&signed_bytes).map_err(|e| {
Error::Custom(format!(
"Cannot deserialize content of signed Trust Anchor response: {e}"
))
})?;
if self.response != signed_response {
Err(Error::custom(
"Clear text request content does not match the contained signed message in Trust Anchor response",
))
} else {
Ok(())
}
}
pub fn content(&self) -> &TrustAnchorSignerResponse {
&self.response
}
pub fn into_content(self) -> TrustAnchorSignerResponse {
self.response
}
}
impl fmt::Display for TrustAnchorSignedResponse {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.response)
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct TrustAnchorSignerResponse {
pub nonce: Nonce, pub objects: TrustAnchorObjects,
pub child_responses:
HashMap<ChildHandle, HashMap<KeyIdentifier, ProvisioningResponse>>,
}
impl TrustAnchorSignerResponse {
pub fn sign(
&self,
validity_days: i64,
signing_key: KeyIdentifier,
signer: &KrillSigner,
) -> Result<TrustAnchorSignedResponse, Error> {
let data = serde_json::to_string_pretty(&self).unwrap();
let data = Bytes::from(data);
signer
.create_ta_signed_message(data, validity_days, &signing_key)
.map(|msg| TrustAnchorSignedResponse {
response: self.clone(),
signed: msg.into(),
})
.map_err(Error::signer)
}
}
impl fmt::Display for TrustAnchorSignerResponse {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "-------------------------------")?;
writeln!(f, "nonce: {}", self.nonce)?;
writeln!(f, "-------------------------------")?;
writeln!(f)?;
writeln!(f, "{}", self.objects)?;
writeln!(f)?;
for (child, responses) in &self.child_responses {
writeln!(f, "-------------------------------")?;
writeln!(f, " child response")?;
writeln!(f, "-------------------------------")?;
writeln!(f, "child: {child}")?;
for (key, response) in responses.iter() {
match response {
ProvisioningResponse::Error => {
writeln!(f, "key: {key} ERROR")?
}
ProvisioningResponse::Issuance(_) => {
writeln!(f, "key: {key} issued")?
}
ProvisioningResponse::Revocation(_) => {
writeln!(f, "key: {key} revoked")?
}
}
}
}
writeln!(f)?;
writeln!(f, "NOTE: use the JSON format for the proxy.")?;
Ok(())
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct TrustAnchorChild {
pub handle: ChildHandle,
pub id: IdCertInfo,
pub resources: ResourceSet,
pub used_keys: HashMap<KeyIdentifier, UsedKeyState>,
pub open_requests: HashMap<KeyIdentifier, ProvisioningRequest>,
pub open_responses: HashMap<KeyIdentifier, ProvisioningResponse>,
}
impl TrustAnchorChild {
pub fn new(
handle: ChildHandle,
id: IdCertInfo,
resources: ResourceSet,
) -> Self {
TrustAnchorChild {
handle,
id,
resources,
used_keys: HashMap::new(),
open_requests: HashMap::new(),
open_responses: HashMap::new(),
}
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[allow(clippy::large_enum_variant)]
pub enum ProvisioningRequest {
Issuance(provisioning::IssuanceRequest),
Revocation(provisioning::RevocationRequest),
}
impl ProvisioningRequest {
pub fn key_identifier(&self) -> KeyIdentifier {
match self {
ProvisioningRequest::Issuance(req) => {
req.csr().public_key().key_identifier()
}
ProvisioningRequest::Revocation(req) => req.key(),
}
}
pub fn matches_response(&self, response: &ProvisioningResponse) -> bool {
match self {
ProvisioningRequest::Issuance(_) => {
!matches!(response, ProvisioningResponse::Revocation(_))
}
ProvisioningRequest::Revocation(_) => {
!matches!(response, ProvisioningResponse::Issuance(_))
}
}
}
}
impl std::fmt::Display for ProvisioningRequest {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
ProvisioningRequest::Issuance(_) => write!(
f,
"issue certificate for key: {}",
self.key_identifier()
),
ProvisioningRequest::Revocation(_) => write!(
f,
"revoke certificates for key: {}",
self.key_identifier()
),
}
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[allow(clippy::large_enum_variant)]
pub enum ProvisioningResponse {
Issuance(provisioning::IssuanceResponse),
Revocation(provisioning::RevocationResponse),
Error,
}
impl ProvisioningResponse {
pub fn to_provisioning_message(
self,
sender: SenderHandle,
recipient: RecipientHandle,
) -> provisioning::Message {
match self {
ProvisioningResponse::Issuance(issuance_response) => {
provisioning::Message::issue_response(
sender,
recipient,
issuance_response,
)
}
ProvisioningResponse::Revocation(revocation_response) => {
provisioning::Message::revoke_response(
sender,
recipient,
revocation_response,
)
}
ProvisioningResponse::Error => {
provisioning::Message::not_performed_response(
sender,
recipient,
provisioning::NotPerformedResponse::err_2001(),
)
.unwrap() }
}
}
}