diff --git a/e2e-tests/tests/60_signing.sh b/e2e-tests/tests/60_signing.sh index 629369d..fe0202e 100644 --- a/e2e-tests/tests/60_signing.sh +++ b/e2e-tests/tests/60_signing.sh @@ -30,9 +30,7 @@ if [ -f /var/mail/juliette ] ; then if [ "$SIGN_FPR" != "$FOUND_FPR" ] ; then echo "Found wrong fingerprint '$FOUND_FPR' - expected '$SIGN_FPR'" - RESULT_CODE=1 - else - RESULT_CODE=0 + exit 1 fi else echo "no encryption detected" @@ -43,3 +41,31 @@ else exit 1 fi +echo "set new signing key" + +SIGN_FPR=$($HUSK_BIN --config config/config.toml signkey create | cut -d ':' -f 2 | tr -d ' ') +echo "new signing key $SIGN_FPR" + +# remove old mail +rm /var/mail/juliette + +echo "send mail to juliette@example.com" +send_test_mail juliette@example.com + +if [ -f /var/mail/juliette ] ; then + if grep -q "BEGIN PGP MESSAGE" /var/mail/juliette ; then + + FOUND_FPR=$(sq packet dump /var/mail/juliette | grep "Issuer Fingerprint:" | cut -d ':' -f 2 | tr -d ' ') + + if [ "$SIGN_FPR" != "$FOUND_FPR" ] ; then + echo "Found wrong fingerprint '$FOUND_FPR' - expected '$SIGN_FPR'" + exit 1 + fi + else + echo "no encryption detected" + exit 1 + fi +else + echo "no mail detected" + exit 1 +fi diff --git a/src/cli/cli_args.rs b/src/cli/cli_args.rs index 718262d..b38e40a 100644 --- a/src/cli/cli_args.rs +++ b/src/cli/cli_args.rs @@ -18,7 +18,7 @@ pub struct CliArgs { #[clap( long, global = true, - help = "Certification store (SEQUOIA_HOME).", + help = "Certificate store (SEQUOIA_HOME).", )] pub home: Option, @@ -207,6 +207,7 @@ pub struct SignkeyCommand { #[derive(Debug, Subcommand)] pub enum SignkeySubcommand { Set(SignkeySetCommand), + Create(SignkeyCreateCommand), #[clap( about = "Show the fingerprint of the signing key." )] @@ -239,3 +240,19 @@ pub struct SignkeySetCommand { pub cert: Option, } + +#[derive(Parser, Debug)] +#[clap( + about = "Create a new signing key.", +)] +pub struct SignkeyCreateCommand { + #[clap( + long, + help = "Set the userid of the new key.", + value_name = "USERID", + default_value = "Husk signing key", + )] + pub userid: String, +} + + diff --git a/src/commands/signkey.rs b/src/commands/signkey.rs index 49a8421..97a235a 100644 --- a/src/commands/signkey.rs +++ b/src/commands/signkey.rs @@ -9,12 +9,16 @@ use crate::config::HuskConfigContainer; use crate::types::husk_context::HuskContext; pub mod set; +pub mod create; pub async fn dispatch(cmd: SignkeyCommand, config: HuskConfigContainer) -> Result<()> { match cmd.subcommand { SignkeySubcommand::Set(set_cmd) => { set::dispatch(set_cmd, config).await?; }, + SignkeySubcommand::Create(create_cmd) => { + create::dispatch(create_cmd, config).await?; + } SignkeySubcommand::Show => { let context = HuskContext::new(&config.into())?; if let Some(key) = context.signing_key { diff --git a/src/commands/signkey/create.rs b/src/commands/signkey/create.rs new file mode 100644 index 0000000..de873c5 --- /dev/null +++ b/src/commands/signkey/create.rs @@ -0,0 +1,35 @@ +//! signkey create command + +use std::path::PathBuf; + +use anyhow::Result; +use sequoia_openpgp::cert::CertBuilder; +use sequoia_directories::Home; + +use crate::cli::cli_args::SignkeyCreateCommand; +use crate::common::crypto; +use crate::config::{HuskConfig, HuskConfigContainer}; +use crate::types::husk_context::HuskContext; + +pub async fn dispatch(cmd: SignkeyCreateCommand, config: HuskConfigContainer) + -> Result<()> { + + let config: HuskConfig = config.into(); + let context = HuskContext::new(&config)?; + let sequoia_home = Home::new(PathBuf::from(&config.sequoia_home))?; + + // XXX: cleanup the userid before using + let (cert, _) = CertBuilder::new() + .add_signing_subkey() + .add_userid(cmd.userid) + .generate()?; + + if crypto::import_key(sequoia_home, &cert) { + let vc = cert.with_policy(&context.policy, None)?; + crypto::set_signing_key(&context, &vc)?; + + println!("New signing key: {}", vc.fingerprint()); + } + + Ok(()) +} diff --git a/src/common/crypto.rs b/src/common/crypto.rs index 9610e9e..7dd56d4 100644 --- a/src/common/crypto.rs +++ b/src/common/crypto.rs @@ -692,6 +692,33 @@ pub fn get_all_keys(sequoia_home: Home) -> Vec { result } +pub fn import_key(sequoia_home: Home, cert: &Cert) -> bool { + let mut imported = false; + std::thread::scope(|s| { + s.spawn(|| { + if let Ok(keystore_base) = Context::configure() + .home(sequoia_home.data_dir(sequoia_directories::Component::Keystore)) + .build() { + if let Ok(mut keystore) = Keystore::connect(&keystore_base) { + if let Ok(mut backends) = keystore.backends() { + for backend in &mut backends { + if let Ok(id) = backend.id() { + if id == "softkeys" { + if let Ok(_) = backend.import(cert) { + imported = true; + } + } + } + } + } + } + } + }); + }); + + imported +} + #[cfg(test)] pub mod tests { use sequoia_cert_store::store::CertD;