pgp/
encrypt.rs

1//! # Encrypt
2//!
3//! Module dedicated to PGP encryption. This module exposes a simple
4//! function [`encrypt`] and its associated [`Error`]s.
5
6use std::io;
7
8use rand::{thread_rng, CryptoRng, Rng};
9
10use crate::{
11    native::{
12        self,
13        crypto::{hash::HashAlgorithm, public_key::PublicKeyAlgorithm},
14        types::{CompressionAlgorithm, KeyId, KeyTrait, Mpi, PublicKeyTrait},
15        Message, SignedPublicKey, SignedPublicSubKey,
16    },
17    utils::spawn_blocking,
18    Error, Result,
19};
20
21/// Wrapper around [`pgp`] public key types.
22///
23/// This enum is used to find the right encryption-capable public
24/// (sub)key.
25#[derive(Debug)]
26pub enum SignedPublicKeyOrSubkey<'a> {
27    Key(&'a SignedPublicKey),
28    Subkey(&'a SignedPublicSubKey),
29}
30
31impl KeyTrait for SignedPublicKeyOrSubkey<'_> {
32    fn fingerprint(&self) -> Vec<u8> {
33        match self {
34            Self::Key(k) => k.fingerprint(),
35            Self::Subkey(k) => k.fingerprint(),
36        }
37    }
38
39    fn key_id(&self) -> KeyId {
40        match self {
41            Self::Key(k) => k.key_id(),
42            Self::Subkey(k) => k.key_id(),
43        }
44    }
45
46    fn algorithm(&self) -> PublicKeyAlgorithm {
47        match self {
48            Self::Key(k) => k.algorithm(),
49            Self::Subkey(k) => k.algorithm(),
50        }
51    }
52}
53
54impl PublicKeyTrait for SignedPublicKeyOrSubkey<'_> {
55    fn verify_signature(
56        &self,
57        hash: HashAlgorithm,
58        data: &[u8],
59        sig: &[Mpi],
60    ) -> native::errors::Result<()> {
61        match self {
62            Self::Key(k) => k.verify_signature(hash, data, sig),
63            Self::Subkey(k) => k.verify_signature(hash, data, sig),
64        }
65    }
66
67    fn encrypt<R: Rng + CryptoRng>(
68        &self,
69        rng: &mut R,
70        plain: &[u8],
71    ) -> native::errors::Result<Vec<Mpi>> {
72        match self {
73            Self::Key(k) => k.encrypt(rng, plain),
74            Self::Subkey(k) => k.encrypt(rng, plain),
75        }
76    }
77
78    fn to_writer_old(&self, writer: &mut impl io::Write) -> native::errors::Result<()> {
79        match self {
80            Self::Key(k) => k.to_writer_old(writer),
81            Self::Subkey(k) => k.to_writer_old(writer),
82        }
83    }
84}
85
86/// Find primary key or subkey to use for encryption.
87///
88/// First, tries to use subkeys. If none of the subkeys are suitable
89/// for encryption, tries to use primary key. Returns `None` if the
90/// public key cannot be used for encryption.
91fn find_pkey_for_encryption(key: &SignedPublicKey) -> Option<SignedPublicKeyOrSubkey> {
92    if key.is_encryption_key() {
93        Some(SignedPublicKeyOrSubkey::Key(key))
94    } else {
95        key.public_subkeys
96            .iter()
97            .find(|subkey| subkey.is_encryption_key())
98            .map(SignedPublicKeyOrSubkey::Subkey)
99    }
100}
101
102/// Encrypts given bytes using the given list of public keys.
103pub async fn encrypt(pkeys: Vec<SignedPublicKey>, plain_bytes: Vec<u8>) -> Result<Vec<u8>> {
104    spawn_blocking(move || {
105        let mut rng = thread_rng();
106
107        let msg = Message::new_literal_bytes("", &plain_bytes);
108
109        let pkeys: Vec<SignedPublicKeyOrSubkey> =
110            pkeys.iter().filter_map(find_pkey_for_encryption).collect();
111        let pkeys_refs: Vec<&SignedPublicKeyOrSubkey> = pkeys.iter().collect();
112
113        let encrypted_bytes = msg
114            .compress(CompressionAlgorithm::ZLIB)
115            .map_err(Error::CompressMessageError)?
116            .encrypt_to_keys(&mut rng, Default::default(), &pkeys_refs)
117            .map_err(Error::EncryptMessageError)?
118            .to_armored_bytes(None)
119            .map_err(Error::ExportEncryptedMessageToArmorError)?;
120
121        Ok(encrypted_bytes)
122    })
123    .await?
124}