Implement hierarchical recipient policies (#5)
This commit is contained in:
@@ -346,6 +346,47 @@ impl KeyStore {
|
||||
.map_err(|_| CryptoError::EncryptionFailed)
|
||||
}
|
||||
|
||||
/// Report whether every public-key session packet names exactly the requested primary
|
||||
/// certificates. Unknown, anonymous, symmetric, missing, or extra recipients do not match.
|
||||
pub fn is_encrypted_for(
|
||||
&self,
|
||||
ciphertext: &EncryptedEntry,
|
||||
recipients: &[KeyHandle],
|
||||
) -> Result<bool, CryptoError> {
|
||||
let message = Message::from_bytes(Cursor::new(ciphertext.as_bytes()))
|
||||
.map_err(|_| CryptoError::CorruptMessage)?;
|
||||
let Message::Encrypted { esk, .. } = message else {
|
||||
return Err(CryptoError::CorruptMessage);
|
||||
};
|
||||
let expected = recipients
|
||||
.iter()
|
||||
.map(|recipient| {
|
||||
self.material(recipient)?;
|
||||
Ok(recipient.0.clone())
|
||||
})
|
||||
.collect::<Result<BTreeSet<_>, CryptoError>>()?;
|
||||
if expected.is_empty() {
|
||||
return Ok(false);
|
||||
}
|
||||
let mut actual = BTreeSet::new();
|
||||
for packet in esk {
|
||||
let Esk::PublicKeyEncryptedSessionKey(packet) = packet else {
|
||||
return Ok(false);
|
||||
};
|
||||
let matches = self
|
||||
.keys
|
||||
.iter()
|
||||
.filter(|(_, material)| packet_matches_public(&packet, &material.public))
|
||||
.map(|(fingerprint, _)| fingerprint)
|
||||
.collect::<Vec<_>>();
|
||||
if matches.len() != 1 {
|
||||
return Ok(false);
|
||||
}
|
||||
actual.insert((*matches[0]).clone());
|
||||
}
|
||||
Ok(actual == expected)
|
||||
}
|
||||
|
||||
/// Decrypt a pass entry with only the secret keys named by its PKESK packets.
|
||||
pub fn decrypt(
|
||||
&self,
|
||||
@@ -760,6 +801,17 @@ fn message_matches_secret(message: &Message<'_>, key: &SignedSecretKey) -> bool
|
||||
})
|
||||
}
|
||||
|
||||
fn packet_matches_public(
|
||||
packet: &pgp::packet::PublicKeyEncryptedSessionKey,
|
||||
key: &SignedPublicKey,
|
||||
) -> bool {
|
||||
packet.match_identity(&key.primary_key)
|
||||
|| key
|
||||
.public_subkeys
|
||||
.iter()
|
||||
.any(|subkey| packet.match_identity(&subkey.key))
|
||||
}
|
||||
|
||||
fn sign_data<K: SigningKey>(
|
||||
key: &K,
|
||||
password: &Password,
|
||||
|
||||
Reference in New Issue
Block a user