keyfork-shard: extract message decryptor functions
This commit is contained in:
parent
1cdbab1a1d
commit
ddefe1c6b5
|
@ -205,6 +205,110 @@ fn get_decryption_keys<'a>(
|
|||
.secret()
|
||||
}
|
||||
|
||||
fn decode_metadata_v1(buf: &[u8]) -> Result<(u8, Cert, Vec<Cert>)> {
|
||||
assert_eq!(
|
||||
SHARD_METADATA_VERSION, buf[0],
|
||||
"Incompatible metadata version"
|
||||
);
|
||||
let threshold = buf[1];
|
||||
|
||||
let mut cert_parser =
|
||||
CertParser::from_bytes(&buf[SHARD_METADATA_OFFSET..]).map_err(Error::Sequoia)?;
|
||||
|
||||
let root_cert = match cert_parser.next() {
|
||||
Some(Ok(c)) => c,
|
||||
Some(Err(e)) => return Err(Error::Sequoia(e)),
|
||||
None => panic!("No data found"),
|
||||
};
|
||||
|
||||
let certs = cert_parser
|
||||
.collect::<openpgp::Result<Vec<_>>>()
|
||||
.map_err(Error::Sequoia)?;
|
||||
|
||||
Ok((threshold, root_cert, certs))
|
||||
}
|
||||
|
||||
// NOTE: When using single-decryptor mechanism, use this method with `threshold = 1` to return a
|
||||
// single message.
|
||||
fn decrypt_with_manager(
|
||||
threshold: u8,
|
||||
certs: &[Cert],
|
||||
messages: &mut HashMap<KeyID, EncryptedMessage>,
|
||||
policy: NullPolicy,
|
||||
manager: &mut SmartcardManager,
|
||||
) -> Result<HashMap<KeyID, Vec<u8>>> {
|
||||
let mut decrypted_messages = HashMap::new();
|
||||
|
||||
while threshold as usize - decrypted_messages.len() > 0 {
|
||||
// Build list of fingerprints that haven't yet been used for decrypting
|
||||
let mut cert_by_fingerprint = HashMap::new();
|
||||
let mut unused_fingerprints = vec![];
|
||||
for valid_cert in certs
|
||||
.iter()
|
||||
.filter(|cert| !decrypted_messages.contains_key(&cert.keyid()))
|
||||
.map(|cert| cert.with_policy(&policy, None))
|
||||
{
|
||||
let valid_cert = valid_cert.map_err(Error::Sequoia)?;
|
||||
let fp = valid_cert
|
||||
.keys()
|
||||
.for_storage_encryption()
|
||||
.map(|k| k.fingerprint())
|
||||
.collect::<Vec<_>>();
|
||||
for fp in &fp {
|
||||
cert_by_fingerprint.insert(fp.clone(), valid_cert.keyid());
|
||||
}
|
||||
unused_fingerprints.extend(fp.into_iter());
|
||||
}
|
||||
|
||||
// Iterate over all fingerprints and use key_by_fingerprints to assoc with Enc. Message
|
||||
if let Some(fp) = manager.load_any_fingerprint(unused_fingerprints)? {
|
||||
let cert_keyid = cert_by_fingerprint.get(&fp).unwrap().clone();
|
||||
if let Some(message) = messages.remove(&cert_keyid) {
|
||||
let message = message.decrypt_with(&policy, &mut *manager)?;
|
||||
decrypted_messages.insert(cert_keyid, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(decrypted_messages)
|
||||
}
|
||||
|
||||
// NOTE: When using single-decryptor mechanism, only a single key should be provided in Keyring to
|
||||
// decrypt messages with.
|
||||
fn decrypt_with_keyring(
|
||||
certs: &[Cert],
|
||||
policy: &NullPolicy,
|
||||
keyring: &mut Keyring,
|
||||
messages: &mut HashMap<KeyID, EncryptedMessage>,
|
||||
) -> Result<HashMap<KeyID, Vec<u8>>, Error> {
|
||||
let mut decrypted_messages = HashMap::new();
|
||||
|
||||
for valid_cert in certs.iter().map(|cert| cert.with_policy(policy, None)) {
|
||||
let valid_cert = valid_cert.map_err(Error::Sequoia)?;
|
||||
let Some(secret_cert) = keyring.get_cert_for_primary_keyid(&valid_cert.keyid()) else {
|
||||
continue;
|
||||
};
|
||||
let secret_cert = secret_cert
|
||||
.with_policy(policy, None)
|
||||
.map_err(Error::Sequoia)?;
|
||||
let keys = get_decryption_keys(&secret_cert).collect::<Vec<_>>();
|
||||
if !keys.is_empty() {
|
||||
if let Some(message) = messages.get_mut(&valid_cert.keyid()) {
|
||||
for (pkesk, key) in message.pkesks.iter_mut().zip(keys) {
|
||||
pkesk.set_recipient(key.keyid());
|
||||
}
|
||||
// we have a pkesk, decrypt via keyring
|
||||
decrypted_messages.insert(
|
||||
valid_cert.keyid(),
|
||||
message.decrypt_with(policy, &mut *keyring)?,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(decrypted_messages)
|
||||
}
|
||||
|
||||
pub fn combine(
|
||||
certs: Vec<Cert>,
|
||||
metadata: EncryptedMessage,
|
||||
|
@ -220,103 +324,41 @@ pub fn combine(
|
|||
let content = if keyring.is_empty() {
|
||||
// NOTE: Any card plugged in that can't decrypt, will raise issues.
|
||||
// This should not be used on a system where OpenPGP cards are available that shouldn't be
|
||||
// used, due to the nature of how wildcard decryption works.
|
||||
// used. The manager will try every wildcard packet for the card on the system, and once no
|
||||
// matching PKESK packet has been found, will return an error.
|
||||
manager.load_any_card()?;
|
||||
metadata.decrypt_with(&policy, &mut manager)?
|
||||
} else {
|
||||
metadata.decrypt_with(&policy, &mut keyring)?
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
SHARD_METADATA_VERSION, content[0],
|
||||
"incompatible metadata version"
|
||||
);
|
||||
let threshold = content[1];
|
||||
let (threshold, root_cert, certs) = decode_metadata_v1(&content)?;
|
||||
|
||||
let mut cert_parser =
|
||||
CertParser::from_bytes(&content[SHARD_METADATA_OFFSET..]).map_err(Error::Sequoia)?;
|
||||
let root_cert = match cert_parser.next() {
|
||||
Some(Ok(c)) => c,
|
||||
Some(Err(e)) => panic!("Could not find root (first) certificate: {e}"),
|
||||
None => panic!("No certs found in cert parser"),
|
||||
};
|
||||
let certs = cert_parser
|
||||
.collect::<openpgp::Result<Vec<_>>>()
|
||||
.map_err(Error::Sequoia)?;
|
||||
keyring.set_root_cert(root_cert.clone());
|
||||
manager.set_root_cert(root_cert);
|
||||
|
||||
// Generate a controlled binding from certificates to encrypted messages. This is stable
|
||||
// because we control the order packets are encrypted and certificates are stored.
|
||||
|
||||
let mut messages: HashMap<KeyID, EncryptedMessage> =
|
||||
HashMap::from_iter(certs.iter().map(|c| c.keyid()).zip(messages));
|
||||
let mut decrypted_messages: HashMap<KeyID, Vec<u8>> = HashMap::new();
|
||||
|
||||
// NOTE: This is ONLY stable because we control the generation of PKESK packets and
|
||||
// encode the policy to ourselves.
|
||||
for valid_cert in certs.iter().map(|cert| cert.with_policy(&policy, None)) {
|
||||
let valid_cert = valid_cert.map_err(Error::Sequoia)?;
|
||||
// get keys from keyring for cert
|
||||
let Some(secret_cert) = keyring.get_cert_for_primary_keyid(&valid_cert.keyid()) else {
|
||||
continue;
|
||||
};
|
||||
let secret_cert = secret_cert
|
||||
.with_policy(&policy, None)
|
||||
.map_err(Error::Sequoia)?;
|
||||
let keys = get_decryption_keys(&secret_cert).collect::<Vec<_>>();
|
||||
if !keys.is_empty() {
|
||||
if let Some(message) = messages.get_mut(&valid_cert.keyid()) {
|
||||
for (pkesk, key) in message.pkesks.iter_mut().zip(keys) {
|
||||
pkesk.set_recipient(key.keyid());
|
||||
}
|
||||
// we have a pkesk, decrypt via keyring
|
||||
decrypted_messages.insert(
|
||||
valid_cert.keyid(),
|
||||
message.decrypt_with(&policy, &mut keyring)?,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut decrypted_messages =
|
||||
decrypt_with_keyring(&certs, &policy, &mut keyring, &mut messages)?;
|
||||
|
||||
// clean decrypted messages from encrypted messages
|
||||
messages.retain(|k, _v| !decrypted_messages.contains_key(k));
|
||||
|
||||
let left_from_threshold = threshold as usize - decrypted_messages.len();
|
||||
if left_from_threshold > 0 {
|
||||
let mut remaining_usable_certs = certs
|
||||
.iter()
|
||||
.filter(|cert| messages.contains_key(&cert.keyid()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
while threshold as usize - decrypted_messages.len() > 0 {
|
||||
remaining_usable_certs.retain(|cert| messages.contains_key(&cert.keyid()));
|
||||
let mut key_by_fingerprints = HashMap::new();
|
||||
let mut total_fingerprints = vec![];
|
||||
for valid_cert in remaining_usable_certs
|
||||
.iter()
|
||||
.map(|cert| cert.with_policy(&policy, None))
|
||||
{
|
||||
let valid_cert = valid_cert.map_err(Error::Sequoia)?;
|
||||
let fp = valid_cert
|
||||
.keys()
|
||||
.for_storage_encryption()
|
||||
.map(|k| k.fingerprint())
|
||||
.collect::<Vec<_>>();
|
||||
for fp in &fp {
|
||||
key_by_fingerprints.insert(fp.clone(), valid_cert.keyid());
|
||||
}
|
||||
total_fingerprints.extend(fp.iter().cloned());
|
||||
}
|
||||
|
||||
// Iterate over all fingerprints and use key_by_fingerprints to assoc with Enc. Message
|
||||
if let Some(fp) = manager.load_any_fingerprint(total_fingerprints)? {
|
||||
// soundness: `key_by_fingerprints` is extended by the same fps that are then
|
||||
// inserted into `total_fingerprints`
|
||||
let cert_keyid = key_by_fingerprints.get(&fp).unwrap().clone();
|
||||
let message = messages.remove(&cert_keyid);
|
||||
if let Some(message) = message {
|
||||
let message = message.decrypt_with(&policy, &mut manager)?;
|
||||
decrypted_messages.insert(cert_keyid, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
let new_messages = decrypt_with_manager(
|
||||
left_from_threshold as u8,
|
||||
&certs,
|
||||
&mut messages,
|
||||
policy,
|
||||
&mut manager,
|
||||
)?;
|
||||
decrypted_messages.extend(new_messages.into_iter());
|
||||
}
|
||||
|
||||
let shares = decrypted_messages
|
||||
|
|
Loading…
Reference in New Issue