mirror of
https://github.com/chatmail/core.git
synced 2026-05-07 08:56:30 +03:00
feat: do not consider encrypting to the primary key
Primary key is usually used for certification. It is possible to make a certification- and encryption- capable key with RSA, but RFC 9580 says that implementations SHOULD NOT generate RSA keys.
This commit is contained in:
124
src/pgp.rs
124
src/pgp.rs
@@ -1,7 +1,6 @@
|
|||||||
//! OpenPGP helper module using [rPGP facilities](https://github.com/rpgp/rpgp).
|
//! OpenPGP helper module using [rPGP facilities](https://github.com/rpgp/rpgp).
|
||||||
|
|
||||||
use std::collections::{BTreeMap, HashSet};
|
use std::collections::{BTreeMap, HashSet};
|
||||||
use std::io;
|
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
use anyhow::{bail, Context as _, Result};
|
use anyhow::{bail, Context as _, Result};
|
||||||
@@ -14,8 +13,8 @@ use pgp::composed::{
|
|||||||
use pgp::crypto::ecc_curve::ECCCurve;
|
use pgp::crypto::ecc_curve::ECCCurve;
|
||||||
use pgp::crypto::hash::HashAlgorithm;
|
use pgp::crypto::hash::HashAlgorithm;
|
||||||
use pgp::crypto::sym::SymmetricKeyAlgorithm;
|
use pgp::crypto::sym::SymmetricKeyAlgorithm;
|
||||||
use pgp::types::{CompressionAlgorithm, PublicKeyTrait, SignatureBytes, StringToKey};
|
use pgp::types::{CompressionAlgorithm, PublicKeyTrait, StringToKey};
|
||||||
use rand::{thread_rng, CryptoRng, Rng};
|
use rand::thread_rng;
|
||||||
use tokio::runtime::Handle;
|
use tokio::runtime::Handle;
|
||||||
|
|
||||||
use crate::key::{DcKey, Fingerprint};
|
use crate::key::{DcKey, Fingerprint};
|
||||||
@@ -31,95 +30,6 @@ const SYMMETRIC_KEY_ALGORITHM: SymmetricKeyAlgorithm = SymmetricKeyAlgorithm::AE
|
|||||||
/// Preferred cryptographic hash.
|
/// Preferred cryptographic hash.
|
||||||
const HASH_ALGORITHM: HashAlgorithm = HashAlgorithm::SHA2_256;
|
const HASH_ALGORITHM: HashAlgorithm = HashAlgorithm::SHA2_256;
|
||||||
|
|
||||||
/// A wrapper for rPGP public key types
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum SignedPublicKeyOrSubkey<'a> {
|
|
||||||
Key(&'a SignedPublicKey),
|
|
||||||
Subkey(&'a SignedPublicSubKey),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PublicKeyTrait for SignedPublicKeyOrSubkey<'_> {
|
|
||||||
fn version(&self) -> pgp::types::KeyVersion {
|
|
||||||
match self {
|
|
||||||
Self::Key(k) => k.version(),
|
|
||||||
Self::Subkey(k) => k.version(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fingerprint(&self) -> pgp::types::Fingerprint {
|
|
||||||
match self {
|
|
||||||
Self::Key(k) => k.fingerprint(),
|
|
||||||
Self::Subkey(k) => k.fingerprint(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn key_id(&self) -> pgp::types::KeyId {
|
|
||||||
match self {
|
|
||||||
Self::Key(k) => k.key_id(),
|
|
||||||
Self::Subkey(k) => k.key_id(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn algorithm(&self) -> pgp::crypto::public_key::PublicKeyAlgorithm {
|
|
||||||
match self {
|
|
||||||
Self::Key(k) => k.algorithm(),
|
|
||||||
Self::Subkey(k) => k.algorithm(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn created_at(&self) -> &chrono::DateTime<chrono::Utc> {
|
|
||||||
match self {
|
|
||||||
Self::Key(k) => k.created_at(),
|
|
||||||
Self::Subkey(k) => k.created_at(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expiration(&self) -> Option<u16> {
|
|
||||||
match self {
|
|
||||||
Self::Key(k) => k.expiration(),
|
|
||||||
Self::Subkey(k) => k.expiration(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn verify_signature(
|
|
||||||
&self,
|
|
||||||
hash: HashAlgorithm,
|
|
||||||
data: &[u8],
|
|
||||||
sig: &SignatureBytes,
|
|
||||||
) -> pgp::errors::Result<()> {
|
|
||||||
match self {
|
|
||||||
Self::Key(k) => k.verify_signature(hash, data, sig),
|
|
||||||
Self::Subkey(k) => k.verify_signature(hash, data, sig),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn encrypt<R: Rng + CryptoRng>(
|
|
||||||
&self,
|
|
||||||
rng: R,
|
|
||||||
plain: &[u8],
|
|
||||||
typ: pgp::types::EskType,
|
|
||||||
) -> pgp::errors::Result<pgp::types::PkeskBytes> {
|
|
||||||
match self {
|
|
||||||
Self::Key(k) => k.encrypt(rng, plain, typ),
|
|
||||||
Self::Subkey(k) => k.encrypt(rng, plain, typ),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_for_hashing(&self, writer: &mut impl io::Write) -> pgp::errors::Result<()> {
|
|
||||||
match self {
|
|
||||||
Self::Key(k) => k.serialize_for_hashing(writer),
|
|
||||||
Self::Subkey(k) => k.serialize_for_hashing(writer),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn public_params(&self) -> &pgp::types::PublicParams {
|
|
||||||
match self {
|
|
||||||
Self::Key(k) => k.public_params(),
|
|
||||||
Self::Subkey(k) => k.public_params(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Split data from PGP Armored Data as defined in <https://tools.ietf.org/html/rfc4880#section-6.2>.
|
/// Split data from PGP Armored Data as defined in <https://tools.ietf.org/html/rfc4880#section-6.2>.
|
||||||
///
|
///
|
||||||
/// Returns (type, headers, base64 encoded body).
|
/// Returns (type, headers, base64 encoded body).
|
||||||
@@ -236,28 +146,15 @@ pub(crate) fn create_keypair(addr: EmailAddress) -> Result<KeyPair> {
|
|||||||
Ok(key_pair)
|
Ok(key_pair)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Select public key or subkey to use for encryption.
|
/// Selects a subkey of the public key to use for encryption.
|
||||||
///
|
///
|
||||||
/// First, tries to use subkeys. If none of the subkeys are suitable
|
/// Returns `None` if the public key cannot be used for encryption.
|
||||||
/// for encryption, tries to use primary key. Returns `None` if the public
|
|
||||||
/// key cannot be used for encryption.
|
|
||||||
///
|
///
|
||||||
/// TODO: take key flags and expiration dates into account
|
/// TODO: take key flags and expiration dates into account
|
||||||
fn select_pk_for_encryption(key: &SignedPublicKey) -> Option<SignedPublicKeyOrSubkey> {
|
fn select_pk_for_encryption(key: &SignedPublicKey) -> Option<&SignedPublicSubKey> {
|
||||||
key.public_subkeys
|
key.public_subkeys
|
||||||
.iter()
|
.iter()
|
||||||
.find(|subkey| subkey.is_encryption_key())
|
.find(|subkey| subkey.is_encryption_key())
|
||||||
.map_or_else(
|
|
||||||
|| {
|
|
||||||
// No usable subkey found, try primary key
|
|
||||||
if key.is_encryption_key() {
|
|
||||||
Some(SignedPublicKeyOrSubkey::Key(key))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|subkey| Some(SignedPublicKeyOrSubkey::Subkey(subkey)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encrypts `plain` text using `public_keys_for_encryption`
|
/// Encrypts `plain` text using `public_keys_for_encryption`
|
||||||
@@ -272,11 +169,10 @@ pub async fn pk_encrypt(
|
|||||||
|
|
||||||
Handle::current()
|
Handle::current()
|
||||||
.spawn_blocking(move || {
|
.spawn_blocking(move || {
|
||||||
let pkeys: Vec<SignedPublicKeyOrSubkey> = public_keys_for_encryption
|
let pkeys: Vec<&SignedPublicSubKey> = public_keys_for_encryption
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(select_pk_for_encryption)
|
.filter_map(select_pk_for_encryption)
|
||||||
.collect();
|
.collect();
|
||||||
let pkeys_refs: Vec<&SignedPublicKeyOrSubkey> = pkeys.iter().collect();
|
|
||||||
|
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
@@ -287,13 +183,9 @@ pub async fn pk_encrypt(
|
|||||||
} else {
|
} else {
|
||||||
signed_msg
|
signed_msg
|
||||||
};
|
};
|
||||||
compressed_msg.encrypt_to_keys_seipdv1(
|
compressed_msg.encrypt_to_keys_seipdv1(&mut rng, SYMMETRIC_KEY_ALGORITHM, &pkeys)?
|
||||||
&mut rng,
|
|
||||||
SYMMETRIC_KEY_ALGORITHM,
|
|
||||||
&pkeys_refs,
|
|
||||||
)?
|
|
||||||
} else {
|
} else {
|
||||||
lit_msg.encrypt_to_keys_seipdv1(&mut rng, SYMMETRIC_KEY_ALGORITHM, &pkeys_refs)?
|
lit_msg.encrypt_to_keys_seipdv1(&mut rng, SYMMETRIC_KEY_ALGORITHM, &pkeys)?
|
||||||
};
|
};
|
||||||
|
|
||||||
let encoded_msg = encrypted_msg.to_armored_string(Default::default())?;
|
let encoded_msg = encrypted_msg.to_armored_string(Default::default())?;
|
||||||
|
|||||||
Reference in New Issue
Block a user