use std::{sync::Arc, time::Duration};
use bytes::Bytes;
use log::info;
use rpki::{
ca::{
csr::{Csr, RpkiCaCsr},
idcert::IdCert,
idexchange::RepoInfo,
provisioning, publication,
sigmsg::SignedMessage,
},
crypto::{
KeyIdentifier, PublicKey, PublicKeyFormat, RpkiSignature,
RpkiSignatureAlgorithm, Signer,
},
repository::{
aspa::{Aspa, AspaBuilder},
cert::TbsCert,
crl::{CrlEntry, TbsCertList},
manifest::ManifestContent,
roa::RoaBuilder,
rta,
sigobj::SignedObjectBuilder,
x509::{Serial, Time, Validity},
Cert, Crl, Manifest, Roa,
},
};
use url::Url;
use crate::{
commons::{
crypto::{
self,
dispatch::{
signerinfo::SignerMapper,
signerprovider::{SignerFlags, SignerProvider},
signerrouter::SignerRouter,
},
CryptoResult, OpenSslSigner, SignSupport,
},
error::Error,
KrillResult,
},
constants::ID_CERTIFICATE_VALIDITY_YEARS,
config::{SignerConfig, SignerType},
};
use crate::api::ca::ObjectName;
#[cfg(feature = "hsm")]
use std::collections::HashMap;
#[cfg(feature = "hsm")]
use crate::commons::crypto::{
signers::{kmip::KmipSigner, pkcs11::Pkcs11Signer},
SignerHandle,
};
type SignerBuilderFn = fn(
&SignerType,
SignerFlags,
&Url,
&str,
std::time::Duration,
&Option<Arc<SignerMapper>>,
) -> KrillResult<SignerProvider>;
#[derive(Debug)]
pub struct KrillSignerBuilder<'a> {
storage_uri: Url,
probe_interval: Duration,
signer_configs: &'a [SignerConfig],
default_signer: Option<&'a SignerConfig>,
one_off_signer: Option<&'a SignerConfig>,
}
impl<'a> KrillSignerBuilder<'a> {
pub fn new(
storage_uri: &Url,
probe_interval: Duration,
signer_configs: &'a [SignerConfig],
) -> Self {
Self {
storage_uri: storage_uri.clone(),
probe_interval,
signer_configs,
default_signer: None,
one_off_signer: None,
}
}
pub fn with_default_signer(
&'a mut self,
signer_config: &'a SignerConfig,
) -> &'a mut Self {
self.default_signer = Some(signer_config);
self
}
pub fn with_one_off_signer(
&'a mut self,
signer_config: &'a SignerConfig,
) -> &'a mut Self {
self.one_off_signer = Some(signer_config);
self
}
pub fn build(&'a mut self) -> KrillResult<KrillSigner> {
if self.signer_configs.is_empty() {
return Err(Error::ConfigError(
"At least one signer must be defined".to_string(),
));
}
if self.signer_configs.len() == 1 {
if self.default_signer.is_none() {
self.default_signer = Some(&self.signer_configs[0]);
}
if self.one_off_signer.is_none() {
self.one_off_signer = Some(&self.signer_configs[0]);
}
}
if self.default_signer.is_none() {
return Err(Error::ConfigError(
"No default signer is defined".to_string(),
));
}
let default_signer = self.default_signer.unwrap();
if !self.signer_configs.contains(default_signer) {
return Err(Error::ConfigError(
"The default signer must be one of the defined signers"
.to_string(),
));
}
if self.one_off_signer.is_none() {
return Err(Error::ConfigError(
"No one-off signer is defined".to_string(),
));
}
let one_off_signer = self.one_off_signer.unwrap();
if !self.signer_configs.contains(one_off_signer) {
return Err(Error::ConfigError(
"The one-off signer must be one of the defined signers"
.to_string(),
));
}
KrillSigner::build(
&self.storage_uri,
self.probe_interval,
self.signer_configs,
default_signer,
one_off_signer,
)
}
}
#[derive(Debug)]
pub struct KrillSigner {
router: SignerRouter,
}
impl KrillSigner {
fn build(
storage_uri: &Url,
probe_interval: Duration,
signer_configs: &[SignerConfig],
default_signer: &SignerConfig,
one_off_signer: &SignerConfig,
) -> KrillResult<Self> {
#[cfg(not(feature = "hsm"))]
let signer_mapper = None;
#[cfg(feature = "hsm")]
let signer_mapper = Some(Arc::new(SignerMapper::build(storage_uri)?));
let signers = Self::build_signers(
signer_builder,
storage_uri,
probe_interval,
&signer_mapper,
signer_configs,
default_signer,
one_off_signer,
)?;
let router = SignerRouter::build(signer_mapper, signers)?;
Ok(KrillSigner { router })
}
#[cfg(feature = "hsm")]
pub fn get_mapper(&self) -> Option<Arc<SignerMapper>> {
self.router.get_mapper()
}
#[cfg(feature = "hsm")]
pub fn get_active_signers(
&self,
) -> HashMap<SignerHandle, Arc<SignerProvider>> {
self.router.get_active_signers()
}
pub fn create_key(&self) -> CryptoResult<KeyIdentifier> {
self.router
.create_key(PublicKeyFormat::Rsa)
.map_err(crypto::Error::signer)
}
pub fn import_key(&self, pem: &str) -> CryptoResult<KeyIdentifier> {
self.router.import_key(pem).map_err(crypto::Error::signer)
}
pub fn create_self_signed_id_cert(&self) -> CryptoResult<IdCert> {
let key = self.create_key()?;
let validity = Validity::new(
Time::five_minutes_ago(),
Time::years_from_now(ID_CERTIFICATE_VALIDITY_YEARS),
);
IdCert::new_ta(validity, &key, &self.router)
.map_err(crypto::Error::signer)
}
pub fn destroy_key(&self, key_id: &KeyIdentifier) -> CryptoResult<()> {
self.router
.destroy_key(key_id)
.map_err(crypto::Error::key_error)
}
pub fn get_key_info(
&self,
key_id: &KeyIdentifier,
) -> CryptoResult<PublicKey> {
self.router
.get_key_info(key_id)
.map_err(crypto::Error::key_error)
}
pub fn random_serial(&self) -> CryptoResult<Serial> {
Serial::random(&self.router).map_err(crypto::Error::signer)
}
pub fn sign<D: AsRef<[u8]> + ?Sized>(
&self,
key_id: &KeyIdentifier,
data: &D,
) -> CryptoResult<RpkiSignature> {
self.router
.sign(key_id, RpkiSignatureAlgorithm::default(), data)
.map_err(crypto::Error::signing)
}
pub fn sign_one_off<D: AsRef<[u8]> + ?Sized>(
&self,
data: &D,
) -> CryptoResult<(RpkiSignature, PublicKey)> {
self.router
.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<RpkiCaCsr> {
let signing_key_id = self
.router
.get_key_info(key)
.map_err(crypto::Error::key_error)?;
let mft_file_name =
ObjectName::mft_from_ca_key(&signing_key_id.key_identifier());
let signed_and_encoded_csr = Csr::construct_rpki_ca(
&self.router,
key,
&base_repo.ca_repository(name_space),
&base_repo.resolve(name_space, mft_file_name.as_ref()),
base_repo.rpki_notify(),
)
.map_err(crypto::Error::signing)?;
RpkiCaCsr::decode(signed_and_encoded_csr.as_slice())
.map_err(crypto::Error::signing)
}
pub fn sign_cert(
&self,
tbs: TbsCert,
key_id: &KeyIdentifier,
) -> CryptoResult<Cert> {
tbs.into_cert(&self.router, key_id)
.map_err(crypto::Error::signing)
}
pub fn sign_crl(
&self,
tbs: TbsCertList<Vec<CrlEntry>>,
key_id: &KeyIdentifier,
) -> CryptoResult<Crl> {
tbs.into_crl(&self.router, key_id)
.map_err(crypto::Error::signing)
}
pub fn sign_manifest(
&self,
content: ManifestContent,
builder: SignedObjectBuilder,
key_id: &KeyIdentifier,
) -> CryptoResult<Manifest> {
content
.into_manifest(builder, &self.router, key_id)
.map_err(crypto::Error::signing)
}
pub fn sign_roa(
&self,
roa_builder: RoaBuilder,
object_builder: SignedObjectBuilder,
key_id: &KeyIdentifier,
) -> CryptoResult<Roa> {
roa_builder
.finalize(object_builder, &self.router, key_id)
.map_err(crypto::Error::signing)
}
pub fn sign_aspa(
&self,
aspa_builder: AspaBuilder,
object_builder: SignedObjectBuilder,
key_id: &KeyIdentifier,
) -> CryptoResult<Aspa> {
aspa_builder
.finalize(object_builder, &self.router, key_id)
.map_err(crypto::Error::signing)
}
pub fn sign_rta(
&self,
rta_builder: &mut rta::RtaBuilder,
signing_time: Time,
ee: Cert,
) -> CryptoResult<()> {
let key = ee.subject_key_identifier();
rta_builder.push_cert(ee);
rta_builder
.sign(&self.router, &key, signing_time)
.map_err(crypto::Error::signing)
}
pub fn create_rfc6492_cms(
&self,
message: provisioning::Message,
signing_key: &KeyIdentifier,
) -> CryptoResult<provisioning::ProvisioningCms> {
provisioning::ProvisioningCms::create(
message,
signing_key,
&self.router,
)
.map_err(crypto::Error::signing)
}
pub fn create_rfc8181_cms(
&self,
message: publication::Message,
signing_key: &KeyIdentifier,
) -> CryptoResult<publication::PublicationCms> {
publication::PublicationCms::create(
message,
signing_key,
&self.router,
)
.map_err(crypto::Error::signing)
}
pub fn create_ta_signed_message(
&self,
data: Bytes,
validity_days: i64,
signing_key: &KeyIdentifier,
) -> CryptoResult<SignedMessage> {
let validity = SignSupport::sign_validity_days(validity_days);
SignedMessage::create(data, validity, signing_key, &self.router)
.map_err(crypto::Error::signing)
}
}
impl KrillSigner {
fn build_signers(
signer_builder: SignerBuilderFn,
storage_uri: &Url,
probe_interval: std::time::Duration,
mapper: &Option<Arc<SignerMapper>>,
configs: &[SignerConfig],
default_signer: &SignerConfig,
one_off_signer: &SignerConfig,
) -> KrillResult<Vec<SignerProvider>> {
if configs.is_empty() {
return Err(Error::signer(
"Internal error: At least one signer config must be provided",
));
}
let mut signers = Vec::new();
for config in configs.iter() {
let flags = SignerFlags::new(
config.name == default_signer.name,
config.name == one_off_signer.name,
);
info!(
"Configuring signer '{}' (type: {}, {})",
config.name, config.signer_type, flags
);
let signer = (signer_builder)(
&config.signer_type,
flags,
storage_uri,
&config.name,
probe_interval,
mapper,
)?;
signers.push(signer);
}
Ok(signers)
}
}
fn signer_builder(
r#type: &SignerType,
flags: SignerFlags,
storage_uri: &Url,
name: &str,
#[cfg(feature = "hsm")] probe_interval: Duration,
#[cfg(not(feature = "hsm"))] _probe_interval: Duration,
mapper: &Option<Arc<SignerMapper>>,
) -> KrillResult<SignerProvider> {
match r#type {
SignerType::OpenSsl(conf) => {
let storage_uri =
conf.keys_storage_uri.as_ref().unwrap_or(storage_uri);
let signer =
OpenSslSigner::build(storage_uri, name, mapper.clone())?;
Ok(SignerProvider::OpenSsl(flags, signer))
}
#[cfg(feature = "hsm")]
SignerType::Pkcs11(conf) => {
let signer = Pkcs11Signer::build(
name,
conf,
probe_interval,
mapper.as_ref().unwrap().clone(),
)?;
Ok(SignerProvider::Pkcs11(flags, signer))
}
#[cfg(feature = "hsm")]
SignerType::Kmip(conf) => {
let signer = KmipSigner::build(
name,
conf,
probe_interval,
mapper.as_ref().unwrap().clone(),
)?;
Ok(SignerProvider::Kmip(flags, signer))
}
}
}
#[cfg(all(
test,
feature = "hsm",
not(any(feature = "hsm-tests-kmip", feature = "hsm-tests-pkcs11"))
))]
pub mod tests {
use std::time::Duration;
use crate::{
commons::crypto::signers::mocksigner::{
MockSigner, MockSignerCallCounts,
},
commons::test,
config::Config,
};
use super::*;
fn mock_signer_builder(
r#type: &SignerType,
flags: SignerFlags,
_storage_uri: &Url,
name: &str,
_probe_interval: Duration,
mapper: &Option<Arc<SignerMapper>>,
) -> KrillResult<SignerProvider> {
let call_counts = Arc::new(MockSignerCallCounts::new());
let mut mock_signer = MockSigner::new(
name,
mapper.as_ref().unwrap().clone(),
call_counts,
None,
None,
);
mock_signer.set_info(&format!("mock {} signer", r#type));
Ok(SignerProvider::Mock(flags, mock_signer))
}
fn config_fragment_to_config_object(
fragment: &str,
) -> Result<Config, toml::de::Error> {
let mut config_str = r#"admin_token = "***""#.to_string();
config_str.push_str(fragment);
toml::from_str(&config_str)
}
fn build_krill_signer_from_config(
signers_config_fragment: &str,
storage_uri: &Url,
mapper: Arc<SignerMapper>,
) -> KrillResult<Vec<SignerProvider>> {
let mut config =
config_fragment_to_config_object(signers_config_fragment)
.unwrap();
config
.process()
.map_err(|err| Error::ConfigError(err.to_string()))?;
let mapper = Some(mapper);
let probe_interval = std::time::Duration::from_secs(1);
KrillSigner::build_signers(
mock_signer_builder,
storage_uri,
probe_interval,
&mapper,
&config.signers,
config.default_signer(),
config.one_off_signer(),
)
}
fn assert_signer_name(signer: &SignerProvider, expected: &str) {
assert_eq!(signer.get_name(), expected);
}
fn assert_signer_type(signer: &SignerProvider, expected: &str) {
assert_eq!(
signer.get_info().unwrap(),
format!("mock {} signer", expected)
);
}
fn assert_signer_name_and_type(signer: &SignerProvider, expected: &str) {
assert_signer_name(signer, expected);
assert_signer_type(signer, expected);
}
fn assert_signer_flags(
signer: &SignerProvider,
expected_default: bool,
expected_one_off: bool,
) {
assert_eq!(expected_default, signer.is_default_signer());
assert_eq!(expected_one_off, signer.is_one_off_signer());
}
#[test]
pub fn no_signers_equals_one_openssl_signer_for_backward_compatibility() {
test::test_in_memory(|storage_uri| {
let mapper = Arc::new(SignerMapper::build(storage_uri).unwrap());
let signers =
build_krill_signer_from_config("", storage_uri, mapper)
.unwrap();
assert_eq!(signers.len(), 1);
let signer = &signers[0];
assert_signer_name(signer, "Default OpenSSL signer");
assert_signer_type(signer, "OpenSSL");
assert_signer_flags(signer, true, true);
});
}
#[test]
pub fn signer_name_is_respected() {
test::test_in_memory(|storage_uri| {
let mapper = Arc::new(SignerMapper::build(storage_uri).unwrap());
let signers_config_fragment = r#"
[[signers]]
type = "OpenSSL"
name = "Some test name"
"#;
let signers = build_krill_signer_from_config(
signers_config_fragment,
storage_uri,
mapper,
)
.unwrap();
assert_eq!(signers.len(), 1);
let signer = &signers[0];
assert_signer_name(signer, "Some test name");
assert_signer_type(signer, "OpenSSL");
assert_signer_flags(signer, true, true);
});
}
#[test]
pub fn single_openssl_signer_is_made_the_default_all_signer() {
test::test_in_memory(|storage_uri| {
let mapper = Arc::new(SignerMapper::build(storage_uri).unwrap());
let signers_config_fragment = r#"
[[signers]]
type = "OpenSSL"
name = "OpenSSL"
"#;
let signers = build_krill_signer_from_config(
signers_config_fragment,
storage_uri,
mapper,
)
.unwrap();
assert_eq!(signers.len(), 1);
let signer = &signers[0];
assert_signer_name_and_type(signer, "OpenSSL");
assert_signer_flags(signer, true, true);
});
}
#[test]
pub fn create_openssl_signer_for_one_off_signing() {
test::test_in_memory(|storage_uri| {
let mapper = Arc::new(SignerMapper::build(storage_uri).unwrap());
let signer_config_fragment = r#"
[[signers]]
type = "KMIP"
name = "KMIP"
host = "dummy host"
"#;
let signers = build_krill_signer_from_config(
signer_config_fragment,
storage_uri,
mapper.clone(),
)
.unwrap();
assert_eq!(signers.len(), 2);
let signer = &signers[0];
assert_signer_name_and_type(signer, "KMIP");
assert_signer_flags(signer, true, false);
let signer = &signers[1];
assert_signer_name(signer, "OpenSSL one-off signer");
assert_signer_type(signer, "OpenSSL");
assert_signer_flags(signer, false, true);
let signer_config_fragment = r#"
[[signers]]
type = "PKCS#11"
name = "PKCS#11"
lib_path = "dummy"
slot = "dummy slot"
"#;
let signers = build_krill_signer_from_config(
signer_config_fragment,
storage_uri,
mapper,
)
.unwrap();
assert_eq!(signers.len(), 2);
let signer = &signers[0];
assert_signer_name_and_type(signer, "PKCS#11");
assert_signer_flags(signer, true, false);
let signer = &signers[1];
assert_signer_name(signer, "OpenSSL one-off signer");
assert_signer_type(signer, "OpenSSL");
assert_signer_flags(signer, false, true);
});
}
#[test]
pub fn one_off_signer_is_respected() {
test::test_in_memory(|storage_uri| {
let mapper = Arc::new(SignerMapper::build(storage_uri).unwrap());
let signer_config_fragment = r#"
one_off_signer = "KMIP"
[[signers]]
type = "KMIP"
name = "KMIP"
host = "dummy host"
"#;
let signers = build_krill_signer_from_config(
signer_config_fragment,
storage_uri,
mapper.clone(),
)
.unwrap();
assert_eq!(signers.len(), 1);
let signer = &signers[0];
assert_signer_name_and_type(signer, "KMIP");
assert_signer_flags(signer, true, true);
let signer_config_fragment = r#"
one_off_signer = "PKCS#11"
[[signers]]
type = "PKCS#11"
name = "PKCS#11"
lib_path = "dummy"
slot = "dummy slot"
"#;
let signers = build_krill_signer_from_config(
signer_config_fragment,
storage_uri,
mapper,
)
.unwrap();
assert_eq!(signers.len(), 1);
let signer = &signers[0];
assert_signer_name_and_type(signer, "PKCS#11");
assert_signer_flags(signer, true, true);
});
}
#[test]
pub fn default_signer_is_respected() {
test::test_in_memory(|storage_uri| {
let mapper = Arc::new(SignerMapper::build(storage_uri).unwrap());
let signer_config_fragment = r#"
default_signer = "Signer 2"
[[signers]]
type = "OpenSSL"
name = "Signer 1"
[[signers]]
type = "KMIP"
name = "Signer 2"
host = "dummy host"
"#;
let signers = build_krill_signer_from_config(
signer_config_fragment,
storage_uri,
mapper,
)
.unwrap();
assert_eq!(signers.len(), 2);
let signer = &signers[0];
assert_signer_type(signer, "OpenSSL");
assert_signer_name(signer, "Signer 1");
assert_signer_flags(signer, false, true);
let signer = &signers[1];
assert_signer_type(signer, "KMIP");
assert_signer_name(signer, "Signer 2");
assert_signer_flags(signer, true, false);
});
}
#[test]
pub fn default_signer_and_one_off_signer_are_respected() {
test::test_in_memory(|storage_uri| {
let mapper = Arc::new(SignerMapper::build(storage_uri).unwrap());
let signer_config_fragment = r#"
default_signer = "Signer 2"
one_off_signer = "Signer 2"
[[signers]]
type = "OpenSSL"
name = "Signer 1" # unused / historic signer
[[signers]]
type = "KMIP"
name = "Signer 2" # default and one off signer
host = "dummy host"
"#;
let signers = build_krill_signer_from_config(
signer_config_fragment,
storage_uri,
mapper,
)
.unwrap();
assert_eq!(signers.len(), 2);
let signer = &signers[0];
assert_signer_type(signer, "OpenSSL");
assert_signer_name(signer, "Signer 1");
assert_signer_flags(signer, false, false);
let signer = &signers[1];
assert_signer_type(signer, "KMIP");
assert_signer_name(signer, "Signer 2");
assert_signer_flags(signer, true, true);
});
}
#[test]
pub fn historic_signers_are_permitted() {
test::test_in_memory(|storage_uri| {
let mapper = Arc::new(SignerMapper::build(storage_uri).unwrap());
let signer_config_fragment = r#"
default_signer = "Signer 2"
one_off_signer = "Signer 3"
[[signers]]
type = "OpenSSL"
name = "Signer 1" # historic signer only used with previously created keys
[[signers]]
type = "KMIP"
name = "Signer 2" # default signer for new keys
host = "dummy host"
[[signers]]
type = "PKCS#11"
name = "Signer 3" # one off signer
lib_path = "dummy"
slot = "dummy slot"
"#;
let signers = build_krill_signer_from_config(
signer_config_fragment,
storage_uri,
mapper,
)
.unwrap();
assert_eq!(signers.len(), 3);
let signer = &signers[0];
assert_signer_type(signer, "OpenSSL");
assert_signer_name(signer, "Signer 1");
assert_signer_flags(signer, false, false);
let signer = &signers[1];
assert_signer_type(signer, "KMIP");
assert_signer_name(signer, "Signer 2");
assert_signer_flags(signer, true, false);
let signer = &signers[2];
assert_signer_type(signer, "PKCS#11");
assert_signer_name(signer, "Signer 3");
assert_signer_flags(signer, false, true);
});
}
}