Sign BGP router certificates (#827)

This commit is contained in:
Tim Bruijnzeels
2022-07-07 13:20:00 +02:00
committed by GitHub
parent 4605bf93da
commit d68c07b59c
53 changed files with 1939 additions and 217 deletions
Generated
+2 -2
View File
@@ -1624,8 +1624,8 @@ dependencies = [
[[package]]
name = "rpki"
version = "0.14.3-dev"
source = "git+https://github.com/NLnetLabs/rpki-rs/#61ce3bf5817d5c62eb38267b4bcdd1a7112d1f38"
version = "0.15.0-dev"
source = "git+https://github.com/NLnetLabs/rpki-rs/#666e09357493aec6f0dc030f0e387c756423e2cb"
dependencies = [
"base64",
"bcder",
+1 -1
View File
@@ -51,7 +51,7 @@ regex = { version = "1.5.5", optional = true, default_features =
reqwest = { version = "0.11", features = ["json"] }
rpassword = { version = "^5.0", optional = true }
# rpki = { version = "0.13.2", features = [ "compat", "repository", "rrdp", "serde" ] }
rpki = { version = "0.14.3-dev", git = "https://github.com/NLnetLabs/rpki-rs/", features = [ "ca", "rrdp" ] }
rpki = { version = "0.15.0-dev", git = "https://github.com/NLnetLabs/rpki-rs/", features = [ "ca", "rrdp" ] }
# rpki = { version = "0.14.3-dev", path = "../rpki-rs/", features = [ "ca", "repository", "rrdp" ] }
scrypt = { version = "^0.6", optional = true, default-features = false }
serde = { version = "^1.0", features = ["derive"] }
+17
View File
@@ -21,6 +21,23 @@ For more information please refer to the [documentation](https://krill.docs.nlne
# Changelog
## 0.10.0
In this release we introduce the following feature:
- BGPSec Router Certificate Signing (CLI/API only)
## 0.9.6 'Newer ROAs Please'
This release fixes an issue introduced in 0.9.5 where the background job to
automatically renew ROAs was not added to Krill's task queue on startup. Thanks
to Alberto Leiva for finding this issue!
All users who upgraded to 0.9.5 are advised to upgrade to this version as soon
as possible. Not doing so can lead to ROAs expiring and becoming invalid. If you
did not upgrade to 0.9.5 you are not affected by this issue.
This release contains no other changes.
## 0.9.5 'Have You considered these Upgrades?'
This release is primarily intended to improve support for migrations of pre-0.9.0
+3
View File
@@ -70,6 +70,7 @@ role_allow("readonly", action: Permission) if
ROUTES_ANALYSIS,
ASPAS_READ,
ASPAS_ANALYSIS,
BGPSEC_READ,
RTA_LIST,
RTA_READ
];
@@ -101,6 +102,8 @@ role_allow("readwrite", action: Permission) if
ASPAS_READ,
ASPAS_UPDATE,
ASPAS_ANALYSIS,
BGPSEC_READ,
BGPSEC_UPDATE,
RTA_LIST,
RTA_READ,
RTA_UPDATE
+23 -3
View File
@@ -11,9 +11,9 @@ use crate::{
},
commons::{
api::{
AllCertAuthIssues, AspaDefinitionUpdates, CaRepoDetails, CertAuthIssues, ChildCaInfo,
ChildrenConnectionStats, ParentCaContact, ParentStatuses, PublisherDetails, PublisherList, RepoStatus,
Token,
AllCertAuthIssues, AspaDefinitionUpdates, BgpSecDefinitionUpdates, CaRepoDetails, CertAuthIssues,
ChildCaInfo, ChildrenConnectionStats, ParentCaContact, ParentStatuses, PublisherDetails, PublisherList,
RepoStatus, Token,
},
bgp::BgpAnalysisAdvice,
error::KrillIoError,
@@ -335,6 +335,26 @@ impl KrillClient {
Ok(ApiResponse::BgpAnalysisSuggestions(suggestions))
}
CaCommand::BgpSecList(handle) => {
let uri = format!("api/v1/cas/{}/bgpsec", handle);
let bgpsec_list = get_json(&self.server, &self.token, &uri).await?;
Ok(ApiResponse::BgpSecDefinitions(bgpsec_list))
}
CaCommand::BgpSecAdd(handle, addition) => {
let uri = format!("api/v1/cas/{}/bgpsec", handle);
let update = BgpSecDefinitionUpdates::new(vec![addition], vec![]);
post_json(&self.server, &self.token, &uri, update).await?;
Ok(ApiResponse::Empty)
}
CaCommand::BgpSecRemove(handle, removal) => {
let uri = format!("api/v1/cas/{}/bgpsec", handle);
let update = BgpSecDefinitionUpdates::new(vec![], vec![removal]);
post_json(&self.server, &self.token, &uri, update).await?;
Ok(ApiResponse::Empty)
}
CaCommand::AspasList(handle) => {
let uri = format!("api/v1/cas/{}/aspas", handle);
let aspas = get_json(&self.server, &self.token, &uri).await?;
+148 -4
View File
@@ -13,14 +13,15 @@ use clap::{App, Arg, ArgMatches, SubCommand};
use rpki::{
ca::{
csr::BgpsecCsr,
idcert::IdCert,
idexchange,
idexchange::{CaHandle, ChildHandle, ParentHandle, PublisherHandle},
},
crypto::KeyIdentifier,
repository::{
aspa::{DuplicateProviderAs, ProviderAs},
crypto::KeyIdentifier,
resources::ResourceSet,
resources::{Asn, ResourceSet},
x509::Time,
},
uri,
@@ -31,8 +32,9 @@ use crate::{
commons::{
api::{
AddChildRequest, AspaCustomer, AspaDefinition, AspaDefinitionFormatError, AspaProvidersUpdate,
AuthorizationFmtError, CertAuthInit, ParentCaContact, ParentCaReq, PublicationServerUris,
RepositoryContact, RoaDefinition, RoaDefinitionUpdates, RtaName, Token, UpdateChildRequest,
AuthorizationFmtError, BgpSecAsnKey, BgpSecDefinition, CertAuthInit, ParentCaContact, ParentCaReq,
PublicationServerUris, RepositoryContact, RoaDefinition, RoaDefinitionUpdates, RtaName, Token,
UpdateChildRequest,
},
crypto::SignSupport,
error::KrillIoError,
@@ -741,6 +743,77 @@ impl Options {
app.subcommand(sub)
}
fn make_cas_bgpsec_list_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
let mut sub = SubCommand::with_name("list").about("Show current BGPSec configurations");
sub = Self::add_general_args(sub);
sub = Self::add_my_ca_arg(sub);
app.subcommand(sub)
}
fn make_cas_bgpsec_add_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
let mut sub = SubCommand::with_name("add").about("Add BGPSec configurations");
sub = Self::add_general_args(sub);
sub = Self::add_my_ca_arg(sub);
sub = sub
.arg(
Arg::with_name("asn")
.short("a")
.long("asn")
.value_name("ASN")
.help("The ASN of the router for the key used in the CSR. E.g. AS65000")
.required(true),
)
.arg(
Arg::with_name("csr")
.long("csr")
.value_name("CSR")
.help("The file containing the DER encoded Certificate Sign Request")
.required(true),
);
app.subcommand(sub)
}
fn make_cas_bgpsec_remove_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
let mut sub = SubCommand::with_name("remove").about("Remove a BGPSec definition");
sub = Self::add_general_args(sub);
sub = Self::add_my_ca_arg(sub);
sub = sub
.arg(
Arg::with_name("asn")
.short("a")
.long("asn")
.value_name("ASN")
.help("The ASN used in the BGPSec definition. E.g. AS65000")
.required(true),
)
.arg(
Arg::with_name("key")
.long("key")
.value_name("key")
.help("The hex encoded key identifier used in the BGPSec definition")
.required(true),
);
app.subcommand(sub)
}
fn make_cas_bgpsec_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
let mut sub = SubCommand::with_name("bgpsec").about("Manage BGPSec certificates");
sub = Self::make_cas_bgpsec_list_sc(sub);
sub = Self::make_cas_bgpsec_add_sc(sub);
sub = Self::make_cas_bgpsec_remove_sc(sub);
app.subcommand(sub)
}
#[cfg(feature = "aspa")]
fn make_cas_aspas_add_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
let mut sub = SubCommand::with_name("add").about("Add or replace an ASPA configuration");
@@ -1269,6 +1342,7 @@ impl Options {
app = Self::make_cas_parents_sc(app);
app = Self::make_cas_keyroll_sc(app);
app = Self::make_cas_routes_sc(app);
app = Self::make_cas_bgpsec_sc(app);
app = Self::make_cas_repo_sc(app);
app = Self::make_cas_issues_sc(app);
app = Self::make_pubserver_sc(app);
@@ -1824,6 +1898,69 @@ impl Options {
}
}
fn parse_matches_cas_bgpsec_list(matches: &ArgMatches) -> Result<Options, Error> {
let general_args = GeneralArgs::from_matches(matches)?;
let my_ca = Self::parse_my_ca(matches)?;
let command = Command::CertAuth(CaCommand::BgpSecList(my_ca));
Ok(Options::make(general_args, command))
}
fn parse_matches_cas_bgpsec_add(matches: &ArgMatches) -> Result<Options, Error> {
let general_args = GeneralArgs::from_matches(matches)?;
let my_ca = Self::parse_my_ca(matches)?;
let asn_str = matches.value_of("asn").unwrap();
let asn = Asn::from_str(asn_str).map_err(|_| Error::invalid_asn(asn_str))?;
let csr_file = matches.value_of("csr").unwrap();
let csr_file_path = PathBuf::from(csr_file);
let bytes = file::read(&csr_file_path)
.map_err(|e| Error::GeneralArgumentError(format!("Cannot read file '{}', error: {}", csr_file, e,)))?;
let csr = BgpsecCsr::decode(bytes.as_ref())
.map_err(|e| Error::GeneralArgumentError(format!("Cannot parse CSR file '{}', error: {}", csr_file, e)))?;
csr.validate()
.map_err(|_| Error::GeneralArgumentError(format!("CSR in file '{}' is not valid", csr_file)))?;
let definition = BgpSecDefinition::new(asn, csr);
let command = Command::CertAuth(CaCommand::BgpSecAdd(my_ca, definition));
Ok(Options::make(general_args, command))
}
fn parse_matches_cas_bgpsec_remove(matches: &ArgMatches) -> Result<Options, Error> {
let general_args = GeneralArgs::from_matches(matches)?;
let my_ca = Self::parse_my_ca(matches)?;
let asn_str = matches.value_of("asn").unwrap();
let asn = Asn::from_str(asn_str).map_err(|_| Error::invalid_asn(asn_str))?;
let key_str = matches.value_of("key").unwrap();
let key = KeyIdentifier::from_str(key_str).map_err(|_| Error::general("Cannot parse key identifier"))?;
let definition = BgpSecAsnKey::new(asn, key);
let command = Command::CertAuth(CaCommand::BgpSecRemove(my_ca, definition));
Ok(Options::make(general_args, command))
}
fn parse_matches_cas_bgpsec(matches: &ArgMatches) -> Result<Options, Error> {
if let Some(m) = matches.subcommand_matches("list") {
Self::parse_matches_cas_bgpsec_list(m)
} else if let Some(m) = matches.subcommand_matches("add") {
Self::parse_matches_cas_bgpsec_add(m)
} else if let Some(m) = matches.subcommand_matches("remove") {
Self::parse_matches_cas_bgpsec_remove(m)
} else {
Err(Error::UnrecognizedSubCommand)
}
}
fn parse_matches_cas_aspas_add(matches: &ArgMatches) -> Result<Options, Error> {
let general_args = GeneralArgs::from_matches(matches)?;
let my_ca = Self::parse_my_ca(matches)?;
@@ -2297,6 +2434,8 @@ impl Options {
Self::parse_matches_cas_keyroll(m)
} else if let Some(m) = matches.subcommand_matches("roas") {
Self::parse_matches_cas_routes(m)
} else if let Some(m) = matches.subcommand_matches("bgpsec") {
Self::parse_matches_cas_bgpsec(m)
} else if let Some(m) = matches.subcommand_matches("aspas") {
Self::parse_matches_cas_aspas(m)
} else if let Some(m) = matches.subcommand_matches("repo") {
@@ -2385,6 +2524,11 @@ pub enum CaCommand {
AspasUpdate(CaHandle, AspaCustomer, AspaProvidersUpdate),
AspasRemove(CaHandle, AspaCustomer),
// BGPSec
BgpSecList(CaHandle),
BgpSecAdd(CaHandle, BgpSecDefinition),
BgpSecRemove(CaHandle, BgpSecAsnKey),
// Show details for this CA
Show(CaHandle),
ShowHistoryCommands(CaHandle, HistoryOptions),
+10 -3
View File
@@ -8,9 +8,10 @@ use rpki::ca::idexchange;
use crate::{
commons::{
api::{
AllCertAuthIssues, AspaDefinitionList, CaCommandDetails, CaRepoDetails, CertAuthInfo, CertAuthIssues,
CertAuthList, ChildCaInfo, ChildrenConnectionStats, CommandHistory, ParentCaContact, ParentStatuses,
PublisherDetails, PublisherList, RepoStatus, RoaDefinitions, RtaList, RtaPrepResponse, ServerInfo,
AllCertAuthIssues, AspaDefinitionList, BgpSecCsrInfoList, CaCommandDetails, CaRepoDetails, CertAuthInfo,
CertAuthIssues, CertAuthList, ChildCaInfo, ChildrenConnectionStats, CommandHistory, ParentCaContact,
ParentStatuses, PublisherDetails, PublisherList, RepoStatus, RoaDefinitions, RtaList, RtaPrepResponse,
ServerInfo,
},
bgp::{BgpAnalysisAdvice, BgpAnalysisReport, BgpAnalysisSuggestion},
},
@@ -41,6 +42,9 @@ pub enum ApiResponse {
// ASPA related
AspaDefinitions(AspaDefinitionList),
// BGPSec related
BgpSecDefinitions(BgpSecCsrInfoList),
ParentCaContact(ParentCaContact),
ParentStatuses(ParentStatuses),
@@ -88,6 +92,7 @@ impl ApiResponse {
ApiResponse::BgpAnalysisFull(table) => Ok(Some(table.report(fmt)?)),
ApiResponse::BgpAnalysisSuggestions(suggestions) => Ok(Some(suggestions.report(fmt)?)),
ApiResponse::AspaDefinitions(definitions) => Ok(Some(definitions.report(fmt)?)),
ApiResponse::BgpSecDefinitions(definitions) => Ok(Some(definitions.report(fmt)?)),
ApiResponse::ParentCaContact(contact) => Ok(Some(contact.report(fmt)?)),
ApiResponse::ParentStatuses(statuses) => Ok(Some(statuses.report(fmt)?)),
ApiResponse::ChildInfo(info) => Ok(Some(info.report(fmt)?)),
@@ -203,6 +208,8 @@ impl Report for BgpAnalysisSuggestion {}
impl Report for AspaDefinitionList {}
impl Report for BgpSecCsrInfoList {}
impl Report for CaRepoDetails {}
impl Report for RepoStatus {}
+232
View File
@@ -0,0 +1,232 @@
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;
//------------ BgpSecDefinition --------------------------------------------
#[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 {}
//------------ BgpSecAsnKey ------------------------------------------------
#[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 {
// We use a format similar to the recommendation for the router
// certificate subject from section 3.1.1 in RFC 8209.
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.get(0).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>"
)
}
}
/// We use BgpSecAsnKey as (JSON) map keys and therefore we need it
/// to be serializable to a single simple string.
impl Serialize for BgpSecAsnKey {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.to_string().serialize(s)
}
}
/// We use BgpSecAsnKey as (JSON) map keys and therefore we need it
/// to be deserializable from a single simple string.
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)
}
}
//------------ BgpSecDefinitionUpdates -------------------------------------
/// Contains BGPSec definition updates sent to the API.
#[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)
}
}
/// This type is shown through the API
#[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);
}
}
+30 -2
View File
@@ -20,11 +20,11 @@ use rpki::{
},
publication::Base64,
},
crypto::KeyIdentifier,
repository::{
aspa::Aspa,
cert::Cert,
crl::{Crl, CrlEntry},
crypto::KeyIdentifier,
manifest::Manifest,
resources::{Asn, ResourceSet},
roa::Roa,
@@ -34,6 +34,7 @@ use rpki::{
uri,
};
use crate::daemon::ca::BgpSecCertInfo;
use crate::{
commons::{
api::{
@@ -46,6 +47,8 @@ use crate::{
daemon::ca::RouteAuthorization,
};
use super::BgpSecAsnKey;
//------------ IdCertPem -----------------------------------------------------
/// A PEM encoded IdCert and sha256 of the encoding, for easier
@@ -524,6 +527,10 @@ impl ObjectName {
pub fn aspa(customer: Asn) -> Self {
ObjectName(format!("{}.asa", customer))
}
pub fn bgpsec(asn: Asn, key: KeyIdentifier) -> Self {
ObjectName(format!("ROUTER-{:08X}-{}.cer", asn.into_u32(), key))
}
}
impl From<&Cert> for ObjectName {
@@ -571,6 +578,18 @@ impl From<&AspaDefinition> for ObjectName {
}
}
impl From<&BgpSecCertInfo> for ObjectName {
fn from(info: &BgpSecCertInfo) -> Self {
Self::bgpsec(info.asn(), info.public_key().key_identifier())
}
}
impl From<&BgpSecAsnKey> for ObjectName {
fn from(asn_key: &BgpSecAsnKey) -> Self {
Self::bgpsec(asn_key.asn(), asn_key.key_identifier())
}
}
impl From<&str> for ObjectName {
fn from(s: &str) -> Self {
ObjectName(s.to_string())
@@ -640,6 +659,15 @@ impl From<&Aspa> for Revocation {
}
}
impl From<&BgpSecCertInfo> for Revocation {
fn from(info: &BgpSecCertInfo) -> Self {
Revocation {
serial: info.serial(),
expires: info.expires(),
}
}
}
//------------ Revocations ---------------------------------------------------
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
@@ -2102,7 +2130,7 @@ mod test {
use bytes::Bytes;
use std::convert::TryFrom;
use rpki::repository::crypto::PublicKeyFormat;
use rpki::crypto::PublicKeyFormat;
use crate::{commons::crypto::OpenSslSigner, test};
+11 -1
View File
@@ -7,7 +7,8 @@ use rpki::{
idexchange::{ChildHandle, MyHandle, ParentHandle, PublisherHandle, ServiceUri},
provisioning::{RequestResourceLimit, ResourceClassName, RevocationRequest},
},
repository::{crypto::KeyIdentifier, resources::ResourceSet, x509::Time},
crypto::KeyIdentifier,
repository::{resources::ResourceSet, x509::Time},
};
use crate::{
@@ -487,6 +488,7 @@ pub enum StorableCaCommand {
AspaRemove {
customer: AspaCustomer,
},
BgpSecDefinitionUpdates, // details in events
RepoUpdate {
service_uri: ServiceUri,
},
@@ -597,6 +599,9 @@ impl WithStorableDetails for StorableCaCommand {
StorableCaCommand::AspasUpdateExisting { .. } => CommandSummary::new("cmd-ca-aspas-update-existing", &self),
StorableCaCommand::AspaRemove { .. } => CommandSummary::new("cmd-ca-aspas-remove", &self),
// BGPSec
StorableCaCommand::BgpSecDefinitionUpdates => CommandSummary::new("cmd-bgpsec-update", &self),
// REPO
StorableCaCommand::RepoUpdate { service_uri } => {
CommandSummary::new("cmd-ca-repo-update", &self).with_service_uri(service_uri)
@@ -770,6 +775,11 @@ impl fmt::Display for StorableCaCommand {
write!(f, "Remove ASPA for customer AS: {}", customer)
}
// ------------------------------------------------------------
// BGPSec Support
// ------------------------------------------------------------
StorableCaCommand::BgpSecDefinitionUpdates => write!(f, "Update BGPSec definitions"),
// ------------------------------------------------------------
// Publishing
// ------------------------------------------------------------
+12 -1
View File
@@ -6,6 +6,9 @@ pub use self::admin::*;
mod aspa;
pub use self::aspa::*;
mod bgpsec;
pub use self::bgpsec::*;
mod ca;
pub use self::ca::*;
@@ -19,12 +22,15 @@ pub mod rrdp;
use std::{collections::HashMap, fmt};
use rpki::ca::csr::BgpsecCsr;
use rpki::ca::provisioning::ResourceClassName;
use rpki::ca::publication::Base64;
use serde::{Deserialize, Serialize};
use rpki::{
ca::idexchange::{CaHandle, ChildHandle, ParentHandle, PublisherHandle},
repository::{crypto::KeyIdentifier, resources::Asn},
crypto::KeyIdentifier,
repository::resources::Asn,
};
use crate::{commons::error::RoaDeltaError, daemon::ca::RouteAuthorization};
@@ -112,6 +118,11 @@ impl ErrorResponse {
self.with_arg("asn", asn)
}
pub fn with_bgpsec_csr(self, csr: &BgpsecCsr) -> Self {
let base64 = Base64::from_content(csr.to_captured().as_slice());
self.with_arg("bgpsec_csr", base64)
}
pub fn with_roa_delta_error(mut self, roa_delta_error: &RoaDeltaError) -> Self {
self.delta_error = Some(roa_delta_error.clone());
self
@@ -1,18 +1,23 @@
use std::{path::Path, sync::Arc, time::Duration};
use rpki::{
ca::{idcert::IdCert, idexchange::RepoInfo, provisioning, publication},
ca::{
csr::{Csr, RpkiCaCsr},
idcert::IdCert,
idexchange::RepoInfo,
provisioning, publication,
},
crypto::{KeyIdentifier, PublicKey, PublicKeyFormat, RpkiSignature, RpkiSignatureAlgorithm, Signer},
repository::{
aspa::{Aspa, AspaBuilder},
cert::TbsCert,
crl::{CrlEntry, TbsCertList},
crypto::{KeyIdentifier, PublicKey, PublicKeyFormat, Signature, SignatureAlgorithm, Signer},
manifest::ManifestContent,
roa::RoaBuilder,
rta,
sigobj::SignedObjectBuilder,
x509::{Serial, Time, Validity},
Cert, Crl, Csr, Manifest, Roa,
Cert, Crl, Manifest, Roa,
},
};
@@ -217,22 +222,24 @@ impl KrillSigner {
Serial::random(&self.router).map_err(crypto::Error::signer)
}
pub fn sign<D: AsRef<[u8]> + ?Sized>(&self, key_id: &KeyIdentifier, data: &D) -> CryptoResult<Signature> {
pub fn sign<D: AsRef<[u8]> + ?Sized>(&self, key_id: &KeyIdentifier, data: &D) -> CryptoResult<RpkiSignature> {
self.router
.sign(key_id, SignatureAlgorithm::default(), data)
.sign(key_id, RpkiSignatureAlgorithm::default(), data)
.map_err(crypto::Error::signing)
}
pub fn sign_one_off<D: AsRef<[u8]> + ?Sized>(&self, data: &D) -> CryptoResult<(Signature, PublicKey)> {
pub fn sign_one_off<D: AsRef<[u8]> + ?Sized>(&self, data: &D) -> CryptoResult<(RpkiSignature, PublicKey)> {
self.router
.sign_one_off(SignatureAlgorithm::default(), data)
.sign_one_off(RpkiSignatureAlgorithm::default(), data)
.map_err(crypto::Error::signer)
}
pub fn sign_csr(&self, base_repo: &RepoInfo, name_space: &str, key: &KeyIdentifier) -> CryptoResult<Csr> {
let pub_key = self.router.get_key_info(key).map_err(crypto::Error::key_error)?;
let mft_file_name = ObjectName::mft_for_key(&pub_key.key_identifier());
let enc = Csr::construct(
pub fn sign_csr(&self, base_repo: &RepoInfo, name_space: &str, key: &KeyIdentifier) -> CryptoResult<RpkiCaCsr> {
let signing_key_id = self.router.get_key_info(key).map_err(crypto::Error::key_error)?;
let mft_file_name = ObjectName::mft_for_key(&signing_key_id.key_identifier());
// The rpki-rs library returns a signed and encoded CSR for a CA certificate.
let signed_and_encoded_csr = Csr::construct_rpki_ca(
&self.router,
key,
&base_repo.ca_repository(name_space).join(&[]).unwrap(), // force trailing slash
@@ -240,7 +247,9 @@ impl KrillSigner {
base_repo.rpki_notify(),
)
.map_err(crypto::Error::signing)?;
Ok(Csr::decode(enc.as_slice())?)
// Decode the encoded CSR again to get a typed RpkiCaCsr
Ok(RpkiCaCsr::decode(signed_and_encoded_csr.as_slice())?)
}
pub fn sign_cert(&self, tbs: TbsCert, key_id: &KeyIdentifier) -> CryptoResult<Cert> {
@@ -2,7 +2,7 @@
use std::{collections::HashMap, fmt, path::Path, str::FromStr};
use rpki::repository::crypto::{KeyIdentifier, PublicKey};
use rpki::crypto::{KeyIdentifier, PublicKey};
use crate::{
commons::{
@@ -1,5 +1,6 @@
use rpki::repository::crypto::{
signer::KeyError, KeyIdentifier, PublicKey, PublicKeyFormat, Signature, SignatureAlgorithm, SigningError,
use rpki::crypto::{
signer::{KeyError, SigningAlgorithm},
KeyIdentifier, PublicKey, PublicKeyFormat, RpkiSignature, Signature, SignatureAlgorithm, SigningError,
};
use crate::commons::crypto::{
@@ -110,7 +111,7 @@ impl SignerProvider {
&self,
signer_private_key_id: &str,
challenge: &D,
) -> Result<Signature, SignerError> {
) -> Result<RpkiSignature, SignerError> {
match self {
SignerProvider::OpenSsl(_, signer) => signer.sign_registration_challenge(signer_private_key_id, challenge),
#[cfg(feature = "hsm")]
@@ -206,12 +207,17 @@ impl SignerProvider {
}
}
pub fn sign<D: AsRef<[u8]> + ?Sized>(
pub fn sign<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
&self,
key: &KeyIdentifier,
algorithm: SignatureAlgorithm,
algorithm: Alg,
data: &D,
) -> Result<Signature, SigningError<SignerError>> {
) -> Result<Signature<Alg>, SigningError<SignerError>> {
let signing_algorithm = algorithm.signing_algorithm();
if !matches!(signing_algorithm, SigningAlgorithm::RsaSha256) {
return Err(SignerError::UnsupportedSigningAlg(signing_algorithm).into());
}
match self {
SignerProvider::OpenSsl(_, signer) => signer.sign(key, algorithm, data),
#[cfg(feature = "hsm")]
@@ -223,11 +229,16 @@ impl SignerProvider {
}
}
pub fn sign_one_off<D: AsRef<[u8]> + ?Sized>(
pub fn sign_one_off<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
&self,
algorithm: SignatureAlgorithm,
algorithm: Alg,
data: &D,
) -> Result<(Signature, PublicKey), SignerError> {
) -> Result<(Signature<Alg>, PublicKey), SignerError> {
let signing_algorithm = algorithm.signing_algorithm();
if !matches!(signing_algorithm, SigningAlgorithm::RsaSha256) {
return Err(SignerError::UnsupportedSigningAlg(signing_algorithm));
}
match self {
SignerProvider::OpenSsl(_, signer) => signer.sign_one_off(algorithm, data),
#[cfg(feature = "hsm")]
@@ -1,7 +1,7 @@
use std::sync::Arc;
use std::{collections::HashMap, sync::RwLock};
use rpki::repository::crypto::{
use rpki::crypto::{
signer::KeyError, KeyIdentifier, PublicKey, PublicKeyFormat, Signature, SignatureAlgorithm, Signer, SigningError,
};
@@ -586,21 +586,21 @@ impl Signer for SignerRouter {
self.get_signer_for_key(key_id)?.destroy_key(key_id)
}
fn sign<D: AsRef<[u8]> + ?Sized>(
fn sign<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
&self,
key_id: &KeyIdentifier,
algorithm: SignatureAlgorithm,
algorithm: Alg,
data: &D,
) -> Result<Signature, SigningError<Self::Error>> {
) -> Result<Signature<Alg>, SigningError<Self::Error>> {
self.bind_ready_signers();
self.get_signer_for_key(key_id)?.sign(key_id, algorithm, data)
}
fn sign_one_off<D: AsRef<[u8]> + ?Sized>(
fn sign_one_off<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
&self,
algorithm: SignatureAlgorithm,
algorithm: Alg,
data: &D,
) -> Result<(Signature, PublicKey), Self::Error> {
) -> Result<(Signature<Alg>, PublicKey), Self::Error> {
self.bind_ready_signers();
self.one_off_signer.sign_one_off(algorithm, data)
}
@@ -613,6 +613,8 @@ impl Signer for SignerRouter {
#[cfg(all(test, feature = "hsm"))]
pub mod tests {
use rpki::crypto::RpkiSignatureAlgorithm;
use crate::{
commons::crypto::{
dispatch::signerprovider::SignerFlags,
@@ -639,7 +641,7 @@ pub mod tests {
pub fn verify_that_a_usable_signer_is_registered_and_can_be_used() {
test::test_under_tmp(|d| {
#[allow(non_snake_case)]
let DEF_SIG_ALG = SignatureAlgorithm::default();
let DEF_SIG_ALG = RpkiSignatureAlgorithm::default();
// Build a mock signer that is contactable and usable for the SignerRouter
let call_counts = Arc::new(MockSignerCallCounts::new());
@@ -712,7 +714,7 @@ pub mod tests {
router.destroy_key(&key_identifier).unwrap();
assert_eq!(1, call_counts.get(FnIdx::DestroyKey));
let err = router.sign(&key_identifier, SignatureAlgorithm::default(), &out_buf);
let err = router.sign(&key_identifier, RpkiSignatureAlgorithm::default(), &out_buf);
// TODO: Should this error from the SignerRouter actually be SigningError::KeyNotFound instead of
// SigningError::Signer(SignerError::KeyNotFound)?
assert!(matches!(err, Err(SigningError::Signer(SignerError::KeyNotFound))));
+5 -5
View File
@@ -5,14 +5,14 @@ use std::convert::TryFrom;
use bytes::Bytes;
use rpki::{
ca::provisioning::RequestResourceLimit,
ca::{csr::RpkiCaCsr, provisioning::RequestResourceLimit},
crypto::{DigestAlgorithm, KeyIdentifier, PublicKey},
repository::{
cert::{KeyUsage, Overclaim, TbsCert},
crypto::{DigestAlgorithm, KeyIdentifier, PublicKey},
manifest::FileAndHash,
resources::ResourceSet,
x509::{Name, Time, Validity},
Cert, Crl, Csr,
Cert, Crl,
},
uri,
};
@@ -75,10 +75,10 @@ impl CsrInfo {
}
}
impl TryFrom<&Csr> for CsrInfo {
impl TryFrom<&RpkiCaCsr> for CsrInfo {
type Error = Error;
fn try_from(csr: &Csr) -> KrillResult<CsrInfo> {
fn try_from(csr: &RpkiCaCsr) -> KrillResult<CsrInfo> {
csr.validate().map_err(|_| Error::invalid_csr("invalid signature"))?;
let ca_repository = csr
.ca_repository()
@@ -1,6 +1,7 @@
use std::{fmt, path::PathBuf};
use openssl::error::ErrorStack;
use rpki::crypto::signer::SigningAlgorithm;
use crate::commons::error::KrillIoError;
@@ -17,6 +18,7 @@ pub enum SignerError {
PermanentlyUnusable,
Pkcs11Error(String),
TemporarilyUnavailable,
UnsupportedSigningAlg(SigningAlgorithm),
}
impl fmt::Display for SignerError {
@@ -33,6 +35,10 @@ impl fmt::Display for SignerError {
SignerError::PermanentlyUnusable => write!(f, "Signer is unusable"),
SignerError::Pkcs11Error(e) => write!(f, "PKCS#11 Error: {}", e),
SignerError::TemporarilyUnavailable => write!(f, "Signer is unavailable"),
SignerError::UnsupportedSigningAlg(key_format) => match key_format {
SigningAlgorithm::RsaSha256 => write!(f, "Signing with RSA not supported"),
SigningAlgorithm::EcdsaP256Sha256 => write!(f, "Signing with EcdsaP256 not supported"),
},
}
}
}
@@ -21,8 +21,11 @@ use openssl::ssl::SslStream;
use r2d2::PooledConnection;
use rpki::{
repository::crypto::signer::KeyError,
repository::crypto::{KeyIdentifier, PublicKey, PublicKeyFormat, Signature, SignatureAlgorithm, SigningError},
crypto::signer::KeyError,
crypto::{
KeyIdentifier, PublicKey, PublicKeyFormat, RpkiSignature, RpkiSignatureAlgorithm, Signature,
SignatureAlgorithm, SigningError,
},
};
use crate::commons::{
@@ -329,8 +332,12 @@ impl KmipSigner {
&self,
signer_private_key_id: &str,
challenge: &D,
) -> Result<Signature, SignerError> {
self.sign_with_key(signer_private_key_id, SignatureAlgorithm::default(), challenge.as_ref())
) -> Result<RpkiSignature, SignerError> {
self.sign_with_key(
signer_private_key_id,
RpkiSignatureAlgorithm::default(),
challenge.as_ref(),
)
}
}
@@ -782,12 +789,12 @@ impl KmipSigner {
Ok(public_key)
}
pub(super) fn sign_with_key(
pub(super) fn sign_with_key<Alg: SignatureAlgorithm>(
&self,
private_key_id: &str,
algorithm: SignatureAlgorithm,
algorithm: Alg,
data: &[u8],
) -> Result<Signature, SignerError> {
) -> Result<Signature<Alg>, SignerError> {
if algorithm.public_key_format() != PublicKeyFormat::Rsa {
return Err(SignerError::KmipError(format!(
"Algorithm '{:?}' not supported",
@@ -797,7 +804,7 @@ impl KmipSigner {
let signed = self.with_conn("sign", |conn| conn.sign(&private_key_id, data))?;
let sig = Signature::new(SignatureAlgorithm::default(), Bytes::from(signed.signature_data));
let sig = Signature::new(algorithm, Bytes::from(signed.signature_data));
Ok(sig)
}
@@ -913,12 +920,12 @@ impl KmipSigner {
res
}
pub fn sign<D: AsRef<[u8]> + ?Sized>(
pub fn sign<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
&self,
key_id: &KeyIdentifier,
algorithm: SignatureAlgorithm,
algorithm: Alg,
data: &D,
) -> Result<Signature, SigningError<SignerError>> {
) -> Result<Signature<Alg>, SigningError<SignerError>> {
let kmip_key_pair_ids = self.lookup_kmip_key_ids(key_id)?;
let signature = self
@@ -933,11 +940,11 @@ impl KmipSigner {
Ok(signature)
}
pub fn sign_one_off<D: AsRef<[u8]> + ?Sized>(
pub fn sign_one_off<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
&self,
algorithm: SignatureAlgorithm,
algorithm: Alg,
data: &D,
) -> Result<(Signature, PublicKey), SignerError> {
) -> Result<(Signature<Alg>, PublicKey), SignerError> {
// TODO: Is it possible to use a KMIP batch request to implement the create, activate, sign, deactivate, delete
// in one round-trip to the server?
let (key, kmip_key_pair_ids) = self.build_key(PublicKeyFormat::Rsa)?;
@@ -11,8 +11,11 @@ use openssl::{
};
use rpki::{
repository::crypto::signer::KeyError,
repository::crypto::{KeyIdentifier, PublicKey, PublicKeyFormat, Signature, SignatureAlgorithm, SigningError},
crypto::signer::KeyError,
crypto::{
signer::SigningAlgorithm, KeyIdentifier, PublicKey, PublicKeyFormat, RpkiSignature, RpkiSignatureAlgorithm,
Signature, SignatureAlgorithm, SigningError,
},
};
use crate::commons::crypto::{dispatch::signerinfo::SignerMapper, SignerError, SignerHandle};
@@ -119,10 +122,19 @@ impl MockSigner {
Ok((public_key, pkey, key_identifier, internal_id))
}
fn sign_with_key<D: AsRef<[u8]> + ?Sized>(pkey: &PKey<Private>, challenge: &D) -> Result<Signature, SignerError> {
fn sign_with_key<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
alg: Alg,
pkey: &PKey<Private>,
challenge: &D,
) -> Result<Signature<Alg>, SignerError> {
let signing_algorithm = alg.signing_algorithm();
if !matches!(signing_algorithm, SigningAlgorithm::RsaSha256) {
return Err(SignerError::UnsupportedSigningAlg(signing_algorithm).into());
}
let mut signer = ::openssl::sign::Signer::new(MessageDigest::sha256(), &pkey)?;
signer.update(challenge.as_ref())?;
let signature = Signature::new(SignatureAlgorithm::default(), Bytes::from(signer.sign_to_vec()?));
let signature = Signature::new(alg, Bytes::from(signer.sign_to_vec()?));
Ok(signature)
}
@@ -161,7 +173,7 @@ impl MockSigner {
&self,
signer_private_key_id: &str,
challenge: &D,
) -> Result<Signature, SignerError> {
) -> Result<RpkiSignature, SignerError> {
self.inc_fn_call_count(FnIdx::SignRegistrationChallenge);
if let Some(err_cb) = &self.sign_registration_challenge_error_cb {
let _ = (err_cb)(&self.fn_call_counts)?;
@@ -169,7 +181,7 @@ impl MockSigner {
let pkey = self.load_key(signer_private_key_id).ok_or(SignerError::KeyNotFound)?;
// sign the given data using the loaded private key
let signature = Self::sign_with_key(&pkey, challenge)?;
let signature = Self::sign_with_key(RpkiSignatureAlgorithm::default(), &pkey, challenge)?;
// return the generated signature to the caller
Ok(signature)
@@ -233,26 +245,26 @@ impl MockSigner {
Ok(())
}
pub fn sign<D: AsRef<[u8]> + ?Sized>(
pub fn sign<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
&self,
key_identifier: &KeyIdentifier,
_algorithm: SignatureAlgorithm,
algorithm: Alg,
data: &D,
) -> Result<Signature, SigningError<SignerError>> {
) -> Result<Signature<Alg>, SigningError<SignerError>> {
self.inc_fn_call_count(FnIdx::Sign);
let internal_id = self.internal_id_from_key_identifier(key_identifier)?;
let pkey = self.load_key(&internal_id).ok_or(SignerError::KeyNotFound)?;
Self::sign_with_key(&pkey, data).map_err(|err| SigningError::Signer(err))
Self::sign_with_key(algorithm, &pkey, data).map_err(|err| SigningError::Signer(err))
}
pub fn sign_one_off<D: AsRef<[u8]> + ?Sized>(
pub fn sign_one_off<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
&self,
_algorithm: SignatureAlgorithm,
algorithm: Alg,
data: &D,
) -> Result<(Signature, PublicKey), SignerError> {
) -> Result<(Signature<Alg>, PublicKey), SignerError> {
self.inc_fn_call_count(FnIdx::SignOneOff);
let (public_key, pkey, _, internal_id) = self.build_key().unwrap();
let signature = Self::sign_with_key(&pkey, data).unwrap();
let signature = Self::sign_with_key(algorithm, &pkey, data).unwrap();
let _ = self.keys.write().unwrap().remove(&internal_id);
Ok((signature, public_key))
}
@@ -20,8 +20,11 @@ use cryptoki::{
};
use rpki::{
repository::crypto::signer::KeyError,
repository::crypto::{KeyIdentifier, PublicKey, PublicKeyFormat, Signature, SignatureAlgorithm, SigningError},
crypto::signer::KeyError,
crypto::{
KeyIdentifier, PublicKey, PublicKeyFormat, RpkiSignature, RpkiSignatureAlgorithm, Signature,
SignatureAlgorithm, SigningError,
},
};
use crate::commons::crypto::{
@@ -243,14 +246,14 @@ impl Pkcs11Signer {
&self,
key_id: &str,
challenge: &D,
) -> Result<Signature, SignerError> {
) -> Result<RpkiSignature, SignerError> {
let priv_handle = self
.find_key(key_id, ObjectClass::PRIVATE_KEY)
.map_err(|err| match err {
KeyError::KeyNotFound => SignerError::KeyNotFound,
KeyError::Signer(err) => err,
})?;
self.sign_with_key(priv_handle, SignatureAlgorithm::default(), challenge.as_ref())
self.sign_with_key(priv_handle, RpkiSignatureAlgorithm::default(), challenge.as_ref())
}
}
@@ -633,7 +636,7 @@ impl Pkcs11Signer {
impl Pkcs11Signer {
pub(super) fn remember_key_id(
&self,
key_id: &rpki::repository::crypto::KeyIdentifier,
key_id: &rpki::crypto::KeyIdentifier,
internal_key_id: String,
) -> Result<(), SignerError> {
let readable_handle = self.handle.read().unwrap();
@@ -765,12 +768,12 @@ impl Pkcs11Signer {
Ok(public_key)
}
pub(super) fn sign_with_key(
pub(super) fn sign_with_key<Alg: SignatureAlgorithm>(
&self,
private_key_handle: ObjectHandle,
algorithm: SignatureAlgorithm,
algorithm: Alg,
data: &[u8],
) -> Result<Signature, SignerError> {
) -> Result<Signature<Alg>, SignerError> {
if algorithm.public_key_format() != PublicKeyFormat::Rsa {
return Err(SignerError::KmipError(format!(
"Algorithm '{:?}' not supported",
@@ -798,7 +801,7 @@ impl Pkcs11Signer {
let signature_data = self.with_conn("sign", |conn| conn.sign(&mechanism, private_key_handle, data))?;
let sig = Signature::new(SignatureAlgorithm::default(), Bytes::from(signature_data));
let sig = Signature::new(algorithm, Bytes::from(signature_data));
Ok(sig)
}
@@ -918,12 +921,12 @@ impl Pkcs11Signer {
res
}
pub fn sign<D: AsRef<[u8]> + ?Sized>(
pub fn sign<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
&self,
key_id: &KeyIdentifier,
algorithm: SignatureAlgorithm,
algorithm: Alg,
data: &D,
) -> Result<Signature, SigningError<SignerError>> {
) -> Result<Signature<Alg>, SigningError<SignerError>> {
let internal_key_id = self.lookup_key_id(key_id)?;
let priv_handle = self
.find_key(&internal_key_id, ObjectClass::PRIVATE_KEY)
@@ -936,11 +939,11 @@ impl Pkcs11Signer {
.map_err(|err| SigningError::Signer(err))
}
pub fn sign_one_off<D: AsRef<[u8]> + ?Sized>(
pub fn sign_one_off<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
&self,
algorithm: SignatureAlgorithm,
algorithm: Alg,
data: &D,
) -> Result<(Signature, PublicKey), SignerError> {
) -> Result<(Signature<Alg>, PublicKey), SignerError> {
let (key, pub_handle, priv_handle, _) = self.build_key(PublicKeyFormat::Rsa)?;
let signature_res = self
+3 -3
View File
@@ -328,19 +328,19 @@ pub mod tests {
assert_eq!(None, conn.last_probe_time()?);
// The first call to .get() should trigger a probe
let _ = conn.status(|_, _| Err(ProbeError::AwaitingNextProbe));
let _probe1 = conn.status(|_, _| Err(ProbeError::AwaitingNextProbe));
let t1 = conn.last_probe_time()?;
assert!(t1.is_some());
// A call to .get() before the next probe interval should NOT result in an updated last probe time
std::thread::sleep(Duration::from_millis(10));
let _ = conn.status(|_, _| Err(ProbeError::AwaitingNextProbe));
let _probe2 = conn.status(|_, _| Err(ProbeError::AwaitingNextProbe));
let t2 = conn.last_probe_time()?;
assert!(t2 == t1);
// A call to .get() after the next probe interval SHOULD result in an updated last probe time
std::thread::sleep(Duration::from_millis(200));
let _ = conn.status(|_, _| Err(ProbeError::AwaitingNextProbe));
let _probe3 = conn.status(|_, _| Err(ProbeError::AwaitingNextProbe));
let t3 = conn.last_probe_time()?;
assert!(t3 > t1);
@@ -19,8 +19,10 @@ use openssl::{
rsa::Rsa,
};
use rpki::repository::crypto::{
signer::KeyError, KeyIdentifier, PublicKey, PublicKeyFormat, Signature, SignatureAlgorithm, SigningError,
use rpki::crypto::{
signer::{KeyError, SigningAlgorithm},
KeyIdentifier, PublicKey, PublicKeyFormat, RpkiSignature, RpkiSignatureAlgorithm, Signature, SignatureAlgorithm,
SigningError,
};
use crate::{
@@ -112,10 +114,10 @@ impl OpenSslSigner {
&self,
signer_private_key_id: &str,
challenge: &D,
) -> Result<Signature, SignerError> {
) -> Result<RpkiSignature, SignerError> {
let key_id = KeyIdentifier::from_str(signer_private_key_id).map_err(|_| SignerError::KeyNotFound)?;
let key_pair = self.load_key(&key_id)?;
let signature = Self::sign_with_key(key_pair.pkey.as_ref(), challenge)?;
let signature = Self::sign_with_key(key_pair.pkey.as_ref(), RpkiSignatureAlgorithm::default(), challenge)?;
Ok(signature)
}
}
@@ -165,11 +167,20 @@ impl OpenSslSigner {
Ok(key_id)
}
fn sign_with_key<D: AsRef<[u8]> + ?Sized>(pkey: &PKeyRef<Private>, data: &D) -> Result<Signature, SignerError> {
fn sign_with_key<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
pkey: &PKeyRef<Private>,
algorithm: Alg,
data: &D,
) -> Result<Signature<Alg>, SignerError> {
let signing_algorithm = algorithm.signing_algorithm();
if !matches!(signing_algorithm, SigningAlgorithm::RsaSha256) {
return Err(SignerError::UnsupportedSigningAlg(signing_algorithm));
}
let mut signer = ::openssl::sign::Signer::new(MessageDigest::sha256(), pkey)?;
signer.update(data.as_ref())?;
let signature = Signature::new(SignatureAlgorithm::default(), Bytes::from(signer.sign_to_vec()?));
let signature = Signature::new(algorithm, Bytes::from(signer.sign_to_vec()?));
Ok(signature)
}
@@ -238,25 +249,23 @@ impl OpenSslSigner {
Ok(())
}
pub fn sign<D: AsRef<[u8]> + ?Sized>(
pub fn sign<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
&self,
key_id: &KeyIdentifier,
_algorithm: SignatureAlgorithm,
algorithm: Alg,
data: &D,
) -> Result<Signature, SigningError<SignerError>> {
) -> Result<Signature<Alg>, SigningError<SignerError>> {
let key_pair = self.load_key(key_id)?;
Self::sign_with_key(key_pair.pkey.as_ref(), data).map_err(SigningError::Signer)
Self::sign_with_key(key_pair.pkey.as_ref(), algorithm, data).map_err(SigningError::Signer)
}
pub fn sign_one_off<D: AsRef<[u8]> + ?Sized>(
pub fn sign_one_off<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
&self,
_algorithm: SignatureAlgorithm,
algorithm: Alg,
data: &D,
) -> Result<(Signature, PublicKey), SignerError> {
) -> Result<(Signature<Alg>, PublicKey), SignerError> {
let kp = OpenSslKeyPair::build()?;
let signature = Self::sign_with_key(kp.pkey.as_ref(), data)?;
let signature = Self::sign_with_key(kp.pkey.as_ref(), algorithm, data)?;
let key = kp.subject_public_key_info()?;
Ok((signature, key))
+35 -1
View File
@@ -11,7 +11,8 @@ use rpki::{
provisioning::ResourceClassName,
publication,
},
repository::{crypto::KeyIdentifier, x509::ValidationError},
crypto::KeyIdentifier,
repository::x509::ValidationError,
uri,
};
@@ -26,6 +27,8 @@ use crate::{
upgrades::PrepareUpgradeError,
};
use super::api::{BgpSecAsnKey, BgpSecDefinition};
//------------ RoaDeltaError -----------------------------------------------
/// This type contains a detailed error report for a ROA delta
@@ -273,6 +276,13 @@ pub enum Error {
AspaProvidersUpdateEmpty(CaHandle, AspaCustomer),
AspaProvidersUpdateConflict(CaHandle, AspaProvidersUpdateConflict),
//-----------------------------------------------------------------
// BGP Sec
//-----------------------------------------------------------------
BgpSecDefinitionUnknown(CaHandle, BgpSecAsnKey),
BgpSecDefinitionInvalidlySigned(CaHandle, BgpSecDefinition),
BgpSecDefinitionNotEntitled(CaHandle, BgpSecAsnKey),
//-----------------------------------------------------------------
// Key Usage Issues
//-----------------------------------------------------------------
@@ -442,6 +452,14 @@ impl fmt::Display for Error {
Error::AspaCustomerUnknown(_ca, asn) => write!(f, "No current ASPA exists for customer AS '{}'", asn),
Error::AspaProvidersUpdateEmpty(_ca, asn) => write!(f, "Received empty update for ASPA for customer AS '{}'", asn),
Error::AspaProvidersUpdateConflict(_ca, e) => write!(f, "ASPA delta rejected:\n\n'{}'", e),
//-----------------------------------------------------------------
// BGPSec
//-----------------------------------------------------------------
Error::BgpSecDefinitionUnknown(_ca, key) => write!(f, "Cannot remove BGPSec CSR for unknown combination of ASN '{}' and key '{}'", key.asn(), key.key_identifier()),
Error::BgpSecDefinitionInvalidlySigned(_ca, def) => write!(f, "Invalidly signed BGPSec CSR remove BGPSec CSR for ASN '{}' and key '{}'", def.asn(), def.csr().public_key().key_identifier()),
Error::BgpSecDefinitionNotEntitled(_ca, key) => write!(f, "AS '{}' is not held by you", key.asn()),
//-----------------------------------------------------------------
// Key Usage Issues
@@ -830,6 +848,22 @@ impl Error {
.with_ca(ca)
.with_aspa_providers_conflict(conflict),
//-----------------------------------------------------------------
// BGP Sec
//-----------------------------------------------------------------
Error::BgpSecDefinitionUnknown(ca, key) => ErrorResponse::new("ca-bgpsec-unknown", &self)
.with_ca(ca)
.with_asn(key.asn())
.with_key_identifier(&key.key_identifier()),
Error::BgpSecDefinitionInvalidlySigned(ca, def) => ErrorResponse::new("ca-bgpsec-invalidly-signed", &self)
.with_ca(ca)
.with_asn(def.asn())
.with_key_identifier(&def.csr().public_key().key_identifier())
.with_bgpsec_csr(def.csr()),
Error::BgpSecDefinitionNotEntitled(ca, key) => ErrorResponse::new("ca-bgpsec-not-entitled", &self)
.with_ca(ca)
.with_asn(key.asn()),
//-----------------------------------------------------------------
// Key Usage Issues (key-*)
//-----------------------------------------------------------------
+2 -2
View File
@@ -9,9 +9,9 @@ use log::LevelFilter;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use syslog::Facility;
use rpki::repository::{
use rpki::{
crypto::PublicKey,
resources::{AsBlocks, IpBlocks},
repository::resources::{AsBlocks, IpBlocks},
};
//------------ Bytes ---------------------------------------------------------
+1 -1
View File
@@ -5,7 +5,7 @@ use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use bytes::Bytes;
use rpki::{
repository::crypto::DigestAlgorithm,
crypto::DigestAlgorithm,
uri::{Https, Rsync},
};
+2
View File
@@ -70,6 +70,8 @@ iterable_enum! {
ASPAS_READ,
ASPAS_UPDATE,
ASPAS_ANALYSIS,
BGPSEC_READ,
BGPSEC_UPDATE,
RTA_LIST,
RTA_READ,
RTA_UPDATE
+318
View File
@@ -0,0 +1,318 @@
use std::collections::HashMap;
use rpki::{
ca::{csr::BgpsecCsr, publication::Base64},
crypto::PublicKey,
repository::{
cert::{ExtendedKeyUsage, KeyUsage, Overclaim, TbsCert},
resources::Asn,
x509::{Serial, Time},
Cert,
},
};
use crate::{
commons::{
api::{BgpSecAsnKey, BgpSecCsrInfo, BgpSecCsrInfoList},
crypto::{KrillSigner, SignSupport},
KrillResult,
},
daemon::config::{Config, IssuanceTimingConfig},
};
use super::{BgpSecCertificateUpdates, CertifiedKey};
//------------ BgpSecCertificates ------------------------------------------
/// The issued BGPSec certificates under a resource class in a CA.
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct BgpSecCertificates(HashMap<BgpSecAsnKey, BgpSecCertInfo>);
impl BgpSecCertificates {
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn len(&self) -> usize {
self.0.len()
}
/// Update issued BGPSec certificates
///
/// Will issue new BGPSec certificates for definitions using the resources of
/// this certified key which did not yet exist.
///
/// Will remove any existing BGPSec certificates which:
/// - are no longer present in the definitions; or
/// - for which the certified key no longer holds the asn.
///
///
/// Note that we pass in ALL BGPSec definitions, including definitions that may only
/// be eligible under another owning RC.
pub fn update(
&self,
definitions: &BgpSecDefinitions,
certified_key: &CertifiedKey,
config: &Config,
signer: &KrillSigner,
) -> KrillResult<BgpSecCertificateUpdates> {
let mut updates = BgpSecCertificateUpdates::default();
let resources = certified_key.incoming_cert().resources();
let issuance_timing = &config.issuance_timing;
// Issue BGPSec certificates for any ASN held by the certified key
// for which the required router key has not yet been certified.
for (key, csr) in definitions
.iter()
.filter(|(k, _)| !self.0.contains_key(k) && resources.contains_asn(k.asn()))
{
// resource held here, but BGPSec certificate was not yet issued.
let cert = self.make_bgpsec_cert(key.asn(), csr.key().clone(), certified_key, issuance_timing, signer)?;
updates.add_updated(cert);
}
// Will remove any existing BGPSec certificates which:
// - are no longer present in the definitions; or
// - for which the certified key no longer holds the asn.
for (key, _) in self
.0
.iter()
.filter(|(k, _)| !definitions.has(k) || !resources.contains_asn(k.asn()))
{
updates.add_removed(*key);
}
Ok(updates)
}
/// Re-new BGPSec certificates
///
/// Used to renew certificates which would expire, in which case the renew_threshold
/// should be specified. Or, to re-issue all existing certificates during a key rollover
/// activation of a new certified_key - in which case the renew_threshold is expected to
/// be None, and the certified_key is expected to have changed.
pub fn renew(
&self,
certified_key: &CertifiedKey,
renew_threshold: Option<Time>,
issuance_timing: &IssuanceTimingConfig,
signer: &KrillSigner,
) -> KrillResult<BgpSecCertificateUpdates> {
let mut updates = BgpSecCertificateUpdates::default();
for cert in self.0.values().filter(|cert| {
renew_threshold
.map(|threshold| cert.expires() < threshold) // will expire
.unwrap_or(true) // always renew if no renew_threshold was given
}) {
let asn = cert.asn();
let public_key = cert.public_key().clone();
let cert = self.make_bgpsec_cert(asn, public_key, certified_key, issuance_timing, signer)?;
updates.add_updated(cert);
}
Ok(updates)
}
fn make_bgpsec_cert(
&self,
asn: Asn,
public_key: PublicKey,
certified_key: &CertifiedKey,
issuance_timing: &IssuanceTimingConfig,
signer: &KrillSigner,
) -> KrillResult<BgpSecCertInfo> {
let serial_number = signer.random_serial()?;
let incoming_cert = certified_key.incoming_cert();
let issuer = incoming_cert.subject().clone();
let crl_uri = incoming_cert.crl_uri();
let aki = incoming_cert.subject_public_key_info().key_identifier();
let aia = incoming_cert.uri().clone();
let validity = SignSupport::sign_validity_weeks(issuance_timing.timing_bgpsec_valid_weeks);
// Perhaps implement recommendation of 3.1.1 RFC 8209 somehow. However, it is
// not at all clear how/why this is relevant. RPs will typically discard this
// information and the subject is not communicated to routers. If this is for
// debugging purposes then using a sensible file name (like we do) is more
// important.
let subject = None;
let mut router_cert = TbsCert::new(
serial_number,
issuer,
validity,
subject,
public_key,
KeyUsage::Ee,
Overclaim::Refuse,
);
router_cert.set_extended_key_usage(Some(ExtendedKeyUsage::create_router()));
router_cert.set_authority_key_identifier(Some(aki));
router_cert.set_ca_issuer(Some(aia));
router_cert.set_crl_uri(Some(crl_uri));
router_cert.build_as_resource_blocks(|b| b.push(asn));
let signing_key = certified_key.key_id();
let cert = signer.sign_cert(router_cert, signing_key)?;
Ok(BgpSecCertInfo::new(asn, cert))
}
/// Applies updates from an event.
pub fn updated(&mut self, updates: BgpSecCertificateUpdates) {
let (updated, removed) = updates.unpack();
for info in updated {
let key = info.asn_key();
self.0.insert(key, info);
}
for key in removed {
self.0.remove(&key);
}
}
}
//------------ BgpSecCertInfo ----------------------------------------------
/// An issued BGPSec certificate under a resource class
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct BgpSecCertInfo {
asn: Asn,
public_key: PublicKey,
serial: Serial,
expires: Time,
cert: Base64,
}
impl BgpSecCertInfo {
fn new(asn: Asn, cert: Cert) -> Self {
let public_key = cert.subject_public_key_info().clone();
let serial = cert.serial_number();
let expires = cert.validity().not_after();
let cert = Base64::from(&cert);
BgpSecCertInfo {
asn,
public_key,
serial,
expires,
cert,
}
}
pub fn asn_key(&self) -> BgpSecAsnKey {
BgpSecAsnKey::new(self.asn, self.public_key.key_identifier())
}
pub fn asn(&self) -> Asn {
self.asn
}
pub fn public_key(&self) -> &PublicKey {
&self.public_key
}
pub fn serial(&self) -> Serial {
self.serial
}
pub fn expires(&self) -> Time {
self.expires
}
pub fn cert(&self) -> &Base64 {
&self.cert
}
}
//------------ BgpSecDefinitions -------------------------------------------
/// All BGPSec definitions held by a CA.
///
/// Actual BGPSec certificates will be issued under the relevant
/// resource classes. The resulting published objects are held by
/// the CaObjects structure.
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct BgpSecDefinitions(HashMap<BgpSecAsnKey, StoredBgpSecCsr>);
impl BgpSecDefinitions {
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = (&BgpSecAsnKey, &StoredBgpSecCsr)> {
self.0.iter()
}
pub fn info_list(&self) -> BgpSecCsrInfoList {
BgpSecCsrInfoList::new(
self.0
.iter()
.map(|(key, csr)| BgpSecCsrInfo::new(key.asn(), key.key_identifier(), csr.csr().clone()))
.collect(),
)
}
pub fn get_stored_csr(&self, key: &BgpSecAsnKey) -> Option<&StoredBgpSecCsr> {
self.0.get(key)
}
pub fn has(&self, key: &BgpSecAsnKey) -> bool {
self.0.contains_key(key)
}
/// Inserts or updates the CSR entry for the given key.
pub fn add_or_replace(&mut self, key: BgpSecAsnKey, csr: StoredBgpSecCsr) {
self.0.insert(key, csr);
}
/// Removes the CSR entry for the given key.
pub fn remove(&mut self, key: &BgpSecAsnKey) -> bool {
self.0.remove(key).is_some()
}
}
//------------ StoredBgpSecCsr ---------------------------------------------
/// A stored BGP Sec CSR.
///
/// The original CSR is stored as a base64 structure in order to avoid
/// issues if (when?) our CSR parsing should become more strict in a
/// future release.
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct StoredBgpSecCsr {
since: Time,
key: PublicKey,
csr: Base64,
}
impl StoredBgpSecCsr {
pub fn key(&self) -> &PublicKey {
&self.key
}
pub fn csr(&self) -> &Base64 {
&self.csr
}
}
impl From<&BgpsecCsr> for StoredBgpSecCsr {
fn from(csr: &BgpsecCsr) -> Self {
let since = Time::now();
let key = csr.public_key().clone();
let binary = Base64::from_content(csr.to_captured().as_slice());
StoredBgpSecCsr {
since,
key,
csr: binary,
}
}
}
+142 -7
View File
@@ -14,9 +14,9 @@ use rpki::{
ResourceClassListResponse, ResourceClassName, RevocationRequest, RevocationResponse, SigningCert,
},
},
crypto::{KeyIdentifier, PublicKey},
repository::{
cert::{Cert, KeyUsage, Overclaim, TbsCert},
crypto::{KeyIdentifier, PublicKey},
resources::ResourceSet,
rta::RtaBuilder,
x509::{Serial, Time, Validity},
@@ -27,9 +27,10 @@ use rpki::{
use crate::{
commons::{
api::{
AspaCustomer, AspaDefinitionList, AspaDefinitionUpdates, AspaProvidersUpdate, CertAuthInfo,
DelegatedCertificate, IdCertPem, ObjectName, ParentCaContact, RcvdCert, RepositoryContact, Revocation,
RoaDefinition, RtaList, RtaName, RtaPrepResponse, StorableCaCommand, TaCertDetails, TrustAnchorLocator,
AspaCustomer, AspaDefinitionList, AspaDefinitionUpdates, AspaProvidersUpdate, BgpSecAsnKey,
BgpSecCsrInfoList, BgpSecDefinitionUpdates, CertAuthInfo, DelegatedCertificate, IdCertPem, ObjectName,
ParentCaContact, RcvdCert, RepositoryContact, Revocation, RoaDefinition, RtaList, RtaName, RtaPrepResponse,
StorableCaCommand, TaCertDetails, TrustAnchorLocator,
},
crypto::{CsrInfo, KrillSigner},
error::{Error, RoaDeltaError},
@@ -41,12 +42,14 @@ use crate::{
ca::{
events::ChildCertificateUpdates, ta_handle, AspaDefinitions, CaEvt, CaEvtDet, ChildDetails, Cmd, CmdDet,
DropReason, Ini, PreparedRta, ResourceClass, ResourceTaggedAttestation, RouteAuthorization,
RouteAuthorizationUpdates, Routes, RtaContentRequest, RtaPrepareRequest, Rtas, SignedRta,
RouteAuthorizationUpdates, Routes, RtaContentRequest, RtaPrepareRequest, Rtas, SignedRta, StoredBgpSecCsr,
},
config::{Config, IssuanceTimingConfig},
},
};
use super::BgpSecDefinitions;
//------------ Rfc8183Id ---------------------------------------------------
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
@@ -96,6 +99,9 @@ pub struct CertAuth {
#[serde(skip_serializing_if = "AspaDefinitions::is_empty", default)]
aspas: AspaDefinitions,
#[serde(skip_serializing_if = "BgpSecDefinitions::is_empty", default)]
bgpsec_defs: BgpSecDefinitions,
}
impl Aggregate for CertAuth {
@@ -109,14 +115,18 @@ impl Aggregate for CertAuth {
let (handle, _version, details) = event.unpack();
let id = details.unpack();
let repository = None;
let parents = HashMap::new();
let resources = HashMap::new();
let next_class_name = 0;
let resources = HashMap::new();
let children = HashMap::new();
let routes = Routes::default();
let rtas = Rtas::default();
let aspas = AspaDefinitions::default();
let repository = None;
let bgpsec_defs = BgpSecDefinitions::default();
Ok(CertAuth {
handle,
@@ -135,6 +145,7 @@ impl Aggregate for CertAuth {
routes,
rtas,
aspas,
bgpsec_defs,
})
}
@@ -375,6 +386,22 @@ impl Aggregate for CertAuth {
.unwrap()
.aspa_objects_updated(updates),
//-----------------------------------------------------------------------
// BGPSec
//-----------------------------------------------------------------------
CaEvtDet::BgpSecDefinitionAdded { key, csr } => self.bgpsec_defs.add_or_replace(key, csr),
CaEvtDet::BgpSecDefinitionUpdated { key, csr } => self.bgpsec_defs.add_or_replace(key, csr),
CaEvtDet::BgpSecDefinitionRemoved { key } => {
self.bgpsec_defs.remove(&key);
}
CaEvtDet::BgpSecCertificatesUpdated {
resource_class_name,
updates,
} => {
let rc = self.resources.get_mut(&resource_class_name).unwrap();
rc.bgpsec_certificates_updated(updates);
}
//-----------------------------------------------------------------------
// Publication
//-----------------------------------------------------------------------
@@ -460,6 +487,12 @@ impl Aggregate for CertAuth {
}
CmdDet::AspasRenew(config, signer) => self.aspas_renew(&config, &signer),
// BGPSec
CmdDet::BgpSecUpdateDefinitions(updates, config, signer) => {
self.bgpsec_definitions_update(updates, &config, &signer)
}
CmdDet::BgpSecRenew(config, signer) => self.bgpsec_renew(&config, &signer),
// Republish
CmdDet::RepoUpdate(contact, signer) => self.update_repo(contact, &signer),
@@ -1444,6 +1477,7 @@ impl CertAuth {
rcvd_cert,
&self.routes,
&self.aspas,
&self.bgpsec_defs,
config,
signer.deref(),
)?;
@@ -1898,6 +1932,107 @@ impl CertAuth {
}
}
/// # BGPSec
///
impl CertAuth {
pub fn bgpsec_definitions_show(&self) -> BgpSecCsrInfoList {
self.bgpsec_defs.info_list()
}
/// Process BGPSec Definition updates
pub fn bgpsec_definitions_update(
&self,
updates: BgpSecDefinitionUpdates,
config: &Config,
signer: &KrillSigner,
) -> KrillResult<Vec<CaEvt>> {
let mut res = vec![];
let (additions, removals) = updates.unpack();
// We keep a copy of the definitions so that we can:
// a. remove and then re-add definitions
// b. use the updated definitions to generate objects in
// applicable RCs
//
// (note: actual modifications of self are done when the events are applied)
let mut definitions = self.bgpsec_defs.clone();
for key in removals {
if !definitions.remove(&key) {
return Err(Error::BgpSecDefinitionUnknown(self.handle.clone(), key));
} else {
res.push(CaEvtDet::BgpSecDefinitionRemoved { key });
}
}
// Verify that the CSR in each 'addition' is valid. Then either add
// a new or update an existing definition.
for definition in additions {
// ensure the CSR is validly signed
definition
.csr()
.validate()
.map_err(|_| Error::BgpSecDefinitionInvalidlySigned(self.handle.clone(), definition.clone()))?;
let key = BgpSecAsnKey::from(&definition);
let csr = StoredBgpSecCsr::from(definition.csr());
// ensure this CA holds the AS
if !self.all_resources().contains_asn(key.asn()) {
return Err(Error::BgpSecDefinitionNotEntitled(self.handle.clone(), key));
}
if let Some(stored_csr) = definitions.get_stored_csr(&key) {
if stored_csr != &csr {
res.push(CaEvtDet::BgpSecDefinitionUpdated { key, csr: csr.clone() });
definitions.add_or_replace(key, csr);
}
} else {
res.push(CaEvtDet::BgpSecDefinitionAdded { key, csr: csr.clone() });
definitions.add_or_replace(key, csr);
}
}
// Process the updated BGPSec definitions in each RC and add/remove
// BGPSec certificates as needed.
for (rcn, rc) in self.resources.iter() {
let updates = rc.update_bgpsec_certs(&definitions, config, signer)?;
if !updates.is_empty() {
res.push(CaEvtDet::BgpSecCertificatesUpdated {
resource_class_name: rcn.clone(),
updates,
});
}
}
Ok(self.events_from_details(res))
}
/// Renew any BGPSec certificates if needed.
pub fn bgpsec_renew(&self, config: &Config, signer: &KrillSigner) -> KrillResult<Vec<CaEvt>> {
let mut evt_dets = vec![];
for (rcn, rc) in self.resources.iter() {
let updates = rc.renew_bgpsec_certs(&config.issuance_timing, signer)?;
if updates.contains_changes() {
info!(
"CA '{}' reissued BGPSec certificates under RC '{}' before they would expire",
self.handle, rcn
);
evt_dets.push(CaEvtDet::BgpSecCertificatesUpdated {
resource_class_name: rcn.clone(),
updates,
});
}
}
Ok(self.events_from_details(evt_dets))
}
}
/// # Resource Tagged Attestations
///
impl CertAuth {
+6 -1
View File
@@ -4,7 +4,8 @@ use chrono::Duration;
use rpki::{
ca::{idcert::IdCert, idexchange::ChildHandle, provisioning::ResourceClassName},
repository::{crypto::KeyIdentifier, resources::ResourceSet, x509::Time},
crypto::KeyIdentifier,
repository::{resources::ResourceSet, x509::Time},
};
use crate::{
@@ -153,6 +154,10 @@ pub struct ChildCertificates {
}
impl ChildCertificates {
pub fn is_empty(&self) -> bool {
self.issued.is_empty() && self.suspended.is_empty()
}
pub fn certificate_issued(&mut self, issued: DelegatedCertificate) {
let ki = issued.cert().subject_key_identifier();
self.issued.insert(ki, issued);
+38 -2
View File
@@ -19,8 +19,8 @@ use crate::{
commons::{
actor::Actor,
api::{
AspaCustomer, AspaDefinitionUpdates, AspaProvidersUpdate, ParentCaContact, RcvdCert, RepositoryContact,
RtaName, StorableCaCommand, StorableRcEntitlement,
AspaCustomer, AspaDefinitionUpdates, AspaProvidersUpdate, BgpSecDefinitionUpdates, ParentCaContact,
RcvdCert, RepositoryContact, RtaName, StorableCaCommand, StorableRcEntitlement,
},
crypto::KrillSigner,
eventsourcing::{self, StoredCommand},
@@ -175,9 +175,21 @@ pub enum CmdDet {
// will only be stored if there are any updates to be done.
AspasRenew(Arc<Config>, Arc<KrillSigner>),
// ------------------------------------------------------------
// BGPSec Support
// ------------------------------------------------------------
// Update BgpSecDefinitions
BgpSecUpdateDefinitions(BgpSecDefinitionUpdates, Arc<Config>, Arc<KrillSigner>),
// Re-issue any and all BgpSec certificates which would otherwise
// expire in some time.
BgpSecRenew(Arc<Config>, Arc<KrillSigner>),
// ------------------------------------------------------------
// Publishing
// ------------------------------------------------------------
// Update the repository where this CA publishes
RepoUpdate(RepositoryContact, Arc<KrillSigner>),
@@ -310,6 +322,12 @@ impl From<CmdDet> for StorableCaCommand {
}
CmdDet::AspasRenew(_, _) => StorableCaCommand::ReissueBeforeExpiring,
// ------------------------------------------------------------
// BGPSec Support
// ------------------------------------------------------------
CmdDet::BgpSecUpdateDefinitions(_, _, _) => StorableCaCommand::BgpSecDefinitionUpdates,
CmdDet::BgpSecRenew(_, _) => StorableCaCommand::ReissueBeforeExpiring,
// ------------------------------------------------------------
// Publishing
// ------------------------------------------------------------
@@ -549,6 +567,24 @@ impl CmdDet {
)
}
//-------------------------------------------------------------------------------
// BGPSec
//-------------------------------------------------------------------------------
pub fn bgpsec_update_definitions(
ca: &CaHandle,
updates: BgpSecDefinitionUpdates,
config: Arc<Config>,
signer: Arc<KrillSigner>,
actor: &Actor,
) -> Cmd {
eventsourcing::SentCommand::new(
ca,
None,
CmdDet::BgpSecUpdateDefinitions(updates, config, signer),
actor,
)
}
//-------------------------------------------------------------------------------
// Resource Tagged Attestations
//-------------------------------------------------------------------------------
+120 -4
View File
@@ -6,15 +6,16 @@ use rpki::{
idexchange::{CaHandle, ChildHandle, ParentHandle},
provisioning::{IssuanceRequest, ParentResourceClassName, ResourceClassName, RevocationRequest},
},
repository::{crypto::KeyIdentifier, resources::ResourceSet},
crypto::KeyIdentifier,
repository::resources::ResourceSet,
};
use crate::{
commons::{
api::{
AspaCustomer, AspaDefinition, AspaProvidersUpdate, DelegatedCertificate, ObjectName, ParentCaContact,
RcvdCert, RepositoryContact, RevokedObject, RoaAggregateKey, RtaName, SuspendedCert, TaCertDetails,
UnsuspendedCert,
AspaCustomer, AspaDefinition, AspaProvidersUpdate, BgpSecAsnKey, DelegatedCertificate, ObjectName,
ParentCaContact, RcvdCert, RepositoryContact, RevokedObject, RoaAggregateKey, RtaName, SuspendedCert,
TaCertDetails, UnsuspendedCert,
},
crypto::KrillSigner,
eventsourcing::StoredEvent,
@@ -26,6 +27,8 @@ use crate::{
},
};
use super::{BgpSecCertInfo, StoredBgpSecCsr};
//------------ Ini -----------------------------------------------------------
pub type Ini = StoredEvent<IniDet>;
@@ -417,6 +420,50 @@ impl AspaObjectsUpdates {
}
}
//------------ BgpSecCertificateUpdates ------------------------------------
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct BgpSecCertificateUpdates {
#[serde(skip_serializing_if = "Vec::is_empty", default)]
updated: Vec<BgpSecCertInfo>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
removed: Vec<BgpSecAsnKey>,
}
impl BgpSecCertificateUpdates {
pub fn is_empty(&self) -> bool {
self.updated.is_empty() && self.removed.is_empty()
}
pub fn len(&self) -> usize {
self.updated.len() + self.removed.len()
}
pub fn contains_changes(&self) -> bool {
!self.is_empty()
}
pub fn updated(&self) -> &Vec<BgpSecCertInfo> {
&self.updated
}
pub fn removed(&self) -> &Vec<BgpSecAsnKey> {
&self.removed
}
pub fn unpack(self) -> (Vec<BgpSecCertInfo>, Vec<BgpSecAsnKey>) {
(self.updated, self.removed)
}
pub fn add_updated(&mut self, update: BgpSecCertInfo) {
self.updated.push(update);
}
pub fn add_removed(&mut self, remove: BgpSecAsnKey) {
self.removed.push(remove);
}
}
//------------ ChildCertificateUpdates -------------------------------------
/// Describes an update to the set of ROAs under a ResourceClass.
@@ -693,6 +740,24 @@ pub enum CaEvtDet {
updates: AspaObjectsUpdates,
},
// BGPSec
BgpSecDefinitionAdded {
key: BgpSecAsnKey,
csr: StoredBgpSecCsr,
},
BgpSecDefinitionUpdated {
key: BgpSecAsnKey,
csr: StoredBgpSecCsr,
},
BgpSecDefinitionRemoved {
key: BgpSecAsnKey,
},
BgpSecCertificatesUpdated {
// Tracks the actual BGPSec certificates (re-)issued in a resource class
resource_class_name: ResourceClassName,
updates: BgpSecCertificateUpdates,
},
// Publishing
RepoUpdated {
// Adds the repository contact for this CA so that publication can commence,
@@ -1092,6 +1157,57 @@ impl fmt::Display for CaEvtDet {
Ok(())
}
// BGPSec
CaEvtDet::BgpSecDefinitionAdded { key, .. } => {
write!(
f,
"added BGPSec definition for ASN: {} and key id: {}",
key.asn(),
key.key_identifier()
)
}
CaEvtDet::BgpSecDefinitionUpdated { key, .. } => {
write!(
f,
"updated CSR for BGPSec definition for ASN: {} and key id: {}",
key.asn(),
key.key_identifier()
)
}
CaEvtDet::BgpSecDefinitionRemoved { key } => {
write!(
f,
"removed BGPSec definition for ASN: {} and key id: {}",
key.asn(),
key.key_identifier()
)
}
CaEvtDet::BgpSecCertificatesUpdated {
resource_class_name,
updates,
} => {
write!(
f,
"updated BGPSec certificates under resource class '{}'",
resource_class_name
)?;
let updated = updates.updated();
if !updated.is_empty() {
write!(f, " added: ")?;
for cert in updated {
write!(f, "{} ", ObjectName::from(cert))?;
}
}
let removed = updates.removed();
if !removed.is_empty() {
write!(f, " removed: ")?;
for key in removed {
write!(f, "{} ", ObjectName::from(key))?;
}
}
Ok(())
}
// Publishing
CaEvtDet::RepoUpdated { contact } => {
write!(f, "updated repository to remote server: {}", contact.service_uri())
+2 -1
View File
@@ -9,7 +9,8 @@ use rpki::{
IssuanceRequest, RequestResourceLimit, ResourceClassEntitlements, ResourceClassName, RevocationRequest,
},
},
repository::{crypto::KeyIdentifier, resources::ResourceSet, x509::Time},
crypto::KeyIdentifier,
repository::{resources::ResourceSet, x509::Time},
};
use crate::{
+41 -3
View File
@@ -15,14 +15,15 @@ use rpki::{
publication,
publication::{ListReply, Publish, PublishDelta, Update, Withdraw},
},
repository::{crypto::KeyIdentifier, resources::ResourceSet},
crypto::KeyIdentifier,
repository::resources::ResourceSet,
uri,
};
use crate::{
commons::{
actor::Actor,
api::{rrdp::PublishElement, Timestamp},
api::{rrdp::PublishElement, BgpSecCsrInfoList, BgpSecDefinitionUpdates, Timestamp},
api::{
AddChildRequest, AspaCustomer, AspaDefinitionList, AspaDefinitionUpdates, AspaProvidersUpdate,
CaCommandDetails, CaCommandResult, CertAuthList, CertAuthSummary, ChildCaInfo, CommandHistory,
@@ -1824,6 +1825,32 @@ impl CaManager {
}
}
/// # BGPSec functions
///
impl CaManager {
pub async fn ca_bgpsec_definitions_show(&self, ca: CaHandle) -> KrillResult<BgpSecCsrInfoList> {
let ca = self.get_ca(&ca).await?;
Ok(ca.bgpsec_definitions_show())
}
pub async fn ca_bgpsec_definitions_update(
&self,
ca: CaHandle,
updates: BgpSecDefinitionUpdates,
actor: &Actor,
) -> KrillResult<()> {
self.send_command(CmdDet::bgpsec_update_definitions(
&ca,
updates,
self.config.clone(),
self.signer.clone(),
actor,
))
.await?;
Ok(())
}
}
/// # Route Authorization functions
///
impl CaManager {
@@ -1883,11 +1910,22 @@ impl CaManager {
if let Err(e) = self.send_command(cmd).await {
error!("Renewing ASPAs for CA '{}' failed with error: {}", ca, e);
}
let cmd = Cmd::new(
&ca,
None,
CmdDet::BgpSecRenew(self.config.clone(), self.signer.clone()),
actor,
);
if let Err(e) = self.send_command(cmd).await {
error!("Renewing BGPSec certificates for CA '{}' failed with error: {}", ca, e);
}
}
Ok(())
}
/// Force the reissuance of all ROAs in all CAs. This function was added
/// Force the re-issuance of all ROAs in all CAs. This function was added
/// because we need to re-issue ROAs in Krill 0.9.3 to force that a short
/// subject CN is used for the EE certificate: i.e. the SKI rather than the
/// full public key. But there may also be other cases in future where
+3
View File
@@ -7,6 +7,9 @@ use crate::commons::error::Error;
mod aspa;
pub use self::aspa::*;
mod bgpsec;
pub use self::bgpsec::*;
mod certauth;
pub use self::certauth::CertAuth;
pub use self::certauth::Rfc8183Id;
+121 -7
View File
@@ -14,11 +14,11 @@ use chrono::Duration;
use rpki::{
ca::{idexchange::CaHandle, provisioning::ResourceClassName, publication::Base64},
crypto::{DigestAlgorithm, PublicKey},
repository::{
aspa::Aspa,
cert::Cert,
crl::{Crl, TbsCertList},
crypto::{DigestAlgorithm, PublicKey},
manifest::{FileAndHash, Manifest, ManifestContent},
roa::Roa,
sigobj::SignedObjectBuilder,
@@ -44,7 +44,7 @@ use crate::{
},
};
use super::AspaObjectsUpdates;
use super::{AspaObjectsUpdates, BgpSecCertInfo, BgpSecCertificateUpdates};
//------------ CaObjectsStore ----------------------------------------------
@@ -103,6 +103,12 @@ impl PreSaveEventListener<CertAuth> for CaObjectsStore {
} => {
objects.update_aspas(resource_class_name, updates, timing, signer)?;
}
super::CaEvtDet::BgpSecCertificatesUpdated {
resource_class_name,
updates,
} => {
objects.update_bgpsec_certs(resource_class_name, updates, timing, signer)?;
}
super::CaEvtDet::ChildCertificatesUpdated {
resource_class_name,
updates,
@@ -498,6 +504,18 @@ impl CaObjects {
rco.update_aspas(updates, timing, signer)
}
// Update the BGPSec certificates in the current set
fn update_bgpsec_certs(
&mut self,
rcn: &ResourceClassName,
updates: &BgpSecCertificateUpdates,
timing: &IssuanceTimingConfig,
signer: &KrillSigner,
) -> KrillResult<()> {
let rco = self.get_class_mut(rcn)?;
rco.update_bgpsec_certs(updates, timing, signer)
}
// Update the delegated certificates in the current set
fn update_certs(
&mut self,
@@ -668,6 +686,19 @@ impl ResourceClassObjects {
}
}
fn update_bgpsec_certs(
&mut self,
updates: &BgpSecCertificateUpdates,
timing: &IssuanceTimingConfig,
signer: &KrillSigner,
) -> KrillResult<()> {
match self.keys.borrow_mut() {
ResourceClassKeyState::Current(state) => state.current_set.update_bgpsec_certs(updates, timing, signer),
ResourceClassKeyState::Staging(state) => state.current_set.update_bgpsec_certs(updates, timing, signer),
ResourceClassKeyState::Old(state) => state.current_set.update_bgpsec_certs(updates, timing, signer),
}
}
fn update_certs(
&mut self,
cert_updates: &ChildCertificateUpdates,
@@ -825,6 +856,12 @@ pub struct CurrentKeyObjectSet {
roas: HashMap<ObjectName, PublishedRoa>,
#[serde(with = "objects_to_aspas_serde", skip_serializing_if = "HashMap::is_empty", default)]
aspas: HashMap<ObjectName, PublishedAspa>,
#[serde(
with = "objects_to_bgpsec_certs_serde",
skip_serializing_if = "HashMap::is_empty",
default
)]
bgpsec_certs: HashMap<ObjectName, BgpSecCertInfo>,
#[serde(with = "objects_to_certs_serde")]
certs: HashMap<ObjectName, PublishedCert>,
}
@@ -834,12 +871,14 @@ impl CurrentKeyObjectSet {
basic: BasicKeyObjectSet,
roas: HashMap<ObjectName, PublishedRoa>,
aspas: HashMap<ObjectName, PublishedAspa>,
bgpsec_certs: HashMap<ObjectName, BgpSecCertInfo>,
certs: HashMap<ObjectName, PublishedCert>,
) -> Self {
CurrentKeyObjectSet {
basic,
roas,
aspas,
bgpsec_certs,
certs,
}
}
@@ -873,6 +912,13 @@ impl CurrentKeyObjectSet {
));
}
for (name, bgpsec_cert) in &self.bgpsec_certs {
elements.push(PublishElement::new(
bgpsec_cert.cert().clone(),
base_uri.join(name.as_bytes()).unwrap(),
));
}
for (name, cert) in &self.certs {
elements.push(PublishElement::new(
Base64::from(cert.as_ref()),
@@ -924,6 +970,29 @@ impl CurrentKeyObjectSet {
self.reissue(timing, signer)
}
fn update_bgpsec_certs(
&mut self,
updates: &BgpSecCertificateUpdates,
timing: &IssuanceTimingConfig,
signer: &KrillSigner,
) -> KrillResult<()> {
for bgpsec_cert in updates.updated() {
let name = ObjectName::from(bgpsec_cert);
if let Some(old) = self.bgpsec_certs.insert(name, bgpsec_cert.clone()) {
self.revocations.add(Revocation::from(&old));
}
}
for removed in updates.removed() {
let name = ObjectName::from(removed);
if let Some(old) = self.bgpsec_certs.remove(&name) {
self.revocations.add(Revocation::from(&old));
}
}
self.reissue(timing, signer)
}
fn update_certs(
&mut self,
cert_updates: &ChildCertificateUpdates,
@@ -998,7 +1067,7 @@ impl CurrentKeyObjectSet {
}
fn reissue_mft(&self, new_crl: &PublishedCrl, signer: &KrillSigner) -> KrillResult<PublishedManifest> {
ManifestBuilder::with_objects(new_crl, &self.roas, &self.aspas, &self.certs)
ManifestBuilder::with_objects(new_crl, &self.roas, &self.aspas, &self.certs, &self.bgpsec_certs)
.build_new_mft(&self.signing_cert, self.next(), signer)
.map(|m| m.into())
}
@@ -1010,6 +1079,7 @@ impl From<BasicKeyObjectSet> for CurrentKeyObjectSet {
basic,
roas: HashMap::new(),
aspas: HashMap::new(),
bgpsec_certs: HashMap::new(),
certs: HashMap::new(),
}
}
@@ -1071,13 +1141,13 @@ mod objects_to_aspas_serde {
use serde::de::{Deserialize, Deserializer};
use serde::ser::Serializer;
#[derive(Debug, Deserialize)]
struct NameRoaItem {
struct NameItem {
name: ObjectName,
aspa: PublishedAspa,
}
#[derive(Debug, Serialize)]
struct NameRoaItemRef<'a> {
struct NameItemRef<'a> {
name: &'a ObjectName,
aspa: &'a PublishedAspa,
}
@@ -1086,7 +1156,7 @@ mod objects_to_aspas_serde {
where
S: Serializer,
{
serializer.collect_seq(map.iter().map(|(name, aspa)| NameRoaItemRef { name, aspa }))
serializer.collect_seq(map.iter().map(|(name, aspa)| NameItemRef { name, aspa }))
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<HashMap<ObjectName, PublishedAspa>, D::Error>
@@ -1094,13 +1164,49 @@ mod objects_to_aspas_serde {
D: Deserializer<'de>,
{
let mut map = HashMap::new();
for item in Vec::<NameRoaItem>::deserialize(deserializer)? {
for item in Vec::<NameItem>::deserialize(deserializer)? {
map.insert(item.name, item.aspa);
}
Ok(map)
}
}
mod objects_to_bgpsec_certs_serde {
use super::*;
use serde::de::{Deserialize, Deserializer};
use serde::ser::Serializer;
#[derive(Debug, Deserialize)]
struct NameItem {
name: ObjectName,
bgpsec_cert: BgpSecCertInfo,
}
#[derive(Debug, Serialize)]
struct NameItemRef<'a> {
name: &'a ObjectName,
bgpsec_cert: &'a BgpSecCertInfo,
}
pub fn serialize<S>(map: &HashMap<ObjectName, BgpSecCertInfo>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_seq(map.iter().map(|(name, bgpsec_cert)| NameItemRef { name, bgpsec_cert }))
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<HashMap<ObjectName, BgpSecCertInfo>, D::Error>
where
D: Deserializer<'de>,
{
let mut map = HashMap::new();
for item in Vec::<NameItem>::deserialize(deserializer)? {
map.insert(item.name, item.bgpsec_cert);
}
Ok(map)
}
}
mod objects_to_certs_serde {
use super::*;
@@ -1533,6 +1639,7 @@ impl ManifestBuilder {
roas: &HashMap<ObjectName, PublishedRoa>,
aspas: &HashMap<ObjectName, PublishedAspa>,
certs: &HashMap<ObjectName, PublishedCert>,
bgpsec_certs: &HashMap<ObjectName, BgpSecCertInfo>,
) -> Self {
let mut entries: HashMap<Bytes, Bytes> = HashMap::new();
@@ -1557,6 +1664,13 @@ impl ManifestBuilder {
entries.insert(name.clone().into(), hash);
}
// Add all bgpsec certs
for (name, info) in bgpsec_certs {
let hash = info.cert().to_hash();
let hash_bytes = Bytes::copy_from_slice(hash.as_slice());
entries.insert(name.clone().into(), hash_bytes);
}
ManifestBuilder {
this_update: crl.this_update(),
next_update: crl.next_update(),
+131 -12
View File
@@ -8,9 +8,9 @@ use rpki::{
IssuanceRequest, RequestResourceLimit, ResourceClassEntitlements, ResourceClassName, RevocationRequest,
},
},
crypto::KeyIdentifier,
repository::{
cert::Cert,
crypto::KeyIdentifier,
resources::ResourceSet,
x509::{Time, Validity},
},
@@ -33,7 +33,7 @@ use crate::{
},
};
use super::AspaDefinitions;
use super::{AspaDefinitions, BgpSecCertificateUpdates, BgpSecCertificates, BgpSecDefinitions};
//------------ ResourceClass -----------------------------------------------
@@ -60,6 +60,11 @@ pub struct ResourceClass {
#[serde(skip_serializing_if = "AspaObjects::is_empty", default)]
aspas: AspaObjects,
#[serde(skip_serializing_if = "BgpSecCertificates::is_empty", default)]
bgpsec_certificates: BgpSecCertificates,
#[serde(skip_serializing_if = "ChildCertificates::is_empty", default)]
certificates: ChildCertificates,
last_key_change: Time,
@@ -85,6 +90,7 @@ impl ResourceClass {
roas: Roas::default(),
aspas: AspaObjects::default(),
certificates: ChildCertificates::default(),
bgpsec_certificates: BgpSecCertificates::default(),
last_key_change: Time::now(),
key_state: KeyState::create(pending_key),
}
@@ -99,6 +105,7 @@ impl ResourceClass {
roas: Roas::default(),
aspas: AspaObjects::default(),
certificates: ChildCertificates::default(),
bgpsec_certificates: BgpSecCertificates::default(),
last_key_change: Time::now(),
key_state: KeyState::create(pending_key),
}
@@ -188,12 +195,14 @@ impl ResourceClass {
///
impl ResourceClass {
/// Returns event details for receiving the certificate.
#[allow(clippy::too_many_arguments)]
pub fn update_received_cert(
&self,
handle: &CaHandle,
rcvd_cert: RcvdCert,
all_routes: &Routes,
all_aspas: &AspaDefinitions,
all_bgpsecs: &BgpSecDefinitions,
config: &Config,
signer: &KrillSigner,
) -> KrillResult<Vec<CaEvtDet>> {
@@ -218,6 +227,9 @@ impl ResourceClass {
let roa_updates = self.roas.update(all_routes, &current_key, config, signer)?;
let aspa_updates = self.aspas.update(all_aspas, &current_key, config, signer)?;
let bgpsec_updates = self
.bgpsec_certificates
.update(all_bgpsecs, &current_key, config, signer)?;
let mut events = vec![CaEvtDet::KeyPendingToActive {
resource_class_name: self.name.clone(),
@@ -238,12 +250,26 @@ impl ResourceClass {
})
}
if bgpsec_updates.contains_changes() {
events.push(CaEvtDet::BgpSecCertificatesUpdated {
resource_class_name: self.name.clone(),
updates: bgpsec_updates,
})
}
Ok(events)
}
}
KeyState::Active(current) => {
self.update_rcvd_cert_current(handle, current, rcvd_cert, all_routes, all_aspas, config, signer)
}
KeyState::Active(current) => self.update_rcvd_cert_current(
handle,
current,
rcvd_cert,
all_routes,
all_aspas,
all_bgpsecs,
config,
signer,
),
KeyState::RollPending(pending, current) => {
if rcvd_cert_ki == pending.key_id() {
let new_key = CertifiedKey::create(rcvd_cert);
@@ -252,7 +278,16 @@ impl ResourceClass {
new_key,
}])
} else {
self.update_rcvd_cert_current(handle, current, rcvd_cert, all_routes, all_aspas, config, signer)
self.update_rcvd_cert_current(
handle,
current,
rcvd_cert,
all_routes,
all_aspas,
all_bgpsecs,
config,
signer,
)
}
}
KeyState::RollNew(new, current) => {
@@ -263,12 +298,30 @@ impl ResourceClass {
rcvd_cert,
}])
} else {
self.update_rcvd_cert_current(handle, current, rcvd_cert, all_routes, all_aspas, config, signer)
self.update_rcvd_cert_current(
handle,
current,
rcvd_cert,
all_routes,
all_aspas,
all_bgpsecs,
config,
signer,
)
}
}
KeyState::RollOld(current, _old) => {
// We will never request a new certificate for an old key
self.update_rcvd_cert_current(handle, current, rcvd_cert, all_routes, all_aspas, config, signer)
self.update_rcvd_cert_current(
handle,
current,
rcvd_cert,
all_routes,
all_aspas,
all_bgpsecs,
config,
signer,
)
}
}
}
@@ -279,8 +332,9 @@ impl ResourceClass {
handle: &CaHandle,
current_key: &CurrentKey,
rcvd_cert: RcvdCert,
routes: &Routes,
aspas: &AspaDefinitions,
all_routes: &Routes,
all_aspas: &AspaDefinitions,
all_bgpsecs: &BgpSecDefinitions,
config: &Config,
signer: &KrillSigner,
) -> KrillResult<Vec<CaEvtDet>> {
@@ -325,7 +379,7 @@ impl ResourceClass {
// Re-issue ROAs based on updated resources.
// Note that route definitions will not have changed in this case, but the decision logic is all the same.
{
let updates = self.roas.update(routes, &updated_key, config, signer)?;
let updates = self.roas.update(all_routes, &updated_key, config, signer)?;
if !updates.is_empty() {
res.push(CaEvtDet::RoasUpdated {
resource_class_name: self.name.clone(),
@@ -337,7 +391,7 @@ impl ResourceClass {
// Re-issue ASPA objects based on updated resources.
// Note that aspa definitions will not have changed in this case, but the decision logic is all the same.
{
let updates = self.aspas.update(aspas, &updated_key, config, signer)?;
let updates = self.aspas.update(all_aspas, &updated_key, config, signer)?;
if !updates.is_empty() {
res.push(CaEvtDet::AspaObjectsUpdated {
resource_class_name: self.name.clone(),
@@ -345,6 +399,20 @@ impl ResourceClass {
})
}
}
// Re-issue BGPSec certificates based on updated resources.
// Note that definitions will not have changed in this case, but the decision logic is all the same.
{
let updates = self
.bgpsec_certificates
.update(all_bgpsecs, &updated_key, config, signer)?;
if !updates.is_empty() {
res.push(CaEvtDet::BgpSecCertificatesUpdated {
resource_class_name: self.name.clone(),
updates,
})
}
}
} else {
info!(
"Received new certificate for CA '{}' under RC '{}', valid until: {}",
@@ -561,6 +629,14 @@ impl ResourceClass {
events.push(certs_updated);
}
let bgpsec_updates = self.bgpsec_certificates.renew(new_key, None, issuance_timing, signer)?;
if !bgpsec_updates.is_empty() {
events.push(CaEvtDet::BgpSecCertificatesUpdated {
resource_class_name: self.name.clone(),
updates: bgpsec_updates,
});
}
Ok(events)
}
} else {
@@ -731,6 +807,49 @@ impl ResourceClass {
}
}
/// # BGPSec
///
impl ResourceClass {
/// Updates the BGPSec certificates in accordance with the supplied definitions
/// and the resources (still) held in this resource class
pub fn update_bgpsec_certs(
&self,
definitions: &BgpSecDefinitions,
config: &Config,
signer: &KrillSigner,
) -> KrillResult<BgpSecCertificateUpdates> {
if let Ok(key) = self.get_current_key() {
self.bgpsec_certificates.update(definitions, key, config, signer)
} else {
debug!("no BGPSec certificates to update - resource class has no current key");
Ok(BgpSecCertificateUpdates::default())
}
}
/// Renew BGPSec certificates that would expire otherwise.
pub fn renew_bgpsec_certs(
&self,
issuance_timing: &IssuanceTimingConfig,
signer: &KrillSigner,
) -> KrillResult<BgpSecCertificateUpdates> {
if let Ok(key) = self.get_current_key() {
let renew_threshold =
Some(Time::now() + Duration::weeks(issuance_timing.timing_bgpsec_reissue_weeks_before));
self.bgpsec_certificates
.renew(key, renew_threshold, issuance_timing, signer)
} else {
debug!("no BGPSec certificates to renew - resource class has no current key");
Ok(BgpSecCertificateUpdates::default())
}
}
/// Apply BGPSec Certificate changes from events
pub fn bgpsec_certificates_updated(&mut self, updates: BgpSecCertificateUpdates) {
self.bgpsec_certificates.updated(updates)
}
}
/// # Resource Tagged Attestations (RTA)
///
impl ResourceClass {
+4
View File
@@ -404,6 +404,10 @@ pub struct Roas {
}
impl Roas {
pub fn is_empty(&self) -> bool {
self.simple.is_empty() && self.aggregate.is_empty()
}
pub fn get(&self, auth: &RouteAuthorization) -> Option<&RoaInfo> {
self.simple.get(auth)
}
+2 -7
View File
@@ -4,13 +4,8 @@ use bytes::Bytes;
use rpki::{
ca::{provisioning::ResourceClassName, publication::Base64},
repository::{
crypto::{DigestAlgorithm, KeyIdentifier},
resources::ResourceSet,
rta,
sigobj::MessageDigest,
x509::Validity,
},
crypto::{DigestAlgorithm, KeyIdentifier},
repository::{resources::ResourceSet, rta, sigobj::MessageDigest, x509::Validity},
};
use crate::commons::{
+16
View File
@@ -211,6 +211,14 @@ impl ConfigDefaults {
4
}
fn timing_bgpsec_valid_weeks() -> i64 {
52
}
fn timing_bgpsec_reissue_weeks_before() -> i64 {
4
}
pub fn signers() -> Vec<SignerConfig> {
#[cfg(not(any(feature = "hsm-tests-kmip", feature = "hsm-tests-pkcs11")))]
{
@@ -489,6 +497,10 @@ pub struct IssuanceTimingConfig {
pub timing_aspa_valid_weeks: i64,
#[serde(default = "ConfigDefaults::timing_aspa_reissue_weeks_before")]
pub timing_aspa_reissue_weeks_before: i64,
#[serde(default = "ConfigDefaults::timing_bgpsec_valid_weeks")]
pub timing_bgpsec_valid_weeks: i64,
#[serde(default = "ConfigDefaults::timing_bgpsec_reissue_weeks_before")]
pub timing_bgpsec_reissue_weeks_before: i64,
}
impl IssuanceTimingConfig {
@@ -831,6 +843,8 @@ impl Config {
let timing_roa_reissue_weeks_before = ConfigDefaults::timing_roa_reissue_weeks_before();
let timing_aspa_valid_weeks = ConfigDefaults::timing_aspa_valid_weeks();
let timing_aspa_reissue_weeks_before = ConfigDefaults::timing_aspa_reissue_weeks_before();
let timing_bgpsec_valid_weeks = ConfigDefaults::timing_bgpsec_valid_weeks();
let timing_bgpsec_reissue_weeks_before = ConfigDefaults::timing_bgpsec_reissue_weeks_before();
let issuance_timing = IssuanceTimingConfig {
timing_publish_next_hours,
@@ -842,6 +856,8 @@ impl Config {
timing_roa_reissue_weeks_before,
timing_aspa_valid_weeks,
timing_aspa_reissue_weeks_before,
timing_bgpsec_valid_weeks,
timing_bgpsec_reissue_weeks_before,
};
let repository_retention = RepositoryRetentionConfig {
+51 -9
View File
@@ -40,7 +40,8 @@ use crate::{
bgp::BgpAnalysisAdvice,
error::Error,
eventsourcing::AggregateStoreError,
util::file, KrillResult,
util::file,
KrillResult,
},
constants::{
KRILL_ENV_HTTP_LOG_INFO, KRILL_ENV_UPGRADE_ONLY, KRILL_VERSION_MAJOR, KRILL_VERSION_MINOR, KRILL_VERSION_PATCH,
@@ -192,7 +193,8 @@ pub async fn start_krill_daemon(config: Arc<Config>) -> Result<(), Error> {
lock.handle_ctrl_c(),
#[cfg(unix)]
lock.handle_sig_term()
).map(|_| ())
)
.map(|_| ())
}
struct RequestLogger {
@@ -1102,6 +1104,7 @@ async fn api_cas(req: Request, path: &mut RequestPath) -> RoutingResult {
_ => render_unknown_method(),
},
Some("aspas") => api_ca_aspas(req, path, ca).await,
Some("bgpsec") => api_ca_bgpsec(req, path, ca).await,
Some("children") => api_ca_children(req, path, ca).await,
Some("history") => api_ca_history(req, path, ca).await,
@@ -1544,6 +1547,37 @@ async fn api_ca_aspas(req: Request, path: &mut RequestPath, ca: CaHandle) -> Rou
}
}
async fn api_ca_bgpsec(req: Request, path: &mut RequestPath, ca: CaHandle) -> RoutingResult {
// Handles /api/v1/cas/{ca}/bgpsec/:
// GET /api/v1/cas/{ca}/bgpsec/ -> List BGPSec Definitions
// POST /api/v1/cas/{ca}/bgpsec/ -> Send BgpSecDefinitionUpdates
match path.next() {
None => match *req.method() {
Method::GET => api_ca_bgpsec_definitions_show(req, ca).await,
Method::POST => api_ca_bgpsec_definitions_update(req, ca).await,
_ => render_unknown_method(),
},
_ => render_unknown_method(),
}
}
async fn api_ca_bgpsec_definitions_show(req: Request, ca: CaHandle) -> RoutingResult {
aa!(req, Permission::BGPSEC_READ, Handle::from(&ca), {
render_json_res(req.state().ca_bgpsec_definitions_show(ca).await)
})
}
async fn api_ca_bgpsec_definitions_update(req: Request, ca: CaHandle) -> RoutingResult {
aa!(req, Permission::BGPSEC_UPDATE, Handle::from(&ca), {
let actor = req.actor();
let server = req.state().clone();
match req.json().await {
Ok(updates) => render_empty_res(server.ca_bgpsec_definitions_update(ca, updates, &actor).await),
Err(e) => render_error(e),
}
})
}
async fn api_ca_children(req: Request, path: &mut RequestPath, ca: CaHandle) -> RoutingResult {
match path.path_arg() {
Some(child) => match path.next() {
@@ -2106,7 +2140,6 @@ async fn api_ca_rta_multi_sign(req: Request, ca: CaHandle, name: RtaName) -> Rou
})
}
/// A naive lock implementation used to prevent that two Krill instances
/// access the same krill data directory simultaneously.
struct KrillLock(PathBuf);
@@ -2116,12 +2149,19 @@ impl KrillLock {
let lock_file_path = config.data_dir.join("krill.lock");
if lock_file_path.exists() {
error!("Cannot start Krill: existing lock file found at: {}", lock_file_path.display());
error!(
"Cannot start Krill: existing lock file found at: {}",
lock_file_path.display()
);
::std::process::exit(1);
}
if let Err(e) = file::save(b"lock", &lock_file_path) {
error!("Cannot start Krill: cannot create lock file at: {}. Error: {}", lock_file_path.display(), e);
error!(
"Cannot start Krill: cannot create lock file at: {}. Error: {}",
lock_file_path.display(),
e
);
::std::process::exit(1);
}
@@ -2138,10 +2178,13 @@ impl KrillLock {
self.clean();
std::process::exit(0)
}
#[cfg(unix)]
async fn handle_sig_term(&self) -> KrillResult<()> {
tokio::signal::unix::signal(SignalKind::terminate()).unwrap().recv().await;
tokio::signal::unix::signal(SignalKind::terminate())
.unwrap()
.recv()
.await;
self.clean();
std::process::exit(0)
}
@@ -2174,4 +2217,3 @@ mod tests {
let _ = fs::remove_dir_all(dir);
}
}
+25 -22
View File
@@ -13,10 +13,8 @@ use openssl::{
use rpki::{
ca::idcert::IdCert,
repository::{
crypto::{KeyIdentifier, PublicKey, Signature, SignatureAlgorithm},
x509::{Time, Validity},
},
crypto::{signer::SigningAlgorithm, KeyIdentifier, PublicKey, Signature, SignatureAlgorithm},
repository::x509::{Time, Validity},
};
use crate::commons::{api::IdCertPem, error::KrillIoError, util::file};
@@ -70,40 +68,36 @@ struct HttpsSigner {
private: PKey<Private>,
}
impl rpki::repository::crypto::Signer for HttpsSigner {
impl rpki::crypto::Signer for HttpsSigner {
type KeyId = KeyIdentifier;
type Error = Error;
fn create_key(&self, _algorithm: rpki::repository::crypto::PublicKeyFormat) -> Result<Self::KeyId, Self::Error> {
fn create_key(&self, _algorithm: rpki::crypto::PublicKeyFormat) -> Result<Self::KeyId, Self::Error> {
unimplemented!("not needed in this context")
}
fn get_key_info(
&self,
_key: &Self::KeyId,
) -> Result<PublicKey, rpki::repository::crypto::signer::KeyError<Self::Error>> {
self.public_key_info()
.map_err(rpki::repository::crypto::signer::KeyError::Signer)
fn get_key_info(&self, _key: &Self::KeyId) -> Result<PublicKey, rpki::crypto::signer::KeyError<Self::Error>> {
self.public_key_info().map_err(rpki::crypto::signer::KeyError::Signer)
}
fn destroy_key(&self, _key: &Self::KeyId) -> Result<(), rpki::repository::crypto::signer::KeyError<Self::Error>> {
fn destroy_key(&self, _key: &Self::KeyId) -> Result<(), rpki::crypto::signer::KeyError<Self::Error>> {
unimplemented!("not needed in this context")
}
fn sign<D: AsRef<[u8]> + ?Sized>(
fn sign<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
&self,
_key: &Self::KeyId,
_algorithm: SignatureAlgorithm,
algorithm: Alg,
data: &D,
) -> Result<Signature, rpki::repository::crypto::SigningError<Self::Error>> {
self.sign(data).map_err(rpki::repository::crypto::SigningError::Signer)
) -> Result<Signature<Alg>, rpki::crypto::SigningError<Self::Error>> {
self.sign(algorithm, data).map_err(rpki::crypto::SigningError::Signer)
}
fn sign_one_off<D: AsRef<[u8]> + ?Sized>(
fn sign_one_off<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
&self,
_algorithm: SignatureAlgorithm,
_algorithm: Alg,
_data: &D,
) -> Result<(Signature, PublicKey), Self::Error> {
) -> Result<(Signature<Alg>, PublicKey), Self::Error> {
unimplemented!("not needed in this context")
}
@@ -140,11 +134,20 @@ impl HttpsSigner {
}
// See OpenSslSigner::sign_with_key for reference.
fn sign<D: AsRef<[u8]> + ?Sized>(&self, data: &D) -> Result<Signature, Error> {
fn sign<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
&self,
algorithm: Alg,
data: &D,
) -> Result<Signature<Alg>, Error> {
let signing_algorithm = algorithm.signing_algorithm();
if !matches!(signing_algorithm, SigningAlgorithm::RsaSha256) {
return Err(Error::SignerError("Only RSA SHA256 signing is supported.".to_string()));
}
let mut signer = ::openssl::sign::Signer::new(MessageDigest::sha256(), &self.private)?;
signer.update(data.as_ref())?;
let signature = Signature::new(SignatureAlgorithm::default(), Bytes::from(signer.sign_to_vec()?));
let signature = Signature::new(algorithm, Bytes::from(signer.sign_to_vec()?));
Ok(signature)
}
+22 -5
View File
@@ -21,11 +21,11 @@ use crate::{
actor::{Actor, ActorDef},
api::{
AddChildRequest, AllCertAuthIssues, AspaCustomer, AspaDefinitionList, AspaDefinitionUpdates,
AspaProvidersUpdate, CaCommandDetails, CaRepoDetails, CertAuthInfo, CertAuthInit, CertAuthIssues,
CertAuthList, CertAuthStats, ChildCaInfo, ChildrenConnectionStats, CommandHistory, CommandHistoryCriteria,
ParentCaContact, ParentCaReq, PublicationServerUris, PublisherDetails, RepositoryContact, RoaDefinition,
RoaDefinitionUpdates, RtaList, RtaName, RtaPrepResponse, ServerInfo, TaCertDetails, Timestamp,
UpdateChildRequest,
AspaProvidersUpdate, BgpSecCsrInfoList, BgpSecDefinitionUpdates, CaCommandDetails, CaRepoDetails,
CertAuthInfo, CertAuthInit, CertAuthIssues, CertAuthList, CertAuthStats, ChildCaInfo,
ChildrenConnectionStats, CommandHistory, CommandHistoryCriteria, ParentCaContact, ParentCaReq,
PublicationServerUris, PublisherDetails, RepositoryContact, RoaDefinition, RoaDefinitionUpdates, RtaList,
RtaName, RtaPrepResponse, ServerInfo, TaCertDetails, Timestamp, UpdateChildRequest,
},
bgp::{BgpAnalyser, BgpAnalysisReport, BgpAnalysisSuggestion},
crypto::KrillSignerBuilder,
@@ -850,6 +850,23 @@ impl KrillServer {
}
}
/// # Handle BGPSec requests
///
impl KrillServer {
pub async fn ca_bgpsec_definitions_show(&self, ca: CaHandle) -> KrillResult<BgpSecCsrInfoList> {
self.ca_manager.ca_bgpsec_definitions_show(ca).await
}
pub async fn ca_bgpsec_definitions_update(
&self,
ca: CaHandle,
updates: BgpSecDefinitionUpdates,
actor: &Actor,
) -> KrillResult<()> {
self.ca_manager.ca_bgpsec_definitions_update(ca, updates, actor).await
}
}
/// # Handle route authorization requests
///
impl KrillServer {
+1
View File
@@ -207,6 +207,7 @@ impl eventsourcing::PostSaveEventListener<CertAuth> for TaskQueue {
CaEvtDet::RoasUpdated { .. }
| CaEvtDet::AspaObjectsUpdated { .. }
| CaEvtDet::ChildCertificatesUpdated { .. }
| CaEvtDet::BgpSecCertificatesUpdated { .. }
| CaEvtDet::ChildKeyRevoked { .. }
| CaEvtDet::KeyPendingToNew { .. }
| CaEvtDet::KeyPendingToActive { .. }
+1
View File
@@ -205,6 +205,7 @@ impl Scheduler {
}
self.tasks.republish_if_needed(now());
self.tasks.renew_if_needed(now());
self.tasks.refresh_announcements_info(now());
#[cfg(feature = "multi-user")]
+2 -1
View File
@@ -14,7 +14,8 @@ use rpki::{
publication,
publication::{ListReply, PublicationCms, PublishDelta},
},
repository::{crypto::KeyIdentifier, x509::Time},
crypto::KeyIdentifier,
repository::x509::Time,
rrdp::Hash,
uri,
};
+27 -5
View File
@@ -21,7 +21,8 @@ use rpki::{
idexchange::{CaHandle, ChildHandle, ParentHandle, PublisherHandle},
provisioning::ResourceClassName,
},
repository::{crypto::KeyIdentifier, resources::ResourceSet},
crypto::KeyIdentifier,
repository::resources::ResourceSet,
uri,
};
@@ -33,10 +34,11 @@ use crate::{
},
commons::{
api::{
AddChildRequest, AspaCustomer, AspaDefinition, AspaDefinitionList, AspaProvidersUpdate, CertAuthInfo,
CertAuthInit, CertifiedKeyInfo, ObjectName, ParentCaContact, ParentCaReq, ParentStatuses,
PublicationServerUris, PublisherDetails, PublisherList, RepositoryContact, ResourceClassKeysInfo,
RoaDefinition, RoaDefinitionUpdates, RtaList, RtaName, RtaPrepResponse, TypedPrefix, UpdateChildRequest,
AddChildRequest, AspaCustomer, AspaDefinition, AspaDefinitionList, AspaProvidersUpdate, BgpSecAsnKey,
BgpSecCsrInfoList, BgpSecDefinition, CertAuthInfo, CertAuthInit, CertifiedKeyInfo, ObjectName,
ParentCaContact, ParentCaReq, ParentStatuses, PublicationServerUris, PublisherDetails, PublisherList,
RepositoryContact, ResourceClassKeysInfo, RoaDefinition, RoaDefinitionUpdates, RtaList, RtaName,
RtaPrepResponse, TypedPrefix, UpdateChildRequest,
},
bgp::{Announcement, BgpAnalysisReport, BgpAnalysisSuggestion},
crypto::SignSupport,
@@ -423,6 +425,26 @@ pub async fn ca_route_authorization_dryrun(ca: &CaHandle, updates: RoaDefinition
}
}
pub async fn ca_bgpsec_add(ca: &CaHandle, definition: BgpSecDefinition) {
krill_admin(Command::CertAuth(CaCommand::BgpSecAdd(ca.clone(), definition))).await;
}
pub async fn ca_bgpsec_add_expect_error(ca: &CaHandle, definition: BgpSecDefinition) {
krill_admin_expect_error(Command::CertAuth(CaCommand::BgpSecAdd(ca.clone(), definition))).await;
}
pub async fn ca_bgpsec_remove(ca: &CaHandle, key: BgpSecAsnKey) {
krill_admin(Command::CertAuth(CaCommand::BgpSecRemove(ca.clone(), key))).await;
}
pub async fn ca_bgpsec_list(ca: &CaHandle) -> BgpSecCsrInfoList {
let res = krill_admin(Command::CertAuth(CaCommand::BgpSecList(ca.clone()))).await;
match res {
ApiResponse::BgpSecDefinitions(list) => list,
_ => panic!("Expected BGPSec definitions"),
}
}
pub async fn ca_aspas_add(ca: &CaHandle, aspa: AspaDefinition) {
krill_admin(Command::CertAuth(CaCommand::AspasAddOrReplace(ca.clone(), aspa))).await;
}
+2 -5
View File
@@ -23,7 +23,7 @@ use crate::{
};
#[cfg(feature = "hsm")]
use rpki::repository::crypto::KeyIdentifier;
use rpki::crypto::KeyIdentifier;
#[cfg(feature = "hsm")]
use crate::{
@@ -297,10 +297,7 @@ pub trait UpgradeStore {
/// started, it will call this again - to do the final preparation for a migration -
/// knowing that no changes are added to the event history at this time. After this,
/// the migration will be finalised.
pub fn prepare_upgrade_data_migrations(
mode: UpgradeMode,
config: Arc<Config>,
) -> UpgradeResult<Option<UpgradeReport>> {
pub fn prepare_upgrade_data_migrations(mode: UpgradeMode, config: Arc<Config>) -> UpgradeResult<Option<UpgradeReport>> {
match upgrade_versions(config.as_ref()) {
None => Ok(None),
Some(versions) => {
+3 -2
View File
@@ -9,7 +9,8 @@ use rpki::{
idexchange::{CaHandle, ChildHandle, ParentHandle, RepoInfo},
provisioning::{IssuanceRequest, ResourceClassName, RevocationRequest},
},
repository::{crl::Crl, crypto::KeyIdentifier, manifest::Manifest, resources::ResourceSet, x509::Time},
crypto::KeyIdentifier,
repository::{crl::Crl, manifest::Manifest, resources::ResourceSet, x509::Time},
rrdp::Hash,
uri,
};
@@ -789,7 +790,7 @@ impl OldResourceClass {
certs: HashMap<ObjectName, PublishedCert>,
) -> CurrentKeyObjectSet {
let basic = Self::object_set_for_certified_key(key);
CurrentKeyObjectSet::new(basic, roas, HashMap::new(), certs)
CurrentKeyObjectSet::new(basic, roas, HashMap::new(), HashMap::new(), certs)
}
fn object_set_for_certified_key(key: &OldCertifiedKey) -> BasicKeyObjectSet {
+2 -3
View File
@@ -1,5 +1,3 @@
use rpki::repository::{crypto::KeyIdentifier, x509::Time};
use std::{
collections::{BTreeMap, HashMap},
fmt,
@@ -10,7 +8,8 @@ use rpki::{
idexchange::{CaHandle, ChildHandle, MyHandle, ParentHandle, PublisherHandle, ServiceUri},
provisioning::{RequestResourceLimit, ResourceClassName, RevocationRequest},
},
repository::resources::ResourceSet,
crypto::KeyIdentifier,
repository::{resources::ResourceSet, x509::Time},
};
use crate::{
+1 -1
View File
@@ -13,8 +13,8 @@ use rpki::{
provisioning::{IssuanceRequest, ResourceClassName, RevocationRequest},
publication::Base64,
},
crypto::KeyIdentifier,
repository::{
crypto::KeyIdentifier,
resources::ResourceSet,
roa::Roa,
x509::{Serial, Time},
@@ -12,7 +12,8 @@ use rpki::{
idcert::IdCert,
idexchange::{MyHandle, PublisherHandle},
},
repository::{crypto::KeyIdentifier, x509::Time},
crypto::KeyIdentifier,
repository::x509::Time,
rrdp::Hash,
uri,
};
Binary file not shown.
+170
View File
@@ -0,0 +1,170 @@
//! Perform functional tests on a Krill instance, using the API
//!
use std::fs;
use std::str::FromStr;
use bytes::Bytes;
use rpki::{
ca::{csr::BgpsecCsr, idexchange::CaHandle, provisioning::ResourceClassName},
repository::resources::{Asn, ResourceSet},
};
use krill::{
commons::api::{
AspaCustomer, AspaDefinition, AspaDefinitionList, AspaProvidersUpdate, BgpSecAsnKey, BgpSecCsrInfo,
BgpSecDefinition, ObjectName,
},
daemon::ca::ta_handle,
test::*,
};
use rpki::repository::aspa::ProviderAs;
#[tokio::test]
async fn functional_bgpsec() {
let krill_dir = start_krill_with_default_test_config(true, false, false, false).await;
info("##################################################################");
info("# #");
info("# Test BGPSec support. #");
info("# #");
info("# Uses the following lay-out: #");
info("# #");
info("# TA #");
info("# | #");
info("# testbed #");
info("# | #");
info("# CA #");
info("# #");
info("# #");
info("##################################################################");
info("");
let ta = ta_handle();
let testbed = ca_handle("testbed");
let ca = ca_handle("CA");
let ca_res = resources("AS65000", "10.0.0.0/16", "");
let ca_res_shrunk = resources("", "10.0.0.0/16", "");
let rcn_0 = rcn(0);
info("##################################################################");
info("# #");
info("# Wait for the *testbed* CA to get its certificate, this means #");
info("# that all CAs which are set up as part of krill_start under the #");
info("# testbed config have been set up. #");
info("# #");
info("##################################################################");
info("");
assert!(ca_contains_resources(&testbed, &ResourceSet::all()).await);
// Verify that the TA published expected objects
{
let mut expected_files = expected_mft_and_crl(&ta, &rcn_0).await;
expected_files.push(expected_issued_cer(&testbed, &rcn_0).await);
assert!(
will_publish_embedded(
"TA should have manifest, crl and cert for testbed",
&ta,
&expected_files
)
.await
);
}
{
info("##################################################################");
info("# #");
info("# Set up CA under testbed #");
info("# #");
info("##################################################################");
info("");
set_up_ca_with_repo(&ca).await;
set_up_ca_under_parent_with_resources(&ca, &testbed, &ca_res).await;
}
// short hand to expect published BGPSec certs under CA
async fn expect_bgpsec_objects(ca: &CaHandle, definitions: &[BgpSecCsrInfo]) {
let rcn_0 = ResourceClassName::from(0);
let mut expected_files = expected_mft_and_crl(ca, &rcn_0).await;
for csr_info in definitions {
expected_files.push(csr_info.object_name().to_string());
}
assert!(
will_publish_embedded(
"published BGPSec certificates do not match expectations",
ca,
&expected_files
)
.await
);
}
let csr_bytes = include_bytes!("../test-resources/bgpsec/router-csr.der");
let csr_bytes = Bytes::copy_from_slice(csr_bytes);
let csr = BgpsecCsr::decode(csr_bytes.as_ref()).unwrap();
let asn_owned = Asn::from_u32(65000);
let asn_not_owned = Asn::from_u32(65001);
let bgpsec_def_owned = BgpSecDefinition::new(asn_owned, csr.clone());
let bgpsec_def_not_owned = BgpSecDefinition::new(asn_not_owned, csr);
let bgpsec_def_key = BgpSecAsnKey::from(&bgpsec_def_owned);
// Refuse adding BGPSec definition for ASN which is not held
ca_bgpsec_add_expect_error(&ca, bgpsec_def_not_owned).await;
// Add BGPSec definition
{
ca_bgpsec_add(&ca, bgpsec_def_owned).await;
// List definitions
let definitions = ca_bgpsec_list(&ca).await.unpack();
assert_eq!(1, definitions.len());
// Expect it's published
expect_bgpsec_objects(&ca, &definitions).await;
}
// Shrink resources.
{
update_child(&testbed, &ca.convert(), &ca_res_shrunk).await;
ca_equals_resources(&ca, &ca_res_shrunk).await;
// Expect the definition still exists
let definitions = ca_bgpsec_list(&ca).await.unpack();
assert_eq!(1, definitions.len());
// But expect that the BGPSec certificate is removed.
expect_bgpsec_objects(&ca, &[]).await;
}
// Grow resources
{
update_child(&testbed, &ca.convert(), &ca_res).await;
ca_equals_resources(&ca, &ca_res).await;
// Expect the definition still exists
let definitions = ca_bgpsec_list(&ca).await.unpack();
assert_eq!(1, definitions.len());
// And expect that the BGPSec certificate is published again.
expect_bgpsec_objects(&ca, &definitions).await;
}
// Remove BGPSec definition
{
ca_bgpsec_remove(&ca, bgpsec_def_key).await;
// Expect the definition is removed
let definitions = ca_bgpsec_list(&ca).await.unpack();
assert_eq!(0, definitions.len());
// Expect that the BGPSec certificate is removed.
expect_bgpsec_objects(&ca, &[]).await;
}
let _ = fs::remove_dir_all(krill_dir);
}