mirror of
https://github.com/chatmail/core.git
synced 2026-05-07 17:06:35 +03:00
Validate detached signatures
This commit is contained in:
40
src/e2ee.rs
40
src/e2ee.rs
@@ -295,6 +295,31 @@ async fn decrypt_if_autocrypt_message(
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Validates signatures of Multipart/Signed message part, as defined in RFC 1847.
|
||||||
|
///
|
||||||
|
/// Returns `None` if the part is not a Multipart/Signed part, otherwise retruns the set of key
|
||||||
|
/// fingerprints for which there is a valid signature.
|
||||||
|
async fn validate_detached_signature(
|
||||||
|
mail: &ParsedMail<'_>,
|
||||||
|
public_keyring_for_validate: &Keyring<SignedPublicKey>,
|
||||||
|
) -> Result<Option<(Vec<u8>, HashSet<Fingerprint>)>> {
|
||||||
|
if mail.ctype.mimetype != "multipart/signed" {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let [first_part, second_part] = &mail.subparts[..] {
|
||||||
|
// First part is the content, second part is the signature.
|
||||||
|
let content = first_part.raw_bytes;
|
||||||
|
let signature = second_part.get_body_raw()?;
|
||||||
|
let ret_valid_signatures =
|
||||||
|
pgp::pk_validate(content, &signature, public_keyring_for_validate).await?;
|
||||||
|
|
||||||
|
Ok(Some((content.to_vec(), ret_valid_signatures)))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns Ok(None) if nothing encrypted was found.
|
/// Returns Ok(None) if nothing encrypted was found.
|
||||||
async fn decrypt_part(
|
async fn decrypt_part(
|
||||||
mail: &ParsedMail<'_>,
|
mail: &ParsedMail<'_>,
|
||||||
@@ -307,10 +332,19 @@ async fn decrypt_part(
|
|||||||
let (plain, ret_valid_signatures) =
|
let (plain, ret_valid_signatures) =
|
||||||
pgp::pk_decrypt(data, private_keyring, &public_keyring_for_validate).await?;
|
pgp::pk_decrypt(data, private_keyring, &public_keyring_for_validate).await?;
|
||||||
|
|
||||||
// If the message was wrongly or not signed, still return the plain text.
|
// Check for detached signatures.
|
||||||
// The caller has to check the signatures then.
|
// If decrypted part is a multipart/signed, then there is a detached signature.
|
||||||
|
let decrypted_part = mailparse::parse_mail(&plain)?;
|
||||||
|
if let Some((content, valid_detached_signatures)) =
|
||||||
|
validate_detached_signature(&decrypted_part, &public_keyring_for_validate).await?
|
||||||
|
{
|
||||||
|
return Ok(Some((content, valid_detached_signatures)));
|
||||||
|
} else {
|
||||||
|
// If the message was wrongly or not signed, still return the plain text.
|
||||||
|
// The caller has to check the signatures then.
|
||||||
|
|
||||||
return Ok(Some((plain, ret_valid_signatures)));
|
return Ok(Some((plain, ret_valid_signatures)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
|||||||
29
src/pgp.rs
29
src/pgp.rs
@@ -8,7 +8,7 @@ use anyhow::{bail, ensure, format_err, Result};
|
|||||||
use pgp::armor::BlockType;
|
use pgp::armor::BlockType;
|
||||||
use pgp::composed::{
|
use pgp::composed::{
|
||||||
Deserializable, KeyType as PgpKeyType, Message, SecretKeyParamsBuilder, SignedPublicKey,
|
Deserializable, KeyType as PgpKeyType, Message, SecretKeyParamsBuilder, SignedPublicKey,
|
||||||
SignedPublicSubKey, SignedSecretKey, SubkeyParamsBuilder,
|
SignedPublicSubKey, SignedSecretKey, StandaloneSignature, SubkeyParamsBuilder,
|
||||||
};
|
};
|
||||||
use pgp::crypto::{HashAlgorithm, SymmetricKeyAlgorithm};
|
use pgp::crypto::{HashAlgorithm, SymmetricKeyAlgorithm};
|
||||||
use pgp::types::{
|
use pgp::types::{
|
||||||
@@ -330,6 +330,33 @@ pub async fn pk_decrypt(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Validates detached signature.
|
||||||
|
pub async fn pk_validate(
|
||||||
|
content: &[u8],
|
||||||
|
signature: &[u8],
|
||||||
|
public_keys_for_validation: &Keyring<SignedPublicKey>,
|
||||||
|
) -> Result<HashSet<Fingerprint>> {
|
||||||
|
let mut ret: HashSet<Fingerprint> = Default::default();
|
||||||
|
|
||||||
|
let standalone_signature = StandaloneSignature::from_armor_single(Cursor::new(signature))?.0;
|
||||||
|
let pkeys = public_keys_for_validation.keys();
|
||||||
|
|
||||||
|
// Remove trailing CRLF before the delimiter.
|
||||||
|
// According to RFC 3156 it is considered to be part of the MIME delimiter for the purpose of
|
||||||
|
// OpenPGP signature calculation.
|
||||||
|
let content = content
|
||||||
|
.get(..content.len().saturating_sub(2))
|
||||||
|
.ok_or_else(|| format_err!("index is out of range"))?;
|
||||||
|
|
||||||
|
for pkey in pkeys {
|
||||||
|
if standalone_signature.verify(pkey, content).is_ok() {
|
||||||
|
let fp = DcKey::fingerprint(pkey);
|
||||||
|
ret.insert(fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
|
||||||
/// Symmetric encryption.
|
/// Symmetric encryption.
|
||||||
pub async fn symm_encrypt(passphrase: &str, plain: &[u8]) -> Result<String> {
|
pub async fn symm_encrypt(passphrase: &str, plain: &[u8]) -> Result<String> {
|
||||||
let lit_msg = Message::new_literal_bytes("", plain);
|
let lit_msg = Message::new_literal_bytes("", plain);
|
||||||
|
|||||||
Reference in New Issue
Block a user