Files
NLnetLabs-krill/src/daemon/ca/keys.rs
T

493 lines
16 KiB
Rust

use std::ops::{Deref, DerefMut};
use chrono::Duration;
use serde::{Deserialize, Serialize};
use rpki::crypto::{KeyIdentifier, PublicKeyFormat};
use rpki::csr::Csr;
use rpki::x509::Time;
use crate::commons::api::{
CertifiedKeyInfo, EntitlementClass, IssuanceRequest, PendingKeyInfo, RcvdCert, RepoInfo,
RequestResourceLimit, ResourceClassKeysInfo, ResourceClassName, ResourceSet, RevocationRequest,
};
use crate::daemon::ca::{
self, CurrentObjectSet, CurrentObjectSetDelta, Error, EvtDet, Result, Signer,
};
//------------ CertifiedKey --------------------------------------------------
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
/// Describes a Key that is certified. I.e. it received an incoming certificate
/// and has at least a MFT and CRL.
pub struct CertifiedKey {
key_id: KeyIdentifier,
incoming_cert: RcvdCert,
current_set: CurrentObjectSet,
request: Option<IssuanceRequest>,
}
impl CertifiedKey {
pub fn create<S: Signer>(incoming_cert: RcvdCert, signer: &S) -> ca::Result<Self> {
let key_id = incoming_cert.cert().subject_key_identifier();
let key = signer.get_key_info(&key_id).map_err(ca::Error::signer)?;
let current_set = CurrentObjectSet::create(&key, &incoming_cert, signer)?;
Ok(CertifiedKey {
key_id,
incoming_cert,
current_set,
request: None,
})
}
pub fn as_info(&self) -> CertifiedKeyInfo {
CertifiedKeyInfo::new(self.key_id, self.incoming_cert.clone())
}
pub fn key_id(&self) -> &KeyIdentifier {
&self.key_id
}
pub fn incoming_cert(&self) -> &RcvdCert {
&self.incoming_cert
}
pub fn set_incoming_cert(&mut self, incoming_cert: RcvdCert) {
self.request = None;
self.incoming_cert = incoming_cert;
}
pub fn current_set(&self) -> &CurrentObjectSet {
&self.current_set
}
pub fn request(&self) -> Option<&IssuanceRequest> {
self.request.as_ref()
}
pub fn add_request(&mut self, req: IssuanceRequest) {
self.request = Some(req)
}
pub fn wants_update(&self, new_resources: &ResourceSet, new_not_after: Time) -> bool {
let not_after = self.incoming_cert().cert().validity().not_after();
self.incoming_cert.resources() != new_resources || not_after != new_not_after
}
pub fn close_to_next_update(&self) -> bool {
self.current_set.next_update() < Time::now() + Duration::hours(8)
}
pub fn with_new_cert(mut self, cert: RcvdCert) -> Self {
self.incoming_cert = cert;
self
}
pub fn apply_delta(&mut self, delta: CurrentObjectSetDelta) {
self.current_set.apply_delta(delta)
}
}
pub type NewKey = CertifiedKey;
pub type CurrentKey = CertifiedKey;
//------------ PendingKey ----------------------------------------------------
/// A Pending Key in a resource class. Should usually have an open
/// IssuanceRequest, and will be move to a 'new' or 'current' CertifiedKey
/// when a certificate is received.
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct PendingKey {
key_id: KeyIdentifier,
request: Option<IssuanceRequest>,
}
impl PendingKey {
pub fn new(key_id: KeyIdentifier) -> Self {
PendingKey {
key_id,
request: None,
}
}
pub fn as_info(&self) -> PendingKeyInfo {
PendingKeyInfo::new(self.key_id)
}
pub fn unwrap(self) -> (KeyIdentifier, Option<IssuanceRequest>) {
(self.key_id, self.request)
}
pub fn key_id(&self) -> &KeyIdentifier {
&self.key_id
}
pub fn request(&self) -> Option<&IssuanceRequest> {
self.request.as_ref()
}
pub fn add_request(&mut self, req: IssuanceRequest) {
self.request = Some(req)
}
pub fn clear_request(&mut self) {
self.request = None
}
}
//------------ OldKey --------------------------------------------------------
#[derive(Clone, Debug, Deserialize, Eq, Serialize, PartialEq)]
pub struct OldKey {
key: CertifiedKey,
revoke_req: RevocationRequest,
}
impl OldKey {
pub fn new(key: CertifiedKey, revoke_req: RevocationRequest) -> Self {
OldKey { key, revoke_req }
}
pub fn key(&self) -> &CertifiedKey {
&self.key
}
pub fn revoke_req(&self) -> &RevocationRequest {
&self.revoke_req
}
}
impl Deref for OldKey {
type Target = CertifiedKey;
fn deref(&self) -> &Self::Target {
&self.key
}
}
impl DerefMut for OldKey {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.key
}
}
//------------ KeyState ------------------------------------------------------
/// This type contains the keys for a resource class and guards that keys
/// are created, activated, rolled and retired properly.
#[derive(Clone, Debug, Deserialize, Eq, Serialize, PartialEq)]
#[allow(clippy::large_enum_variant)]
pub enum KeyState {
Pending(PendingKey),
Active(CurrentKey),
RollPending(PendingKey, CurrentKey),
RollNew(NewKey, CurrentKey),
RollOld(CurrentKey, OldKey),
}
impl KeyState {
pub fn create(pending_key: KeyIdentifier) -> Self {
KeyState::Pending(PendingKey::new(pending_key))
}
pub fn add_request(&mut self, key_id: KeyIdentifier, req: IssuanceRequest) {
match self {
KeyState::Pending(pending) => pending.add_request(req),
KeyState::Active(current) => current.add_request(req),
KeyState::RollPending(pending, current) => {
if pending.key_id() == &key_id {
pending.add_request(req)
} else {
current.add_request(req)
}
}
KeyState::RollNew(new, current) => {
if new.key_id() == &key_id {
new.add_request(req)
} else {
current.add_request(req)
}
}
KeyState::RollOld(current, old) => {
if current.key_id() == &key_id {
current.add_request(req)
} else {
old.add_request(req)
}
}
}
}
/// Revoke all current keys
pub fn revoke<S: Signer>(
&self,
class_name: ResourceClassName,
signer: &S,
) -> ca::Result<Vec<RevocationRequest>> {
match self {
KeyState::Pending(_pending) => Ok(vec![]), // nothing to revoke
KeyState::Active(current) | KeyState::RollPending(_, current) => {
let revoke_current = Self::revoke_key(class_name, current.key_id(), signer)?;
Ok(vec![revoke_current])
}
KeyState::RollNew(new, current) => {
let revoke_new = Self::revoke_key(class_name.clone(), new.key_id(), signer)?;
let revoke_current = Self::revoke_key(class_name, current.key_id(), signer)?;
Ok(vec![revoke_new, revoke_current])
}
KeyState::RollOld(current, old) => {
let revoke_current = Self::revoke_key(class_name, current.key_id(), signer)?;
let revoke_old = old.revoke_req().clone();
Ok(vec![revoke_current, revoke_old])
}
}
}
fn revoke_key<S: Signer>(
class_name: ResourceClassName,
key_id: &KeyIdentifier,
signer: &S,
) -> ca::Result<RevocationRequest> {
let ki = signer
.get_key_info(key_id)
.map_err(Error::signer)?
.key_identifier();
Ok(RevocationRequest::new(class_name, ki))
}
pub fn apply_delta(&mut self, delta: CurrentObjectSetDelta, key_id: KeyIdentifier) {
match self {
KeyState::Pending(_pending) => panic!("Should never have delta for pending"),
KeyState::Active(current) => current.apply_delta(delta),
KeyState::RollPending(_pending, current) => current.apply_delta(delta),
KeyState::RollNew(new, current) => {
if new.key_id() == &key_id {
new.apply_delta(delta)
} else {
current.apply_delta(delta)
}
}
KeyState::RollOld(current, old) => {
if current.key_id() == &key_id {
current.apply_delta(delta)
} else {
old.apply_delta(delta)
}
}
}
}
pub fn request_certs<S: Signer>(
&self,
rcn: ResourceClassName,
entitlement: &EntitlementClass,
base_repo: &RepoInfo,
name_space: &str,
signer: &S,
) -> Result<Vec<EvtDet>> {
let mut keys_for_requests = vec![];
match self {
KeyState::Pending(pending) => {
keys_for_requests.push(pending.key_id());
}
KeyState::Active(current) => {
if current.wants_update(entitlement.resource_set(), entitlement.not_after()) {
keys_for_requests.push(current.key_id());
}
}
KeyState::RollPending(pending, current) => {
keys_for_requests.push(pending.key_id());
if current.wants_update(entitlement.resource_set(), entitlement.not_after()) {
keys_for_requests.push(current.key_id());
}
}
KeyState::RollNew(new, current) => {
if new.wants_update(entitlement.resource_set(), entitlement.not_after()) {
keys_for_requests.push(new.key_id());
}
if current.wants_update(entitlement.resource_set(), entitlement.not_after()) {
keys_for_requests.push(current.key_id());
}
}
KeyState::RollOld(current, old) => {
if current.wants_update(entitlement.resource_set(), entitlement.not_after()) {
keys_for_requests.push(current.key_id());
}
if old.wants_update(entitlement.resource_set(), entitlement.not_after()) {
keys_for_requests.push(old.key_id());
}
}
}
let mut res = vec![];
for key_id in keys_for_requests.into_iter() {
let req = self.create_issuance_req(
base_repo,
name_space,
entitlement.class_name().clone(),
key_id,
signer,
)?;
res.push(EvtDet::CertificateRequested(rcn.clone(), req, *key_id));
}
Ok(res)
}
/// Returns all open certificate requests
pub fn cert_requests(&self) -> Vec<IssuanceRequest> {
let mut res = vec![];
match self {
KeyState::Pending(pending) => {
if let Some(r) = pending.request() {
res.push(r.clone())
}
}
KeyState::Active(current) => {
if let Some(r) = current.request() {
res.push(r.clone())
}
}
KeyState::RollPending(pending, current) => {
if let Some(r) = pending.request() {
res.push(r.clone())
}
if let Some(r) = current.request() {
res.push(r.clone())
}
}
KeyState::RollNew(new, current) => {
if let Some(r) = new.request() {
res.push(r.clone())
}
if let Some(r) = current.request() {
res.push(r.clone())
}
}
KeyState::RollOld(current, old) => {
if let Some(r) = current.request() {
res.push(r.clone())
}
if let Some(r) = old.request() {
res.push(r.clone())
}
}
}
res
}
/// Creates a Csr for the given key. Note that this parses the encoded
/// key. This is not the most efficient way, but makes storing and
/// serializing the Csr in an event possible (the Captured cannot be
/// stored).
fn create_issuance_req<S: Signer>(
&self,
base_repo: &RepoInfo,
name_space: &str,
class_name: ResourceClassName,
key: &KeyIdentifier,
signer: &S,
) -> Result<IssuanceRequest> {
let pub_key = signer.get_key_info(key).map_err(Error::signer)?;
let enc = Csr::construct(
signer,
key,
&base_repo.ca_repository(name_space),
&base_repo.rpki_manifest(name_space, &pub_key.key_identifier()),
Some(&base_repo.rpki_notify()),
)
.map_err(Error::signer)?;
let csr = Csr::decode(enc.as_slice()).map_err(Error::signer)?;
Ok(IssuanceRequest::new(
class_name,
RequestResourceLimit::default(),
csr,
))
}
/// Returns the revoke request if there is an old key.
pub fn revoke_request(&self) -> Option<&RevocationRequest> {
match self {
KeyState::RollOld(_current, old) => Some(old.revoke_req()),
_ => None,
}
}
pub fn as_info(&self) -> ResourceClassKeysInfo {
match self.clone() {
KeyState::Pending(p) => ResourceClassKeysInfo::Pending(p.as_info()),
KeyState::Active(c) => ResourceClassKeysInfo::Active(c.as_info()),
KeyState::RollPending(p, c) => {
ResourceClassKeysInfo::RollPending(p.as_info(), c.as_info())
}
KeyState::RollNew(n, c) => ResourceClassKeysInfo::RollNew(n.as_info(), c.as_info()),
KeyState::RollOld(c, o) => ResourceClassKeysInfo::RollOld(c.as_info(), o.as_info()),
}
}
}
/// # Key Life Cycle
///
impl KeyState {
/// Initiates a key roll if the current state is 'Active'. This will return event details
/// for a newly create pending key and requested certificate for it.
pub fn keyroll_initiate<S: Signer>(
&self,
class_name: ResourceClassName,
parent_class_name: ResourceClassName,
base_repo: &RepoInfo,
name_space: &str,
signer: &mut S,
) -> ca::Result<Vec<EvtDet>> {
match self {
KeyState::Active(_current) => {
let key_id = {
signer
.create_key(PublicKeyFormat::default())
.map_err(Error::signer)?
};
let issuance_req = self.create_issuance_req(
base_repo,
name_space,
parent_class_name,
&key_id,
signer,
)?;
Ok(vec![
EvtDet::KeyRollPendingKeyAdded(class_name.clone(), key_id),
EvtDet::CertificateRequested(class_name, issuance_req, key_id),
])
}
_ => Ok(vec![]),
}
}
/// Marks the new key as current, and the current key as old, and requests revocation of
/// the old key.
pub fn keyroll_activate<S: Signer>(
&self,
class_name: ResourceClassName,
parent_class_name: ResourceClassName,
signer: &S,
) -> ca::Result<EvtDet> {
match self {
KeyState::RollNew(_new, current) => {
let revoke_req = Self::revoke_key(parent_class_name, current.key_id(), signer)?;
Ok(EvtDet::KeyRollActivated(class_name, revoke_req))
}
_ => Err(Error::ResourceClassNoNewKey),
}
}
/// Returns true if there is a new key
pub fn has_new_key(&self) -> bool {
match self {
KeyState::RollNew(_, _) => true,
_ => false,
}
}
}