Use normalised route authorizations in the API. (Closes #126)

This commit is contained in:
Tim Bruijnzeels
2019-11-13 12:11:13 +01:00
parent 2d57d9f6cd
commit 601a667272
16 changed files with 395 additions and 255 deletions
+3 -3
View File
@@ -14,7 +14,7 @@ use crate::commons::api::RepositoryUpdate;
use crate::commons::api::{
AddChildRequest, AuthorizationFmtError, CertAuthInit, ChildAuthRequest, ChildHandle, Handle,
ParentCaContact, ParentCaReq, ParentHandle, PublisherHandle, ResSetErr, ResourceSet,
RouteAuthorizationUpdates, Token, UpdateChildRequest,
RoaDefinitionUpdates, Token, UpdateChildRequest,
};
use crate::commons::remote::id::IdCert;
use crate::commons::remote::rfc8183;
@@ -919,7 +919,7 @@ impl Options {
let path = matches.value_of("delta").map(PathBuf::from).unwrap();
let bytes = file::read(&path)?;
let updates_str = unsafe { from_utf8_unchecked(&bytes) };
RouteAuthorizationUpdates::from_str(updates_str)?
RoaDefinitionUpdates::from_str(updates_str)?
};
let command = Command::CertAuth(CaCommand::RouteAuthorizationsUpdate(my_ca, updates));
@@ -1175,7 +1175,7 @@ pub enum CaCommand {
RouteAuthorizationsList(Handle),
// Update the Route Authorizations for this CA
RouteAuthorizationsUpdate(Handle, RouteAuthorizationUpdates),
RouteAuthorizationsUpdate(Handle, RoaDefinitionUpdates),
// Show details for this CA
Show(Handle),
+2 -1
View File
@@ -2,10 +2,11 @@ use std::str::{from_utf8_unchecked, FromStr};
use crate::commons::api::{
CaRepoDetails, CertAuthHistory, CertAuthInfo, CertAuthList, CurrentObjects, CurrentRepoState,
ParentCaContact, PublisherDetails, PublisherList, RepositoryContact, RouteAuthorization,
ParentCaContact, PublisherDetails, PublisherList, RepositoryContact,
};
use crate::commons::remote::api::ClientInfo;
use crate::commons::remote::rfc8183;
use crate::daemon::ca::RouteAuthorization;
//------------ ApiResponse ---------------------------------------------------
+8 -2
View File
@@ -24,12 +24,12 @@ use crate::commons::api::publication;
use crate::commons::api::publication::Publish;
use crate::commons::api::{
Base64, HexEncodedHash, IssuanceRequest, ListReply, ParentHandle, RepositoryContact,
RequestResourceLimit, RouteAuthorization,
RequestResourceLimit, RoaDefinition,
};
use crate::commons::eventsourcing::AggregateHistory;
use crate::commons::remote::id::IdCert;
use crate::commons::util::ext_serde;
use crate::daemon::ca::{self, CertAuth, Signer};
use crate::daemon::ca::{self, CertAuth, RouteAuthorization, Signer};
//------------ ResourceClassName -------------------------------------------
@@ -617,6 +617,12 @@ impl From<&RouteAuthorization> for ObjectName {
}
}
impl From<&RoaDefinition> for ObjectName {
fn from(def: &RoaDefinition) -> Self {
ObjectName(format!("{}.roa", hex::encode(def.to_string())))
}
}
impl Into<Bytes> for ObjectName {
fn into(self) -> Bytes {
Bytes::from(self.0)
+218 -212
View File
@@ -1,15 +1,93 @@
use std::collections::HashSet;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::net::IpAddr;
use std::ops::Deref;
use std::str::FromStr;
use serde::de;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use rpki::resources::{AddressFamily, AsBlocks, AsId, IpBlocks, IpBlocksBuilder, Prefix};
use rpki::resources::{AsBlocks, AsId, IpBlocks, IpBlocksBuilder, Prefix};
use crate::commons::api::ca::ResourceSet;
use crate::commons::api::ResourceSet;
//------------ RoaDefinition -----------------------------------------------
/// This type defines the definition of a Route Origin Authorization (ROA), i.e.
/// the originating asn, IPv4 or IPv6 prefix, and optionally a max length.
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct RoaDefinition {
asn: AsNumber,
prefix: TypedPrefix,
#[serde(skip_serializing_if = "Option::is_none")]
max_length: Option<u8>,
}
impl RoaDefinition {
pub fn new(asn: AsNumber, prefix: TypedPrefix, max_length: Option<u8>) -> Self {
RoaDefinition {
asn,
prefix,
max_length,
}
}
pub fn asn(&self) -> AsNumber {
self.asn
}
pub fn prefix(&self) -> TypedPrefix {
self.prefix
}
pub fn max_length(&self) -> Option<u8> {
self.max_length
}
}
impl FromStr for RoaDefinition {
type Err = AuthorizationFmtError;
// "192.168.0.0/16 => 64496"
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s.split("=>");
let prefix_part = parts.next().ok_or_else(|| AuthorizationFmtError::auth(s))?;
let mut prefix_parts = prefix_part.split('-');
let prefix_str = prefix_parts
.next()
.ok_or_else(|| AuthorizationFmtError::auth(s))?;
let prefix = TypedPrefix::from_str(&prefix_str.trim())?;
let max_length = match prefix_parts.next() {
None => None,
Some(length_str) => {
Some(u8::from_str(&length_str.trim()).map_err(|_| AuthorizationFmtError::auth(s))?)
}
};
let asn_str = parts.next().ok_or_else(|| AuthorizationFmtError::auth(s))?;
if parts.next().is_some() {
return Err(AuthorizationFmtError::auth(s));
}
let origin = AsNumber::from_str(&asn_str.trim())?;
Ok(RoaDefinition {
asn: origin,
prefix,
max_length,
})
}
}
impl fmt::Display for RoaDefinition {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.max_length {
None => write!(f, "{} => {}", self.prefix, self.asn),
Some(length) => write!(f, "{}-{} => {}", self.prefix, length, self.asn),
}
}
}
//------------ RouteAuthorizationUpdates -----------------------------------
@@ -22,19 +100,19 @@ use crate::commons::api::ca::ResourceSet;
/// all authorisations for a given prefix are published together in order to
/// avoid invalidating announcements.
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct RouteAuthorizationUpdates {
added: HashSet<RouteAuthorization>,
removed: HashSet<RouteAuthorization>,
pub struct RoaDefinitionUpdates {
added: HashSet<RoaDefinition>,
removed: HashSet<RoaDefinition>,
}
impl RouteAuthorizationUpdates {
pub fn new(added: HashSet<RouteAuthorization>, removed: HashSet<RouteAuthorization>) -> Self {
RouteAuthorizationUpdates { added, removed }
impl RoaDefinitionUpdates {
pub fn new(added: HashSet<RoaDefinition>, removed: HashSet<RoaDefinition>) -> Self {
RoaDefinitionUpdates { added, removed }
}
/// Unpack this and return all added (left), and all removed (right) route
/// authorizations.
pub fn unpack(self) -> (HashSet<RouteAuthorization>, HashSet<RouteAuthorization>) {
pub fn unpack(self) -> (HashSet<RoaDefinition>, HashSet<RoaDefinition>) {
(self.added, self.removed)
}
@@ -42,25 +120,25 @@ impl RouteAuthorizationUpdates {
Self::default()
}
pub fn add(&mut self, add: RouteAuthorization) {
pub fn add(&mut self, add: RoaDefinition) {
self.added.insert(add);
}
pub fn remove(&mut self, rem: RouteAuthorization) {
pub fn remove(&mut self, rem: RoaDefinition) {
self.removed.insert(rem);
}
}
impl Default for RouteAuthorizationUpdates {
impl Default for RoaDefinitionUpdates {
fn default() -> Self {
RouteAuthorizationUpdates {
RoaDefinitionUpdates {
added: HashSet::new(),
removed: HashSet::new(),
}
}
}
impl fmt::Display for RouteAuthorizationUpdates {
impl fmt::Display for RoaDefinitionUpdates {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for a in &self.added {
writeln!(f, "A: {}", a)?;
@@ -72,7 +150,7 @@ impl fmt::Display for RouteAuthorizationUpdates {
}
}
impl FromStr for RouteAuthorizationUpdates {
impl FromStr for RoaDefinitionUpdates {
type Err = AuthorizationFmtError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
@@ -91,71 +169,87 @@ impl FromStr for RouteAuthorizationUpdates {
} else if line.starts_with("A:") {
let line = &line[2..];
let line = line.trim();
let auth = RouteAuthorization::from_str(line)?;
let auth = RoaDefinition::from_str(line)?;
added.insert(auth);
} else if line.starts_with("R:") {
let line = &line[2..];
let line = line.trim();
let auth = RouteAuthorization::from_str(line)?;
let auth = RoaDefinition::from_str(line)?;
removed.insert(auth);
} else {
return Err(AuthorizationFmtError::delta(line));
}
}
Ok(RouteAuthorizationUpdates { added, removed })
Ok(RoaDefinitionUpdates { added, removed })
}
}
//------------ RouteAuthorization ------------------------------------------
/// This type defines a prefix and optional maximum length (other than the
/// prefix length) which is to be authorized for the given origin ASN.
//------------ TypedPrefix -------------------------------------------------
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct RouteAuthorization {
origin: AsNumber,
prefix: RoaPrefix,
pub enum TypedPrefix {
V4(Ipv4Prefix),
V6(Ipv6Prefix),
}
impl RouteAuthorization {
pub fn new(origin: AsNumber, prefix: RoaPrefix) -> Self {
RouteAuthorization { origin, prefix }
impl TypedPrefix {
pub fn prefix(&self) -> &Prefix {
self.as_ref()
}
pub fn origin(&self) -> AsNumber {
self.origin
}
pub fn prefix(&self) -> RoaPrefix {
self.prefix
pub fn ip_addr(&self) -> IpAddr {
match self {
TypedPrefix::V4(v4) => IpAddr::V4(v4.0.to_v4()),
TypedPrefix::V6(v6) => IpAddr::V6(v6.0.to_v6()),
}
}
}
impl FromStr for RouteAuthorization {
impl FromStr for TypedPrefix {
type Err = AuthorizationFmtError;
// "192.168.0.0/16 => 64496"
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s.split("=>");
let prefix_str = parts.next().ok_or_else(|| AuthorizationFmtError::auth(s))?;
let asn_str = parts.next().ok_or_else(|| AuthorizationFmtError::auth(s))?;
if parts.next().is_some() {
return Err(AuthorizationFmtError::auth(s));
fn from_str(prefix: &str) -> Result<Self, Self::Err> {
if prefix.contains('.') {
Ok(TypedPrefix::V4(Ipv4Prefix(
Prefix::from_v4_str(prefix.trim())
.map_err(|_| AuthorizationFmtError::pfx(prefix))?,
)))
} else {
Ok(TypedPrefix::V6(Ipv6Prefix(
Prefix::from_v6_str(prefix.trim())
.map_err(|_| AuthorizationFmtError::pfx(prefix))?,
)))
}
let prefix = RoaPrefix::from_str(&prefix_str)?;
let origin = AsNumber::from_str(&asn_str)?;
Ok(RouteAuthorization { origin, prefix })
}
}
impl fmt::Display for RouteAuthorization {
impl fmt::Display for TypedPrefix {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} => {}", self.prefix, self.origin)
match self {
TypedPrefix::V4(pfx) => pfx.fmt(f),
TypedPrefix::V6(pfx) => pfx.fmt(f),
}
}
}
impl Serialize for RouteAuthorization {
impl AsRef<Prefix> for TypedPrefix {
fn as_ref(&self) -> &Prefix {
match self {
TypedPrefix::V4(v4) => &v4.0,
TypedPrefix::V6(v6) => &v6.0,
}
}
}
impl Deref for TypedPrefix {
type Target = Prefix;
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl Serialize for TypedPrefix {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
@@ -164,157 +258,54 @@ impl Serialize for RouteAuthorization {
}
}
impl<'de> Deserialize<'de> for RouteAuthorization {
fn deserialize<D>(d: D) -> Result<RouteAuthorization, D::Error>
impl<'de> Deserialize<'de> for TypedPrefix {
fn deserialize<D>(d: D) -> Result<TypedPrefix, D::Error>
where
D: Deserializer<'de>,
{
let string = String::deserialize(d)?;
RouteAuthorization::from_str(string.as_str()).map_err(de::Error::custom)
TypedPrefix::from_str(string.as_str()).map_err(de::Error::custom)
}
}
//------------ RoaPrefix ---------------------------------------------------
impl From<TypedPrefix> for ResourceSet {
fn from(tp: TypedPrefix) -> ResourceSet {
match tp {
TypedPrefix::V4(v4) => {
let mut builder = IpBlocksBuilder::new();
builder.push(v4.0);
let blocks = builder.finalize();
/// This type defines a ROA IPv4 or IPv6 prefix and optional max length.
#[derive(Clone, Copy, Debug)]
pub struct RoaPrefix {
prefix: Prefix,
max_length: Option<u8>,
family: AddressFamily,
}
ResourceSet::new(AsBlocks::empty(), blocks, IpBlocks::empty())
}
TypedPrefix::V6(v6) => {
let mut builder = IpBlocksBuilder::new();
builder.push(v6.0);
let blocks = builder.finalize();
impl RoaPrefix {
pub fn addr(&self) -> IpAddr {
match self.family {
AddressFamily::Ipv4 => IpAddr::V4(self.prefix.to_v4()),
AddressFamily::Ipv6 => IpAddr::V6(self.prefix.to_v6()),
}
}
pub fn length(&self) -> u8 {
self.prefix.addr_len()
}
pub fn max_length(&self) -> Option<u8> {
self.max_length
}
}
impl From<RoaPrefix> for ResourceSet {
fn from(pfx: RoaPrefix) -> Self {
let mut builder = IpBlocksBuilder::new();
builder.push(pfx.prefix);
let blocks = builder.finalize();
match pfx.family {
AddressFamily::Ipv4 => ResourceSet::new(AsBlocks::empty(), blocks, IpBlocks::empty()),
AddressFamily::Ipv6 => ResourceSet::new(AsBlocks::empty(), IpBlocks::empty(), blocks),
ResourceSet::new(AsBlocks::empty(), IpBlocks::empty(), blocks)
}
}
}
}
impl Hash for RoaPrefix {
fn hash<H: Hasher>(&self, state: &mut H) {
self.prefix.hash(state);
self.max_length.hash(state);
match self.family {
AddressFamily::Ipv4 => 1.hash(state),
AddressFamily::Ipv6 => 2.hash(state),
}
}
}
//------------ Ipv4Prefix --------------------------------------------------
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Ipv4Prefix(Prefix);
impl PartialEq for RoaPrefix {
fn eq(&self, other: &RoaPrefix) -> bool {
self.prefix == other.prefix
&& self.max_length == other.max_length
&& self.family == other.family
}
}
impl Eq for RoaPrefix {}
impl fmt::Display for RoaPrefix {
impl fmt::Display for Ipv4Prefix {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let add = self.prefix.addr();
let add_str = match self.family {
AddressFamily::Ipv4 => add.to_v4().to_string(),
AddressFamily::Ipv6 => add.to_v6().to_string(),
};
match self.max_length {
None => write!(f, "{}/{}", add_str, self.prefix.addr_len()),
Some(max) => write!(f, "{}/{}-{}", add_str, self.prefix.addr_len(), max),
}
write!(f, "{}/{}", self.0.to_v4(), self.0.addr_len())
}
}
impl FromStr for RoaPrefix {
type Err = AuthorizationFmtError;
//------------ Ipv6Prefix --------------------------------------------------
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Ipv6Prefix(Prefix);
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim();
let mut parts = s.split('-');
let prefix = parts.next().ok_or_else(|| AuthorizationFmtError::pfx(s))?;
let family = if s.contains('.') {
AddressFamily::Ipv4
} else if s.contains(':') {
AddressFamily::Ipv6
} else {
return Err(AuthorizationFmtError::pfx(s));
};
let prefix = match family {
AddressFamily::Ipv4 => {
Prefix::from_v4_str(prefix).map_err(|_| AuthorizationFmtError::pfx(s))
}
AddressFamily::Ipv6 => {
Prefix::from_v6_str(prefix).map_err(|_| AuthorizationFmtError::pfx(s))
}
}
.map_err(|_| AuthorizationFmtError::pfx(s))?;
let max_length = match parts.next() {
None => None,
Some(s) => Some(u8::from_str(s).map_err(|_| AuthorizationFmtError::pfx(s))?),
};
if let Some(max) = max_length {
let too_long = match family {
AddressFamily::Ipv4 => max > 32,
AddressFamily::Ipv6 => max > 128,
};
if max < prefix.addr_len() || too_long {
return Err(AuthorizationFmtError::pfx(s));
}
}
Ok(RoaPrefix {
prefix,
max_length,
family,
})
}
}
impl Serialize for RoaPrefix {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.to_string().serialize(serializer)
}
}
impl<'de> Deserialize<'de> for RoaPrefix {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let string = String::deserialize(deserializer)?;
RoaPrefix::from_str(string.as_str()).map_err(de::Error::custom)
impl fmt::Display for Ipv6Prefix {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}/{}", self.0.to_v6(), self.0.addr_len())
}
}
@@ -377,11 +368,11 @@ impl AuthorizationFmtError {
AuthorizationFmtError::Asn(s.to_string())
}
fn auth(s: &str) -> Self {
pub fn auth(s: &str) -> Self {
AuthorizationFmtError::Auth(s.to_string())
}
fn delta(s: &str) -> Self {
pub fn delta(s: &str) -> Self {
AuthorizationFmtError::Delta(s.to_string())
}
}
@@ -392,26 +383,6 @@ impl AuthorizationFmtError {
mod tests {
use super::*;
#[test]
fn parse_roa_prefix() {
assert!(RoaPrefix::from_str("192.168.0.0/16").is_ok());
assert!(RoaPrefix::from_str("192.168.0.0/16-16").is_ok());
assert!(RoaPrefix::from_str("192.168.0.0/16-24").is_ok());
assert!(RoaPrefix::from_str("192.168.0.0/16-15").is_err());
assert!(RoaPrefix::from_str("192.168.0.0/16-33").is_err());
}
#[test]
fn parse_route_authorization() {
fn parse_encode_authorization(s: &str) {
let authz = RouteAuthorization::from_str(s).unwrap();
assert_eq!(s, authz.to_string().as_str());
}
parse_encode_authorization("192.168.0.0/16 => 64496");
parse_encode_authorization("192.168.0.0/16-24 => 64496");
}
#[test]
fn parse_delta() {
let delta = concat!(
@@ -425,19 +396,54 @@ mod tests {
let expected = {
let mut added = HashSet::new();
added.insert(RouteAuthorization::from_str("192.168.0.0/16 => 64496").unwrap());
added.insert(RouteAuthorization::from_str("192.168.1.0/24 => 64496").unwrap());
added.insert(RoaDefinition::from_str("192.168.0.0/16 => 64496").unwrap());
added.insert(RoaDefinition::from_str("192.168.1.0/24 => 64496").unwrap());
let mut removed = HashSet::new();
removed.insert(RouteAuthorization::from_str("192.168.3.0/24 => 64496").unwrap());
RouteAuthorizationUpdates::new(added, removed)
removed.insert(RoaDefinition::from_str("192.168.3.0/24 => 64496").unwrap());
RoaDefinitionUpdates::new(added, removed)
};
let parsed = RouteAuthorizationUpdates::from_str(delta).unwrap();
let parsed = RoaDefinitionUpdates::from_str(delta).unwrap();
assert_eq!(expected, parsed);
let reparsed = RouteAuthorizationUpdates::from_str(&parsed.to_string()).unwrap();
let reparsed = RoaDefinitionUpdates::from_str(&parsed.to_string()).unwrap();
assert_eq!(parsed, reparsed);
}
#[test]
fn parse_type_prefix() {
assert!(TypedPrefix::from_str("192.168.0.0/16").is_ok());
assert!(TypedPrefix::from_str("2001:db8::/32").is_ok());
}
#[test]
fn normalize_roa_definition_json() {
let def = RoaDefinition::from_str("192.168.0.0/16 => 64496").unwrap();
let json = serde_json::to_string(&def).unwrap();
let expected = "{\"asn\":64496,\"prefix\":\"192.168.0.0/16\"}";
assert_eq!(json, expected);
let def = RoaDefinition::from_str("192.168.0.0/16-24 => 64496").unwrap();
let json = serde_json::to_string(&def).unwrap();
let expected = "{\"asn\":64496,\"prefix\":\"192.168.0.0/16\",\"max_length\":24}";
assert_eq!(json, expected);
}
#[test]
fn serde_roa_definition() {
fn parse_ser_de_print_definition(s: &str) {
let def = RoaDefinition::from_str(s).unwrap();
let ser = serde_json::to_string(&def).unwrap();
let de = serde_json::from_str(&ser).unwrap();
assert_eq!(def, de);
assert_eq!(s, de.to_string().as_str())
}
parse_ser_de_print_definition("192.168.0.0/16 => 64496");
parse_ser_de_print_definition("192.168.0.0/16-24 => 64496");
parse_ser_de_print_definition("2001:db8::/32 => 64496");
parse_ser_de_print_definition("2001:db8::/32-48 => 64496");
}
}
+2 -2
View File
@@ -16,7 +16,7 @@ use crate::commons::api::{
self, CertAuthInfo, ChildHandle, EntitlementClass, Entitlements, Handle, IssuanceRequest,
IssuedCert, ObjectsDelta, ParentCaContact, ParentHandle, RcvdCert, RepositoryContact,
RequestResourceLimit, ResourceClassName, ResourceSet, RevocationRequest, RevocationResponse,
RouteAuthorization, RouteAuthorizationUpdates, SigningCert, UpdateChildRequest,
SigningCert, UpdateChildRequest,
};
use crate::commons::eventsourcing::{Aggregate, StoredEvent};
use crate::commons::remote::builder::{IdCertBuilder, SignedMessageBuilder};
@@ -30,7 +30,7 @@ use crate::daemon::ca::rc::PublishMode;
use crate::daemon::ca::signing::CsrInfo;
use crate::daemon::ca::{
self, ta_handle, ChildDetails, Cmd, CmdDet, CurrentObjectSetDelta, Error, Evt, EvtDet, Ini,
ResourceClass, Result, Routes, Signer,
ResourceClass, Result, RouteAuthorization, RouteAuthorizationUpdates, Routes, Signer,
};
//------------ Rfc8183Id ---------------------------------------------------
+2 -2
View File
@@ -6,11 +6,11 @@ use chrono::Duration;
use crate::commons::api::{
ChildHandle, Entitlements, Handle, IssuanceRequest, ParentCaContact, ParentHandle, RcvdCert,
RepositoryContact, ResourceClassName, ResourceSet, RevocationRequest, RevocationResponse,
RouteAuthorizationUpdates, UpdateChildRequest,
UpdateChildRequest,
};
use crate::commons::eventsourcing;
use crate::commons::remote::id::IdCert;
use crate::daemon::ca::{Evt, Signer};
use crate::daemon::ca::{Evt, RouteAuthorizationUpdates, Signer};
//------------ Command -----------------------------------------------------
+2 -1
View File
@@ -3,10 +3,11 @@ use std::{fmt, io};
use rpki::crypto::KeyIdentifier;
use crate::commons::api::{Handle, RouteAuthorization};
use crate::commons::api::Handle;
use crate::commons::eventsourcing::AggregateStoreError;
use crate::commons::remote::rfc6492;
use crate::commons::util::httpclient;
use crate::daemon::ca::RouteAuthorization;
//------------ Error ---------------------------------------------------------
+3 -3
View File
@@ -11,15 +11,15 @@ use rpki::x509::{Serial, Time, Validity};
use crate::commons::api::{
AddedObject, ChildHandle, CurrentObject, Handle, IssuanceRequest, IssuedCert, ObjectName,
ObjectsDelta, ParentCaContact, ParentHandle, RcvdCert, RepoInfo, RepositoryContact,
ResourceClassName, ResourceSet, Revocation, RevocationRequest, RevokedObject,
RouteAuthorization, TaCertDetails, TrustAnchorLocator, UpdatedObject, WithdrawnObject,
ResourceClassName, ResourceSet, Revocation, RevocationRequest, RevokedObject, TaCertDetails,
TrustAnchorLocator, UpdatedObject, WithdrawnObject,
};
use crate::commons::eventsourcing::StoredEvent;
use crate::commons::remote::id::IdCert;
use crate::daemon::ca::signing::Signer;
use crate::daemon::ca::{
CertifiedKey, ChildDetails, CurrentObjectSetDelta, Error, ResourceClass, Result, Rfc8183Id,
RoaInfo,
RoaInfo, RouteAuthorization,
};
//------------ Ini -----------------------------------------------------------
+2 -2
View File
@@ -12,9 +12,9 @@ use rpki::x509::{Serial, Time, Validity};
use crate::commons::api::{
AddedObject, CurrentObject, HexEncodedHash, IssuedCert, ObjectName, ObjectsDelta, RcvdCert,
Revocation, Revocations, RevocationsDelta, RouteAuthorization, UpdatedObject, WithdrawnObject,
Revocation, Revocations, RevocationsDelta, UpdatedObject, WithdrawnObject,
};
use crate::daemon::ca::{self, RoaInfo, Signer};
use crate::daemon::ca::{self, RoaInfo, RouteAuthorization, Signer};
//------------ AddedOrUpdated ----------------------------------------------
+2 -2
View File
@@ -13,14 +13,14 @@ use crate::commons::api::{
AddedObject, CurrentObject, CurrentObjects, EntitlementClass, HexEncodedHash, IssuanceRequest,
IssuedCert, ObjectName, ObjectsDelta, ParentHandle, RcvdCert, ReplacedObject, RepoInfo,
RequestResourceLimit, ResourceClassInfo, ResourceClassName, ResourceSet, Revocation,
RevocationRequest, RevokedObject, RouteAuthorization, UpdatedObject, WithdrawnObject,
RevocationRequest, RevokedObject, UpdatedObject, WithdrawnObject,
};
use crate::daemon::ca::events::{ChildCertificateUpdates, RoaUpdates};
use crate::daemon::ca::signing::CsrInfo;
use crate::daemon::ca::{
self, ta_handle, AddedOrUpdated, CertifiedKey, ChildCertificates, CrlBuilder, CurrentKey,
CurrentObjectSetDelta, Error, EvtDet, KeyState, ManifestBuilder, NewKey, OldKey, PendingKey,
Result, RoaInfo, Roas, SignSupport, Signer,
Result, RoaInfo, Roas, RouteAuthorization, SignSupport, Signer,
};
//------------ ResourceClass -----------------------------------------------
+132 -4
View File
@@ -1,14 +1,115 @@
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::fmt;
use std::ops::Deref;
use std::str::FromStr;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use rpki::roa::{Roa, RoaBuilder};
use rpki::sigobj::SignedObjectBuilder;
use rpki::uri;
use rpki::x509::{Serial, Time};
use crate::commons::api::{ObjectName, ReplacedObject, RouteAuthorization};
use crate::commons::api::{ObjectName, ReplacedObject, RoaDefinition, RoaDefinitionUpdates};
use crate::daemon::ca::events::RoaUpdates;
use crate::daemon::ca::{self, CertifiedKey, SignSupport, Signer};
//------------ RouteAuthorization ------------------------------------------
/// This type defines a prefix and optional maximum length (other than the
/// prefix length) which is to be authorized for the given origin ASN.
#[derive(Clone, Copy, Debug, Display, Eq, Hash, PartialEq)]
pub struct RouteAuthorization(RoaDefinition);
impl RouteAuthorization {
pub fn new(definition: RoaDefinition) -> Self {
RouteAuthorization(definition)
}
}
impl AsRef<RoaDefinition> for RouteAuthorization {
fn as_ref(&self) -> &RoaDefinition {
&self.0
}
}
impl Deref for RouteAuthorization {
type Target = RoaDefinition;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Serialize for RouteAuthorization {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.to_string().serialize(s)
}
}
impl<'de> Deserialize<'de> for RouteAuthorization {
fn deserialize<D>(d: D) -> Result<RouteAuthorization, D::Error>
where
D: Deserializer<'de>,
{
let string = String::deserialize(d)?;
let def = RoaDefinition::from_str(string.as_str()).map_err(de::Error::custom)?;
Ok(RouteAuthorization(def))
}
}
impl From<RoaDefinition> for RouteAuthorization {
fn from(def: RoaDefinition) -> Self {
RouteAuthorization(def)
}
}
//------------ RouteAuthorizationUpdates -----------------------------------
///
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct RouteAuthorizationUpdates {
added: HashSet<RouteAuthorization>,
removed: HashSet<RouteAuthorization>,
}
impl RouteAuthorizationUpdates {
pub fn unpack(self) -> (HashSet<RouteAuthorization>, HashSet<RouteAuthorization>) {
(self.added, self.removed)
}
}
impl From<RoaDefinitionUpdates> for RouteAuthorizationUpdates {
fn from(definitions: RoaDefinitionUpdates) -> Self {
let (added, removed) = definitions.unpack();
let added = added.into_iter().map(RoaDefinition::into).collect();
let removed = removed.into_iter().map(RoaDefinition::into).collect();
RouteAuthorizationUpdates { added, removed }
}
}
impl fmt::Display for RouteAuthorizationUpdates {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if !self.added.is_empty() {
write!(f, "added:")?;
for a in &self.added {
write!(f, " {}", a)?;
}
write!(f, " ")?;
}
if !self.removed.is_empty() {
write!(f, "removed:")?;
for r in &self.removed {
write!(f, " {}", r)?;
}
}
Ok(())
}
}
//------------ Routes ------------------------------------------------------
/// The current authorizations and corresponding meta-information for a CA.
@@ -181,8 +282,8 @@ impl Roas {
let signing_key = certified_key.key_id();
let mut roa_builder = RoaBuilder::new(auth.origin().into());
roa_builder.push_addr(prefix.addr(), prefix.length(), prefix.max_length());
let mut roa_builder = RoaBuilder::new(auth.asn().into());
roa_builder.push_addr(prefix.ip_addr(), prefix.addr_len(), auth.max_length());
let mut object_builder = SignedObjectBuilder::new(
Serial::random(signer).map_err(ca::Error::signer)?,
SignSupport::sign_validity_year(),
@@ -198,3 +299,30 @@ impl Roas {
.map_err(ca::Error::signer)
}
}
//------------ Tests -------------------------------------------------------
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serde_route_authorization() {
fn parse_encode_authorization(s: &str) {
let def = RoaDefinition::from_str(s).unwrap();
let auth = RouteAuthorization(def);
let json = serde_json::to_string(&auth).unwrap();
assert_eq!(format!("\"{}\"", s), json);
let des: RouteAuthorization = serde_json::from_str(&json).unwrap();
assert_eq!(des, auth);
}
parse_encode_authorization("192.168.0.0/16 => 64496");
parse_encode_authorization("192.168.0.0/16-24 => 64496");
parse_encode_authorization("2001:db8::/32 => 64496");
parse_encode_authorization("2001:db8::/32-48 => 64496");
}
}
+3 -2
View File
@@ -15,7 +15,7 @@ use crate::commons::api::{
ChildHandle, Entitlements, Handle, IssuanceRequest, IssuanceResponse, IssuedCert, ListReply,
ParentCaContact, ParentCaReq, ParentHandle, PublishDelta, RcvdCert, RepoInfo,
RepositoryContact, ResourceClassName, ResourceSet, RevocationRequest, RevocationResponse,
RouteAuthorizationUpdates, UpdateChildRequest,
UpdateChildRequest,
};
use crate::commons::eventsourcing::{Aggregate, AggregateStore, Command, DiskAggregateStore};
use crate::commons::remote::builder::SignedMessageBuilder;
@@ -24,7 +24,8 @@ use crate::commons::remote::sigmsg::SignedMessage;
use crate::commons::remote::{rfc6492, rfc8181, rfc8183};
use crate::commons::util::httpclient;
use crate::daemon::ca::{
self, ta_handle, CertAuth, Cmd, CmdDet, IniDet, ServerError, ServerResult, Signer,
self, ta_handle, CertAuth, Cmd, CmdDet, IniDet, RouteAuthorizationUpdates, ServerError,
ServerResult, Signer,
};
use crate::daemon::mq::EventQueueListener;
+2 -2
View File
@@ -8,7 +8,7 @@ use serde::Serialize;
use crate::commons::api::rrdp::VerificationError;
use crate::commons::api::{
AddChildRequest, CertAuthInit, ErrorCode, ErrorResponse, Handle, ParentCaContact, ParentCaReq,
ParentHandle, PublisherHandle, PublisherList, RepositoryUpdate, RouteAuthorizationUpdates,
ParentHandle, PublisherHandle, PublisherList, RepositoryUpdate, RoaDefinitionUpdates,
UpdateChildRequest,
};
use crate::commons::remote::sigmsg::SignedMessage;
@@ -450,7 +450,7 @@ pub fn ca_routes_update(
server: web::Data<AppServer>,
auth: Auth,
handle: Path<Handle>,
updates: Json<RouteAuthorizationUpdates>,
updates: Json<RoaDefinitionUpdates>,
) -> HttpResponse {
if_api_allowed(&server, &auth, || {
render_empty_res(
+3 -3
View File
@@ -12,7 +12,7 @@ use crate::commons::api::{
AddChildRequest, CaRepoDetails, CertAuthHistory, CertAuthInfo, CertAuthInit, CertAuthList,
ChildCaInfo, ChildHandle, CurrentRepoState, Handle, ListReply, ParentCaContact, ParentCaReq,
ParentHandle, PublishDelta, PublisherDetails, PublisherHandle, RepoInfo, RepositoryContact,
RepositoryUpdate, RouteAuthorizationUpdates, TaCertDetails, Token, UpdateChildRequest,
RepositoryUpdate, RoaDefinitionUpdates, TaCertDetails, Token, UpdateChildRequest,
};
use crate::commons::remote::rfc8183;
use crate::commons::remote::sigmsg::SignedMessage;
@@ -458,8 +458,8 @@ impl KrillServer {
/// # Handle route authorization requests
///
impl KrillServer {
pub fn ca_routes_update(&self, handle: Handle, updates: RouteAuthorizationUpdates) -> EmptyRes {
Ok(self.caserver.ca_routes_update(handle, updates)?)
pub fn ca_routes_update(&self, handle: Handle, updates: RoaDefinitionUpdates) -> EmptyRes {
Ok(self.caserver.ca_routes_update(handle, updates.into())?)
}
}
+3 -6
View File
@@ -11,7 +11,7 @@ use crate::cli::{Error, KrillClient};
use crate::commons::api::{
AddChildRequest, CertAuthInfo, CertAuthInit, CertifiedKeyInfo, ChildAuthRequest, ChildHandle,
Handle, ParentCaContact, ParentCaReq, ParentHandle, Publish, PublisherDetails, PublisherHandle,
ResourceClassKeysInfo, ResourceClassName, ResourceSet, RouteAuthorizationUpdates,
ResourceClassKeysInfo, ResourceClassName, ResourceSet, RoaDefinitionUpdates,
UpdateChildRequest,
};
use crate::commons::remote::rfc8183;
@@ -280,17 +280,14 @@ pub fn ca_roll_activate(handle: &Handle) {
)));
}
pub fn ca_route_authorizations_update(handle: &Handle, updates: RouteAuthorizationUpdates) {
pub fn ca_route_authorizations_update(handle: &Handle, updates: RoaDefinitionUpdates) {
krill_admin(Command::CertAuth(CaCommand::RouteAuthorizationsUpdate(
handle.clone(),
updates,
)));
}
pub fn ca_route_authorizations_update_expect_error(
handle: &Handle,
updates: RouteAuthorizationUpdates,
) {
pub fn ca_route_authorizations_update_expect_error(handle: &Handle, updates: RoaDefinitionUpdates) {
krill_admin_expect_error(Command::CertAuth(CaCommand::RouteAuthorizationsUpdate(
handle.clone(),
updates,