mirror of
https://github.com/chatmail/core.git
synced 2026-05-04 05:46:29 +03:00
Implement public key selection
First, try to use subkeys, because they are usually short-term encryption keys. If none of the subkeys are encryption keys, try to use the primary key. rPGP is updated to the master branch because the latest release does not have .is_encryption_key() yet.
This commit is contained in:
committed by
holger krekel
parent
f505ff03e4
commit
4b8252e001
9
Cargo.lock
generated
9
Cargo.lock
generated
@@ -506,7 +506,7 @@ dependencies = [
|
||||
"num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pgp 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pgp 0.3.1 (git+https://github.com/rpgp/rpgp)",
|
||||
"pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pretty_env_logger 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proptest 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1505,8 +1505,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "pgp"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
version = "0.3.1"
|
||||
source = "git+https://github.com/rpgp/rpgp#6977b14479ffa079d5857861ca89e69e93c8bd55"
|
||||
dependencies = [
|
||||
"aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1520,6 +1520,7 @@ dependencies = [
|
||||
"cfb-mode 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"circular 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crc24 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"derive_builder 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"des 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -2969,7 +2970,7 @@ dependencies = [
|
||||
"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b"
|
||||
"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
|
||||
"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||
"checksum pgp 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a14471cfc3855455f5bbb639e367cedd69fbce71b32bfb83aff20c3deafce36e"
|
||||
"checksum pgp 0.3.1 (git+https://github.com/rpgp/rpgp)" = "<none>"
|
||||
"checksum pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "72d5370d90f49f70bd033c3d75e87fc529fbfff9d6f7cccef07d6170079d91ea"
|
||||
"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
|
||||
"checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427"
|
||||
|
||||
@@ -10,7 +10,7 @@ deltachat_derive = { path = "./deltachat_derive" }
|
||||
mmime = { version = "0.1.2", path = "./mmime" }
|
||||
|
||||
libc = "0.2.51"
|
||||
pgp = { version = "0.2.3", default-features = false }
|
||||
pgp = { git = "https://github.com/rpgp/rpgp", branch = "master", default-features = false }
|
||||
hex = "0.3.2"
|
||||
sha2 = "0.8.0"
|
||||
rand = "0.6.5"
|
||||
|
||||
103
src/pgp.rs
103
src/pgp.rs
@@ -2,6 +2,7 @@
|
||||
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
use std::convert::TryInto;
|
||||
use std::io;
|
||||
use std::io::Cursor;
|
||||
|
||||
use pgp::armor::BlockType;
|
||||
@@ -10,8 +11,10 @@ use pgp::composed::{
|
||||
SignedPublicSubKey, SignedSecretKey, SubkeyParamsBuilder,
|
||||
};
|
||||
use pgp::crypto::{HashAlgorithm, SymmetricKeyAlgorithm};
|
||||
use pgp::types::{CompressionAlgorithm, KeyTrait, SecretKeyTrait, StringToKey};
|
||||
use rand::thread_rng;
|
||||
use pgp::types::{
|
||||
CompressionAlgorithm, KeyTrait, Mpi, PublicKeyTrait, SecretKeyTrait, StringToKey,
|
||||
};
|
||||
use rand::{thread_rng, CryptoRng, Rng};
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::key::*;
|
||||
@@ -20,6 +23,68 @@ use crate::keyring::*;
|
||||
pub const HEADER_AUTOCRYPT: &str = "autocrypt-prefer-encrypt";
|
||||
pub const HEADER_SETUPCODE: &str = "passphrase-begin";
|
||||
|
||||
/// A wrapper for rPGP public key types
|
||||
#[derive(Debug)]
|
||||
enum SignedPublicKeyOrSubkey<'a> {
|
||||
Key(&'a SignedPublicKey),
|
||||
Subkey(&'a SignedPublicSubKey),
|
||||
}
|
||||
|
||||
impl<'a> KeyTrait for SignedPublicKeyOrSubkey<'a> {
|
||||
fn fingerprint(&self) -> Vec<u8> {
|
||||
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::PublicKeyAlgorithm {
|
||||
match self {
|
||||
Self::Key(k) => k.algorithm(),
|
||||
Self::Subkey(k) => k.algorithm(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PublicKeyTrait for SignedPublicKeyOrSubkey<'a> {
|
||||
fn verify_signature(
|
||||
&self,
|
||||
hash: HashAlgorithm,
|
||||
data: &[u8],
|
||||
sig: &[Mpi],
|
||||
) -> 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: &mut R,
|
||||
plain: &[u8],
|
||||
) -> pgp::errors::Result<Vec<Mpi>> {
|
||||
match self {
|
||||
Self::Key(k) => k.encrypt(rng, plain),
|
||||
Self::Subkey(k) => k.encrypt(rng, plain),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_writer_old(&self, writer: &mut impl io::Write) -> pgp::errors::Result<()> {
|
||||
match self {
|
||||
Self::Key(k) => k.to_writer_old(writer),
|
||||
Self::Subkey(k) => k.to_writer_old(writer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Split data from PGP Armored Data as defined in https://tools.ietf.org/html/rfc4880#section-6.2.
|
||||
///
|
||||
/// Returns (type, headers, base64 encoded body).
|
||||
@@ -99,13 +164,28 @@ pub fn create_keypair(addr: impl AsRef<str>) -> Option<(Key, Key)> {
|
||||
Some((Key::Public(public_key), Key::Secret(private_key)))
|
||||
}
|
||||
|
||||
/// Select subkey of the public key to use for encryption.
|
||||
/// Select public key or subkey to use for encryption.
|
||||
///
|
||||
/// Currently the first subkey is selected.
|
||||
fn select_pk_for_encryption(key: &SignedPublicKey) -> Option<&SignedPublicSubKey> {
|
||||
key.public_subkeys.iter().find(|_k|
|
||||
// TODO: check if it is an encryption subkey
|
||||
true)
|
||||
/// First, tries to use subkeys. If none of the subkeys are suitable
|
||||
/// 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
|
||||
fn select_pk_for_encryption(key: &SignedPublicKey) -> Option<SignedPublicKeyOrSubkey> {
|
||||
key.public_subkeys
|
||||
.iter()
|
||||
.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`
|
||||
@@ -116,7 +196,7 @@ pub fn pk_encrypt(
|
||||
private_key_for_signing: Option<&Key>,
|
||||
) -> Result<String, Error> {
|
||||
let lit_msg = Message::new_literal_bytes("", plain);
|
||||
let pkeys: Vec<&SignedPublicSubKey> = public_keys_for_encryption
|
||||
let pkeys: Vec<SignedPublicKeyOrSubkey> = public_keys_for_encryption
|
||||
.keys()
|
||||
.iter()
|
||||
.filter_map(|key| {
|
||||
@@ -126,6 +206,7 @@ pub fn pk_encrypt(
|
||||
.and_then(select_pk_for_encryption)
|
||||
})
|
||||
.collect();
|
||||
let pkeys_refs: Vec<&SignedPublicKeyOrSubkey> = pkeys.iter().collect();
|
||||
|
||||
let mut rng = thread_rng();
|
||||
|
||||
@@ -138,9 +219,9 @@ pub fn pk_encrypt(
|
||||
lit_msg
|
||||
.sign(skey, || "".into(), Default::default())
|
||||
.and_then(|msg| msg.compress(CompressionAlgorithm::ZLIB))
|
||||
.and_then(|msg| msg.encrypt_to_keys(&mut rng, Default::default(), &pkeys))
|
||||
.and_then(|msg| msg.encrypt_to_keys(&mut rng, Default::default(), &pkeys_refs))
|
||||
} else {
|
||||
lit_msg.encrypt_to_keys(&mut rng, Default::default(), &pkeys)
|
||||
lit_msg.encrypt_to_keys(&mut rng, Default::default(), &pkeys_refs)
|
||||
};
|
||||
|
||||
let msg = encrypted_msg?;
|
||||
|
||||
Reference in New Issue
Block a user