mirror of
https://github.com/NLnetLabs/krill.git
synced 2026-09-21 00:47:41 +02:00
Support RFC6492 Issuance Report XML, and use it internally as well. (Issue #13)
This commit is contained in:
+112
-45
@@ -13,7 +13,7 @@ use rpki::csr::Csr;
|
||||
use rpki::uri;
|
||||
use rpki::x509::{Serial, Validity, Time, Name};
|
||||
|
||||
use krill_commons::api::{self, DFLT_CLASS, EncodedHash, EntitlementClass, Entitlements, IssuanceRequest, SigningCert, RequestResourceLimit};
|
||||
use krill_commons::api::{self, DFLT_CLASS, EncodedHash, EntitlementClass, Entitlements, IssuanceRequest, SigningCert, RequestResourceLimit, IssuanceResponse};
|
||||
use krill_commons::api::admin::{
|
||||
Handle,
|
||||
ParentCaContact,
|
||||
@@ -134,39 +134,50 @@ impl CaIniDet {
|
||||
pub type CaEvt = StoredEvent<CaEvtDet>;
|
||||
|
||||
|
||||
//------------ CertIssued ---------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct CertIssued {
|
||||
child: Handle,
|
||||
response: IssuanceResponse
|
||||
}
|
||||
|
||||
impl CertIssued {
|
||||
pub fn unwrap(self) -> (Handle, IssuanceResponse) {
|
||||
(self.child, self.response)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ CertRequested -----------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct CertRequested {
|
||||
parent: ParentCaContact,
|
||||
class_name: String,
|
||||
resource_limit: RequestResourceLimit,
|
||||
csr: Csr
|
||||
request: IssuanceRequest
|
||||
}
|
||||
|
||||
impl CertRequested {
|
||||
pub fn unwrap(self) -> (ParentCaContact, String, RequestResourceLimit, Csr) {
|
||||
(self.parent, self.class_name, self.resource_limit, self.csr)
|
||||
pub fn unwrap(self) -> (ParentCaContact, IssuanceRequest) {
|
||||
(self.parent, self.request)
|
||||
}
|
||||
pub fn parent(&self) -> &ParentCaContact {
|
||||
&self.parent
|
||||
}
|
||||
pub fn class_name(&self) -> &str {
|
||||
&self.class_name
|
||||
self.request.class_name()
|
||||
}
|
||||
pub fn resource_limit(&self) -> &RequestResourceLimit {
|
||||
&self.resource_limit
|
||||
pub fn limit(&self) -> &RequestResourceLimit {
|
||||
self.request.limit()
|
||||
}
|
||||
pub fn csr(&self) -> &Csr {
|
||||
&self.csr
|
||||
self.request.csr()
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<IssuanceRequest> for CertRequested {
|
||||
fn into(self) -> IssuanceRequest {
|
||||
IssuanceRequest::new(
|
||||
self.class_name, self.resource_limit, self.csr
|
||||
)
|
||||
self.request
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +200,7 @@ pub struct CertReceived {
|
||||
pub enum CaEvtDet {
|
||||
// Being a parent Events
|
||||
ChildAdded(ChildCa),
|
||||
CertificateIssued(Handle, String, IssuedCert),
|
||||
CertificateIssued(CertIssued),
|
||||
|
||||
// Being a child Events
|
||||
ParentAdded(ParentHandle, ParentCaContact),
|
||||
@@ -324,18 +335,12 @@ impl CaEvtDet {
|
||||
fn certificate_issued(
|
||||
handle: &Handle,
|
||||
version: u64,
|
||||
child_handle: Handle,
|
||||
class_name: &str,
|
||||
cert: IssuedCert
|
||||
cert_issued: CertIssued
|
||||
) -> CaEvt {
|
||||
StoredEvent::new(
|
||||
handle,
|
||||
version,
|
||||
CaEvtDet::CertificateIssued(
|
||||
child_handle,
|
||||
class_name.to_string(),
|
||||
cert
|
||||
)
|
||||
CaEvtDet::CertificateIssued(cert_issued)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -365,7 +370,7 @@ type ResourceClassName = String;
|
||||
pub enum CaCmdDet<S: CaSigner> {
|
||||
// Being a parent
|
||||
AddChild(Handle, Token, ResourceSet),
|
||||
CertifyChild(Handle, Csr, RequestResourceLimit, Token, Arc<RwLock<S>>),
|
||||
CertifyChild(Handle, IssuanceRequest, Token, Arc<RwLock<S>>),
|
||||
|
||||
// Being a child
|
||||
AddParent(ParentHandle, ParentCaContact),
|
||||
@@ -409,15 +414,14 @@ impl<S: CaSigner> CaCmdDet<S> {
|
||||
pub fn certify_child(
|
||||
handle: &Handle,
|
||||
child_handle: Handle,
|
||||
csr: Csr,
|
||||
limit: RequestResourceLimit,
|
||||
request: IssuanceRequest,
|
||||
token: Token,
|
||||
signer: Arc<RwLock<S>>
|
||||
) -> CaCmd<S> {
|
||||
SentCommand::new(
|
||||
handle,
|
||||
None,
|
||||
CaCmdDet::CertifyChild(child_handle, csr, limit, token, signer)
|
||||
CaCmdDet::CertifyChild(child_handle, request, token, signer)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -642,9 +646,13 @@ impl<S: CaSigner> Aggregate for CertAuth<S> {
|
||||
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);
|
||||
CaEvtDet::CertificateIssued(cert_issued) => {
|
||||
let (child_hndl, response) = cert_issued.unwrap();
|
||||
let (class_name, _, _, issued) = response.unwrap();
|
||||
|
||||
let child = self.children.get_mut(&child_hndl).unwrap();
|
||||
|
||||
child.add_cert(&class_name, issued);
|
||||
},
|
||||
|
||||
// Being a child
|
||||
@@ -660,7 +668,7 @@ impl<S: CaSigner> Aggregate for CertAuth<S> {
|
||||
CaEvtDet::CertificateRequested(req) => {
|
||||
info!(
|
||||
"Certificate requested for class {} from {}",
|
||||
req.class_name,
|
||||
req.class_name(),
|
||||
req.parent
|
||||
);
|
||||
// do nothing, this should be picked up by listener and sent
|
||||
@@ -696,8 +704,8 @@ impl<S: CaSigner> Aggregate for CertAuth<S> {
|
||||
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)
|
||||
CaCmdDet::CertifyChild(child, request, token, signer) => {
|
||||
self.certify_child(child, request, token, signer)
|
||||
}
|
||||
|
||||
// being a child
|
||||
@@ -843,9 +851,47 @@ impl<S: CaSigner> CertAuth<S> {
|
||||
child_handle: &Handle,
|
||||
token: &Token
|
||||
) -> CaRes<api::Entitlements> {
|
||||
// TODO: Support arbitrary resource classes. See issue #25.
|
||||
let dflt_entitlement_class = self.entitlement_class(
|
||||
child_handle,
|
||||
DFLT_CLASS,
|
||||
token
|
||||
)?;
|
||||
|
||||
Ok(Entitlements::new(vec![dflt_entitlement_class]))
|
||||
}
|
||||
|
||||
/// Returns an issuance response for a child and a specific resource
|
||||
/// class name and public key for the issued certificate.
|
||||
pub fn issuance_response(
|
||||
&self,
|
||||
child_handle: &Handle,
|
||||
class_name: &str,
|
||||
pub_key: &PublicKey,
|
||||
token: &Token
|
||||
) -> CaRes<api::IssuanceResponse> {
|
||||
let entitlement_class = self.entitlement_class(
|
||||
child_handle,
|
||||
class_name,
|
||||
token
|
||||
)?;
|
||||
|
||||
entitlement_class
|
||||
.into_issuance_response(pub_key)
|
||||
.ok_or_else(|| Error::NoIssuedCert)
|
||||
}
|
||||
|
||||
|
||||
/// Returns the EntitlementClass for this child for the given class name.
|
||||
fn entitlement_class(
|
||||
&self,
|
||||
child_handle: &Handle,
|
||||
class_name: &str,
|
||||
token: &Token
|
||||
) -> CaRes<api::EntitlementClass> {
|
||||
let child = self.get_authorised_child(child_handle, token)?;
|
||||
|
||||
let child_resources = child.resources_for_class(DFLT_CLASS)
|
||||
let child_resources = child.resources_for_class(class_name)
|
||||
.ok_or_else(|| Error::MissingResources)?;
|
||||
|
||||
let until = child_resources.not_after();
|
||||
@@ -858,8 +904,12 @@ impl<S: CaSigner> CertAuth<S> {
|
||||
let resources = cert.resources().clone();
|
||||
let cert = SigningCert::new(cert.uri().clone(), cert.cert().clone());
|
||||
|
||||
Ok(Entitlements::with_default_class(
|
||||
cert, resources, until, issued
|
||||
Ok(EntitlementClass::new(
|
||||
class_name.to_string(),
|
||||
cert,
|
||||
resources,
|
||||
until,
|
||||
issued
|
||||
))
|
||||
}
|
||||
|
||||
@@ -942,11 +992,12 @@ impl<S: CaSigner> CertAuth<S> {
|
||||
fn certify_child(
|
||||
&self,
|
||||
child: Handle,
|
||||
csr: Csr,
|
||||
limit: RequestResourceLimit,
|
||||
request: IssuanceRequest,
|
||||
token: Token,
|
||||
signer: Arc<RwLock<S>>
|
||||
) -> CaEvtsRes {
|
||||
let (class_name, limit, csr) = request.unwrap();
|
||||
|
||||
let issuing_key = match &self.parents {
|
||||
CaParents::SelfSigned(key, _tal) => key,
|
||||
CaParents::Parents(_) => unimplemented!("Issue #25 (delegate from CA)")
|
||||
@@ -956,7 +1007,7 @@ impl<S: CaSigner> CertAuth<S> {
|
||||
|
||||
// verify child and resources
|
||||
let child_resources = self.get_authorised_child(&child, &token)?
|
||||
.resources_for_class(DFLT_CLASS)
|
||||
.resources_for_class(&class_name)
|
||||
.ok_or_else(|| Error::MissingResources)?;
|
||||
|
||||
let resources = limit.resolve(child_resources.resources())
|
||||
@@ -1034,12 +1085,23 @@ impl<S: CaSigner> CertAuth<S> {
|
||||
let version = self.version;
|
||||
let cert_object = CurrentObject::from(issued_cert.cert());
|
||||
|
||||
let signing_cert = SigningCert::from(issuing_cert);
|
||||
|
||||
let cert_issued = CertIssued {
|
||||
child,
|
||||
response: IssuanceResponse::new(
|
||||
DFLT_CLASS.to_string(),
|
||||
signing_cert,
|
||||
resources,
|
||||
issued_cert.cert().validity().not_after(),
|
||||
issued_cert.clone()
|
||||
)
|
||||
};
|
||||
|
||||
let issued_event = CaEvtDet::certificate_issued(
|
||||
&self.handle,
|
||||
version,
|
||||
child,
|
||||
DFLT_CLASS,
|
||||
issued_cert.clone()
|
||||
cert_issued
|
||||
);
|
||||
|
||||
let delta = {
|
||||
@@ -1145,7 +1207,7 @@ impl<S: CaSigner> CertAuth<S> {
|
||||
let mut version = self.version;
|
||||
for ent in entitlements.classes() {
|
||||
|
||||
let name = ent.name();
|
||||
let name = ent.class_name();
|
||||
|
||||
if let Some(_rc) = parent.resources.get(name) {
|
||||
// Check whether a new certificate
|
||||
@@ -1179,9 +1241,11 @@ impl<S: CaSigner> CertAuth<S> {
|
||||
|
||||
let cert_issue_req = CertRequested {
|
||||
parent: parent.contact.clone(),
|
||||
class_name: ent.name().to_string(),
|
||||
resource_limit: RequestResourceLimit::default(),
|
||||
csr
|
||||
request: IssuanceRequest::new(
|
||||
ent.class_name().to_string(),
|
||||
RequestResourceLimit::default(),
|
||||
csr
|
||||
)
|
||||
};
|
||||
|
||||
let added = CaEvtDet::resource_class_added(
|
||||
@@ -1731,6 +1795,9 @@ pub enum Error {
|
||||
#[display(fmt = "Child CA MUST have resources.")]
|
||||
MusthaveResources,
|
||||
|
||||
#[display(fmt = "No issued cert matching pub key in resource class.")]
|
||||
NoIssuedCert,
|
||||
|
||||
#[display(fmt = "Invalidly CSR for child {}: {}.", _0, _1)]
|
||||
InvalidCsr(Handle, String),
|
||||
|
||||
|
||||
+19
-19
@@ -6,11 +6,7 @@ use std::ops::Deref;
|
||||
use rpki::crypto::PublicKeyFormat;
|
||||
use rpki::uri;
|
||||
|
||||
use krill_commons::api::{
|
||||
DFLT_CLASS,
|
||||
Entitlements,
|
||||
IssuanceRequest,
|
||||
};
|
||||
use krill_commons::api::{DFLT_CLASS, Entitlements, IssuanceRequest, IssuanceResponse};
|
||||
use krill_commons::api::admin::{
|
||||
AddChildRequest,
|
||||
Handle,
|
||||
@@ -213,16 +209,14 @@ impl<S: CaSigner> CaServer<S> {
|
||||
child: &Handle,
|
||||
issue_req: IssuanceRequest,
|
||||
token: Token,
|
||||
) -> CaResult<IssuedCert, S> {
|
||||
) -> CaResult<IssuanceResponse, S> {
|
||||
if parent != & ta_handle() {
|
||||
unimplemented!("https://github.com/NLnetLabs/krill/issues/25");
|
||||
} else {
|
||||
let ta = self.get_trust_anchor()?;
|
||||
|
||||
// class name can be ignored for TA, only uses one.
|
||||
let (class_name, limit, csr) = issue_req.unwrap();
|
||||
|
||||
let pub_key = csr.public_key().clone();
|
||||
let class_name = issue_req.class_name();
|
||||
let pub_key = issue_req.csr().public_key();
|
||||
|
||||
if class_name != DFLT_CLASS {
|
||||
unimplemented!("Issue for multiple classes from CAs, issue #25")
|
||||
@@ -231,21 +225,24 @@ impl<S: CaSigner> CaServer<S> {
|
||||
let cmd = CaCmdDet::certify_child(
|
||||
parent,
|
||||
child.clone(),
|
||||
csr,
|
||||
limit,
|
||||
token,
|
||||
issue_req.clone(),
|
||||
token.clone(),
|
||||
self.signer.clone()
|
||||
);
|
||||
|
||||
let events = ta.process_command(cmd)?;
|
||||
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();
|
||||
let resources = child.resources_for_class(&class_name).unwrap();
|
||||
let issued_cert = resources.cert(&pub_key).unwrap();
|
||||
// New entitlements will include this resource class, and
|
||||
// the newly issued certificate.
|
||||
let response = ta.issuance_response(
|
||||
child,
|
||||
&class_name,
|
||||
&pub_key,
|
||||
&token
|
||||
)?;
|
||||
|
||||
Ok(issued_cert.clone())
|
||||
Ok(response)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,12 +366,15 @@ impl<S: CaSigner> CaServer<S> {
|
||||
ParentCaContact::Embedded(parent_handle, token) => {
|
||||
for cert_req in cert_reqs {
|
||||
let class_name = cert_req.class_name().to_string();
|
||||
let issued = self.issue(
|
||||
let issue_res = self.issue(
|
||||
parent_handle,
|
||||
&handle,
|
||||
cert_req,
|
||||
token.clone()
|
||||
)?;
|
||||
|
||||
let (_,_,_, issued) = issue_res.unwrap();
|
||||
|
||||
issued_certs.push((class_name, issued));
|
||||
}
|
||||
}
|
||||
|
||||
+17
-8
@@ -40,7 +40,7 @@ mod tests {
|
||||
use rpki::crypto::signer::Signer;
|
||||
use rpki::crypto::PublicKeyFormat;
|
||||
|
||||
use krill_commons::api::DFLT_CLASS;
|
||||
use krill_commons::api::{DFLT_CLASS, IssuanceRequest};
|
||||
use krill_commons::api::admin::{
|
||||
Handle,
|
||||
Token,
|
||||
@@ -212,7 +212,8 @@ mod tests {
|
||||
_ => panic!("Expected Csr")
|
||||
};
|
||||
|
||||
let (parent_info, class_name, limit, csr) = req.unwrap();
|
||||
let (parent_info, issuance_req) = req.unwrap();
|
||||
let (class_name, limit, csr) = issuance_req.unwrap();
|
||||
assert_eq!("all", &class_name);
|
||||
assert!(limit.is_empty());
|
||||
if let ParentCaContact::Embedded(handle, token) = parent_info {
|
||||
@@ -230,11 +231,14 @@ mod tests {
|
||||
// - Publication
|
||||
//
|
||||
|
||||
let request = IssuanceRequest::new(
|
||||
DFLT_CLASS.to_string(), limit, csr
|
||||
);
|
||||
|
||||
let ta_cmd = CaCmdDet::certify_child(
|
||||
&ta_handle,
|
||||
child_handle.clone(),
|
||||
csr,
|
||||
limit,
|
||||
request,
|
||||
child_token.clone(),
|
||||
signer.clone()
|
||||
);
|
||||
@@ -243,12 +247,17 @@ mod tests {
|
||||
let issued_evt = ta_events[0].clone().into_details();
|
||||
let _ta = ca_store.update(&ta_handle, ta, ta_events).unwrap();
|
||||
|
||||
let (h, n, issued) = match issued_evt {
|
||||
CaEvtDet::CertificateIssued(h, n, c) => (h, n, c),
|
||||
let issued = match issued_evt {
|
||||
CaEvtDet::CertificateIssued(issued) => issued,
|
||||
_ => panic!("Expected issued certificate.")
|
||||
};
|
||||
assert_eq!(child_handle, h);
|
||||
assert_eq!(DFLT_CLASS, n);
|
||||
|
||||
let (handle, issuance_res) = issued.unwrap();
|
||||
|
||||
let (class_name, _, _, issued) = issuance_res.unwrap();
|
||||
|
||||
assert_eq!(child_handle, handle);
|
||||
assert_eq!(DFLT_CLASS, class_name);
|
||||
|
||||
//
|
||||
// --- Return issued certificate to child CA
|
||||
|
||||
+198
-36
@@ -1,18 +1,20 @@
|
||||
use std::io;
|
||||
use std::str::FromStr;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use chrono::{Utc, DateTime, SecondsFormat};
|
||||
use serde::export::fmt::Display;
|
||||
|
||||
use rpki::cert::Cert;
|
||||
use rpki::crypto::KeyIdentifier;
|
||||
use rpki::csr::Csr;
|
||||
use rpki::resources::{AsResources, Ipv4Resources, Ipv6Resources};
|
||||
use rpki::uri;
|
||||
use rpki::x509::Time;
|
||||
|
||||
use krill_commons::util::xml::{XmlReader, XmlReaderErr, AttributesError, XmlWriter};
|
||||
use krill_commons::api::{Entitlements, EntitlementClass, SigningCert, RequestResourceLimit, IssuanceRequest};
|
||||
use krill_commons::api::{Entitlements, EntitlementClass, SigningCert, RequestResourceLimit, IssuanceRequest, IssuanceResponse, RevocationRequest};
|
||||
use krill_commons::api::ca::{ResourceSet, ResSetErr, IssuedCert};
|
||||
use rpki::cert::Cert;
|
||||
use rpki::resources::{AsResources, Ipv4Resources, Ipv6Resources};
|
||||
use serde::export::fmt::Display;
|
||||
use rpki::csr::Csr;
|
||||
use krill_commons::util::xml::{XmlReader, XmlReaderErr, AttributesError, XmlWriter};
|
||||
|
||||
|
||||
//------------ Consts --------------------------------------------------------
|
||||
@@ -23,6 +25,8 @@ const NS: &str = "http://www.apnic.net/specs/rescerts/up-down/";
|
||||
const TYPE_LIST_QRY: &str = "list";
|
||||
const TYPE_LIST_RES: &str = "list_response";
|
||||
const TYPE_ISSUE_QRY: &str = "issue";
|
||||
const TYPE_ISSUE_RES: &str = "issue_response";
|
||||
const TYPE_REVOKE_QRY: &str = "revoke";
|
||||
|
||||
//------------ Message -------------------------------------------------------
|
||||
|
||||
@@ -47,9 +51,19 @@ impl Message {
|
||||
let content = Content::Res(Res::List(entitlements));
|
||||
Message { sender, recipient, content}
|
||||
}
|
||||
|
||||
pub fn revoke(
|
||||
sender: String,
|
||||
recipient: String,
|
||||
revocation: RevocationRequest
|
||||
) -> Self {
|
||||
let content = Content::Qry(Qry::Revoke(revocation));
|
||||
Message { sender, recipient, content }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum Content {
|
||||
Qry(Qry),
|
||||
Res(Res)
|
||||
@@ -82,10 +96,10 @@ impl Message {
|
||||
a.exhausted()?;
|
||||
|
||||
let content = match msg_type.as_ref() {
|
||||
TYPE_LIST_QRY | TYPE_ISSUE_QRY => {
|
||||
TYPE_LIST_QRY | TYPE_ISSUE_QRY | TYPE_REVOKE_QRY => {
|
||||
Ok(Content::Qry(Qry::decode(&msg_type, r)?))
|
||||
},
|
||||
TYPE_LIST_RES => {
|
||||
TYPE_LIST_RES | TYPE_ISSUE_RES => {
|
||||
Ok(Content::Res(Res::decode(&msg_type, r)?))
|
||||
}
|
||||
_ => Err(Error::UnknownMessageType)
|
||||
@@ -139,9 +153,11 @@ impl Message {
|
||||
|
||||
/// This type defines the various RFC6492 queries.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum Qry {
|
||||
List,
|
||||
Issue(IssuanceRequest)
|
||||
Issue(IssuanceRequest),
|
||||
Revoke(RevocationRequest)
|
||||
}
|
||||
|
||||
|
||||
@@ -149,7 +165,8 @@ impl Qry {
|
||||
fn msg_type(&self) -> &str {
|
||||
match self {
|
||||
Qry::List => TYPE_LIST_QRY,
|
||||
Qry::Issue(_) => TYPE_ISSUE_QRY
|
||||
Qry::Issue(_) => TYPE_ISSUE_QRY,
|
||||
Qry::Revoke(_) => TYPE_REVOKE_QRY
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,14 +176,31 @@ impl Qry {
|
||||
) -> Result<Self, Error> where R: io::Read {
|
||||
match msg_type {
|
||||
TYPE_LIST_QRY => Ok(Qry::List),
|
||||
TYPE_ISSUE_QRY => Self::decode_issue(r),
|
||||
TYPE_ISSUE_QRY => Ok(Qry::Issue(Self::decode_issue(r)?)),
|
||||
TYPE_REVOKE_QRY => Ok(Qry::Revoke(Self::decode_revoke(r)?)),
|
||||
_ => Err(Error::UnknownMessageType)
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_revoke<R>(
|
||||
r: &mut XmlReader<R>
|
||||
) -> Result<RevocationRequest, Error> where R: io::Read {
|
||||
r.take_named_element("key", |mut a, r| {
|
||||
let class_name = a.take_req("class_name")?;
|
||||
a.exhausted()?;
|
||||
|
||||
let ski_bytes = r.take_bytes_url_safe_pad()?;
|
||||
|
||||
let ski = KeyIdentifier::try_from(ski_bytes.as_ref())
|
||||
.map_err(|_| Error::InvalidSki)?;
|
||||
|
||||
Ok(RevocationRequest::new(class_name.to_string(), ski))
|
||||
})
|
||||
}
|
||||
|
||||
fn decode_issue<R>(
|
||||
r: &mut XmlReader<R>
|
||||
) -> Result<Qry, Error> where R: io::Read {
|
||||
) -> Result<IssuanceRequest, Error> where R: io::Read {
|
||||
r.take_named_element("request", |mut a, r|{
|
||||
let class_name = a.take_req("class_name")?;
|
||||
let mut limit = RequestResourceLimit::default();
|
||||
@@ -192,11 +226,11 @@ impl Qry {
|
||||
let csr_bytes = r.take_bytes_characters()?;
|
||||
let csr = Csr::decode(csr_bytes).map_err(|_| Error::InvalidCsr)?;
|
||||
|
||||
Ok(Qry::Issue(IssuanceRequest::new(
|
||||
Ok(IssuanceRequest::new(
|
||||
class_name.to_string(),
|
||||
limit,
|
||||
csr
|
||||
)))
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -206,7 +240,8 @@ impl Qry {
|
||||
) -> Result<(), io::Error> {
|
||||
match self {
|
||||
Qry::List => w.empty(),
|
||||
Qry::Issue(issue_req) => Self::encode_issue(issue_req, w)
|
||||
Qry::Issue(issue_req) => Self::encode_issue(issue_req, w),
|
||||
Qry::Revoke(rev) => Self::encode_revoke(rev, w)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,11 +270,22 @@ impl Qry {
|
||||
let mut attrs_str = vec![];
|
||||
attrs_str.push(("class_name", class_name));
|
||||
for (k, v) in &attrs_strings {
|
||||
attrs_str.push((k.clone(), v.as_str()));
|
||||
attrs_str.push((k, v.as_str()));
|
||||
}
|
||||
|
||||
w.put_element("request", Some(attrs_str.as_slice()), |w| {
|
||||
w.put_blob(&csr)
|
||||
w.put_base64_std(&csr)
|
||||
})
|
||||
}
|
||||
|
||||
fn encode_revoke<W: io::Write>(
|
||||
rev: &RevocationRequest,
|
||||
w: &mut XmlWriter<W>
|
||||
) -> Result<(), io::Error> {
|
||||
let att = [ ("class_name", rev.class_name() )];
|
||||
let bytes = rev.key().as_slice();
|
||||
w.put_element("key", Some(&att), |w| {
|
||||
w.put_base64_url_safe(bytes)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -249,14 +295,17 @@ impl Qry {
|
||||
|
||||
/// This type defines the various RFC6492 queries.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum Res {
|
||||
List(Entitlements)
|
||||
List(Entitlements),
|
||||
Issue(IssuanceResponse)
|
||||
}
|
||||
|
||||
impl Res {
|
||||
fn msg_type(&self) -> &str {
|
||||
match self {
|
||||
Res::List(_) => TYPE_LIST_RES
|
||||
Res::List(_) => TYPE_LIST_RES,
|
||||
Res::Issue(_) => TYPE_ISSUE_RES
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,10 +318,53 @@ impl Res {
|
||||
let entitlements = Self::decode_entitlements(r)?;
|
||||
Ok(Res::List(entitlements))
|
||||
},
|
||||
TYPE_ISSUE_RES => {
|
||||
let issuance_response = Self::decode_issue_response(r)?;
|
||||
Ok(Res::Issue(issuance_response))
|
||||
}
|
||||
_ => Err(Error::UnknownMessageType)
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_issue_response<R>(
|
||||
r: &mut XmlReader<R>
|
||||
) -> Result<IssuanceResponse, Error> where R: io::Read {
|
||||
r.take_named_element("class", |mut a, r| {
|
||||
let name = a.take_req("class_name")?;
|
||||
let cert_url = uri::Rsync::from_str(
|
||||
&a.take_req("cert_url")?
|
||||
)?;
|
||||
|
||||
let asn = a.take_req("resource_set_as")?;
|
||||
let v4 = a.take_req("resource_set_ipv4")?;
|
||||
let v6 = a.take_req("resource_set_ipv6")?;
|
||||
let resource_set = ResourceSet::from_strs(&asn, &v4, &v6)?;
|
||||
|
||||
let not_after = a.take_req("resource_set_notafter")?;
|
||||
let not_after = DateTime::<Utc>::from_str(¬_after)?;
|
||||
let not_after = Time::new(not_after);
|
||||
|
||||
a.exhausted()?;
|
||||
|
||||
let issued = Self::decode_issued_cert(r)?;
|
||||
|
||||
let cert = r.take_named_element("issuer", |a, r| {
|
||||
a.exhausted()?;
|
||||
Self::decode_cert(r)
|
||||
})?;
|
||||
|
||||
let issuer = SigningCert::new(cert_url, cert);
|
||||
|
||||
Ok(IssuanceResponse::new(
|
||||
name,
|
||||
issuer,
|
||||
resource_set,
|
||||
not_after,
|
||||
issued
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
fn decode_entitlements<R>(
|
||||
r: &mut XmlReader<R>
|
||||
) -> Result<Entitlements, Error> where R: io::Read {
|
||||
@@ -387,7 +479,8 @@ impl Res {
|
||||
w: &mut XmlWriter<W>
|
||||
) -> Result<(), io::Error> {
|
||||
match self {
|
||||
Res::List(ents) => Self::encode_entitlements(ents, w)
|
||||
Res::List(ents) => Self::encode_entitlements(ents, w),
|
||||
Res::Issue(response) => Self::encode_issuance_response(response, w)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,14 +494,47 @@ impl Res {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn encode_issuance_response<W: io::Write>(
|
||||
res: &IssuanceResponse,
|
||||
w: &mut XmlWriter<W>
|
||||
) -> Result<(), io::Error> {
|
||||
Self::encode_class(
|
||||
res.class_name(),
|
||||
res.issuer().uri(),
|
||||
res.not_after(),
|
||||
res.resource_set(),
|
||||
[res.issued().clone()].iter(),
|
||||
res.issuer(),
|
||||
w
|
||||
)
|
||||
}
|
||||
|
||||
fn encode_entitlement_class<W: io::Write>(
|
||||
c: &EntitlementClass,
|
||||
w: &mut XmlWriter<W>
|
||||
) -> Result<(), io::Error> {
|
||||
let cert_url = c.issuer().uri().to_string();
|
||||
let not_after = c.not_after()
|
||||
.to_rfc3339_opts(SecondsFormat::Secs, true);
|
||||
let inrs = c.resource_set();
|
||||
Self::encode_class(
|
||||
c.class_name(),
|
||||
c.issuer().uri(),
|
||||
c.not_after(),
|
||||
c.resource_set(),
|
||||
c.issued().iter(),
|
||||
c.issuer(),
|
||||
w
|
||||
)
|
||||
}
|
||||
|
||||
fn encode_class<'a, W: io::Write>(
|
||||
class_name: &str,
|
||||
cert_url: &uri::Rsync,
|
||||
not_after: Time,
|
||||
inrs: &ResourceSet,
|
||||
issued: impl Iterator<Item=&'a IssuedCert>,
|
||||
issuer: &SigningCert,
|
||||
w: &mut XmlWriter<W>
|
||||
) -> Result<(), io::Error> {
|
||||
let cert_url = cert_url.to_string();
|
||||
let not_after = not_after.to_rfc3339_opts(SecondsFormat::Secs, true);
|
||||
|
||||
let asn = inrs.asn().to_string();
|
||||
let v4 = inrs.v4().to_string();
|
||||
@@ -417,21 +543,23 @@ impl Res {
|
||||
let mut attrs = vec![];
|
||||
|
||||
attrs.push(("cert_url", cert_url.as_str()));
|
||||
attrs.push(("class_name", c.name()));
|
||||
attrs.push(("class_name", class_name));
|
||||
attrs.push(("resource_set_as", asn.as_str()));
|
||||
attrs.push(("resource_set_ipv4", v4.as_str()));
|
||||
attrs.push(("resource_set_ipv6", v6.as_str()));
|
||||
attrs.push(("resource_set_notafter", not_after.as_str()));
|
||||
|
||||
w.put_element("class", Some(&attrs), |w| {
|
||||
for issued in c.issued() {
|
||||
for issued in issued {
|
||||
Self::encode_issued(issued, w)?;
|
||||
}
|
||||
let issuer_cert = c.issuer().cert().to_captured().into_bytes();
|
||||
w.put_element("issuer", None, |w| { w.put_blob(&issuer_cert) })
|
||||
let issuer_cert = issuer.cert().to_captured().into_bytes();
|
||||
w.put_element("issuer", None, |w| { w.put_base64_std(&issuer_cert) })
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn encode_issued<W: io::Write>(
|
||||
issued: &IssuedCert,
|
||||
w: &mut XmlWriter<W>
|
||||
@@ -456,13 +584,13 @@ impl Res {
|
||||
attrs_strings.push(("resource_set_ipv6", v6.to_string()));
|
||||
}
|
||||
|
||||
let mut attrs_str = vec![];
|
||||
let mut attrs_str: Vec<(&str, &str)> = vec![];
|
||||
for (k, v) in &attrs_strings {
|
||||
attrs_str.push((k.clone(), v.as_str()));
|
||||
attrs_str.push((k, v.as_str()));
|
||||
}
|
||||
|
||||
w.put_element("certificate", Some(attrs_str.as_slice()), |w| {
|
||||
w.put_blob(&cert_bytes)
|
||||
w.put_base64_std(&cert_bytes)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -500,10 +628,12 @@ pub enum Error {
|
||||
#[display(fmt = "Could not parse encoded certificate.")]
|
||||
InvalidCert,
|
||||
|
||||
|
||||
#[display(fmt = "Could not parse encoded certificate request.")]
|
||||
InvalidCsr,
|
||||
|
||||
#[display(fmt = "Could not parse SKI in revoke request.")]
|
||||
InvalidSki,
|
||||
|
||||
#[display(fmt = "{}", _0)]
|
||||
InrSyntax(String),
|
||||
}
|
||||
@@ -547,11 +677,14 @@ impl From<chrono::ParseError> for Error {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use std::str;
|
||||
use sigmsg::SignedMessage;
|
||||
use std::str::from_utf8_unchecked;
|
||||
|
||||
use crate::sigmsg::SignedMessage;
|
||||
use crate::id::tests::test_id_certificate;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Test that the we can re-encode the object to xml, parse that
|
||||
/// xml, and end up with an equal object.
|
||||
fn assert_re_encode_equals(object: Message) {
|
||||
@@ -598,10 +731,39 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn parse_and_encode_issue_response() {
|
||||
let xml = extract_xml(
|
||||
include_bytes!("../test/remote/rpkid-rfc6492-issue_response.der")
|
||||
);
|
||||
let issue = Message::decode(xml.as_bytes()).unwrap();
|
||||
assert_re_encode_equals(issue);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_and_parse_revocation_request() {
|
||||
// No example CMS found for this one, so just composing and
|
||||
// reading the XML based on the RFC spec only.
|
||||
let cert = test_id_certificate();
|
||||
|
||||
let sender = "child".to_string();
|
||||
let rcpt = "parent".to_string();
|
||||
let class = "all".to_string();
|
||||
|
||||
let ski = cert.subject_public_key_info().key_identifier();
|
||||
let revocation = RevocationRequest::new(class, ski);
|
||||
|
||||
let rev = Message::revoke(sender, rcpt, revocation);
|
||||
|
||||
let decoded_rev = Message::decode(rev.encode_vec().as_slice()).unwrap();
|
||||
|
||||
assert_eq!(rev, decoded_rev);
|
||||
}
|
||||
|
||||
#[test]
|
||||
// #[ignore]
|
||||
fn print_cms_content() {
|
||||
let xml = extract_xml(
|
||||
include_bytes!("../test/remote/rpkid-rfc6492-list_response.der")
|
||||
include_bytes!("../test/remote/rpkid-rfc6492-issue_response.der")
|
||||
);
|
||||
|
||||
eprintln!("{}", xml);
|
||||
|
||||
@@ -115,7 +115,7 @@ impl PublisherRequest {
|
||||
"publisher_bpki_ta",
|
||||
None,
|
||||
|w| {
|
||||
w.put_blob(&self.id_cert.to_bytes())
|
||||
w.put_base64_std(&self.id_cert.to_bytes())
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -341,7 +341,7 @@ impl RepositoryResponse {
|
||||
"repository_bpki_ta",
|
||||
None,
|
||||
|w| {
|
||||
w.put_blob(&self.id_cert.to_bytes())
|
||||
w.put_base64_std(&self.id_cert.to_bytes())
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -1,9 +1,10 @@
|
||||
use api::ca::{ResourceSet, IssuedCert};
|
||||
use api::ca::{ResourceSet, IssuedCert, RcvdCert};
|
||||
use rpki::x509::Time;
|
||||
use rpki::cert::{Cert, Overclaim};
|
||||
use rpki::csr::Csr;
|
||||
use rpki::uri;
|
||||
use rpki::resources::{AsResources, Ipv4Resources, Ipv6Resources};
|
||||
use rpki::crypto::{PublicKey, KeyIdentifier};
|
||||
|
||||
pub const DFLT_CLASS: &str = "all";
|
||||
|
||||
@@ -48,7 +49,7 @@ impl Entitlements {
|
||||
) -> Self {
|
||||
let name = DFLT_CLASS.to_string();
|
||||
Entitlements { classes: vec![
|
||||
EntitlementClass { name, issuer, resource_set, not_after, issued }
|
||||
EntitlementClass { class_name: name, issuer, resource_set, not_after, issued }
|
||||
]}
|
||||
}
|
||||
pub fn new(classes: Vec<EntitlementClass>) -> Self {
|
||||
@@ -63,7 +64,7 @@ impl Entitlements {
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct EntitlementClass {
|
||||
name: String,
|
||||
class_name: String,
|
||||
issuer: SigningCert,
|
||||
resource_set: ResourceSet,
|
||||
not_after: Time,
|
||||
@@ -72,20 +73,51 @@ pub struct EntitlementClass {
|
||||
|
||||
impl EntitlementClass {
|
||||
pub fn new(
|
||||
name: String,
|
||||
class_name: String,
|
||||
issuer: SigningCert,
|
||||
resource_set: ResourceSet,
|
||||
not_after: Time,
|
||||
issued: Vec<IssuedCert>
|
||||
) -> Self {
|
||||
EntitlementClass { name, issuer, resource_set, not_after, issued }
|
||||
EntitlementClass { class_name, issuer, resource_set, not_after, issued }
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str { &self.name }
|
||||
fn unwrap(
|
||||
self
|
||||
) -> (String, SigningCert, ResourceSet, Time, Vec<IssuedCert>) {
|
||||
(
|
||||
self.class_name,
|
||||
self.issuer,
|
||||
self.resource_set,
|
||||
self.not_after,
|
||||
self.issued
|
||||
)
|
||||
}
|
||||
|
||||
pub fn class_name(&self) -> &str { &self.class_name }
|
||||
pub fn issuer(&self) -> &SigningCert { &self.issuer }
|
||||
pub fn resource_set(&self) -> &ResourceSet { &self.resource_set }
|
||||
pub fn not_after(&self) -> Time { self.not_after }
|
||||
pub fn issued(&self) -> &Vec<IssuedCert> { &self.issued }
|
||||
|
||||
/// Converts this into an IssuanceResponse for the given key. I.e. includes
|
||||
/// the issued certificate matching the given public key only. Returns a
|
||||
/// None if no match is found.
|
||||
pub fn into_issuance_response(self, key: &PublicKey) -> Option<IssuanceResponse> {
|
||||
let (class_name, issuer, resource_set, not_after, issued) = self.unwrap();
|
||||
|
||||
issued.into_iter()
|
||||
.find(|issued| issued.cert().subject_public_key_info() == key)
|
||||
.map(|issued| {
|
||||
IssuanceResponse::new(
|
||||
class_name,
|
||||
issuer,
|
||||
resource_set,
|
||||
not_after,
|
||||
issued
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -116,6 +148,15 @@ impl PartialEq for SigningCert {
|
||||
|
||||
impl Eq for SigningCert {}
|
||||
|
||||
impl From<&RcvdCert> for SigningCert {
|
||||
fn from(c: &RcvdCert) -> Self {
|
||||
SigningCert {
|
||||
uri: c.uri().clone(),
|
||||
cert: c.cert().clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ IssuanceRequest -----------------------------------------------
|
||||
|
||||
@@ -157,6 +198,45 @@ impl PartialEq for IssuanceRequest {
|
||||
impl Eq for IssuanceRequest {}
|
||||
|
||||
|
||||
//------------ IssuanceResponse ----------------------------------------------
|
||||
|
||||
/// A Certificate Issuance Response equivalent to the one defined in
|
||||
/// section 3.4.2 of RFC6492.
|
||||
///
|
||||
/// Note that this is like a single EntitlementClass response, except that
|
||||
/// it includes the one certificate which has just been issued only.
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct IssuanceResponse {
|
||||
class_name: String,
|
||||
issuer: SigningCert,
|
||||
resource_set: ResourceSet, // resources allowed on a cert
|
||||
not_after: Time,
|
||||
issued: IssuedCert
|
||||
}
|
||||
|
||||
impl IssuanceResponse {
|
||||
pub fn new(
|
||||
class_name: String,
|
||||
issuer: SigningCert,
|
||||
resource_set: ResourceSet, // resources allowed on a cert
|
||||
not_after: Time,
|
||||
issued: IssuedCert
|
||||
) -> Self {
|
||||
IssuanceResponse { class_name, issuer, resource_set, not_after, issued }
|
||||
}
|
||||
|
||||
pub fn unwrap(self) -> (String, SigningCert, ResourceSet, IssuedCert) {
|
||||
(self.class_name, self.issuer, self.resource_set, self.issued)
|
||||
}
|
||||
|
||||
pub fn class_name(&self) -> &str { &self.class_name }
|
||||
pub fn issuer(&self) -> &SigningCert { &self.issuer }
|
||||
pub fn resource_set(&self) -> &ResourceSet { &self.resource_set }
|
||||
pub fn not_after(&self) -> Time { self.not_after }
|
||||
pub fn issued(&self) -> &IssuedCert { &self.issued }
|
||||
}
|
||||
|
||||
|
||||
//------------ RequestResourceLimit ------------------------------------------
|
||||
|
||||
/// The scope of resources that a child CA wants to have certified. By default
|
||||
@@ -286,3 +366,25 @@ impl Default for RequestResourceLimit {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ RevocationRequest ---------------------------------------------
|
||||
|
||||
/// This type represents a Certificate Revocation Request as
|
||||
/// defined in section 3.5.1 of RFC6492.
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct RevocationRequest {
|
||||
class_name: String,
|
||||
key: KeyIdentifier
|
||||
}
|
||||
|
||||
impl RevocationRequest {
|
||||
pub fn new(class_name: String, key: KeyIdentifier) -> Self {
|
||||
RevocationRequest { class_name, key}
|
||||
}
|
||||
|
||||
pub fn class_name(&self) -> &str { &self.class_name }
|
||||
pub fn key(&self) -> &KeyIdentifier { &self.key }
|
||||
}
|
||||
|
||||
|
||||
|
||||
+35
-21
@@ -218,11 +218,32 @@ impl <R: io::Read> XmlReader<R> {
|
||||
|
||||
/// Takes base64 encoded bytes from the next 'characters' event.
|
||||
pub fn take_bytes_characters(&mut self) -> Result<Bytes, XmlReaderErr> {
|
||||
let chars = Self::strip_whitespace(self.take_chars()?);
|
||||
let b64 = base64::decode_config(&chars, base64::STANDARD_NO_PAD)?;
|
||||
self.take_bytes(base64::STANDARD_NO_PAD)
|
||||
}
|
||||
|
||||
/// Takes base64 encoded bytes from the next 'characters' event.
|
||||
pub fn take_bytes_url_safe_pad(&mut self) -> Result<Bytes, XmlReaderErr> {
|
||||
self.take_bytes(base64::URL_SAFE_NO_PAD)
|
||||
}
|
||||
|
||||
fn take_bytes(
|
||||
&mut self,
|
||||
config: base64::Config
|
||||
) -> Result<Bytes, XmlReaderErr> {
|
||||
let chars = self.take_chars()?;
|
||||
// strip whitespace and padding (we are liberal in what we accept here)
|
||||
// TODO: Avoid allocation, pass in an AsRef<[u8]> that
|
||||
// removes any whitespace on the fly.
|
||||
let chars: Vec<u8> = chars.into_bytes()
|
||||
.into_iter()
|
||||
.filter(|c| !b" \n\t\r\x0b\x0c=".contains(c))
|
||||
.collect();
|
||||
|
||||
let b64 = base64::decode_config(&chars, config)?;
|
||||
Ok(Bytes::from(b64))
|
||||
}
|
||||
|
||||
|
||||
pub fn take_empty(&mut self) -> Result<(), XmlReaderErr> {
|
||||
Ok(())
|
||||
}
|
||||
@@ -247,18 +268,6 @@ impl <R: io::Read> XmlReader<R> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience function to strip all whitespace from chars. E.g.
|
||||
/// prior to decoding base64. I am quite sure this could be done
|
||||
/// more efficiently.
|
||||
///
|
||||
/// Asked the
|
||||
fn strip_whitespace(s: String) -> Vec<u8> {
|
||||
s.into_bytes()
|
||||
.into_iter()
|
||||
.filter(|c| !b" \n\t\r\x0b\x0c".contains(c))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl XmlReader<fs::File> {
|
||||
@@ -473,12 +482,17 @@ impl <W: io::Write> XmlWriter<W> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Converts bytes to base64 encoded Characters as the content. Note
|
||||
/// that you cannot have both Characters and other included elements.
|
||||
/// This would be valid XML, but it's not used by any of the RPKI XML
|
||||
/// structures.
|
||||
pub fn put_blob(&mut self, bytes: &Bytes) -> Result<(), io::Error> {
|
||||
let b64 = base64::encode(bytes);
|
||||
/// Converts bytes to base64 encoded Characters as the content, using the
|
||||
/// Standard character set, without padding.
|
||||
pub fn put_base64_std(&mut self, bytes: &Bytes) -> Result<(), io::Error> {
|
||||
let b64 = base64::encode_config(bytes, base64::STANDARD);
|
||||
self.put_text(b64.as_ref())
|
||||
}
|
||||
|
||||
/// Converts bytes to base64 encoded Characters as the content, using the
|
||||
/// URL safe character set and padding.
|
||||
pub fn put_base64_url_safe(&mut self, bytes: &[u8]) -> Result<(), io::Error> {
|
||||
let b64 = base64::encode_config(bytes, base64::URL_SAFE);
|
||||
self.put_text(b64.as_ref())
|
||||
}
|
||||
|
||||
@@ -547,7 +561,7 @@ mod tests {
|
||||
]),
|
||||
|w| {
|
||||
w.put_element("b", None, |w| {
|
||||
w.put_blob(&Bytes::from("X"))
|
||||
w.put_base64_std(&Bytes::from("X"))
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
@@ -7,10 +7,10 @@ use bcder::Captured;
|
||||
use rpki::uri;
|
||||
|
||||
use krill_ca::{ta_handle, CaServer, CaServerError, PubClients, PubClientError};
|
||||
use krill_commons::api::{publication, Entitlements, IssuanceRequest};
|
||||
use krill_commons::api::{publication, Entitlements, IssuanceRequest, IssuanceResponse};
|
||||
use krill_commons::api::admin;
|
||||
use krill_commons::api::admin::{Handle, Token, PubServerInfo, CertAuthInit, CertAuthPubMode, ParentCaContact, AddChildRequest, ParentCaReq};
|
||||
use krill_commons::api::ca::{TrustAnchorInfo, RcvdCert, IssuedCert, CertAuthList, CertAuthInfo};
|
||||
use krill_commons::api::ca::{TrustAnchorInfo, RcvdCert, CertAuthList, CertAuthInfo};
|
||||
use krill_commons::util::softsigner::{OpenSslSigner, SignerError};
|
||||
use krill_cms_proxy::api::ClientInfo;
|
||||
use krill_cms_proxy::proxy;
|
||||
@@ -432,7 +432,7 @@ impl KrillServer {
|
||||
child: &Handle,
|
||||
issue_req: IssuanceRequest,
|
||||
auth: Auth
|
||||
) -> KrillRes<IssuedCert> {
|
||||
) -> KrillRes<IssuanceResponse> {
|
||||
Ok(self.caserver.issue(
|
||||
parent,
|
||||
child,
|
||||
|
||||
Reference in New Issue
Block a user