Change sq key adopt to support the cert store.
- Change `sq key adopt` to use the cert store. - See #205.
This commit is contained in:
parent
a08b536225
commit
f1a99b10d9
2
NEWS
2
NEWS
@ -58,7 +58,7 @@
|
||||
- `sq pki certify` can now use the cert store and the key store.
|
||||
- In `sq key adopt`, change the certificate file parameter from a
|
||||
positional parameter to a named parameter, `--cert-file`.
|
||||
- `sq key adopt` can now use the key store.
|
||||
- `sq key adopt` can now use the cert store and the key store.
|
||||
* Changes in 0.36.0
|
||||
- Missing
|
||||
* Changes in 0.35.0
|
||||
|
@ -1003,6 +1003,7 @@ $ sq key adopt --keyring juliet-old.pgp --key 0123456789ABCDEF \\
|
||||
juliet-new.pgp
|
||||
",
|
||||
)]
|
||||
#[clap(group(ArgGroup::new("cert_input").args(&["cert_file", "cert"]).required(true)))]
|
||||
pub struct AdoptCommand {
|
||||
#[clap(
|
||||
short = 'k',
|
||||
@ -1026,19 +1027,23 @@ pub struct AdoptCommand {
|
||||
pub allow_broken_crypto: bool,
|
||||
#[clap(
|
||||
long,
|
||||
default_value_t = FileOrStdin::default(),
|
||||
value_name = "TARGET-KEY",
|
||||
help = "Add keys to TARGET-KEY or reads keys from stdin if omitted",
|
||||
help = "Add keys to TARGET-KEY",
|
||||
value_name = FileOrStdin::VALUE_NAME,
|
||||
)]
|
||||
pub cert_file: FileOrStdin,
|
||||
pub cert: Option<KeyHandle>,
|
||||
#[clap(
|
||||
long,
|
||||
value_name = "TARGET-KEY",
|
||||
help = "Add keys to TARGET-KEY",
|
||||
)]
|
||||
pub cert_file: Option<FileOrStdin>,
|
||||
#[clap(
|
||||
default_value_t = FileOrStdout::default(),
|
||||
help = FileOrStdout::HELP_OPTIONAL,
|
||||
long,
|
||||
short,
|
||||
value_name = FileOrStdout::VALUE_NAME,
|
||||
)]
|
||||
pub output: FileOrStdout,
|
||||
pub output: Option<FileOrStdout>,
|
||||
#[clap(
|
||||
short = 'B',
|
||||
long,
|
||||
|
@ -6,7 +6,6 @@ use openpgp::packet::signature::subpacket::SubpacketTag;
|
||||
use openpgp::packet::signature::SignatureBuilder;
|
||||
use openpgp::packet::Key;
|
||||
use openpgp::packet::Signature;
|
||||
use openpgp::parse::Parse;
|
||||
use openpgp::policy::Policy;
|
||||
use openpgp::serialize::Serialize;
|
||||
use openpgp::types::SignatureType;
|
||||
@ -18,11 +17,29 @@ use sequoia_openpgp as openpgp;
|
||||
|
||||
use crate::Sq;
|
||||
use crate::cli;
|
||||
use crate::cli::types::FileOrStdout;
|
||||
use crate::cli::types::FileStdinOrKeyHandle;
|
||||
|
||||
pub fn adopt(sq: Sq, command: cli::key::AdoptCommand) -> Result<()>
|
||||
pub fn adopt(sq: Sq, mut command: cli::key::AdoptCommand) -> Result<()>
|
||||
{
|
||||
let input = command.cert_file.open()?;
|
||||
let cert = Cert::from_buffered_reader(input)?;
|
||||
let handle: FileStdinOrKeyHandle = if let Some(file) = command.cert_file {
|
||||
assert!(command.cert.is_none());
|
||||
file.into()
|
||||
} else if let Some(kh) = command.cert {
|
||||
kh.into()
|
||||
} else {
|
||||
panic!("clap enforces --cert or --cert-file is set");
|
||||
};
|
||||
|
||||
if handle.is_file() {
|
||||
if command.output.is_none() {
|
||||
// None means to write to the cert store. When reading
|
||||
// from a file, we want to write to stdout by default.
|
||||
command.output = Some(FileOrStdout::new(None));
|
||||
}
|
||||
}
|
||||
|
||||
let cert = sq.lookup_one(handle, None, true)?;
|
||||
|
||||
let null_policy_;
|
||||
let adoptee_policy: &dyn Policy = if command.allow_broken_crypto {
|
||||
@ -188,13 +205,6 @@ pub fn adopt(sq: Sq, command: cli::key::AdoptCommand) -> Result<()>
|
||||
|
||||
let cert = cert.clone().insert_packets(packets.clone())?;
|
||||
|
||||
let mut sink = command.output.for_secrets().create_safe(sq.force)?;
|
||||
if command.binary {
|
||||
cert.as_tsk().serialize(&mut sink)?;
|
||||
} else {
|
||||
cert.as_tsk().armored().serialize(&mut sink)?;
|
||||
}
|
||||
|
||||
let vc = cert.with_policy(sq.policy, None).expect("still valid");
|
||||
for pair in packets[..].chunks(2) {
|
||||
let newkey: &Key<key::PublicParts, key::UnspecifiedRole> = match pair[0]
|
||||
@ -224,5 +234,34 @@ pub fn adopt(sq: Sq, command: cli::key::AdoptCommand) -> Result<()>
|
||||
assert!(found, "Subkey: {:?}\nSignature: {:?}", newkey, newsig);
|
||||
}
|
||||
|
||||
if let Some(output) = command.output {
|
||||
let mut sink = output.for_secrets().create_safe(sq.force)?;
|
||||
if command.binary {
|
||||
cert.as_tsk().serialize(&mut sink)?;
|
||||
} else {
|
||||
cert.as_tsk().armored().serialize(&mut sink)?;
|
||||
}
|
||||
} else {
|
||||
// Import it into the key store.
|
||||
let keyid = cert.keyid();
|
||||
let result = if cert.is_tsk() {
|
||||
sq.import_key(cert)
|
||||
} else {
|
||||
sq.import_cert(cert)
|
||||
};
|
||||
if let Err(err) = result {
|
||||
wprintln!("Error importing updated cert: {}", err);
|
||||
return Err(err);
|
||||
} else {
|
||||
sq.hint(format_args!(
|
||||
"Imported updated cert into the cert store. \
|
||||
To make the update effective, it has to be published \
|
||||
so that others can find it, for example using:"))
|
||||
.command(format_args!(
|
||||
"sq cert export --cert {} | sq network keyserver publish",
|
||||
keyid));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ use openpgp::packet::Signature;
|
||||
use openpgp::parse::Parse;
|
||||
use openpgp::policy::StandardPolicy;
|
||||
use openpgp::Cert;
|
||||
use openpgp::Fingerprint;
|
||||
use openpgp::KeyHandle;
|
||||
use openpgp::Result;
|
||||
use sequoia_openpgp as openpgp;
|
||||
@ -98,6 +99,18 @@ impl From<KeyHandle> for FileOrKeyHandle {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Fingerprint> for FileOrKeyHandle {
|
||||
fn from(fpr: &Fingerprint) -> Self {
|
||||
KeyHandle::from(fpr).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Fingerprint> for FileOrKeyHandle {
|
||||
fn from(fpr: Fingerprint) -> Self {
|
||||
KeyHandle::from(fpr).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&FileOrKeyHandle> for FileOrKeyHandle {
|
||||
fn from(h: &FileOrKeyHandle) -> Self {
|
||||
h.clone()
|
||||
@ -399,7 +412,11 @@ impl Sq {
|
||||
cmd.arg("--keyring").arg(k.as_ref());
|
||||
}
|
||||
|
||||
cmd.arg("--cert-file").arg(target);
|
||||
if target.is_file() {
|
||||
cmd.arg("--cert-file").arg(target);
|
||||
} else {
|
||||
cmd.arg("--cert").arg(target);
|
||||
};
|
||||
|
||||
assert!(! keys.is_empty());
|
||||
for k in keys.into_iter() {
|
||||
|
@ -1,4 +1,5 @@
|
||||
mod common;
|
||||
use common::FileOrKeyHandle;
|
||||
use common::Sq;
|
||||
|
||||
#[cfg(test)]
|
||||
@ -141,20 +142,42 @@ mod integration {
|
||||
|
||||
#[test]
|
||||
fn adopt_encryption() -> Result<()> {
|
||||
let sq = Sq::new();
|
||||
for (keyrings, key_imports, handle) in [
|
||||
(
|
||||
// Keyrings
|
||||
&[ alice() ][..],
|
||||
// Key store imports.
|
||||
&[][..],
|
||||
// Handle
|
||||
FileOrKeyHandle::from(bob()),
|
||||
),
|
||||
(
|
||||
// Keyrings
|
||||
&[][..],
|
||||
// Key store imports.
|
||||
&[ alice() ],
|
||||
// Handle
|
||||
bob().into()
|
||||
),
|
||||
(
|
||||
// Keyrings
|
||||
&[][..],
|
||||
// Key store imports.
|
||||
&[ alice(), bob() ],
|
||||
// Handle
|
||||
bob_primary().0.into()
|
||||
),
|
||||
] {
|
||||
let sq = Sq::new();
|
||||
|
||||
for keystore in [false, true] {
|
||||
let keyrings = if keystore {
|
||||
sq.key_import(alice());
|
||||
Vec::new()
|
||||
} else {
|
||||
vec![ alice() ]
|
||||
};
|
||||
for file in key_imports {
|
||||
sq.key_import(file);
|
||||
}
|
||||
|
||||
// Have Bob adopt alice's encryption subkey.
|
||||
let cert = sq.key_adopt(
|
||||
keyrings,
|
||||
bob(),
|
||||
keyrings.to_vec(),
|
||||
handle,
|
||||
[ alice_encryption().0.clone() ].to_vec(),
|
||||
None,
|
||||
false,
|
||||
@ -171,20 +194,42 @@ mod integration {
|
||||
|
||||
#[test]
|
||||
fn adopt_signing() -> Result<()> {
|
||||
let sq = Sq::new();
|
||||
for (keyrings, key_imports, handle) in [
|
||||
(
|
||||
// Keyrings
|
||||
&[ alice() ][..],
|
||||
// Key store imports.
|
||||
&[][..],
|
||||
// Handle
|
||||
FileOrKeyHandle::from(bob()),
|
||||
),
|
||||
(
|
||||
// Keyrings
|
||||
&[][..],
|
||||
// Key store imports.
|
||||
&[ alice() ],
|
||||
// Handle
|
||||
bob().into()
|
||||
),
|
||||
(
|
||||
// Keyrings
|
||||
&[][..],
|
||||
// Key store imports.
|
||||
&[ alice(), bob() ],
|
||||
// Handle
|
||||
bob_primary().0.into()
|
||||
),
|
||||
] {
|
||||
let sq = Sq::new();
|
||||
|
||||
for keystore in [false, true] {
|
||||
let keyrings = if keystore {
|
||||
sq.key_import(alice());
|
||||
Vec::new()
|
||||
} else {
|
||||
vec![ alice() ]
|
||||
};
|
||||
for file in key_imports {
|
||||
sq.key_import(file);
|
||||
}
|
||||
|
||||
// Adopt a signing subkey (subkey has secret key material).
|
||||
let cert = sq.key_adopt(
|
||||
keyrings,
|
||||
bob(),
|
||||
keyrings.to_vec(),
|
||||
handle,
|
||||
[ alice_signing().0.clone() ].to_vec(),
|
||||
None,
|
||||
false,
|
||||
@ -201,21 +246,43 @@ mod integration {
|
||||
|
||||
#[test]
|
||||
fn adopt_certification() -> Result<()> {
|
||||
let sq = Sq::new();
|
||||
for (keyrings, key_imports, handle) in [
|
||||
(
|
||||
// Keyrings
|
||||
&[ alice() ][..],
|
||||
// Key store imports.
|
||||
&[][..],
|
||||
// Handle
|
||||
FileOrKeyHandle::from(carol()),
|
||||
),
|
||||
(
|
||||
// Keyrings
|
||||
&[][..],
|
||||
// Key store imports.
|
||||
&[ alice() ],
|
||||
// Handle
|
||||
carol().into()
|
||||
),
|
||||
(
|
||||
// Keyrings
|
||||
&[][..],
|
||||
// Key store imports.
|
||||
&[ alice(), carol() ],
|
||||
// Handle
|
||||
carol_primary().0.into()
|
||||
),
|
||||
] {
|
||||
let sq = Sq::new();
|
||||
|
||||
for keystore in [false, true] {
|
||||
let keyrings = if keystore {
|
||||
sq.key_import(alice());
|
||||
Vec::new()
|
||||
} else {
|
||||
vec![ alice() ]
|
||||
};
|
||||
for file in key_imports {
|
||||
sq.key_import(file);
|
||||
}
|
||||
|
||||
// Adopt a certification subkey (subkey has secret key
|
||||
// material).
|
||||
let cert = sq.key_adopt(
|
||||
keyrings,
|
||||
carol(),
|
||||
keyrings.to_vec(),
|
||||
handle,
|
||||
[ alice_primary().0.clone() ].to_vec(),
|
||||
None,
|
||||
false,
|
||||
@ -231,20 +298,41 @@ mod integration {
|
||||
|
||||
#[test]
|
||||
fn adopt_encryption_and_signing() -> Result<()> {
|
||||
let sq = Sq::new();
|
||||
for (keyrings, key_imports, handle) in [
|
||||
(
|
||||
// Keyrings
|
||||
&[ alice() ][..],
|
||||
// Key store imports.
|
||||
&[][..],
|
||||
// Handle
|
||||
FileOrKeyHandle::from(bob()),
|
||||
),
|
||||
(
|
||||
// Keyrings
|
||||
&[][..],
|
||||
// Key store imports.
|
||||
&[ alice() ],
|
||||
// Handle
|
||||
bob().into()
|
||||
),
|
||||
(
|
||||
// Keyrings
|
||||
&[][..],
|
||||
// Key store imports.
|
||||
&[ alice(), bob() ],
|
||||
// Handle
|
||||
bob_primary().0.into()
|
||||
),
|
||||
] {
|
||||
let sq = Sq::new();
|
||||
|
||||
// Adopt an encryption subkey and a signing subkey.
|
||||
for keystore in [false, true] {
|
||||
let keyrings = if keystore {
|
||||
sq.key_import(alice());
|
||||
Vec::new()
|
||||
} else {
|
||||
vec![ alice() ]
|
||||
};
|
||||
for file in key_imports {
|
||||
sq.key_import(file);
|
||||
}
|
||||
|
||||
let cert = sq.key_adopt(
|
||||
keyrings,
|
||||
bob(),
|
||||
keyrings.to_vec(),
|
||||
handle,
|
||||
[
|
||||
alice_signing().0.clone(),
|
||||
alice_encryption().0.clone(),
|
||||
@ -267,20 +355,42 @@ mod integration {
|
||||
|
||||
#[test]
|
||||
fn adopt_twice() -> Result<()> {
|
||||
let sq = Sq::new();
|
||||
for (keyrings, key_imports, handle) in [
|
||||
(
|
||||
// Keyrings
|
||||
&[ alice() ][..],
|
||||
// Key store imports.
|
||||
&[][..],
|
||||
// Handle
|
||||
FileOrKeyHandle::from(bob()),
|
||||
),
|
||||
(
|
||||
// Keyrings
|
||||
&[][..],
|
||||
// Key store imports.
|
||||
&[ alice() ],
|
||||
// Handle
|
||||
bob().into()
|
||||
),
|
||||
(
|
||||
// Keyrings
|
||||
&[][..],
|
||||
// Key store imports.
|
||||
&[ alice(), bob() ],
|
||||
// Handle
|
||||
bob_primary().0.into()
|
||||
),
|
||||
] {
|
||||
let sq = Sq::new();
|
||||
|
||||
for keystore in [false, true] {
|
||||
let keyrings = if keystore {
|
||||
sq.key_import(alice());
|
||||
Vec::new()
|
||||
} else {
|
||||
vec![ alice() ]
|
||||
};
|
||||
for file in key_imports {
|
||||
sq.key_import(file);
|
||||
}
|
||||
|
||||
// Adopt the same an encryption subkey twice.
|
||||
let cert = sq.key_adopt(
|
||||
keyrings,
|
||||
bob(),
|
||||
keyrings.to_vec(),
|
||||
handle,
|
||||
[
|
||||
alice_encryption().0.clone(),
|
||||
alice_encryption().0.clone(),
|
||||
@ -302,7 +412,7 @@ mod integration {
|
||||
fn adopt_key_appears_twice() -> Result<()> {
|
||||
let sq = Sq::new();
|
||||
|
||||
// Adopt the an encryption subkey that appears twice.
|
||||
// Adopt an encryption subkey that appears twice.
|
||||
let cert = sq.key_adopt(
|
||||
[ alice(), alice(), ].to_vec(),
|
||||
bob(),
|
||||
@ -323,20 +433,42 @@ mod integration {
|
||||
|
||||
#[test]
|
||||
fn adopt_own_encryption() -> Result<()> {
|
||||
let sq = Sq::new();
|
||||
for (keyrings, key_imports, handle) in [
|
||||
(
|
||||
// Keyrings
|
||||
&[ alice() ][..],
|
||||
// Key store imports.
|
||||
&[][..],
|
||||
// Handle
|
||||
FileOrKeyHandle::from(alice()),
|
||||
),
|
||||
(
|
||||
// Keyrings
|
||||
&[][..],
|
||||
// Key store imports.
|
||||
&[ alice() ],
|
||||
// Handle
|
||||
alice().into()
|
||||
),
|
||||
(
|
||||
// Keyrings
|
||||
&[][..],
|
||||
// Key store imports.
|
||||
&[ alice(), bob() ],
|
||||
// Handle
|
||||
alice_primary().0.into()
|
||||
),
|
||||
] {
|
||||
let sq = Sq::new();
|
||||
|
||||
for keystore in [false, true] {
|
||||
let keyrings = if keystore {
|
||||
sq.key_import(alice());
|
||||
Vec::new()
|
||||
} else {
|
||||
vec![ alice() ]
|
||||
};
|
||||
for file in key_imports {
|
||||
sq.key_import(file);
|
||||
}
|
||||
|
||||
// Adopt its own encryption subkey. This should be a noop.
|
||||
let cert = sq.key_adopt(
|
||||
keyrings,
|
||||
alice(),
|
||||
keyrings.to_vec(),
|
||||
handle,
|
||||
[
|
||||
alice_encryption().0.clone(),
|
||||
].to_vec(),
|
||||
@ -355,20 +487,42 @@ mod integration {
|
||||
|
||||
#[test]
|
||||
fn adopt_own_primary() -> Result<()> {
|
||||
let sq = Sq::new();
|
||||
for (keyrings, key_imports, handle) in [
|
||||
(
|
||||
// Keyrings
|
||||
&[ bob() ][..],
|
||||
// Key store imports.
|
||||
&[][..],
|
||||
// Handle
|
||||
FileOrKeyHandle::from(bob()),
|
||||
),
|
||||
(
|
||||
// Keyrings
|
||||
&[][..],
|
||||
// Key store imports.
|
||||
&[ bob() ],
|
||||
// Handle
|
||||
bob().into()
|
||||
),
|
||||
(
|
||||
// Keyrings
|
||||
&[][..],
|
||||
// Key store imports.
|
||||
&[ bob() ],
|
||||
// Handle
|
||||
bob_primary().0.into()
|
||||
),
|
||||
] {
|
||||
let sq = Sq::new();
|
||||
|
||||
for keystore in [false, true] {
|
||||
let keyrings = if keystore {
|
||||
sq.key_import(bob());
|
||||
Vec::new()
|
||||
} else {
|
||||
vec![ bob() ]
|
||||
};
|
||||
for file in key_imports {
|
||||
sq.key_import(file);
|
||||
}
|
||||
|
||||
// Adopt own primary key.
|
||||
let cert = sq.key_adopt(
|
||||
keyrings,
|
||||
bob(),
|
||||
keyrings.to_vec(),
|
||||
handle,
|
||||
[
|
||||
bob_primary().0.clone(),
|
||||
].to_vec(),
|
||||
@ -387,20 +541,50 @@ mod integration {
|
||||
|
||||
#[test]
|
||||
fn adopt_missing() -> Result<()> {
|
||||
let sq = Sq::new();
|
||||
for (keyrings, key_imports, handle) in [
|
||||
(
|
||||
// Keyrings
|
||||
&[ alice(), bob() ][..],
|
||||
// Key store imports.
|
||||
&[][..],
|
||||
// Handle
|
||||
FileOrKeyHandle::from(bob()),
|
||||
),
|
||||
(
|
||||
// Keyrings
|
||||
&[][..],
|
||||
// Key store imports.
|
||||
&[][..],
|
||||
// Handle
|
||||
FileOrKeyHandle::from(bob()),
|
||||
),
|
||||
(
|
||||
// Keyrings
|
||||
&[][..],
|
||||
// Key store imports.
|
||||
&[ bob() ],
|
||||
// Handle
|
||||
bob().into()
|
||||
),
|
||||
(
|
||||
// Keyrings
|
||||
&[][..],
|
||||
// Key store imports.
|
||||
&[ bob() ],
|
||||
// Handle
|
||||
bob_primary().0.into()
|
||||
),
|
||||
] {
|
||||
let sq = Sq::new();
|
||||
|
||||
for keystore in [false, true] {
|
||||
let keyrings = if keystore {
|
||||
sq.key_import(bob());
|
||||
Vec::new()
|
||||
} else {
|
||||
vec![ bob() ]
|
||||
};
|
||||
for file in key_imports {
|
||||
sq.key_import(file);
|
||||
}
|
||||
|
||||
// Adopt a key that is not present.
|
||||
let r = sq.key_adopt(
|
||||
keyrings,
|
||||
bob(),
|
||||
keyrings.to_vec(),
|
||||
handle,
|
||||
[
|
||||
"1234 5678 90AB CDEF 1234 5678 90AB CDEF"
|
||||
.parse::<KeyHandle>()
|
||||
@ -419,21 +603,42 @@ mod integration {
|
||||
|
||||
#[test]
|
||||
fn adopt_from_multiple() -> Result<()> {
|
||||
let sq = Sq::new();
|
||||
for (keyrings, key_imports, handle) in [
|
||||
(
|
||||
// Keyrings
|
||||
&[ alice(), carol() ][..],
|
||||
// Key store imports.
|
||||
&[][..],
|
||||
// Handle
|
||||
FileOrKeyHandle::from(bob()),
|
||||
),
|
||||
(
|
||||
// Keyrings
|
||||
&[][..],
|
||||
// Key store imports.
|
||||
&[ alice(), carol() ],
|
||||
// Handle
|
||||
bob().into()
|
||||
),
|
||||
(
|
||||
// Keyrings
|
||||
&[][..],
|
||||
// Key store imports.
|
||||
&[ alice(), bob(), carol(), ],
|
||||
// Handle
|
||||
bob_primary().0.into()
|
||||
),
|
||||
] {
|
||||
let sq = Sq::new();
|
||||
|
||||
for keystore in [false, true] {
|
||||
let keyrings = if keystore {
|
||||
sq.key_import(alice());
|
||||
sq.key_import(carol());
|
||||
Vec::new()
|
||||
} else {
|
||||
vec![ alice(), carol() ]
|
||||
};
|
||||
for file in key_imports {
|
||||
sq.key_import(file);
|
||||
}
|
||||
|
||||
// Adopt own primary key.
|
||||
let cert = sq.key_adopt(
|
||||
keyrings,
|
||||
bob(),
|
||||
keyrings.to_vec(),
|
||||
handle,
|
||||
[
|
||||
alice_signing().0.clone(),
|
||||
alice_encryption().0.clone(),
|
||||
|
Loading…
Reference in New Issue
Block a user