use std::borrow;
use std::convert::{Infallible, TryFrom};
use std::fmt;
use std::io;
use std::path::PathBuf;
use std::str::from_utf8;
use std::str::FromStr;
use std::sync::Arc;
use log::debug;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::ca::idcert::IdCert;
use crate::ca::publication::Base64;
use crate::repository::error::ValidationError;
use crate::repository::x509::Time;
use crate::uri;
use crate::xml;
use crate::xml::decode::{Error as XmlError, Name};
const VERSION: &str = "1";
const NS: &[u8] = b"http://www.hactrn.net/uris/rpki/rpki-setup/";
const NS_NO_SLASH: &[u8] = b"http://www.hactrn.net/uris/rpki/rpki-setup";
const CHILD_REQUEST: Name = Name::unqualified(b"child_request");
const CHILD_BPKI_TA: Name = Name::unqualified(b"child_bpki_ta");
const PUBLISHER_REQUEST: Name = Name::unqualified(b"publisher_request");
const PUBLISHER_BPKI_TA: Name = Name::unqualified(b"publisher_bpki_ta");
const PARENT_RESPONSE: Name = Name::unqualified(b"parent_response");
const PARENT_BPKI_TA: Name = Name::unqualified(b"parent_bpki_ta");
const PARENT_REFERRAL: Name = Name::unqualified(b"referral");
const PARENT_OFFER: Name = Name::unqualified(b"offer");
const REPOSITORY_RESPONSE: Name = Name::unqualified(b"repository_response");
const REPOSITORY_BPKI_TA: Name = Name::unqualified(b"repository_bpki_ta");
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct Myself;
pub type MyHandle = Handle<Myself>;
pub type CaHandle = Handle<Myself>;
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct Parent;
pub type ParentHandle = Handle<Parent>;
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct Child;
pub type ChildHandle = Handle<Child>;
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct Publisher;
pub type PublisherHandle = Handle<Publisher>;
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct Repository;
pub type RepositoryHandle = Handle<Repository>;
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct Sender;
pub type SenderHandle = Handle<Sender>;
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct Recipient;
pub type RecipientHandle = Handle<Recipient>;
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq)]
#[serde(try_from = "String")]
pub struct Handle<T> {
name: Arc<str>,
marker: std::marker::PhantomData<T>
}
impl<T> Handle<T> {
pub fn new(name: Arc<str>) -> Self {
Handle { name, marker: std::marker::PhantomData }
}
pub fn name(&self) -> &Arc<str> {
&self.name
}
pub fn into_name(self) -> Arc<str> {
self.name
}
pub fn as_str(&self) -> &str {
self.as_ref()
}
pub fn convert<Y>(&self) -> Handle<Y> {
Handle::new(self.name.clone())
}
pub fn into_converted<Y>(self) -> Handle<Y> {
Handle::new(self.name)
}
pub fn to_path_buf(&self) -> PathBuf {
let s = self.to_string();
let s = s.replace('/', "+");
let s = s.replace('\\', "=");
PathBuf::from(s)
}
fn verify_name(s: &str) -> Result<(), InvalidHandle> {
if s.bytes()
.all(|b| b.is_ascii_alphanumeric() || b == b'-' || b == b'_' || b == b'/' || b == b'\\')
&& !s.is_empty()
&& s.len() < 256
{
Ok(())
} else {
Err(InvalidHandle)
}
}
}
impl<T> TryFrom<&PathBuf> for Handle<T> {
type Error = InvalidHandle;
fn try_from(path: &PathBuf) -> Result<Self, Self::Error> {
if let Some(path) = path.file_name() {
let s = path.to_string_lossy().to_string();
let s = s.replace('+', "/");
let s = s.replace('=', "\\");
Self::from_str(&s)
} else {
Err(InvalidHandle)
}
}
}
impl<T> FromStr for Handle<T> {
type Err = InvalidHandle;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::verify_name(s)?;
Ok(Handle::new(s.into()))
}
}
impl<T> TryFrom<String> for Handle<T> {
type Error = InvalidHandle;
fn try_from(value: String) -> Result<Self, Self::Error> {
Self::verify_name(&value)?;
Ok(Handle::new(value.into()))
}
}
impl<T> From<&Arc<str>> for Handle<T> {
fn from(arc: &Arc<str>) -> Self {
Self::new(arc.clone())
}
}
impl<T> AsRef<str> for Handle<T> {
fn as_ref(&self) -> &str {
&self.name
}
}
impl<T> borrow::Borrow<str> for Handle<T> {
fn borrow(&self) -> &str {
self.as_ref()
}
}
impl<T> AsRef<[u8]> for Handle<T> {
fn as_ref(&self) -> &[u8] {
self.name.as_bytes()
}
}
impl<T> fmt::Display for Handle<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl<T> Serialize for Handle<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.as_str().serialize(serializer)
}
}
#[derive(Debug)]
pub struct InvalidHandle;
impl fmt::Display for InvalidHandle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Handle MUST have pattern: [-_A-Za-z0-9/]{{1,255}}")
}
}
impl std::error::Error for InvalidHandle { }
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ServiceUri {
Https(uri::Https),
Http(String),
}
impl ServiceUri {
pub fn as_str(&self) -> &str {
match self {
ServiceUri::Http(http) => http,
ServiceUri::Https(https) => https.as_str(),
}
}
}
impl TryFrom<String> for ServiceUri {
type Error = Error;
fn try_from(value: String) -> Result<Self, Self::Error> {
Self::from_str(&value)
}
}
impl FromStr for ServiceUri {
type Err = Error;
fn from_str(value: &str) -> Result<Self, Self::Err> {
if value.to_lowercase().starts_with("http://") {
Ok(ServiceUri::Http(value.to_string()))
} else {
Ok(ServiceUri::Https(uri::Https::from_str(value)?))
}
}
}
impl fmt::Display for ServiceUri {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ServiceUri::Http(string) => string.fmt(f),
ServiceUri::Https(https) => https.fmt(f),
}
}
}
impl Serialize for ServiceUri {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.as_str().serialize(serializer)
}
}
impl<'de> Deserialize<'de> for ServiceUri {
fn deserialize<D>(deserializer: D) -> Result<ServiceUri, D::Error>
where
D: Deserializer<'de>,
{
let string = String::deserialize(deserializer)?;
ServiceUri::try_from(string).map_err(serde::de::Error::custom)
}
}
impl AsRef<str> for ServiceUri {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct ChildRequest {
id_cert: Base64,
child_handle: ChildHandle,
tag: Option<String>,
}
impl ChildRequest {
pub fn new(id_cert: Base64, child_handle: ChildHandle) -> Self {
ChildRequest {
id_cert,
child_handle,
tag: None,
}
}
pub fn unpack(self) -> (Base64, ChildHandle, Option<String>) {
(self.id_cert, self.child_handle, self.tag)
}
pub fn id_cert(&self) -> &Base64 {
&self.id_cert
}
pub fn child_handle(&self) -> &ChildHandle {
&self.child_handle
}
pub fn tag(&self) -> Option<&String> {
self.tag.as_ref()
}
}
impl ChildRequest {
pub fn parse<R: io::BufRead>(reader: R) -> Result<Self, Error> {
let mut reader = xml::decode::Reader::new(reader);
let mut child_handle: Option<ChildHandle> = None;
let mut tag: Option<String> = None;
let mut outer = reader.start(|element| {
if let Some(ns) = element.name().namespace() {
if ns != NS && ns != NS_NO_SLASH {
debug!("invalid namespace in child request");
return Err(XmlError::Malformed);
}
}
if element.name().into_unqualified() != CHILD_REQUEST {
return Err(XmlError::Malformed);
}
element.attributes(|name, value| match name {
b"version" => {
if value.ascii_into::<String>()? != VERSION {
return Err(XmlError::Malformed);
}
Ok(())
}
b"child_handle" => {
child_handle = Some(value.ascii_into()?);
Ok(())
}
b"tag" => {
tag = Some(value.ascii_into()?);
Ok(())
}
_ => Err(XmlError::Malformed),
})
})?;
let child_handle = child_handle.ok_or(XmlError::Malformed)?;
let mut content = outer.take_element(&mut reader, |element|
match element.name().into_unqualified() {
CHILD_BPKI_TA => Ok(()),
_ => Err(XmlError::Malformed),
}
)?;
let bytes = content.take_text(
&mut reader, |text| text.base64_decode()
)?;
let id_cert = Base64::from_content(bytes.as_slice());
content.take_end(&mut reader)?;
outer.take_end(&mut reader)?;
reader.end()?;
Ok(ChildRequest {
tag,
child_handle,
id_cert,
})
}
pub fn validate(&self) -> Result<IdCert, Error> {
self.validate_at(Time::now())
}
fn validate_at(&self, when: Time) -> Result<IdCert, Error> {
validate_idcert_at(&self.id_cert, when)
}
pub fn write_xml(&self, writer: &mut impl io::Write) -> Result<(), io::Error> {
let mut writer = xml::encode::Writer::new(writer);
writer
.element(CHILD_REQUEST)?
.attr("xmlns", NS)?
.attr("version", VERSION)?
.attr("child_handle", self.child_handle())?
.attr_opt("tag", self.tag())?
.content(|content| {
content
.element(CHILD_BPKI_TA)?
.content(|content| content.raw(&self.id_cert))?;
Ok(())
})?;
writer.done()
}
pub fn to_xml_vec(&self) -> Vec<u8> {
let mut vec = vec![];
self.write_xml(&mut vec).unwrap(); vec
}
pub fn to_xml_string(&self) -> String {
let vec = self.to_xml_vec();
let xml = from_utf8(vec.as_slice()).unwrap(); xml.to_string()
}
}
impl fmt::Display for ChildRequest {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.to_xml_string())
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct ParentResponse {
id_cert: Base64,
parent_handle: ParentHandle,
child_handle: ChildHandle,
service_uri: ServiceUri,
tag: Option<String>,
}
impl ParentResponse {
pub fn new(
id_cert: Base64,
parent_handle: ParentHandle,
child_handle: ChildHandle,
service_uri: ServiceUri,
tag: Option<String>,
) -> Self {
ParentResponse {
id_cert,
parent_handle,
child_handle,
service_uri,
tag,
}
}
pub fn id_cert(&self) -> &Base64 {
&self.id_cert
}
pub fn parent_handle(&self) -> &ParentHandle {
&self.parent_handle
}
pub fn child_handle(&self) -> &ChildHandle {
&self.child_handle
}
pub fn service_uri(&self) -> &ServiceUri {
&self.service_uri
}
pub fn tag(&self) -> Option<&String> {
self.tag.as_ref()
}
}
impl ParentResponse {
pub fn parse<R: io::BufRead>(reader: R) -> Result<Self, Error> {
let mut reader = xml::decode::Reader::new(reader);
let mut child_handle: Option<ChildHandle> = None;
let mut parent_handle: Option<ParentHandle> = None;
let mut service_uri: Option<ServiceUri> = None;
let mut tag: Option<String> = None;
let mut outer = reader.start(|element| {
if let Some(ns) = element.name().namespace() {
if ns != NS && ns != NS_NO_SLASH {
debug!("invalid namespace in parent response");
return Err(XmlError::Malformed);
}
}
if element.name().into_unqualified() != PARENT_RESPONSE {
return Err(XmlError::Malformed);
}
element.attributes(|name, value| match name {
b"version" => {
if value.ascii_into::<String>()? != VERSION {
return Err(XmlError::Malformed);
}
Ok(())
}
b"service_uri" => {
service_uri = Some(value.ascii_into()?);
Ok(())
}
b"parent_handle" => {
parent_handle = Some(value.ascii_into()?);
Ok(())
}
b"child_handle" => {
child_handle = Some(value.ascii_into()?);
Ok(())
}
b"tag" => {
tag = Some(value.ascii_into()?);
Ok(())
}
_ => {
debug!(
"Ignoring attribute in <parent_response />: {:?}",
from_utf8(name).unwrap_or("can't parse attr!?")
);
Ok(())
}
})
})?;
let service_uri = service_uri.ok_or(XmlError::Malformed)?;
let parent_handle = parent_handle.ok_or(XmlError::Malformed)?;
let child_handle = child_handle.ok_or(XmlError::Malformed)?;
let mut id_cert: Option<Base64> = None;
loop {
let mut bpki_ta_element_found = false;
let inner = outer.take_opt_element(&mut reader, |element|
match element.name().into_unqualified() {
PARENT_BPKI_TA => {
bpki_ta_element_found = true;
Ok(())
},
PARENT_OFFER | PARENT_REFERRAL => Ok(()),
_ => Err(XmlError::Malformed),
}
)?;
let mut inner = match inner {
Some(inner) => inner,
None => break,
};
if bpki_ta_element_found {
let bytes = inner.take_text(
&mut reader, |text| text.base64_decode()
)?;
id_cert = Some(Base64::from_content(bytes.as_slice()));
} else {
inner.skip_opt_text(&mut reader)?;
}
inner.take_end(&mut reader)?;
}
let id_cert = id_cert.ok_or(XmlError::Malformed)?;
outer.take_end(&mut reader)?;
reader.end()?;
Ok(ParentResponse {
tag,
id_cert,
parent_handle,
child_handle,
service_uri,
})
}
pub fn write_xml(&self, writer: &mut impl io::Write) -> Result<(), io::Error> {
let mut writer = xml::encode::Writer::new(writer);
writer
.element(PARENT_RESPONSE)?
.attr("xmlns", NS)?
.attr("version", VERSION)?
.attr("parent_handle", self.parent_handle())?
.attr("child_handle", self.child_handle())?
.attr("service_uri", self.service_uri())?
.attr_opt("tag", self.tag())?
.content(|content| {
content
.element(PARENT_BPKI_TA)?
.content(|content| content.raw(&self.id_cert))?;
Ok(())
})?;
writer.done()
}
pub fn validate(&self) -> Result<IdCert, Error> {
self.validate_at(Time::now())
}
pub fn validate_at(&self, when: Time) -> Result<IdCert, Error> {
validate_idcert_at(&self.id_cert, when)
}
pub fn to_xml_vec(&self) -> Vec<u8> {
let mut vec = vec![];
self.write_xml(&mut vec).unwrap(); vec
}
pub fn to_xml_string(&self) -> String {
let vec = self.to_xml_vec();
let xml = from_utf8(vec.as_slice()).unwrap(); xml.to_string()
}
}
impl fmt::Display for ParentResponse {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.to_xml_string())
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct PublisherRequest {
id_cert: Base64,
publisher_handle: PublisherHandle,
tag: Option<String>,
}
impl PublisherRequest {
pub fn new(id_cert: Base64, publisher_handle: PublisherHandle, tag: Option<String>) -> Self {
PublisherRequest {
id_cert,
publisher_handle,
tag,
}
}
pub fn unpack(self) -> (Base64, PublisherHandle, Option<String>) {
(self.id_cert, self.publisher_handle, self.tag)
}
pub fn id_cert(&self) -> &Base64 {
&self.id_cert
}
pub fn publisher_handle(&self) -> &PublisherHandle {
&self.publisher_handle
}
pub fn tag(&self) -> Option<&String> {
self.tag.as_ref()
}
}
impl PublisherRequest {
pub fn parse<R: io::BufRead>(reader: R) -> Result<Self, Error> {
let mut reader = xml::decode::Reader::new(reader);
let mut publisher_handle: Option<PublisherHandle> = None;
let mut tag: Option<String> = None;
let mut outer = reader.start(|element| {
if let Some(ns) = element.name().namespace() {
if ns != NS && ns != NS_NO_SLASH {
debug!("invalid namespace in publisher request");
return Err(XmlError::Malformed);
}
}
if element.name().into_unqualified() != PUBLISHER_REQUEST {
return Err(XmlError::Malformed);
}
element.attributes(|name, value| match name {
b"version" => {
if value.ascii_into::<String>()? != VERSION {
return Err(XmlError::Malformed);
}
Ok(())
}
b"publisher_handle" => {
publisher_handle = Some(value.ascii_into()?);
Ok(())
}
b"tag" => {
tag = Some(value.ascii_into()?);
Ok(())
}
_ => Err(XmlError::Malformed),
})
})?;
let publisher_handle = publisher_handle.ok_or(XmlError::Malformed)?;
let mut content = outer.take_element(&mut reader, |element|
match element.name().into_unqualified() {
PUBLISHER_BPKI_TA => Ok(()),
_ => Err(XmlError::Malformed),
}
)?;
let bytes = content.take_text(
&mut reader, |text| text.base64_decode()
)?;
let id_cert = Base64::from_content(bytes.as_slice());
content.take_end(&mut reader)?;
outer.take_end(&mut reader)?;
reader.end()?;
Ok(PublisherRequest {
tag,
publisher_handle,
id_cert,
})
}
pub fn validate(&self) -> Result<IdCert, Error> {
self.validate_at(Time::now())
}
fn validate_at(&self, when: Time) -> Result<IdCert, Error> {
validate_idcert_at(&self.id_cert, when)
}
pub fn write_xml(&self, writer: &mut impl io::Write) -> Result<(), io::Error> {
let mut writer = xml::encode::Writer::new(writer);
writer
.element(PUBLISHER_REQUEST)?
.attr("xmlns", NS)?
.attr("version", VERSION)?
.attr("publisher_handle", self.publisher_handle())?
.attr_opt("tag", self.tag())?
.content(|content| {
content
.element(PUBLISHER_BPKI_TA)?
.content(|content| content.raw(&self.id_cert))?;
Ok(())
})?;
writer.done()
}
pub fn to_xml_vec(&self) -> Vec<u8> {
let mut vec = vec![];
self.write_xml(&mut vec).unwrap(); vec
}
pub fn to_xml_string(&self) -> String {
let vec = self.to_xml_vec();
let xml = from_utf8(vec.as_slice()).unwrap(); xml.to_string()
}
}
impl fmt::Display for PublisherRequest {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.to_xml_string())
}
}
#[derive(Clone, Debug, Deserialize, Eq, Serialize, PartialEq)]
pub struct RepositoryResponse {
id_cert: Base64,
publisher_handle: PublisherHandle,
service_uri: ServiceUri,
repo_info: RepoInfo,
tag: Option<String>,
}
impl RepositoryResponse {
pub fn new(
id_cert: Base64,
publisher_handle: PublisherHandle,
service_uri: ServiceUri,
sia_base: uri::Rsync,
rrdp_notification_uri: Option<uri::Https>,
tag: Option<String>,
) -> Self {
let repo_info = RepoInfo::new(sia_base, rrdp_notification_uri);
RepositoryResponse {
id_cert,
publisher_handle,
service_uri,
repo_info,
tag,
}
}
pub fn id_cert(&self) -> &Base64 {
&self.id_cert
}
pub fn publisher_handle(&self) -> &PublisherHandle {
&self.publisher_handle
}
pub fn service_uri(&self) -> &ServiceUri {
&self.service_uri
}
pub fn repo_info(&self) -> &RepoInfo {
&self.repo_info
}
pub fn sia_base(&self) -> &uri::Rsync {
&self.repo_info.sia_base
}
pub fn rrdp_notification_uri(&self) -> Option<&uri::Https> {
self.repo_info.rrdp_notification_uri.as_ref()
}
pub fn tag(&self) -> Option<&String> {
self.tag.as_ref()
}
}
impl RepositoryResponse {
pub fn parse<R: io::BufRead>(reader: R) -> Result<Self, Error> {
let mut reader = xml::decode::Reader::new(reader);
let mut tag: Option<String> = None;
let mut publisher_handle: Option<PublisherHandle> = None;
let mut service_uri: Option<ServiceUri> = None;
let mut sia_base: Option<uri::Rsync> = None;
let mut rrdp_notification_uri: Option<uri::Https> = None;
let mut outer = reader.start(|element| {
if let Some(ns) = element.name().namespace() {
if ns != NS && ns != NS_NO_SLASH {
debug!("invalid namespace in repository response");
return Err(XmlError::Malformed);
}
}
if element.name().into_unqualified() != REPOSITORY_RESPONSE {
return Err(XmlError::Malformed);
}
element.attributes(|name, value| match name {
b"version" => {
if value.ascii_into::<String>()? != VERSION {
return Err(XmlError::Malformed);
}
Ok(())
}
b"service_uri" => {
service_uri = Some(value.ascii_into()?);
Ok(())
}
b"publisher_handle" => {
publisher_handle = Some(value.ascii_into()?);
Ok(())
}
b"sia_base" => {
sia_base = Some(value.ascii_into()?);
Ok(())
}
b"rrdp_notification_uri" => {
rrdp_notification_uri = Some(value.ascii_into()?);
Ok(())
}
b"tag" => {
tag = Some(value.ascii_into()?);
Ok(())
}
_ => {
debug!(
"Ignoring attribute in <parent_response />: {:?}",
from_utf8(name).unwrap_or("can't parse attr!?")
);
Ok(())
}
})
})?;
let service_uri = service_uri.ok_or(XmlError::Malformed)?;
let publisher_handle = publisher_handle.ok_or(XmlError::Malformed)?;
let sia_base = sia_base.ok_or(XmlError::Malformed)?;
let mut content = outer.take_element(&mut reader, |element|
match element.name().into_unqualified() {
REPOSITORY_BPKI_TA => Ok(()),
_ => Err(XmlError::Malformed),
}
)?;
let bytes = content.take_text(
&mut reader, |text| text.base64_decode()
)?;
let id_cert = Base64::from_content(bytes.as_slice());
content.take_end(&mut reader)?;
outer.take_end(&mut reader)?;
reader.end()?;
let repo_info = RepoInfo::new(sia_base, rrdp_notification_uri);
Ok(RepositoryResponse {
tag,
publisher_handle,
id_cert,
service_uri,
repo_info
})
}
pub fn write_xml(&self, writer: &mut impl io::Write) -> Result<(), io::Error> {
let mut writer = xml::encode::Writer::new(writer);
writer
.element(REPOSITORY_RESPONSE)?
.attr("xmlns", NS)?
.attr("version", VERSION)?
.attr("publisher_handle", self.publisher_handle())?
.attr("service_uri", self.service_uri())?
.attr("sia_base", self.sia_base())?
.attr_opt("rrdp_notification_uri", self.rrdp_notification_uri())?
.attr_opt("tag", self.tag())?
.content(|content| {
content
.element(REPOSITORY_BPKI_TA)?
.content(|content| content.raw(&self.id_cert))?;
Ok(())
})?;
writer.done()
}
pub fn validate(&self) -> Result<IdCert, Error> {
self.validate_at(Time::now())
}
fn validate_at(&self, when: Time) -> Result<IdCert, Error> {
validate_idcert_at(&self.id_cert, when)
}
pub fn to_xml_vec(&self) -> Vec<u8> {
let mut vec = vec![];
self.write_xml(&mut vec).unwrap(); vec
}
pub fn to_xml_string(&self) -> String {
let vec = self.to_xml_vec();
let xml = from_utf8(vec.as_slice()).unwrap(); xml.to_string()
}
}
impl fmt::Display for RepositoryResponse {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.to_xml_string())
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct RepoInfo {
#[serde(alias = "base_uri")]
sia_base: uri::Rsync,
#[serde(alias = "rpki_notify")]
rrdp_notification_uri: Option<uri::Https>,
}
impl RepoInfo {
pub fn new(sia_base: uri::Rsync, rrdp_notification_uri: Option<uri::Https>) -> Self {
RepoInfo { sia_base, rrdp_notification_uri }
}
pub fn base_uri(&self) -> &uri::Rsync {
&self.sia_base
}
pub fn ca_repository(&self, name_space: &str) -> uri::Rsync {
match name_space {
"" => self.sia_base.clone(),
_ => self.sia_base
.join(format!("{}/", name_space).as_bytes())
.unwrap(),
}
}
pub fn rpki_notify(&self) -> Option<&uri::Https> {
self.rrdp_notification_uri.as_ref()
}
pub fn resolve(&self, name_space: &str, file_name: &str) -> uri::Rsync {
self.ca_repository(name_space).join(file_name.as_ref()).unwrap()
}
}
pub fn validate_idcert_at(
base64: &Base64,
when: Time,
) -> Result<IdCert, Error> {
let bytes = base64.to_bytes();
let id_cert = IdCert::decode(bytes.as_ref())?;
id_cert.validate_ta_at(when)?;
Ok(id_cert)
}
#[derive(Debug)]
pub enum Error {
InvalidXml(xml::decode::Error),
InvalidVersion,
InvalidHandle,
InvalidTaBase64(base64::DecodeError),
InvalidTaCertEncoding(bcder::decode::DecodeError<Infallible>),
InvalidTaCert,
InvalidUri(uri::Error),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::InvalidXml(e) => e.fmt(f),
Error::InvalidVersion => write!(f, "Invalid version"),
Error::InvalidHandle => write!(f, "Invalid handle"),
Error::InvalidTaBase64(e) => e.fmt(f),
Error::InvalidTaCertEncoding(e) => {
write!(f, "Cannot decode TA cert: {}", e)
}
Error::InvalidTaCert => write!(f, "Invalid TA cert"),
&Error::InvalidUri(e) => e.fmt(f),
}
}
}
impl From<xml::decode::Error> for Error {
fn from(e: xml::decode::Error) -> Self {
Error::InvalidXml(e)
}
}
impl From<base64::DecodeError> for Error {
fn from(e: base64::DecodeError) -> Self {
Error::InvalidTaBase64(e)
}
}
impl From<bcder::decode::DecodeError<Infallible>> for Error {
fn from(e: bcder::decode::DecodeError<Infallible>) -> Self {
Error::InvalidTaCertEncoding(e)
}
}
impl From<ValidationError> for Error {
fn from(_e: ValidationError) -> Self {
Error::InvalidTaCert
}
}
impl From<uri::Error> for Error {
fn from(e: uri::Error) -> Self {
Error::InvalidUri(e)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn child_request_codec() {
let xml = include_str!("../../test-data/ca/rfc8183/rpkid-child-id.xml");
let req = ChildRequest::parse(xml.as_bytes()).unwrap();
assert_eq!(&Handle::from_str("Carol").unwrap(), req.child_handle());
assert_eq!(None, req.tag());
let re_encoded_xml = req.to_xml_string();
let re_decoded = ChildRequest::parse(re_encoded_xml.as_bytes()).unwrap();
assert_eq!(req, re_decoded);
}
#[test]
fn parent_response_codec() {
let xml = include_str!("../../test-data/ca/rfc8183/apnic-parent-response.xml");
let req = ParentResponse::parse(xml.as_bytes()).unwrap();
let re_encoded_xml = req.to_xml_string();
let re_decoded =
ParentResponse::parse(re_encoded_xml.as_bytes()).unwrap();
assert_eq!(req, re_decoded);
}
#[test]
fn afrinic_parent_response_codec() {
let xml = include_str!("../../test-data/ca/rfc8183/afrinic-parent-response.xml");
let req = ParentResponse::parse(xml.as_bytes()).unwrap();
let re_encoded_xml = req.to_xml_string();
let re_decoded =
ParentResponse::parse(re_encoded_xml.as_bytes()).unwrap();
assert_eq!(req, re_decoded);
let _ta_cert = req.validate().unwrap();
}
#[test]
fn parent_response_krill_0_9() {
let xml = include_str!("../../test-data/ca/rfc8183/krill-0-9-parent-response.xml");
let req = ParentResponse::parse(xml.as_bytes()).unwrap();
let re_encoded_xml = req.to_xml_string();
let re_decoded =
ParentResponse::parse(re_encoded_xml.as_bytes()).unwrap();
assert_eq!(req, re_decoded);
}
#[test]
fn parent_response_parse_rpkid_referral() {
let xml = include_str!("../../test-data/ca/rfc8183/rpkid-parent-response-referral.xml");
let _req = ParentResponse::parse(xml.as_bytes()).unwrap();
}
#[test]
fn parent_response_parse_rpkid_offer() {
let xml = include_str!("../../test-data/ca/rfc8183/rpkid-parent-response-offer.xml");
let _req = ParentResponse::parse(xml.as_bytes()).unwrap();
}
#[test]
fn publisher_request_codec() {
let xml = include_str!("../../test-data/ca/rfc8183/rpkid-publisher-request.xml");
let req = PublisherRequest::parse(xml.as_bytes()).unwrap();
let re_encoded_xml = req.to_xml_string();
let re_decoded =
PublisherRequest::parse(re_encoded_xml.as_bytes()).unwrap();
assert_eq!(req, re_decoded);
}
#[test]
fn repository_response_codec() {
let xml = include_str!("../../test-data/ca/rfc8183/apnic-repository-response.xml");
let req = RepositoryResponse::parse(xml.as_bytes()).unwrap();
let re_encoded_xml = req.to_xml_string();
let re_decoded =
RepositoryResponse::parse(re_encoded_xml.as_bytes()).unwrap();
assert_eq!(req, re_decoded);
}
#[test]
fn repository_response_krill_0_9() {
let xml = include_str!("../../test-data/ca/rfc8183/krill-0-9-repository-response.xml");
let req = RepositoryResponse::parse(xml.as_bytes()).unwrap();
let re_encoded_xml = req.to_xml_string();
let re_decoded =
RepositoryResponse::parse(re_encoded_xml.as_bytes()).unwrap();
assert_eq!(req, re_decoded);
}
}