use std::{fmt, str::FromStr};
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use rpki::{
ca::{csr::BgpsecCsr, publication::Base64},
crypto::KeyIdentifier,
repository::resources::Asn,
};
use super::ObjectName;
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct BgpSecDefinition {
asn: Asn,
csr: BgpsecCsr,
}
impl BgpSecDefinition {
pub fn new(asn: Asn, csr: BgpsecCsr) -> Self {
BgpSecDefinition { asn, csr }
}
pub fn asn(&self) -> Asn {
self.asn
}
pub fn csr(&self) -> &BgpsecCsr {
&self.csr
}
}
impl PartialEq for BgpSecDefinition {
fn eq(&self, other: &Self) -> bool {
self.asn == other.asn && self.csr.to_captured().as_slice() == other.csr.to_captured().as_slice()
}
}
impl Eq for BgpSecDefinition {}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct BgpSecAsnKey {
asn: Asn,
key: KeyIdentifier,
}
impl BgpSecAsnKey {
pub fn new(asn: Asn, key: KeyIdentifier) -> Self {
BgpSecAsnKey { asn, key }
}
pub fn asn(&self) -> Asn {
self.asn
}
pub fn key_identifier(&self) -> KeyIdentifier {
self.key
}
}
impl fmt::Display for BgpSecAsnKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ROUTER-{:08X}-{}", self.asn.into_u32(), self.key)
}
}
impl From<&BgpSecDefinition> for BgpSecAsnKey {
fn from(def: &BgpSecDefinition) -> Self {
BgpSecAsnKey {
asn: def.asn(),
key: def.csr().public_key().key_identifier(),
}
}
}
impl FromStr for BgpSecAsnKey {
type Err = BgpSecAsnKeyFmtError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.strip_prefix("ROUTER-").ok_or(BgpSecAsnKeyFmtError)?;
let parts: Vec<_> = s.split('-').collect();
if parts.len() != 2 {
return Err(BgpSecAsnKeyFmtError);
}
let asn_hex = parts.first().ok_or(BgpSecAsnKeyFmtError)?;
let key_id_str = parts.get(1).ok_or(BgpSecAsnKeyFmtError)?;
let asn_nr = u32::from_str_radix(asn_hex, 16).map_err(|_| BgpSecAsnKeyFmtError)?;
let asn = Asn::from_u32(asn_nr);
let key = KeyIdentifier::from_str(key_id_str).map_err(|_| BgpSecAsnKeyFmtError)?;
Ok(BgpSecAsnKey { asn, key })
}
}
#[derive(Clone, Debug)]
pub struct BgpSecAsnKeyFmtError;
impl std::error::Error for BgpSecAsnKeyFmtError {}
impl fmt::Display for BgpSecAsnKeyFmtError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Invalid BGPSec ASN and Key format. Expected: ROUTER-<hex-encoded-asn>-<hex-encoded-key-identifier>"
)
}
}
impl Serialize for BgpSecAsnKey {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.to_string().serialize(s)
}
}
impl<'de> Deserialize<'de> for BgpSecAsnKey {
fn deserialize<D>(d: D) -> Result<BgpSecAsnKey, D::Error>
where
D: Deserializer<'de>,
{
let string = String::deserialize(d)?;
BgpSecAsnKey::from_str(string.as_str()).map_err(de::Error::custom)
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct BgpSecDefinitionUpdates {
add: Vec<BgpSecDefinition>,
remove: Vec<BgpSecAsnKey>,
}
impl BgpSecDefinitionUpdates {
pub fn new(add: Vec<BgpSecDefinition>, remove: Vec<BgpSecAsnKey>) -> Self {
BgpSecDefinitionUpdates { add, remove }
}
pub fn unpack(self) -> (Vec<BgpSecDefinition>, Vec<BgpSecAsnKey>) {
(self.add, self.remove)
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct BgpSecCsrInfo {
asn: Asn,
key_identifier: KeyIdentifier,
csr: Base64,
}
impl BgpSecCsrInfo {
pub fn new(asn: Asn, key_identifier: KeyIdentifier, csr: Base64) -> Self {
BgpSecCsrInfo {
asn,
key_identifier,
csr,
}
}
pub fn asn(&self) -> Asn {
self.asn
}
pub fn key_identifier(&self) -> KeyIdentifier {
self.key_identifier
}
pub fn csr(&self) -> &Base64 {
&self.csr
}
pub fn object_name(&self) -> ObjectName {
ObjectName::bgpsec(self.asn, self.key_identifier)
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct BgpSecCsrInfoList(Vec<BgpSecCsrInfo>);
impl BgpSecCsrInfoList {
pub fn new(list: Vec<BgpSecCsrInfo>) -> Self {
BgpSecCsrInfoList(list)
}
pub fn unpack(self) -> Vec<BgpSecCsrInfo> {
self.0
}
}
impl fmt::Display for BgpSecCsrInfoList {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "ASN, key identifier, CSR base64")?;
for info in self.0.iter() {
writeln!(f, "{}, {}, {}", info.asn, info.key_identifier, info.csr)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use super::BgpSecAsnKey;
#[test]
fn bgp_sec_to_from_str() {
let string = "ROUTER-0000FDE8-17316903F0671229E8808BA8E8AB0105FA915A07";
let key = BgpSecAsnKey::from_str(string).unwrap();
let to_string = key.to_string();
assert_eq!(string, &to_string);
}
}