use std::time::{Duration, SystemTime};
use crate::packet::{
Key,
key,
};
use crate::Result;
use crate::Packet;
use crate::packet::signature::{
SignatureBuilder,
SIG_BACKDATE_BY,
subpacket::SubpacketTag,
};
use crate::cert::prelude::*;
use crate::Error;
use crate::Profile;
use crate::crypto::{Password, Signer};
use crate::types::{
HashAlgorithm,
KeyFlags,
SignatureType,
};
pub struct KeyBuilder {
flags: KeyFlags,
cipher_suite: CipherSuite,
password: Option<Password>,
creation_time: Option<SystemTime>,
}
assert_send_and_sync!(KeyBuilder);
impl KeyBuilder {
pub fn new(flags: KeyFlags) -> Self {
KeyBuilder {
flags,
cipher_suite: Default::default(),
creation_time: None,
password: None,
}
}
pub fn cipher_suite(&self) -> CipherSuite {
self.cipher_suite
}
pub fn set_cipher_suite(mut self, cipher_suite: CipherSuite) -> Self {
self.cipher_suite = cipher_suite;
self
}
pub fn creation_time(&self) -> Option<SystemTime> {
self.creation_time
}
pub fn set_creation_time<T>(mut self, creation_time: T) -> Self
where T: Into<Option<SystemTime>>
{
self.creation_time = creation_time.into();
self
}
pub fn password(&self) -> Option<&Password> {
self.password.as_ref()
}
pub fn set_password<T>(mut self, password: T) -> Self
where T: Into<Option<Password>>
{
self.password = password.into();
self
}
pub fn subkey(self, vc: ValidCert) -> Result<SubkeyBuilder<'_>> {
let profile = match vc.primary_key().key().version() {
4 => Profile::RFC4880,
6 => Profile::RFC9580,
n => return Err(Error::InvalidOperation(format!(
"Cannot generate a subkey for version {} primary key", n))
.into()),
};
let mut key: Key<key::SecretParts, key::SubordinateRole>
= self.cipher_suite.generate_key(&self.flags, profile)?
.role_into_subordinate();
let ct = self.creation_time.unwrap_or_else(|| {
crate::now() - Duration::new(SIG_BACKDATE_BY, 0)
});
key.set_creation_time(ct)?;
let signer = key.clone().into_keypair().unwrap();
if let Some(ref password) = self.password {
let (k, mut secret) = key.take_secret();
secret.encrypt_in_place(&k, password)?;
key = k.add_secret(secret).0;
}
let mut builder = SubkeyBuilder::new(
vc, key.parts_into_unspecified(), self.flags)?;
builder = builder.set_signature_creation_time(ct)?;
Ok(builder.set_subkey_signer(signer))
}
}
pub struct SubkeyBuilder<'a> {
vc: ValidCert<'a>,
primary_signer: Option<Box<dyn Signer + Send + Sync + 'a>>,
subkey: Key<key::UnspecifiedParts, key::SubordinateRole>,
subkey_signer: Option<Box<dyn Signer + Send + Sync + 'a>>,
template: SignatureBuilder,
}
assert_send_and_sync!(SubkeyBuilder<'_>);
impl<'a> SubkeyBuilder<'a> {
pub fn new<P>(vc: ValidCert<'a>,
subkey: Key<P, key::SubordinateRole>,
subkey_flags: KeyFlags)
-> Result<Self>
where P: key::KeyParts,
{
let (template, key_expiration): (SignatureBuilder, Option<SystemTime>)
= vc.keys().subkeys()
.filter_map(|ka| {
if ka.key().parts_as_unspecified().public_eq(&subkey) {
let sig = ka.binding_signature().clone();
let e = sig.key_validity_period().map(|v| {
ka.key().creation_time() + v
});
Some((sig.into(), e))
} else {
None
}
})
.next()
.or_else(|| {
vc.keys().subkeys().revoked(false).alive()
.max_by_key(|ka| (ka.key().creation_time(), ka.key().fingerprint()))
.map(|ka| {
let sig = ka.binding_signature().clone();
let e = sig.key_validity_period().map(|v| {
ka.key().creation_time() + v
});
(sig.into(), e)
})
})
.unwrap_or_else(|| {
(SignatureBuilder::new(SignatureType::SubkeyBinding),
vc.primary_key().key_validity_period().map(|v| {
vc.primary_key().key().creation_time() + v
}))
});
let template = template.set_key_flags(subkey_flags)?;
let mut builder = SubkeyBuilder {
vc,
primary_signer: None,
subkey: subkey.parts_into_unspecified(),
subkey_signer: None,
template: SignatureBuilder::new(SignatureType::SubkeyBinding),
};
builder = builder.set_signature_template(template);
builder = builder.set_key_expiration_time(key_expiration)?;
Ok(builder)
}
pub fn new_with<P, T>(vc: ValidCert<'a>,
subkey: Key<P, key::SubordinateRole>,
template: T)
-> Self
where P: key::KeyParts,
T: Into<SignatureBuilder>,
{
let template = template.into();
let mut builder = SubkeyBuilder {
vc,
primary_signer: None,
subkey: subkey.parts_into_unspecified(),
subkey_signer: None,
template: SignatureBuilder::new(SignatureType::SubkeyBinding),
};
builder = builder.set_signature_template(template);
builder
}
pub fn set_signature_template<T>(mut self, template: T) -> Self
where T: Into<SignatureBuilder>,
{
self.template = template.into();
self.template = self.template.set_hash_algo(HashAlgorithm::SHA512);
self
}
pub fn with_signature_template<F>(mut self, f: F) -> Result<Self>
where F: FnOnce(SignatureBuilder) -> Result<SignatureBuilder>
{
self.template = f(self.template.clone())?;
Ok(self)
}
pub fn set_signature_creation_time<T>(mut self, creation_time: T)
-> Result<Self>
where T: Into<SystemTime>
{
self.template = self.template.set_signature_creation_time(
creation_time.into())?;
Ok(self)
}
pub fn preserve_signature_creation_time(mut self) -> Result<Self>
{
self.template
= self.template.preserve_signature_creation_time()?;
Ok(self)
}
pub fn set_key_expiration_time<T>(mut self, key_expiration_time: T)
-> Result<Self>
where T: Into<Option<SystemTime>>
{
let key_expiration_time = key_expiration_time.into();
let validity_period = key_expiration_time
.map(|e| {
e.duration_since(self.subkey.creation_time())
.map_err(|_| {
Error::InvalidArgument(
"expiration time precedes creation time".into())
})
})
.transpose()?;
self = self.with_signature_template(|sig| {
sig.set_key_validity_period(validity_period)
})?;
Ok(self)
}
pub fn set_key_validity_period<T>(mut self, validity: T)
-> Result<Self>
where T: Into<Option<Duration>>
{
self = self.with_signature_template(|sig| {
sig.set_key_validity_period(validity.into())
})?;
Ok(self)
}
pub fn key(&self) -> &Key<key::UnspecifiedParts, key::SubordinateRole> {
&self.subkey
}
pub fn set_primary_key_signer<S>(mut self, signer: S) -> Self
where S: Signer + Send + Sync + 'a,
{
self.primary_signer = Some(Box::new(signer));
self
}
pub fn set_subkey_signer<S>(mut self, signer: S) -> Self
where S: Signer + Send + Sync + 'a,
{
self.subkey_signer = Some(Box::new(signer));
self
}
pub fn attach(self) -> Result<Vec<Packet>> {
let SubkeyBuilder {
vc,
primary_signer,
subkey,
subkey_signer,
template,
} = self;
if template.typ() != SignatureType::SubkeyBinding {
return Err(Error::InvalidArgument(
format!("Expected a SubkeyBinding signature, got a {}",
template.typ())).into());
}
let mut builder = template;
let creation_time = builder.effective_signature_creation_time()?;
if let Some(sig_ct) = creation_time {
if let Some(v) = builder.key_validity_period() {
let e = subkey.creation_time() + v;
if let Err(err) = e.duration_since(sig_ct) {
return Err(Error::InvalidArgument(
format!(
"key expiration precedes signature creation time \
(by {:?})",
err.duration())).into());
}
}
}
if let Some(flags) = builder.key_flags() {
if flags.for_certification() || flags.for_signing()
|| flags.for_authentication()
{
let mut subkey_signer = if let Some(signer) = subkey_signer {
signer
} else {
Box::new(
subkey.clone().parts_into_secret()?.into_keypair()?)
};
let mut backsig =
SignatureBuilder::new(
SignatureType::PrimaryKeyBinding)
.set_hash_algo(HashAlgorithm::SHA512)
.set_reference_time(creation_time)?;
if let Some(t) = creation_time {
backsig = backsig.set_reference_time(t)?;
} else {
backsig = backsig.preserve_signature_creation_time()?;
}
let backsig = backsig.sign_primary_key_binding(
&mut *subkey_signer, vc.primary_key().key(), &subkey)?;
builder = builder.set_embedded_signature(backsig)?;
} else {
builder.hashed_area_mut()
.remove_all(SubpacketTag::EmbeddedSignature);
builder.unhashed_area_mut()
.remove_all(SubpacketTag::EmbeddedSignature);
}
}
let mut primary_signer = if let Some(signer) = primary_signer {
signer
} else {
Box::new(
vc.primary_key().key().clone()
.parts_into_secret()?.into_keypair()?)
};
let signature = subkey.bind(
&mut *primary_signer, vc.cert(), builder)?;
let subkey = if subkey.has_secret() {
Packet::SecretSubkey(subkey.parts_into_secret().unwrap())
} else {
Packet::PublicSubkey(subkey.parts_into_public())
};
Ok(vec![subkey, signature.into()])
}
pub fn attach_cert(self) -> Result<Cert> {
let cert = self.vc.cert().clone();
let packets = self.attach()?;
Ok(cert.insert_packets(packets)?.0)
}
}
impl<'a, P> From<ValidPrimaryKeyAmalgamation<'a, P>> for SubkeyBuilder<'a>
where
P: key::KeyParts + Clone,
{
fn from(ka: ValidPrimaryKeyAmalgamation<'a, P>) -> Self {
ValidErasedKeyAmalgamation::from(ka).into()
}
}
impl<'a, P> From<ValidSubordinateKeyAmalgamation<'a, P>> for SubkeyBuilder<'a>
where
P: key::KeyParts + Clone,
{
fn from(ka: ValidSubordinateKeyAmalgamation<'a, P>) -> Self {
ValidErasedKeyAmalgamation::from(ka).into()
}
}
impl<'a, P> From<ValidErasedKeyAmalgamation<'a, P>> for SubkeyBuilder<'a>
where
P: key::KeyParts + Clone,
{
fn from(ka: ValidErasedKeyAmalgamation<'a, P>) -> SubkeyBuilder<'a> {
let key = ka.key().clone().role_into_subordinate();
SubkeyBuilder::new_with(
ka.valid_cert().clone(), key, ka.binding_signature().clone())
}
}
#[cfg(test)]
mod test {
use super::*;
use std::time::{Duration, UNIX_EPOCH};
use crate::policy::StandardPolicy;
#[test]
fn expiry() -> Result<()> {
let p = &StandardPolicy::new();
let t1 = crate::now() - Duration::new(7 * 24 * 60 * 60, 0);
let t1 = t1.duration_since(UNIX_EPOCH)?.as_secs();
let t1 = UNIX_EPOCH + Duration::new(t1, 0);
let t0 = t1 - Duration::new(60 * 60, 0);
let t2 = t1 + Duration::new(60 * 60, 0);
let validity = t2.duration_since(t0).unwrap();
let (pre, _) =
CertBuilder::general_purpose(Some("alice@example.org"))
.set_creation_time(t0)
.set_validity_period(validity)
.generate()?;
let vc_pre = pre.with_policy(p, t1)?;
let post = KeyBuilder::new(KeyFlags::empty().set_signing())
.set_creation_time(t1)
.subkey(vc_pre)?
.set_signature_creation_time(t1)?
.attach_cert()?;
let vc_post = post.with_policy(p, t1).unwrap();
assert_eq!(pre.keys().count() + 1, post.keys().count());
assert_eq!(post.keys().count(), vc_post.keys().count());
eprintln!("t0: {:?}", t0);
eprintln!("t1: {:?}", t1);
eprintln!("t2: {:?}", t2);
assert!(vc_post.keys().all(|ka| {
eprintln!("{}: {:?} -> {:?}",
ka.key().fingerprint(),
ka.key().creation_time(),
ka.key_expiration_time());
ka.key_expiration_time() == Some(t2)
}));
Ok(())
}
}