1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//! Deletes all of a certificate's secret key material.
use anyhow::Context;
use sequoia_openpgp as openpgp;
use openpgp::Cert;
use openpgp::cert::amalgamation::ValidateAmalgamation;
use openpgp::cert::amalgamation::key::PrimaryKey;
use crate::Result;
use crate::Sq;
use crate::cli;
use crate::common::NULL_POLICY;
use crate::common::key::delete;
use crate::common::key::get_keys;
use crate::sq::TrustThreshold;
pub fn dispatch(sq: Sq, command: cli::key::delete::Command)
-> Result<()>
{
let (cert, cert_source)
= sq.resolve_cert(&command.cert, TrustThreshold::Full)?;
// Fail if the certificate is not valid under the current policy.
Cert::with_policy(&cert, sq.policy, sq.time)
.with_context(|| {
sq.hint(format_args!(
"The certificate {} is not valid under the \
current policy. You can still delete individual \
keys using `sq key subkey delete`.",
cert.fingerprint()));
format!("The certificate {} is not valid under the \
current policy.",
cert.fingerprint())
})?;
// We want to delete all secret key material associated with the
// certificate, but we don't want to delete secret key material
// that we are not confident belongs to the certificate.
//
// Imagine Alice creates a new certificate. Mallory see this, and
// anticipates that she is going to delete the old certificate.
// He attaches her new encryption-capable subkey to the old
// certificate using some weak cryptography, publishes it, and
// then Alice gets the update to her old certificate via
// parcimonie. When she deletes the secret key material
// associated with the old certificate, she would also delete her
// new secret key material. Ouch! Admittedly, this attack is a
// bit contrived.
//
// Alternatively, we could skip subkeys whose bindings rely on
// weak cryptography. This behavior would probably surprise most
// users. It could have serious consequences as well, since the
// user thought they deleted the secret key material, but didn't.
//
// Instead, we are conservative: if a subkey's binding signature
// relies on weak cryptography AND we have secret key material for
// it, we abort, and suggest using `sq key subkey delete` instead.
// Get all keys valid under the NULL policy.
let nc = Cert::with_policy(&cert, NULL_POLICY, sq.time)
.with_context(|| {
format!("The certificate {} is not valid under the \
null policy.",
cert.fingerprint())
})?;
let kas = nc.keys().collect::<Vec<_>>();
let kas = kas.iter().collect::<Vec<_>>();
let to_delete = get_keys(&sq, &cert_source, &kas, true)?;
// Go through the keys with secret key material, and make sure
// their binding is valid under the current policy.
let mut bad = Vec::new();
for (ka, _remote) in to_delete.iter() {
if ka.primary() {
// We check that the primary key is valid above.
continue;
}
if let Err(err) = ka.with_policy(sq.policy, sq.time) {
bad.push((ka.key().fingerprint(), err));
}
}
if ! bad.is_empty() {
weprintln!("Some keys are not valid according \
to the current policy:");
for (fpr, err) in bad.into_iter() {
weprintln!(" - {}: {}",
fpr,
crate::one_line_error_chain(err));
}
weprintln!("Cowardly refusing to delete all of the secret key \
material to avoid accidentally losing data. Use \
`sq key subkey delete` to delete the keys individually.");
return Err(anyhow::anyhow!(
"The authenticity of some subkeys is uncertain."));
}
if to_delete.is_empty() {
return Err(anyhow::anyhow!(
"{} does not contain any secret key material.",
cert.fingerprint()));
}
if cert_source.is_key_handle() {
let mut die = false;
// Make sure this is not ambiguous.
for (ka, _remote) in to_delete.iter() {
if let Ok(certs) = sq.lookup_with_policy(
std::iter::once(ka.key().key_handle()),
None,
true,
true,
NULL_POLICY,
sq.time)
{
if certs.len() > 1 {
die = true;
weprintln!("{} is associated with multiple certificates:",
ka.key().fingerprint());
for cert in certs.iter() {
weprintln!(" - {}", cert.fingerprint());
}
}
}
}
if die {
weprintln!("Cowardly refusing to delete secret key material to \
avoid accidentally losing data. Use \
`sq key subkey delete` to delete the keys \
individually.");
return Err(anyhow::anyhow!(
"Some keys are associated with multiple certificates."));
}
}
delete::delete(sq, &cert, cert_source, to_delete,
command.output, false)
}