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()
|
.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(
|
pub fn combine(
|
||||||
certs: Vec<Cert>,
|
certs: Vec<Cert>,
|
||||||
metadata: EncryptedMessage,
|
metadata: EncryptedMessage,
|
||||||
|
@ -220,103 +324,41 @@ pub fn combine(
|
||||||
let content = if keyring.is_empty() {
|
let content = if keyring.is_empty() {
|
||||||
// NOTE: Any card plugged in that can't decrypt, will raise issues.
|
// 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
|
// 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()?;
|
manager.load_any_card()?;
|
||||||
metadata.decrypt_with(&policy, &mut manager)?
|
metadata.decrypt_with(&policy, &mut manager)?
|
||||||
} else {
|
} else {
|
||||||
metadata.decrypt_with(&policy, &mut keyring)?
|
metadata.decrypt_with(&policy, &mut keyring)?
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(
|
let (threshold, root_cert, certs) = decode_metadata_v1(&content)?;
|
||||||
SHARD_METADATA_VERSION, content[0],
|
|
||||||
"incompatible metadata version"
|
|
||||||
);
|
|
||||||
let threshold = content[1];
|
|
||||||
|
|
||||||
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());
|
keyring.set_root_cert(root_cert.clone());
|
||||||
manager.set_root_cert(root_cert);
|
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> =
|
let mut messages: HashMap<KeyID, EncryptedMessage> =
|
||||||
HashMap::from_iter(certs.iter().map(|c| c.keyid()).zip(messages));
|
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
|
let mut decrypted_messages =
|
||||||
// encode the policy to ourselves.
|
decrypt_with_keyring(&certs, &policy, &mut keyring, &mut messages)?;
|
||||||
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)?,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// clean decrypted messages from encrypted messages
|
// clean decrypted messages from encrypted messages
|
||||||
messages.retain(|k, _v| !decrypted_messages.contains_key(k));
|
messages.retain(|k, _v| !decrypted_messages.contains_key(k));
|
||||||
|
|
||||||
let left_from_threshold = threshold as usize - decrypted_messages.len();
|
let left_from_threshold = threshold as usize - decrypted_messages.len();
|
||||||
if left_from_threshold > 0 {
|
if left_from_threshold > 0 {
|
||||||
let mut remaining_usable_certs = certs
|
let new_messages = decrypt_with_manager(
|
||||||
.iter()
|
left_from_threshold as u8,
|
||||||
.filter(|cert| messages.contains_key(&cert.keyid()))
|
&certs,
|
||||||
.collect::<Vec<_>>();
|
&mut messages,
|
||||||
|
policy,
|
||||||
while threshold as usize - decrypted_messages.len() > 0 {
|
&mut manager,
|
||||||
remaining_usable_certs.retain(|cert| messages.contains_key(&cert.keyid()));
|
)?;
|
||||||
let mut key_by_fingerprints = HashMap::new();
|
decrypted_messages.extend(new_messages.into_iter());
|
||||||
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 shares = decrypted_messages
|
let shares = decrypted_messages
|
||||||
|
|
Loading…
Reference in New Issue