diff --git a/Cargo.lock b/Cargo.lock index 05ab558..92e5967 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -112,6 +112,17 @@ dependencies = [ "term", ] +[[package]] +name = "async-generic" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddf3728566eefa873833159754f5732fb0951d3649e6e5b891cc70d56dd41673" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "async-trait" version = "0.1.89" @@ -165,15 +176,30 @@ dependencies = [ "syn", ] +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec 0.8.0", +] + [[package]] name = "bit-set" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34ddef2995421ab6a5c779542c81ee77c115206f4ad9d5a8e05f4ff49716a3dd" dependencies = [ - "bit-vec", + "bit-vec 0.9.1", ] +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bit-vec" version = "0.9.1" @@ -255,6 +281,46 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "capnp" +version = "0.25.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfd2485d4b36ac9c5aa6572d7d35daa63a5b34f517627d6c34d068e616e4a73" +dependencies = [ + "embedded-io", +] + +[[package]] +name = "capnp-futures" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b69dfddccc57844f9a90f9d72b44b97c326914851ea94fb7da40ef9cad6e8d" +dependencies = [ + "capnp", + "futures-channel", + "futures-util", +] + +[[package]] +name = "capnp-rpc" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c74c337e87f75f3174ffb3513738ab0acc631c04f64cc33b867c60f84771da" +dependencies = [ + "capnp", + "capnp-futures", + "futures", +] + +[[package]] +name = "capnpc" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fca02be865c8c5a78bfc24b9819006ab6b59bef238467203928e26459557af93" +dependencies = [ + "capnp", +] + [[package]] name = "cc" version = "1.2.62" @@ -469,6 +535,22 @@ dependencies = [ "typenum", ] +[[package]] +name = "ctor" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "424e0138278faeb2b401f174ad17e715c829512d74f3d1e81eb43365c2e0590e" +dependencies = [ + "ctor-proc-macro", + "dtor", +] + +[[package]] +name = "ctor-proc-macro" +version = "0.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1" + [[package]] name = "data-encoding" version = "2.11.0" @@ -567,6 +649,21 @@ dependencies = [ "syn", ] +[[package]] +name = "dtor" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "404d02eeb088a82cfd873006cb713fe411306c7d182c344905e101fb1167d301" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" + [[package]] name = "dyn-clone" version = "1.0.20" @@ -579,6 +676,12 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +[[package]] +name = "embedded-io" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eb1aa714776b75c7e67e1da744b81a129b3ff919c8712b5e1b32252c1f07cc7" + [[package]] name = "ena" version = "0.14.4" @@ -615,6 +718,29 @@ dependencies = [ "syn", ] +[[package]] +name = "env_filter" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e90c2accc4b07a8456ea0debdc2e7587bdd890680d71173a15d4ae604f6eef" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0621c04f2196ac3f488dd583365b9c09be011a4ab8b9f37248ffcc8f6198b56a" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -724,6 +850,31 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.32" @@ -731,6 +882,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -739,6 +891,17 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + [[package]] name = "futures-io" version = "0.3.32" @@ -774,9 +937,13 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ + "futures-channel", "futures-core", + "futures-io", "futures-macro", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "slab", ] @@ -1044,11 +1211,13 @@ dependencies = [ "log4rs", "sequoia-cert-store", "sequoia-directories", + "sequoia-keystore", "sequoia-net", "sequoia-openpgp", "sequoia-wot", "serde", "serde_derive", + "tempfile", "thiserror 2.0.18", "tokio", "toml", @@ -1338,6 +1507,30 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jiff" +version = "0.2.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4603d3033e49e2b0e31229fcab20a5d40089c607d975cd9c80551dc69eed9102" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", +] + +[[package]] +name = "jiff-static" +version = "0.2.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "782d32378dddf207193ac91cefb848ad41abb58195c95168e1291227a0832b47" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "js-sys" version = "0.3.99" @@ -1359,6 +1552,27 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "lalrpop" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba4ebbd48ce411c1d10fb35185f5a51a7bfa3d8b24b4e330d30c9e3a34129501" +dependencies = [ + "ascii-canvas", + "bit-set 0.8.0", + "ena", + "itertools 0.14.0", + "lalrpop-util 0.22.2", + "petgraph 0.7.1", + "regex", + "regex-syntax", + "sha3", + "string_cache 0.8.9", + "term", + "unicode-xid", + "walkdir", +] + [[package]] name = "lalrpop" version = "0.23.1" @@ -1366,20 +1580,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98a80a963123205c7157323c99611bc4abb65dcbd62ef46dc4bac74a3941bc75" dependencies = [ "ascii-canvas", - "bit-set", + "bit-set 0.9.1", "ena", "itertools 0.14.0", - "lalrpop-util", - "petgraph", + "lalrpop-util 0.23.1", + "petgraph 0.8.3", "regex", "regex-syntax", "sha3", - "string_cache", + "string_cache 0.9.0", "term", "unicode-xid", "walkdir", ] +[[package]] +name = "lalrpop-util" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5baa5e9ff84f1aefd264e6869907646538a52147a755d494517a8007fb48733" +dependencies = [ + "regex-automata", + "rustversion", +] + [[package]] name = "lalrpop-util" version = "0.23.1" @@ -1777,12 +2001,28 @@ dependencies = [ "subtle", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "percent-encoding" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset", + "indexmap", +] + [[package]] name = "petgraph" version = "0.8.3" @@ -1794,6 +2034,15 @@ dependencies = [ "indexmap", ] +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + [[package]] name = "phf_shared" version = "0.13.1" @@ -1815,6 +2064,21 @@ version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-atomic-util" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" +dependencies = [ + "portable-atomic", +] + [[package]] name = "potential_utf" version = "0.1.5" @@ -2274,6 +2538,126 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "sequoia-gpg-agent" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c36b37720b61230507a953d9879eb0dd8c228d86948d37cba53ced942e862a1" +dependencies = [ + "anyhow", + "chrono", + "futures", + "lalrpop 0.22.2", + "lalrpop-util 0.22.2", + "libc", + "sequoia-ipc", + "sequoia-openpgp", + "stfu8", + "tempfile", + "thiserror 2.0.18", + "tokio", +] + +[[package]] +name = "sequoia-ipc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0770bf144d96733921b4647cd4b243cbebf032127676a441e3ebe702c0768c54" +dependencies = [ + "anyhow", + "capnp-rpc", + "capnpc", + "ctor", + "dirs", + "fs2", + "lalrpop 0.22.2", + "lalrpop-util 0.22.2", + "libc", + "memsec", + "sequoia-openpgp", + "socket2", + "tempfile", + "thiserror 2.0.18", + "tokio", + "tokio-util", + "winapi", +] + +[[package]] +name = "sequoia-keystore" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e5887d0c3c5f0df1d1cab937ef7a5c398111ddfd911c714f28c904438f229c" +dependencies = [ + "anyhow", + "async-generic", + "capnp", + "capnpc", + "dirs", + "env_logger", + "log", + "paste", + "sequoia-directories", + "sequoia-ipc", + "sequoia-keystore-backend", + "sequoia-keystore-gpg-agent", + "sequoia-keystore-softkeys", + "sequoia-openpgp", + "thiserror 2.0.18", + "tokio", + "tokio-util", +] + +[[package]] +name = "sequoia-keystore-backend" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c55b047f9b6412c34dc7a26a42f278205cbf1e29516feab1228edccca215f500" +dependencies = [ + "anyhow", + "async-trait", + "env_logger", + "futures", + "log", + "sequoia-openpgp", + "tempfile", + "thiserror 2.0.18", + "tokio", +] + +[[package]] +name = "sequoia-keystore-gpg-agent" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ad901f1f1d88b38f5f5738d5bf2b80136ec44e94a40716193be0bbf3314df2b" +dependencies = [ + "anyhow", + "async-trait", + "futures", + "log", + "openpgp-cert-d", + "sequoia-gpg-agent", + "sequoia-ipc", + "sequoia-keystore-backend", + "sequoia-openpgp", + "tokio", +] + +[[package]] +name = "sequoia-keystore-softkeys" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c4944848f748ce22875020e9cff9e604d52c563aef8e19053f5abe037139e" +dependencies = [ + "anyhow", + "async-trait", + "dirs", + "futures", + "log", + "sequoia-keystore-backend", + "sequoia-openpgp", +] + [[package]] name = "sequoia-net" version = "0.30.1" @@ -2314,8 +2698,8 @@ dependencies = [ "flate2", "getrandom 0.2.17", "idna", - "lalrpop", - "lalrpop-util", + "lalrpop 0.23.1", + "lalrpop-util 0.23.1", "libc", "memsec", "nettle", @@ -2527,6 +2911,24 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "stfu8" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51f1e89f093f99e7432c491c382b88a6860a5adbe6bf02574bf0a08efff1978" + +[[package]] +name = "string_cache" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared 0.11.3", + "precomputed-hash", +] + [[package]] name = "string_cache" version = "0.9.0" @@ -2535,7 +2937,7 @@ checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" dependencies = [ "new_debug_unreachable", "parking_lot", - "phf_shared", + "phf_shared 0.13.1", "precomputed-hash", ] @@ -2755,6 +3157,7 @@ checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", "pin-project-lite", "tokio", diff --git a/Cargo.toml b/Cargo.toml index a9add1e..b3cfb6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,11 +21,13 @@ log = "0.4.30" log4rs = "1.3.0" sequoia-cert-store = "0.7.3" sequoia-directories = "0.1.0" +sequoia-keystore = "0.7.2" sequoia-net = "0.30.1" sequoia-openpgp = "2.3.0" sequoia-wot = "0.15.0" serde = "1.0.228" serde_derive = "1.0.228" +tempfile = "3.27.0" thiserror = "2.0.17" tokio = { version = "1.52.3", features = [ "tokio-macros", "rt-multi-thread", "signal" ] } toml = "0.9.8" diff --git a/src/commands/locals/add.rs b/src/commands/locals/add.rs index 37193f9..652c087 100644 --- a/src/commands/locals/add.rs +++ b/src/commands/locals/add.rs @@ -37,6 +37,7 @@ pub async fn dispatch(cmd: LocalsAddCommand, config: HuskConfigContainer) -> Res &vc, crypto::Role::Local, true, + None, None ) } diff --git a/src/commands/locals/remove.rs b/src/commands/locals/remove.rs index 85ac4f8..a7addfb 100644 --- a/src/commands/locals/remove.rs +++ b/src/commands/locals/remove.rs @@ -41,6 +41,7 @@ pub async fn dispatch(cmd: LocalsRemoveCommand, config: HuskConfigContainer) -> cert, crypto::Role::Local, false, + None, None) } else { Err(CommandError::CertNotUsable(fpr).into()) diff --git a/src/common/crypto.rs b/src/common/crypto.rs index 575d5f7..40f746e 100644 --- a/src/common/crypto.rs +++ b/src/common/crypto.rs @@ -3,15 +3,20 @@ use std::io::Write; use std::sync::Arc; use std::time::Duration; +use std::time::SystemTime; use std::collections::HashMap; use std::collections::HashSet; use std::collections::hash_map::Entry::{Occupied, Vacant}; use anyhow; use sequoia_openpgp::cert::ValidCert; +use sequoia_openpgp::packet::signature::subpacket::NotationDataFlags; use tokio::task::JoinSet; use bytes::Bytes; use sequoia_net::{KeyServer, wkd, dane}; +use sequoia_directories::Home; +use sequoia_keystore::Keystore; +use sequoia_keystore::Context; use sequoia_openpgp::packet::prelude::SignatureBuilder; use sequoia_openpgp::parse::{PacketParser, PacketParserResult, Parse}; use sequoia_openpgp::policy::StandardPolicy; @@ -437,7 +442,7 @@ pub enum Role { /// `domains`. Passing `None` or an empty list in this case is an error. /// If `activate` is false, create a certifiaction with minimal trust_roots /// amount and no depth. In this case `domains` is ignored. -pub fn authenticate(context: &HuskContext, cert: &ValidCert, role: Role, activate: bool, domains: Option>) +pub fn authenticate(context: &HuskContext, cert: &ValidCert, role: Role, activate: bool, domains: Option>, notation: Option<(String, String)>) -> anyhow::Result<()> { let policy = &context.policy; @@ -502,6 +507,14 @@ pub fn authenticate(context: &HuskContext, cert: &ValidCert, role: Role, activat builder = builder.set_trust_signature(0, 1)?; } + if let Some((name, value)) = notation { + builder = builder.set_notation( + name, + value, + NotationDataFlags::empty().set_human_readable(), + false)?; + } + // For a certification, user ids are needed. Build a list of // valid (non self revoked) user ids. let user_ids: Vec<_> = cert.userids() @@ -559,9 +572,107 @@ pub fn can_encrypt(cert: &ValidCert) -> bool { .count() > 0 } +/// Signing keys used by Husk must have a certification with a +/// notation of the name `HUSK_SIG_NOTATION`. +pub const HUSK_SIG_NOTATION: &str = "signing_key@husk-project.org"; + +/// Get Husks signing key +pub fn get_signing_key(sequoia_home: Home, cert_store: &CertStore<'_>, policy: &StandardPolicy<'_>, local_trust_root: &Cert) -> anyhow::Result> { + + let local_trust_root = local_trust_root.with_policy(policy, None)?; + let local_trust_fpr = local_trust_root.fingerprint(); + + let mut newest = None; + let mut result = None; + + if let Some(keys) = get_all_keys(sequoia_home) { + for key in keys { + if let Ok(cert) = cert_store.lookup_by_cert_fpr(&key) { + if let Ok(vc) = cert.with_policy(policy, None) { + if let Some(time) = contains_signing_key(&vc, &local_trust_fpr) { + if let Some(othertime) = newest { + if time > othertime { + newest = Some(time); + result = Some(vc.cert().clone()); + } + } else { // first detected key + newest = Some(time); + result = Some(vc.cert().clone()); + } + } + } + } + } + Ok(result) + } else { + Ok(None) + } +} + +/// If `vc` is a certificate marked as a Husk signing key +/// key, return the creation time of the signature - else return `None`. +pub fn contains_signing_key(vc: &ValidCert, local_root_fpr: &Fingerprint) -> Option { + for uid in vc.userids().into_iter() { + for sig in uid.certifications() { + if sig.issuer_fingerprints().any(|i| i == local_root_fpr) + && sig.notation(HUSK_SIG_NOTATION).count() > 0 { + + // ignore signatures without creation time + if let Some(time) = sig.signature_creation_time() { + return Some(time); + } + } + } + } + None +} + +/// Get all keys from all backends +pub fn get_all_keys(sequoia_home: Home) -> Option> { + + let mut result = Vec::new(); + 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(devices) = backend.devices() { + for mut device in devices { + if let Ok(keys) = device.keys() { + keys.iter().for_each(|k| { + result.push(k.fingerprint()); + }); + } + } + } + } + } + } + } + }); + }); + Some(result) +} #[cfg(test)] pub mod tests { + use sequoia_cert_store::store::CertD; + use sequoia_cert_store::{CertStore, LazyCert, StoreUpdate}; + use sequoia_keystore::Context; + use sequoia_keystore::Keystore; + use sequoia_directories::Home; + use sequoia_directories::Component; + use sequoia_openpgp::{cert::CertBuilder, policy::StandardPolicy}; + use std::sync::Arc; + + use crate::common::crypto::{HUSK_SIG_NOTATION, get_all_keys}; + use crate::types::{husk_context::HuskContext, mail_context::MailContext}; + + use super::{authenticate, get_signing_key}; #[test] pub fn test_is_encrypted() { @@ -644,4 +755,85 @@ jk6lh01Ej9QTGQ== } + #[test] + fn test_signing_key() -> anyhow::Result<()> { + + let temp_dir = tempfile::tempdir()?; + let temp_path = temp_dir.path().to_path_buf(); + let home = Home::new(temp_path)?; + + // build the keystore + let keystore_home = home.data_dir(Component::Keystore); + let c = Context::configure() + .home(keystore_home) + .build()?; + let mut ks = Keystore::connect(&c)?; + + // get the softkeys backend + let mut softkeys = None; + for mut backend in ks.backends()?.into_iter() { + if backend.id().expect("backend id") == "softkeys" { + softkeys = Some(backend); + } + } + assert!(softkeys.is_some()); + let mut softkeys = softkeys.unwrap(); + + // build the certstore + let cert_store_base = home.data_dir(sequoia_directories::Component::CertD); + std::fs::create_dir_all(&cert_store_base)?; + let _ = CertD::open(&cert_store_base)?; + let store = CertStore::open(cert_store_base)?; + + // build the trust root + let (root, _) = CertBuilder::general_purpose(Some("local root")) + .add_signing_subkey() + .generate()?; + + // create a skeleton HuskContext for later use. + let context = HuskContext { + cert_store: store, + policy: StandardPolicy::new(), + local_trust_root: root, + signing_key: None, + introducers: Vec::new(), + keyservers: Vec::new(), + mail: MailContext::new() + }; + + // store the local trust root + context.cert_store.update(Arc::new(LazyCert::from(context.local_trust_root.clone())))?; + + // the siging key + let (signing_key, _) = CertBuilder::general_purpose(Some("signing key")) + .add_signing_subkey() + .generate()?; + let valid_signing_cert = signing_key.with_policy(&context.policy, None)?; + let signing_fpr = signing_key.fingerprint(); + softkeys.import(&signing_key)?; + + let keys= get_all_keys(home.clone()); + eprintln!("{:?}", keys); + + authenticate(&context, + &valid_signing_cert, + super::Role::Local, + true, + None, + Some((HUSK_SIG_NOTATION.to_string(), "dummy".to_string())))?; + + // get the Husk signing key + let key = get_signing_key(home, + &context.cert_store, + &context.policy, + &context.local_trust_root)?; + + assert!(key.is_some()); + let fpr = key.unwrap().fingerprint(); + + assert_eq!(signing_fpr, fpr); + + Ok(()) + } + } diff --git a/src/types/husk_context.rs b/src/types/husk_context.rs index 3ca6dce..8e839bd 100644 --- a/src/types/husk_context.rs +++ b/src/types/husk_context.rs @@ -26,6 +26,7 @@ pub struct HuskContext<'hc> { pub policy: StandardPolicy<'hc>, pub cert_store: CertStore<'hc>, pub local_trust_root: Cert, + pub signing_key: Option, pub introducers: Vec, pub keyservers: Vec, pub mail: MailContext<'hc>, @@ -45,10 +46,19 @@ impl<'hc> HuskContext<'hc> { let local_trust_root = crypto::get_local_trust_root(&cert_store)?; let introducers = crypto::get_introducers(&cert_store, &policy, local_trust_root.fingerprint()); + let signing_key = match crypto::get_signing_key(sequoia_home, &cert_store, &policy, &local_trust_root) { + Ok(Some(cert)) => Some(cert), + _ => { + log::warn!("No signing key found"); + None + } + }; + Ok(HuskContext { policy, cert_store, local_trust_root, + signing_key, introducers, keyservers: config.get_keyservers(), mail: MailContext::new(), diff --git a/src/types/introducer.rs b/src/types/introducer.rs index d44e82c..142c7f3 100644 --- a/src/types/introducer.rs +++ b/src/types/introducer.rs @@ -83,7 +83,8 @@ impl Introducer { cert, crypto::Role::Introducer, true, - Some(domains)) + Some(domains), + None) } /// Remove an introducer. @@ -101,6 +102,7 @@ impl Introducer { cert, crypto::Role::Introducer, false, + None, None) } }