Add support for using a key store.

- Support using keys managed by `sequoia-keystore`.

  - When decrypting a message, have `sq` automatically ask the
    key store to decrypt the PKESKs.

  - Extend `sq sign` and `sq encrypt` with the `--signer-key`
    parameter to use a key managed by the keystore.

  - Add two top-level options: `--no-key-store`, which disables the
    use of the key store, and `--key-store`, which uses an alternate
    key store instance.

  - Add `sq key list` to list keys on the key store.
This commit is contained in:
Neal H. Walfield 2024-01-18 18:09:59 +01:00 committed by Neal H. Walfield
parent c8567714e5
commit 27093c1709
No known key found for this signature in database
GPG Key ID: 6863C9AD5B4D22D3
30 changed files with 1087 additions and 311 deletions

184
Cargo.lock generated
View File

@ -288,6 +288,17 @@ version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
[[package]]
name = "blanket"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0b121a9fe0df916e362fb3271088d071159cdf11db0e4182d02152850756eff"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
@ -403,6 +414,45 @@ dependencies = [
"cipher",
]
[[package]]
name = "capnp"
version = "0.18.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b671a5b39eadb909b0c2b81d22a400d446526e651e64be9eb6674b33644a4"
dependencies = [
"embedded-io",
]
[[package]]
name = "capnp-futures"
version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8697b857f5b014ff378f02817426d3b6fb90a69f32e330fe010f24fe10cf8f1"
dependencies = [
"capnp",
"futures",
]
[[package]]
name = "capnp-rpc"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94b87a9e0f74600628e227d39b79ef8652c558a408999ac46ba22b19dbad0010"
dependencies = [
"capnp",
"capnp-futures",
"futures",
]
[[package]]
name = "capnpc"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a642faaaa78187e70bdcc0014c593c213553cfeda3b15054426d0d596048b131"
dependencies = [
"capnp",
]
[[package]]
name = "cast5"
version = "0.11.1"
@ -696,6 +746,16 @@ dependencies = [
"typenum",
]
[[package]]
name = "ctor"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30d2b3721e861707777e3195b0158f950ae6dc4a27e4d02ff9f67e3eb3de199e"
dependencies = [
"quote",
"syn 2.0.48",
]
[[package]]
name = "ctr"
version = "0.9.2"
@ -991,6 +1051,12 @@ dependencies = [
"zeroize",
]
[[package]]
name = "embedded-io"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "658bbadc628dc286b9ae02f0cb0f5411c056eb7487b72f0083203f115de94060"
[[package]]
name = "ena"
version = "0.14.2"
@ -1234,6 +1300,21 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "futures"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.30"
@ -1241,6 +1322,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
@ -1249,6 +1331,17 @@ version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
[[package]]
name = "futures-executor"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.30"
@ -1284,9 +1377,13 @@ version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
@ -2356,6 +2453,12 @@ dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "paste"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]]
name = "peeking_take_while"
version = "0.1.2"
@ -3043,6 +3146,84 @@ dependencies = [
"tokio",
]
[[package]]
name = "sequoia-ipc"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94fd8161ea44e9802c622e4cf088a6e9854baa573d1638f40600f76af90a9194"
dependencies = [
"anyhow",
"buffered-reader",
"capnp-rpc",
"crossbeam-utils",
"ctor",
"dirs",
"fs2",
"futures",
"lalrpop",
"lalrpop-util",
"lazy_static",
"libc",
"memsec",
"rand",
"sequoia-openpgp",
"socket2",
"tempfile",
"thiserror",
"tokio",
"tokio-util",
"winapi",
]
[[package]]
name = "sequoia-keystore"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df8e6116f15b027cc5cc23093985cad774fa72671bf643813f9ef331765e9da6"
dependencies = [
"anyhow",
"capnp",
"capnpc",
"dirs",
"env_logger",
"lazy_static",
"log",
"paste",
"sequoia-ipc",
"sequoia-keystore-backend",
"sequoia-keystore-softkeys",
"sequoia-openpgp",
"thiserror",
"tokio",
"tokio-util",
]
[[package]]
name = "sequoia-keystore-backend"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aaef68a858310acd7557868cf35ad4a394e155a1d6e5de8e7c651cb07e6f96fb"
dependencies = [
"anyhow",
"blanket",
"sequoia-openpgp",
"thiserror",
]
[[package]]
name = "sequoia-keystore-softkeys"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d24b52535e007fdefb5559902cf186fa5c2e4709d65e2ab8a99bf2fc320eccaa"
dependencies = [
"anyhow",
"dirs",
"lazy_static",
"log",
"sequoia-keystore-backend",
"sequoia-openpgp",
]
[[package]]
name = "sequoia-net"
version = "0.28.0"
@ -3163,12 +3344,14 @@ dependencies = [
"indicatif",
"itertools",
"libc",
"once_cell",
"predicates",
"regex",
"roff",
"rpassword",
"sequoia-autocrypt",
"sequoia-cert-store",
"sequoia-keystore",
"sequoia-net",
"sequoia-openpgp",
"sequoia-policy-config",
@ -3791,6 +3974,7 @@ checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
dependencies = [
"bytes",
"futures-core",
"futures-io",
"futures-sink",
"pin-project-lite",
"tokio",

View File

@ -42,7 +42,9 @@ clap = { version = "4", features = ["derive", "env", "string", "wrap_help"] }
humantime = "2"
indicatif = "0.17"
itertools = ">=0.10, <0.13"
once_cell = "1.17"
sequoia-cert-store = "0.4.2"
sequoia-keystore = { version = "0.1" }
sequoia-wot = "0.9"
tempfile = "3.1"
tokio = { version = "1.13.1" }

12
NEWS
View File

@ -27,6 +27,18 @@
write the assets to a predictable location, set the environment
variable `ASSET_OUT_DIR` to a suitable location.
- `sq` now uses `sequoia-keystore` for secret key operations.
When decrypting a message, `sq` will automatically ask the
keystore to decrypt the message. `sq sign --signer-key` can be
used to specify a signing key managed by the key store.
- New top-level option: `sq --no-key-store`: A new switch to
disable the use of the key store.
- New top-level option: `sq --key-store`: A new option to use an
alternate key store.
* Changes in 0.32.0
** New functionality
- Support for password-encrypted keys has been improved. For

File diff suppressed because it is too large Load Diff

View File

@ -137,6 +137,12 @@ pub struct Command {
help = "Signs the message using the key in KEY_FILE",
)]
pub signer_key_file: Vec<PathBuf>,
#[clap(
long = "signer-key",
value_name = "KEYID|FINGERPRINT",
help = "Signs the message using the specified key on the key store",
)]
pub signer_key: Vec<KeyHandle>,
#[clap(
long = "private-key-store",
value_name = "KEY_STORE",

View File

@ -167,6 +167,7 @@ macro_rules! test_examples {
Command::cargo_bin(command[0]).unwrap()
.current_dir(&tmp_dir)
.env("SQ_CERT_STORE", &cert_store)
.arg("--no-key-store")
.args(&command[1..])
.assert()
.success();

View File

@ -18,6 +18,11 @@ use crate::cli::types::FileOrStdin;
use crate::cli::types::FileOrStdout;
use crate::cli::types::Time;
use crate::cli::examples;
use examples::Action;
use examples::Actions;
use examples::Example;
pub mod expire;
/// The revocation reason for a certificate or subkey
@ -80,6 +85,7 @@ pub struct Command {
#[derive(Debug, Subcommand)]
pub enum Subcommands {
List(ListCommand),
Generate(GenerateCommand),
Password(PasswordCommand),
Expire(expire::Command),
@ -93,6 +99,27 @@ pub enum Subcommands {
Adopt(AdoptCommand),
}
const LIST_EXAMPLES: Actions = Actions {
actions: &[
Action::Example(Example {
comment: "\
Lists the keys managed by the keystore server.",
command: &[
"sq", "key", "list",
],
}),
]
};
test_examples!(sq_key_list, LIST_EXAMPLES);
#[derive(Debug, Args)]
#[clap(
about = "Lists keys managed by the key store",
after_help = LIST_EXAMPLES,
)]
pub struct ListCommand {
}
#[derive(Debug, Args)]
#[clap(
about = "Generates a new key",

View File

@ -146,7 +146,9 @@ pub fn build() -> Command {
Functionality is grouped and available using subcommands. This
interface is not completely stateless. In particular, the user's
default certificate store is used. This can be disabled using
`--no-cert-store`.
`--no-cert-store`. Similarly, a key store is used to manage and
protect secret key material. This can be disabled using
`--no-key-store`.
OpenPGP data can be provided in binary or ASCII armored form. This
will be handled automatically. Emitted OpenPGP data is ASCII armored
@ -172,6 +174,33 @@ pub struct SqCommand {
help = "Overwrites existing files"
)]
pub force: bool,
#[clap(
long,
help = "Disables the use of the key store.",
long_help = "\
Disables the use of the key store.
It is still possible to use functionality that does not require the
key store."
)]
pub no_key_store: bool,
#[clap(
long,
value_name = "PATH",
env = "SQ_KEY_STORE",
conflicts_with_all = &[ "no_key_store" ],
help = "Overrides the key store server and its data",
long_help = "\
A key store server manages and protects secret key material. By
default, `sq` connects to the key store server listening on
`$XDG_DATA_HOME/sequoia`. If no key store server is running, one is
started.
This option causes `sq` to use an alternate key store server. If
necessary, a key store server is started, and configured to look for
its data in the specified location."
)]
pub key_store: Option<PathBuf>,
#[clap(
long,
global = true,

View File

@ -4,6 +4,9 @@ use std::path::PathBuf;
use clap::Parser;
use sequoia_openpgp as openpgp;
use openpgp::KeyHandle;
use super::types::ClapData;
use super::types::FileOrStdin;
use super::types::FileOrStdout;
@ -118,6 +121,12 @@ pub struct Command {
help = "Signs the message using the key in KEY_FILE",
)]
pub secret_key_file: Vec<PathBuf>,
#[clap(
long = "signer-key",
value_name = "KEYID|FINGERPRINT",
help = "Signs the message using the specified key on the key store",
)]
pub signer_key: Vec<KeyHandle>,
#[clap(
long,
value_names = &["NAME", "VALUE"],

View File

@ -19,6 +19,8 @@ use openpgp::parse::stream::{
VerificationHelper, DecryptionHelper, DecryptorBuilder, MessageStructure,
};
use sequoia_keystore as keystore;
use crate::{
cli,
commands::{
@ -200,6 +202,24 @@ impl<'a, 'certdb> Helper<'a, 'certdb> {
}
}
/// Checks if a session key can decrypt the packet parser using
/// `decrypt`.
fn try_session_key<D>(&self, keyid: &KeyID,
algo: SymmetricAlgorithm, sk: SessionKey,
decrypt: &mut D)
-> Option<Option<Fingerprint>>
where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool
{
if decrypt(algo, &sk) {
if self.dump_session_key {
wprintln!("Session key: {}", hex::encode(&sk));
}
Some(self.key_identities.get(keyid).cloned())
} else {
None
}
}
/// Tries to decrypt the given PKESK packet with `keypair` and try
/// to decrypt the packet parser using `decrypt`.
fn try_decrypt<D>(&self, pkesk: &PKESK,
@ -210,19 +230,8 @@ impl<'a, 'certdb> Helper<'a, 'certdb> {
where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool
{
let keyid = keypair.public().fingerprint().into();
match pkesk.decrypt(&mut *keypair, sym_algo)
.and_then(|(algo, sk)| {
if decrypt(algo, &sk) { Some(sk) } else { None }
})
{
Some(sk) => {
if self.dump_session_key {
wprintln!("Session key: {}", hex::encode(&sk));
}
Some(self.key_identities.get(&keyid).cloned())
},
None => None,
}
let (sym_algo, sk) = pkesk.decrypt(&mut *keypair, sym_algo)?;
self.try_session_key(&keyid, sym_algo, sk, decrypt)
}
}
@ -261,6 +270,9 @@ impl<'a, 'certdb> DecryptionHelper for Helper<'a, 'certdb> {
}
}
// Now, we try the secret keys that the user supplied on the
// command line.
// First, we try those keys that we can use without prompting
// for a password.
for pkesk in pkesks {
@ -365,6 +377,63 @@ impl<'a, 'certdb> DecryptionHelper for Helper<'a, 'certdb> {
}
}
// Try the key store.
match self.vhelper.config.key_store_or_else() {
Ok(ks) => {
let mut ks = ks.lock().unwrap();
match ks.decrypt(&pkesks[..]) {
// Success!
Ok((_i, fpr, sym_algo, sk)) => {
if let Some(fp) =
self.try_session_key(
&KeyID::from(fpr), sym_algo, sk, &mut decrypt)
{
return Ok(fp);
}
}
Err(err) => {
match err.downcast() {
Ok(keystore::Error::InaccessibleDecryptionKey(keys)) => {
for key_status in keys.into_iter() {
let pkesk = key_status.pkesk().clone();
let mut key = key_status.into_key();
let keyid = key.keyid();
let keypair = loop {
let password = rpassword::prompt_password(
format!(
"Enter password to unlock {}: ",
keyid))?
.into();
if let Ok(()) = key.unlock(password) {
break Box::new(key);
} else {
wprintln!("Bad password.");
}
};
if let Some(fp) = self.try_decrypt(
&pkesk, sym_algo, keypair, &mut decrypt)
{
return Ok(fp);
}
}
}
// Failed to decrypt using the keystore.
Ok(_err) => (),
Err(_err) => (),
}
}
}
}
Err(err) => {
wprintln!("Warning: unable to connect to keystore: {}",
err);
}
}
if skesks.is_empty() {
return
Err(anyhow::anyhow!("No key to decrypt message"));

View File

@ -9,6 +9,9 @@ use sequoia_openpgp as openpgp;
use openpgp::armor;
use openpgp::cert::amalgamation::ValidAmalgamation;
use openpgp::crypto;
use openpgp::crypto::Password;
use openpgp::KeyHandle;
use openpgp::KeyID;
use openpgp::policy::Policy;
use openpgp::serialize::stream::Compressor;
use openpgp::serialize::stream::Encryptor2 as Encryptor;
@ -21,6 +24,7 @@ use openpgp::serialize::stream::padding::Padder;
use openpgp::types::CompressionAlgorithm;
use openpgp::types::KeyFlags;
use crate::best_effort_primary_uid;
use crate::cli;
use crate::cli::types::EncryptPurpose;
use crate::cli::types::FileOrStdin;
@ -61,8 +65,10 @@ pub fn dispatch(config: Config, command: cli::encrypt::Command) -> Result<()> {
let additional_secrets =
load_certs(command.signer_key_file.iter().map(|s| s.as_ref()))?;
let signer_keys = &command.signer_key[..];
encrypt(
&config,
&config.policy,
command.private_key_store.as_deref(),
command.input,
@ -70,6 +76,7 @@ pub fn dispatch(config: Config, command: cli::encrypt::Command) -> Result<()> {
command.symmetric as usize,
&recipients,
additional_secrets,
signer_keys,
command.mode,
command.compression,
Some(config.time),
@ -82,6 +89,7 @@ pub fn dispatch(config: Config, command: cli::encrypt::Command) -> Result<()> {
}
pub fn encrypt<'a, 'b: 'a>(
config: &Config,
policy: &'b dyn Policy,
private_key_store: Option<&str>,
input: FileOrStdin,
@ -89,6 +97,7 @@ pub fn encrypt<'a, 'b: 'a>(
npasswords: usize,
recipients: &'b [openpgp::Cert],
signers: Vec<openpgp::Cert>,
signer_keys: &[KeyHandle],
mode: EncryptPurpose,
compression: CompressionMode,
time: Option<SystemTime>,
@ -125,6 +134,75 @@ pub fn encrypt<'a, 'b: 'a>(
let mut signers = get_signing_keys(
&signers, policy, private_key_store, time, None)?;
let mut signer_keys = if signer_keys.is_empty() {
Vec::new()
} else {
let mut ks = config.key_store_or_else()?.lock().unwrap();
signer_keys.into_iter()
.map(|kh| {
let keys = ks.find_key(kh.clone())?;
match keys.len() {
0 => return Err(anyhow::anyhow!(
"{} is not present on keystore", kh)),
1 => (),
n => {
eprintln!("Warning: {} is present on multiple \
({}) devices",
kh, n);
}
}
let mut key = keys.into_iter().next().expect("checked for one");
match key.locked() {
Ok(true) => {
let fpr = key.fingerprint();
let cert = config.lookup_one(
&KeyHandle::from(&fpr), None, true);
let display = match cert {
Ok(cert) => {
format!(" ({})",
String::from_utf8_lossy(
best_effort_primary_uid(
&cert, &config.policy,
config.time)
.value()))
}
Err(_) => {
"".to_string()
}
};
let keyid = KeyID::from(&fpr);
loop {
let password = Password::from(rpassword::prompt_password(
format!("Enter password to unlock {}{}: ",
keyid, display))?);
match key.unlock(password) {
Ok(()) => break,
Err(err) => {
wprintln!("Unlocking {}: {}.", keyid, err);
}
}
}
}
Ok(false) => {
// Already unlocked, nothing to do.
}
Err(err) => {
// Failed to get the key's locked status. Just print
// a warning now. We'll (probably) fail more later.
wprintln!("Getting {}'s status: {}",
key.keyid(), err);
}
}
Ok(key)
})
.collect::<Result<Vec<_>>>()?
};
// Build a vector of recipients to hand to Encryptor.
let mut recipient_subkeys: Vec<Recipient> = Vec::new();
for cert in recipients.iter() {
@ -188,13 +266,20 @@ pub fn encrypt<'a, 'b: 'a>(
}
// Optionally sign message.
if ! signers.is_empty() {
let mut signer = Signer::new(sink, signers.pop().unwrap().0);
if ! signers.is_empty() || ! signer_keys.is_empty() {
let mut signer = if ! signers.is_empty() {
Signer::new(sink, signers.pop().unwrap().0)
} else {
Signer::new(sink, signer_keys.pop().unwrap())
};
if let Some(time) = time {
signer = signer.creation_time(time);
}
for s in signers {
signer = signer.add_signer(s.0);
if let Some(time) = time {
signer = signer.creation_time(time);
}
}
for s in signer_keys {
signer = signer.add_signer(s);
}
for r in recipients.iter() {
signer = signer.add_intended_recipient(r);

View File

@ -11,6 +11,8 @@ use attest_certifications::attest_certifications;
mod expire;
mod extract_cert;
use extract_cert::extract_cert;
mod list;
use list::list;
mod generate;
use generate::generate;
mod password;
@ -24,6 +26,7 @@ pub fn dispatch(config: Config, command: cli::key::Command) -> Result<()>
{
use cli::key::Subcommands::*;
match command.subcommand {
List(c) => list(config, c)?,
Generate(c) => generate(config, c)?,
Password(c) => password(config, c)?,
Expire(c) => expire::dispatch(config, c)?,

79
src/commands/key/list.rs Normal file
View File

@ -0,0 +1,79 @@
use sequoia_openpgp as openpgp;
use openpgp::KeyHandle;
use crate::best_effort_primary_uid;
use crate::cli;
use crate::Config;
use crate::Result;
pub fn list(config: Config, _command: cli::key::ListCommand) -> Result<()> {
// Start and connect to the keystore.
let ks = if let Some(ks) = config.key_store()? {
ks
} else {
// The key store is disabled. Don't fail, just return
// nothing.
return Ok(());
};
let mut ks = ks.lock().unwrap();
let mut backends = ks.backends()?;
for backend in &mut backends {
let devices = backend.list()?;
if devices.len() == 0 {
println!(" - Backend {} has no devices.", backend.id()?);
} else {
println!(" - {}", backend.id()?);
}
for mut device in devices {
let keys = device.list()?;
if keys.len() == 0 {
println!(" - Device {} has no keys.", device.id()?);
} else {
println!(" - {}", device.id()?);
}
for mut key in keys.into_iter() {
let fpr = KeyHandle::from(key.fingerprint());
let userid = if let Ok(cert) = config.lookup_one(&fpr, None, true) {
best_effort_primary_uid(&cert, &config.policy, None).to_string()
} else {
"(Unknown)".to_string()
};
let signing_capable = key.signing_capable().unwrap_or(false);
let decryption_capable = key.decryption_capable().unwrap_or(false);
println!(" - {} {} ({}, {}, {})",
fpr, userid,
if key.available().unwrap_or(false) {
"available"
} else {
"not available"
},
if key.locked().unwrap_or(false) {
"locked"
} else {
"not locked"
},
match (signing_capable, decryption_capable) {
(true, true) => {
"for signing and decryption"
}
(true, false) => {
"for signing"
}
(false, true) => {
"for decryption"
}
(false, false) => {
"unusable"
}
});
}
}
}
Ok(())
}

View File

@ -7,6 +7,9 @@ use tempfile::NamedTempFile;
use sequoia_openpgp as openpgp;
use openpgp::armor;
use openpgp::crypto::Password;
use openpgp::KeyHandle;
use openpgp::KeyID;
use openpgp::{Packet, Result};
use openpgp::packet::prelude::*;
use openpgp::packet::signature::subpacket::NotationData;
@ -20,6 +23,7 @@ use openpgp::serialize::stream::{
};
use openpgp::types::SignatureType;
use crate::best_effort_primary_uid;
use crate::Config;
use crate::load_certs;
use crate::parse_notations;
@ -47,6 +51,7 @@ pub fn dispatch(config: Config, command: cli::sign::Command) -> Result<()> {
let private_key_store = command.private_key_store.as_deref();
let secrets =
load_certs(command.secret_key_file.iter().map(|s| s.as_ref()))?;
let signer_keys = &command.signer_key[..];
let time = Some(config.time);
let notations = parse_notations(command.notation)?;
@ -70,6 +75,7 @@ pub fn dispatch(config: Config, command: cli::sign::Command) -> Result<()> {
&mut input,
output,
secrets,
signer_keys,
detached,
binary,
append,
@ -87,6 +93,7 @@ pub fn sign<'a, 'certdb>(
input: &'a mut (dyn io::Read + Sync + Send),
output: &'a FileOrStdout,
secrets: Vec<openpgp::Cert>,
signer_keys: &[KeyHandle],
detached: bool,
binary: bool,
append: bool,
@ -102,6 +109,7 @@ pub fn sign<'a, 'certdb>(
input,
output,
secrets,
signer_keys,
detached,
binary,
append,
@ -126,6 +134,7 @@ fn sign_data<'a, 'certdb>(
input: &'a mut (dyn io::Read + Sync + Send),
output_path: &'a FileOrStdout,
secrets: Vec<openpgp::Cert>,
signer_keys: &[KeyHandle],
detached: bool,
binary: bool,
append: bool,
@ -169,7 +178,77 @@ fn sign_data<'a, 'certdb>(
let mut keypairs = super::get_signing_keys(
&secrets, &config.policy, private_key_store, time, None)?;
if keypairs.is_empty() {
let mut signer_keys = if signer_keys.is_empty() {
Vec::new()
} else {
let mut ks = config.key_store_or_else()?.lock().unwrap();
signer_keys.into_iter()
.map(|kh| {
let keys = ks.find_key(kh.clone())?;
match keys.len() {
0 => return Err(anyhow::anyhow!(
"{} is not present on keystore", kh)),
1 => (),
n => {
eprintln!("Warning: {} is present on multiple \
({}) devices",
kh, n);
}
}
let mut key = keys.into_iter().next().expect("checked for one");
match key.locked() {
Ok(true) => {
let fpr = key.fingerprint();
let cert = config.lookup_one(
&KeyHandle::from(&fpr), None, true);
let display = match cert {
Ok(cert) => {
format!(" ({})",
String::from_utf8_lossy(
best_effort_primary_uid(
&cert, &config.policy,
config.time)
.value()))
}
Err(_) => {
"".to_string()
}
};
let keyid = KeyID::from(&fpr);
loop {
let password = Password::from(rpassword::prompt_password(
format!("Enter password to unlock {}{}: ",
keyid, display))?);
match key.unlock(password) {
Ok(()) => break,
Err(err) => {
wprintln!("Unlocking {}: {}.", keyid, err);
}
}
}
}
Ok(false) => {
// Already unlocked, nothing to do.
}
Err(err) => {
// Failed to get the key's locked status. Just print
// a warning now. We'll (probably) fail more later.
wprintln!("Getting {}'s status: {}",
key.keyid(), err);
}
}
Ok(key)
})
.collect::<Result<Vec<_>>>()?
};
if keypairs.is_empty() && signer_keys.is_empty() {
return Err(anyhow::anyhow!("No signing keys found"));
}
@ -202,14 +281,26 @@ fn sign_data<'a, 'certdb>(
*critical)?;
}
let mut signer = Signer::with_template(
message, keypairs.pop().unwrap().0, builder);
let mut signer = if keypairs.is_empty() {
Signer::with_template(
message,
signer_keys.pop().unwrap(),
builder)
} else {
Signer::with_template(
message,
keypairs.pop().unwrap().0,
builder)
};
if let Some(time) = time {
signer = signer.creation_time(time);
}
for s in keypairs {
signer = signer.add_signer(s.0);
}
for k in signer_keys {
signer = signer.add_signer(k);
}
if detached {
signer = signer.detached();
}

View File

@ -9,15 +9,17 @@
use anyhow::Context as _;
use std::borrow::Borrow;
use std::cell::OnceCell;
use std::collections::btree_map::{BTreeMap, Entry};
use std::fmt;
use std::io;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::sync::Mutex;
use std::time::SystemTime;
use std::sync::Arc;
use once_cell::sync::OnceCell;
use sequoia_openpgp as openpgp;
use openpgp::{
@ -48,6 +50,8 @@ use cert_store::store::UserIDQueryParams;
use sequoia_wot as wot;
use wot::store::Store as _;
use sequoia_keystore as keystore;
use clap::FromArgMatches;
#[macro_use] mod macros;
@ -332,6 +336,11 @@ pub struct Config<'a> {
trust_roots: Vec<Fingerprint>,
// The local trust root, as set in the cert store.
trust_root_local: OnceCell<Option<Fingerprint>>,
// The key store.
no_key_store: bool,
key_store_path: Option<PathBuf>,
key_store: OnceCell<Mutex<keystore::Keystore>>,
}
impl<'store> Config<'store> {
@ -542,6 +551,50 @@ impl<'store> Config<'store> {
.ok_or_else(|| anyhow::anyhow!(NO_CERTD_ERR))
}
/// Returns the key store.
///
/// If the key store is disabled, returns `Ok(None)`. If it is not yet
/// open, opens it.
fn key_store(&self) -> Result<Option<&Mutex<keystore::Keystore>>> {
if self.no_key_store {
return Ok(None);
}
self.key_store
.get_or_try_init(|| {
let dir_;
let dir = if let Some(dir) = self.key_store_path.as_ref() {
dir
} else if let Some(dir) = dirs::data_dir() {
dir_ = dir.join("sequoia");
&dir_
} else {
return Err(anyhow::anyhow!(
"No key store, the XDG data directory is not defined"));
};
let c = keystore::Context::configure()
.home(dir)
.build()?;
let ks = keystore::Keystore::connect(&c)
.context("Connecting to key store")?;
Ok(Mutex::new(ks))
})
.map(Some)
}
/// Returns the key store.
///
/// If the key store is disabled, returns an error.
fn key_store_or_else(&self) -> Result<&Mutex<keystore::Keystore>> {
self.key_store().and_then(|key_store| key_store.ok_or_else(|| {
anyhow::anyhow!("Operation requires a key store, \
but the key store is disabled")
}))
}
/// Looks up an identifier.
///
/// This matches on both the primary key and the subkeys.
@ -1017,6 +1070,9 @@ fn main() -> Result<()> {
cert_store: OnceCell::new(),
trust_roots: c.trust_roots.clone(),
trust_root_local: Default::default(),
no_key_store: c.no_key_store,
key_store_path: c.key_store.clone(),
key_store: OnceCell::new(),
};
commands::dispatch(config, c)

View File

@ -38,6 +38,7 @@ pub fn sq_key_generate(
let mut cmd = Command::cargo_bin("sq")?;
cmd.args([
"--no-cert-store",
"--no-key-store",
"key",
"generate",
"--time",

View File

@ -76,6 +76,7 @@ mod integration {
sq()
.current_dir(&dir)
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("cert").arg("lint")
.arg("--time").arg(FROZEN_TIME)
.arg(filename)
@ -110,6 +111,7 @@ mod integration {
let mut cmd = cmd.current_dir(&dir)
.args(&[
"--no-cert-store",
"--no-key-store",
"cert", "lint",
"--time", FROZEN_TIME,
"--fix", &format!("{}-{}.pgp", base, suffix)
@ -130,6 +132,7 @@ mod integration {
Command::cargo_bin("sq").unwrap()
.current_dir(&dir)
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("cert").arg("lint")
.arg("--time").arg(FROZEN_TIME)
.arg("-")
@ -428,6 +431,7 @@ mod integration {
.current_dir(&dir())
.args(&[
"--no-cert-store",
"--no-key-store",
"cert", "lint",
"--time", FROZEN_TIME,
"--list-keys",
@ -448,6 +452,7 @@ mod integration {
.current_dir(&dir())
.args(&[
"--no-cert-store",
"--no-key-store",
"cert", "lint",
"--time", FROZEN_TIME,
"msg.sig",

View File

@ -42,6 +42,7 @@ fn sq_certify() -> Result<()> {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("pki").arg("certify")
.arg(alice_pgp.to_str().unwrap())
.arg(bob_pgp.to_str().unwrap())
@ -81,6 +82,7 @@ fn sq_certify() -> Result<()> {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("pki").arg("certify")
.arg(alice_pgp.to_str().unwrap())
.arg(bob_pgp.to_str().unwrap())
@ -120,6 +122,7 @@ fn sq_certify() -> Result<()> {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("pki").arg("certify")
.arg(alice_pgp.to_str().unwrap())
.arg(bob_pgp.to_str().unwrap())
@ -167,6 +170,7 @@ fn sq_certify() -> Result<()> {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("pki").arg("certify")
.arg(alice_pgp.to_str().unwrap())
.arg(bob_pgp.to_str().unwrap())
@ -178,6 +182,7 @@ fn sq_certify() -> Result<()> {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("pki").arg("certify")
.args(["--notation", "foo", "bar"])
.args(["--notation", "!foo", "xyzzy"])
@ -303,6 +308,7 @@ fn sq_certify_creation_time() -> Result<()>
// Build up the command line.
let mut cmd = Command::cargo_bin("sq")?;
cmd.args(["--no-cert-store",
"--no-key-store",
"pki", "certify",
&alice_pgp.to_string_lossy(),
&bob_pgp.to_string_lossy(), bob,
@ -389,6 +395,7 @@ fn sq_certify_with_expired_key() -> Result<()>
// Make sure using an expired key fails by default.
let mut cmd = Command::cargo_bin("sq")?;
cmd.args(["--no-cert-store",
"--no-key-store",
"pki", "certify",
&alice_pgp.to_string_lossy(),
&bob_pgp.to_string_lossy(), bob ]);
@ -398,6 +405,7 @@ fn sq_certify_with_expired_key() -> Result<()>
// Try again.
let mut cmd = Command::cargo_bin("sq")?;
cmd.args(["--no-cert-store",
"--no-key-store",
"pki", "certify",
"--allow-not-alive-certifier",
&alice_pgp.to_string_lossy(),
@ -483,6 +491,7 @@ fn sq_certify_with_revoked_key() -> Result<()>
// Make sure using an expired key fails by default.
let mut cmd = Command::cargo_bin("sq")?;
cmd.args(["--no-cert-store",
"--no-key-store",
"pki", "certify",
&alice_pgp.to_string_lossy(),
&bob_pgp.to_string_lossy(), bob ]);
@ -492,6 +501,7 @@ fn sq_certify_with_revoked_key() -> Result<()>
// Try again.
let mut cmd = Command::cargo_bin("sq")?;
cmd.args(["--no-cert-store",
"--no-key-store",
"pki", "certify",
"--allow-revoked-certifier",
&alice_pgp.to_string_lossy(),

View File

@ -20,6 +20,7 @@ mod integration {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("decrypt")
.args(["--session-key", "1FE820EC21FB5D7E33D83367106D1D3747DCD48E6320C1AEC57EE7D18FC437D4"])
.arg(artifact("messages/rsa.msg.pgp"))
@ -34,6 +35,7 @@ mod integration {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("decrypt")
.args(["--session-key", "9:1FE820EC21FB5D7E33D83367106D1D3747DCD48E6320C1AEC57EE7D18FC437D4"])
.arg(artifact("messages/rsa.msg.pgp"))
@ -48,6 +50,7 @@ mod integration {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("decrypt")
.args(["--session-key", "2FE820EC21FB5D7E33D83367106D1D3747DCD48E6320C1AEC57EE7D18FC437D4"])
.args(["--session-key", "9:1FE820EC21FB5D7E33D83367106D1D3747DCD48E6320C1AEC57EE7D18FC437D4"])
@ -64,6 +67,7 @@ mod integration {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("decrypt")
.args(["--session-key", "BB9CCB8EDE22DC222C83BD1C63AEB97335DDC7B696DB171BD16EAA5784CC0478"])
.arg(artifact("messages/rsa.msg.pgp"))

View File

@ -189,6 +189,7 @@ mod integration {
for key in decryption_keys.iter() {
let mut cmd = Command::cargo_bin("sq").unwrap();
cmd.args(["--no-cert-store",
"--no-key-store",
"decrypt",
"--recipient-file",
&key])
@ -375,6 +376,7 @@ mod integration {
for key in decryption_keys.iter() {
let mut cmd = Command::cargo_bin("sq").unwrap();
cmd.args(["--no-cert-store",
"--no-key-store",
"decrypt",
"--recipient-file",
&key])

View File

@ -141,7 +141,10 @@ mod integration {
#[test]
fn adopt_encryption() -> Result<()> {
// Adopt an encryption subkey.
Command::cargo_bin("sq").unwrap().arg("--no-cert-store").arg("key").arg("adopt")
Command::cargo_bin("sq").unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("key").arg("adopt")
.arg(bob())
.arg("--keyring").arg(alice())
.arg("--key").arg(alice_encryption().0.to_hex())
@ -157,7 +160,10 @@ mod integration {
#[test]
fn adopt_signing() -> Result<()> {
// Adopt a signing subkey (subkey has secret key material).
Command::cargo_bin("sq").unwrap().arg("--no-cert-store").arg("key").arg("adopt")
Command::cargo_bin("sq").unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("key").arg("adopt")
.arg(bob())
.arg("--keyring").arg(alice())
.arg("--key").arg(alice_signing().0.to_hex())
@ -173,7 +179,10 @@ mod integration {
#[test]
fn adopt_certification() -> Result<()> {
// Adopt a certification subkey (subkey has secret key material).
Command::cargo_bin("sq").unwrap().arg("--no-cert-store").arg("key").arg("adopt")
Command::cargo_bin("sq").unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("key").arg("adopt")
.arg(carol())
.arg("--keyring").arg(alice())
.arg("--key").arg(alice_primary().0.to_hex())
@ -189,7 +198,10 @@ mod integration {
#[test]
fn adopt_encryption_and_signing() -> Result<()> {
// Adopt an encryption subkey and a signing subkey.
Command::cargo_bin("sq").unwrap().arg("--no-cert-store").arg("key").arg("adopt")
Command::cargo_bin("sq").unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("key").arg("adopt")
.arg(bob())
.arg("--keyring").arg(alice())
.arg("--key").arg(alice_signing().0.to_hex())
@ -209,7 +221,10 @@ mod integration {
#[test]
fn adopt_twice() -> Result<()> {
// Adopt the same an encryption subkey twice.
Command::cargo_bin("sq").unwrap().arg("--no-cert-store").arg("key").arg("adopt")
Command::cargo_bin("sq").unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("key").arg("adopt")
.arg(bob())
.arg("--keyring").arg(alice())
.arg("--key").arg(alice_encryption().0.to_hex())
@ -226,7 +241,10 @@ mod integration {
#[test]
fn adopt_key_appears_twice() -> Result<()> {
// Adopt the an encryption subkey that appears twice.
Command::cargo_bin("sq").unwrap().arg("--no-cert-store").arg("key").arg("adopt")
Command::cargo_bin("sq").unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("key").arg("adopt")
.arg(bob())
.arg("--keyring").arg(alice())
.arg("--keyring").arg(alice())
@ -243,7 +261,10 @@ mod integration {
#[test]
fn adopt_own_encryption() -> Result<()> {
// Adopt its own encryption subkey. This should be a noop.
Command::cargo_bin("sq").unwrap().arg("--no-cert-store").arg("key").arg("adopt")
Command::cargo_bin("sq").unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("key").arg("adopt")
.arg(alice())
.arg("--keyring").arg(alice())
.arg("--key").arg(alice_encryption().0.to_hex())
@ -259,7 +280,10 @@ mod integration {
#[test]
fn adopt_own_primary() -> Result<()> {
// Adopt own primary key.
Command::cargo_bin("sq").unwrap().arg("--no-cert-store").arg("key").arg("adopt")
Command::cargo_bin("sq").unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("key").arg("adopt")
.arg(bob())
.arg("--keyring").arg(bob())
.arg("--key").arg(bob_primary().0.to_hex())
@ -275,7 +299,10 @@ mod integration {
#[test]
fn adopt_missing() -> Result<()> {
// Adopt a key that is not present.
Command::cargo_bin("sq").unwrap().arg("--no-cert-store").arg("key").arg("adopt")
Command::cargo_bin("sq").unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("key").arg("adopt")
.arg(bob())
.arg("--keyring").arg(bob())
.arg("--key").arg("1234 5678 90AB CDEF 1234 5678 90AB CDEF")
@ -288,7 +315,10 @@ mod integration {
#[test]
fn adopt_from_multiple() -> Result<()> {
// Adopt from multiple certificates simultaneously.
Command::cargo_bin("sq").unwrap().arg("--no-cert-store").arg("key").arg("adopt")
Command::cargo_bin("sq").unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("key").arg("adopt")
.arg(bob())
.arg("--keyring").arg(alice())
.arg("--key").arg(alice_signing().0.to_hex())

View File

@ -27,6 +27,7 @@ mod integration {
// Build up the command line.
let mut cmd = Command::cargo_bin("sq")?;
cmd.args(["--no-cert-store",
"--no-key-store",
"key", "generate",
"--time", iso8601,
"--expiry", "never",

View File

@ -106,6 +106,7 @@ fn sq_key_revoke() -> Result<()> {
let mut cmd = Command::cargo_bin("sq")?;
cmd.args([
"--no-cert-store",
"--no-key-store",
"key",
"revoke",
"--output",
@ -278,6 +279,7 @@ fn sq_key_revoke_thirdparty() -> Result<()> {
let mut cmd = Command::cargo_bin("sq")?;
cmd.args([
"--no-cert-store",
"--no-key-store",
"key",
"revoke",
"--output",

View File

@ -23,6 +23,7 @@ fn sq_key_subkey_generate_authentication_subkey() -> Result<()> {
let mut cmd = Command::cargo_bin("sq")?;
cmd.args([
"--no-cert-store",
"--no-key-store",
"key",
"subkey",
"add",
@ -52,6 +53,7 @@ fn sq_key_subkey_generate_encryption_subkey() -> Result<()> {
let mut cmd = Command::cargo_bin("sq")?;
cmd.args([
"--no-cert-store",
"--no-key-store",
"key",
"subkey",
"add",
@ -91,6 +93,7 @@ fn sq_key_subkey_generate_signing_subkey() -> Result<()> {
let mut cmd = Command::cargo_bin("sq")?;
cmd.args([
"--no-cert-store",
"--no-key-store",
"key",
"subkey",
"add",
@ -208,6 +211,7 @@ fn sq_key_subkey_revoke() -> Result<()> {
let mut cmd = Command::cargo_bin("sq")?;
cmd.args([
"--no-cert-store",
"--no-key-store",
"key",
"subkey",
"revoke",
@ -406,6 +410,7 @@ fn sq_key_subkey_revoke_thirdparty() -> Result<()> {
let mut cmd = Command::cargo_bin("sq")?;
cmd.args([
"--no-cert-store",
"--no-key-store",
"key",
"subkey",
"revoke",

View File

@ -78,6 +78,7 @@ fn sq_key_userid_revoke() -> Result<()> {
let mut cmd = Command::cargo_bin("sq")?;
cmd.args([
"--no-cert-store",
"--no-key-store",
"key",
"userid",
"revoke",
@ -231,6 +232,7 @@ fn sq_key_userid_revoke_thirdparty() -> Result<()> {
let mut cmd = Command::cargo_bin("sq")?;
cmd.args([
"--no-cert-store",
"--no-key-store",
"key",
"userid",
"revoke",

View File

@ -60,6 +60,7 @@ fn sq_gen_key(cert_store: Option<&str>, userids: &[&str], file: &str) -> Cert
{
let mut cmd = Command::cargo_bin("sq").expect("have sq");
cmd.args(["--no-cert-store",
"--no-key-store",
"key", "generate",
"--time", &tick(),
"--expiry", "never",
@ -86,6 +87,7 @@ fn sq_verify(cert_store: Option<&str>,
good_sigs: usize, good_checksums: usize)
{
let mut cmd = Command::cargo_bin("sq").expect("have sq");
cmd.arg("--no-key-store");
if let Some(cert_store) = cert_store {
cmd.args(&["--cert-store", cert_store]);
} else {
@ -270,6 +272,7 @@ fn sq_link_add_retract() -> Result<()> {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("sign")
.args(["--signer-file", &data.key_file])
.args(["--output", &data.sig_file])
@ -403,6 +406,7 @@ fn sq_link_add_retract() -> Result<()> {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("sign")
.args(["--signer-file", &ed_pgp])
.args(["--output", &ed_sig_file])
@ -618,6 +622,7 @@ fn sq_link_add_temporary() -> Result<()> {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("sign")
.args(["--signer-file", &alice_pgp])
.args(["--output", &alice_sig_file])

View File

@ -20,7 +20,9 @@ mod sq_packet_decrypt {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("toolbox")
.arg("packet")
.arg("decrypt")
.args(["--session-key", "1FE820EC21FB5D7E33D83367106D1D3747DCD48E6320C1AEC57EE7D18FC437D4"])
@ -36,7 +38,9 @@ mod sq_packet_decrypt {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("toolbox")
.arg("packet")
.arg("decrypt")
.args(["--session-key", "9:1FE820EC21FB5D7E33D83367106D1D3747DCD48E6320C1AEC57EE7D18FC437D4"])
@ -52,7 +56,9 @@ mod sq_packet_decrypt {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("toolbox")
.arg("packet")
.arg("decrypt")
.args(["--session-key", "2FE820EC21FB5D7E33D83367106D1D3747DCD48E6320C1AEC57EE7D18FC437D4"])
@ -70,7 +76,9 @@ mod sq_packet_decrypt {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("toolbox")
.arg("packet")
.arg("decrypt")
.args(["--session-key", "BB9CCB8EDE22DC222C83BD1C63AEB97335DDC7B696DB171BD16EAA5784CC0478"])

View File

@ -15,6 +15,7 @@ mod sq_packet_dump {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("toolbox")
.arg("packet")
.arg("dump")
@ -31,6 +32,7 @@ mod sq_packet_dump {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("toolbox")
.arg("packet")
.arg("dump")
@ -47,6 +49,7 @@ mod sq_packet_dump {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("toolbox")
.arg("packet")
.arg("dump")
@ -65,6 +68,7 @@ mod sq_packet_dump {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("toolbox")
.arg("packet")
.arg("dump")
@ -78,6 +82,7 @@ mod sq_packet_dump {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("toolbox")
.arg("packet")
.arg("dump")
@ -95,6 +100,7 @@ mod sq_packet_dump {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("toolbox")
.arg("packet")
.arg("dump")
@ -108,6 +114,7 @@ mod sq_packet_dump {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("toolbox")
.arg("packet")
.arg("dump")
@ -124,6 +131,7 @@ mod sq_packet_dump {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("toolbox")
.arg("packet")
.arg("dump")

View File

@ -114,6 +114,7 @@ mod integration {
let mut cmd = Command::cargo_bin("sq")?;
cmd.current_dir(&dir())
.arg("--no-cert-store")
.arg("--no-key-store")
.args(&["--output-format", &format!("{}", outputformat)])
.args(&["--keyring", keyring]);
if let Some(trust_root) = trust_root {

View File

@ -35,6 +35,7 @@ fn sq_sign() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("sign")
.args(["--signer-file", &artifact("keys/dennis-simon-anton-private.pgp")])
.args(["--output", &sig.to_string_lossy()])
@ -70,6 +71,7 @@ fn sq_sign() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("verify")
.args(["--signer-file", &artifact("keys/dennis-simon-anton.pgp")])
.arg(&*sig.to_string_lossy())
@ -86,6 +88,7 @@ fn sq_sign_with_notations() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("sign")
.args(["--signer-file", &artifact("keys/dennis-simon-anton-private.pgp")])
.args(["--output", &sig.to_string_lossy()])
@ -149,6 +152,7 @@ fn sq_sign_with_notations() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.args(["--known-notation", "foo"])
.arg("verify")
.args(["--signer-file", &artifact("keys/dennis-simon-anton.pgp")])
@ -166,6 +170,7 @@ fn sq_sign_append() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("sign")
.args(["--signer-file", &artifact("keys/dennis-simon-anton-private.pgp")])
.args(["--output", &sig0.to_string_lossy()])
@ -201,6 +206,7 @@ fn sq_sign_append() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("verify")
.args(["--signer-file", &artifact("keys/dennis-simon-anton.pgp")])
.arg(&*sig0.to_string_lossy())
@ -212,6 +218,7 @@ fn sq_sign_append() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("sign")
.arg("--append")
.args(["--signer-file", &artifact("keys/erika-corinna-daniela-simone-antonia-nistp256-private.pgp")])
@ -262,6 +269,7 @@ fn sq_sign_append() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("verify")
.args(["--signer-file", &artifact("keys/dennis-simon-anton.pgp")])
.arg(&*sig1.to_string_lossy())
@ -270,6 +278,7 @@ fn sq_sign_append() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("verify")
.args(["--signer-file", &artifact("keys/erika-corinna-daniela-simone-antonia-nistp256.pgp")])
.arg(&*sig1.to_string_lossy())
@ -332,6 +341,7 @@ fn sq_sign_append_on_compress_then_sign() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("verify")
.args(["--signer-file", &artifact("keys/dennis-simon-anton.pgp")])
.arg(&*sig0.to_string_lossy())
@ -343,6 +353,7 @@ fn sq_sign_append_on_compress_then_sign() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("sign")
.arg("--append")
.args(["--signer-file", &artifact("keys/erika-corinna-daniela-simone-antonia-nistp256-private.pgp")])
@ -396,6 +407,7 @@ fn sq_sign_append_on_compress_then_sign() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("verify")
.args(["--signer-file", &artifact("keys/dennis-simon-anton.pgp")])
.arg(&*sig0.to_string_lossy())
@ -405,6 +417,7 @@ fn sq_sign_append_on_compress_then_sign() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("verify")
.args(["--signer-file", &artifact("keys/erika-corinna-daniela-simone-antonia-nistp256.pgp")])
.arg(&*sig0.to_string_lossy())
@ -421,6 +434,7 @@ fn sq_sign_detached() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("sign")
.arg("--detached")
.args(["--signer-file", &artifact("keys/dennis-simon-anton-private.pgp")])
@ -446,6 +460,7 @@ fn sq_sign_detached() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("verify")
.args(["--signer-file", &artifact("keys/dennis-simon-anton.pgp")])
.args(["--detached", &sig.to_string_lossy()])
@ -463,6 +478,7 @@ fn sq_sign_detached_append() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("sign")
.arg("--detached")
.args(["--signer-file", &artifact("keys/dennis-simon-anton-private.pgp")])
@ -488,6 +504,7 @@ fn sq_sign_detached_append() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("verify")
.args(["--signer-file", &artifact("keys/dennis-simon-anton.pgp")])
.args(["--detached", &sig.to_string_lossy()])
@ -499,6 +516,7 @@ fn sq_sign_detached_append() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("sign")
.arg("--detached")
.args(["--signer-file", &artifact("keys/erika-corinna-daniela-simone-antonia-nistp256-private.pgp")])
@ -511,6 +529,7 @@ fn sq_sign_detached_append() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("sign")
.arg("--detached")
.arg("--append")
@ -542,6 +561,7 @@ fn sq_sign_detached_append() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("verify")
.args(["--signer-file", &artifact("keys/dennis-simon-anton.pgp")])
.args(["--detached", &sig.to_string_lossy()])
@ -552,6 +572,7 @@ fn sq_sign_detached_append() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("verify")
.args(["--signer-file", &artifact("keys/erika-corinna-daniela-simone-antonia-nistp256.pgp")])
.args(["--detached", &sig.to_string_lossy()])
@ -564,6 +585,7 @@ fn sq_sign_detached_append() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("sign")
.arg("--detached")
.arg("--append")
@ -602,6 +624,7 @@ fn sq_sign_append_a_notarization() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("sign")
.arg("--append")
.args(["--signer-file", &artifact("keys/erika-corinna-daniela-simone-antonia-nistp256-private.pgp")])
@ -663,6 +686,7 @@ fn sq_sign_append_a_notarization() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("verify")
.args(["--signer-file", &artifact("keys/neal.pgp")])
.arg(&*sig0.to_string_lossy())
@ -671,6 +695,7 @@ fn sq_sign_append_a_notarization() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("verify")
.args(["--signer-file", &artifact("keys/emmelie-dorothea-dina-samantha-awina-ed25519.pgp")])
.arg(&*sig0.to_string_lossy())
@ -679,6 +704,7 @@ fn sq_sign_append_a_notarization() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("verify")
.args(["--signer-file", &artifact("keys/erika-corinna-daniela-simone-antonia-nistp256.pgp")])
.arg(&*sig0.to_string_lossy())
@ -696,6 +722,7 @@ fn sq_sign_notarize() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("sign")
.arg("--notarize")
.args(["--signer-file", &artifact("keys/erika-corinna-daniela-simone-antonia-nistp256-private.pgp")])
@ -745,6 +772,7 @@ fn sq_sign_notarize() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("verify")
.args(["--signer-file", &artifact("keys/neal.pgp")])
.arg(&*sig0.to_string_lossy())
@ -753,6 +781,7 @@ fn sq_sign_notarize() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("verify")
.args(["--signer-file", &artifact("keys/erika-corinna-daniela-simone-antonia-nistp256.pgp")])
.arg(&*sig0.to_string_lossy())
@ -770,6 +799,7 @@ fn sq_sign_notarize_a_notarization() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("sign")
.arg("--notarize")
.args(["--signer-file", &artifact("keys/erika-corinna-daniela-simone-antonia-nistp256-private.pgp")])
@ -831,6 +861,7 @@ fn sq_sign_notarize_a_notarization() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("verify")
.args(["--signer-file", &artifact("keys/neal.pgp")])
.arg(&*sig0.to_string_lossy())
@ -839,6 +870,7 @@ fn sq_sign_notarize_a_notarization() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("verify")
.args(["--signer-file", &artifact("keys/emmelie-dorothea-dina-samantha-awina-ed25519.pgp")])
.arg(&*sig0.to_string_lossy())
@ -847,6 +879,7 @@ fn sq_sign_notarize_a_notarization() {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("verify")
.args(["--signer-file", &artifact("keys/erika-corinna-daniela-simone-antonia-nistp256.pgp")])
.arg(&*sig0.to_string_lossy())
@ -878,7 +911,7 @@ fn sq_multiple_signers() -> Result<()> {
// Sign message.
let assertion = Command::cargo_bin("sq")?
.args([
"--no-cert-store",
"--no-cert-store", "--no-key-store",
"sign",
"--signer-file", alice_pgp.to_str().unwrap(),
"--signer-file", &bob_pgp.to_str().unwrap(),
@ -957,6 +990,7 @@ fn sq_sign_using_cert_store() -> Result<()> {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("sign")
.args(["--signer-file", &alice_pgp])
.args(["--output", &msg_pgp])
@ -998,6 +1032,7 @@ fn sq_sign_using_cert_store() -> Result<()> {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("verify")
.args(["--signer-file", &alice_pgp])
.arg(&msg_pgp)
@ -1008,6 +1043,7 @@ fn sq_sign_using_cert_store() -> Result<()> {
// certificate or use a certificate store.
let mut cmd = Command::cargo_bin("sq").unwrap();
cmd.arg("--no-cert-store")
.arg("--no-key-store")
.arg("verify")
.arg(&msg_pgp);
let output = cmd.output().expect("success");
@ -1098,6 +1134,7 @@ fn sq_verify_wot() -> Result<()> {
{
let mut cmd = Command::cargo_bin("sq").expect("have sq");
cmd.args(["--no-cert-store",
"--no-key-store",
"key", "generate",
"--expiry", "never",
"--output", file]);
@ -1120,6 +1157,7 @@ fn sq_verify_wot() -> Result<()> {
msg_pgp: &str|
{
let mut cmd = Command::cargo_bin("sq").expect("have sq");
cmd.arg("--no-key-store");
if let Some(cert_store) = cert_store {
cmd.args(&["--cert-store", cert_store]);
} else {
@ -1186,6 +1224,7 @@ fn sq_verify_wot() -> Result<()> {
Command::cargo_bin("sq")
.unwrap()
.arg("--no-cert-store")
.arg("--no-key-store")
.arg("sign")
.args(["--signer-file", &bob_pgp])
.args(["--signer-file", &carol_pgp])