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:
Alexander Krotov
2019-11-17 13:35:12 +03:00
committed by holger krekel
parent f505ff03e4
commit 4b8252e001
3 changed files with 98 additions and 16 deletions

9
Cargo.lock generated
View File

@@ -506,7 +506,7 @@ dependencies = [
"num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "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_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)", "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)", "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]] [[package]]
name = "pgp" name = "pgp"
version = "0.2.5" version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/rpgp/rpgp#6977b14479ffa079d5857861ca89e69e93c8bd55"
dependencies = [ dependencies = [
"aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "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)", "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)", "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)", "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)", "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)", "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 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 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 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 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 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" "checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427"

View File

@@ -10,7 +10,7 @@ deltachat_derive = { path = "./deltachat_derive" }
mmime = { version = "0.1.2", path = "./mmime" } mmime = { version = "0.1.2", path = "./mmime" }
libc = "0.2.51" 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" hex = "0.3.2"
sha2 = "0.8.0" sha2 = "0.8.0"
rand = "0.6.5" rand = "0.6.5"

View File

@@ -2,6 +2,7 @@
use std::collections::{BTreeMap, HashSet}; use std::collections::{BTreeMap, HashSet};
use std::convert::TryInto; use std::convert::TryInto;
use std::io;
use std::io::Cursor; use std::io::Cursor;
use pgp::armor::BlockType; use pgp::armor::BlockType;
@@ -10,8 +11,10 @@ use pgp::composed::{
SignedPublicSubKey, SignedSecretKey, SubkeyParamsBuilder, SignedPublicSubKey, SignedSecretKey, SubkeyParamsBuilder,
}; };
use pgp::crypto::{HashAlgorithm, SymmetricKeyAlgorithm}; use pgp::crypto::{HashAlgorithm, SymmetricKeyAlgorithm};
use pgp::types::{CompressionAlgorithm, KeyTrait, SecretKeyTrait, StringToKey}; use pgp::types::{
use rand::thread_rng; CompressionAlgorithm, KeyTrait, Mpi, PublicKeyTrait, SecretKeyTrait, StringToKey,
};
use rand::{thread_rng, CryptoRng, Rng};
use crate::error::Error; use crate::error::Error;
use crate::key::*; use crate::key::*;
@@ -20,6 +23,68 @@ use crate::keyring::*;
pub const HEADER_AUTOCRYPT: &str = "autocrypt-prefer-encrypt"; pub const HEADER_AUTOCRYPT: &str = "autocrypt-prefer-encrypt";
pub const HEADER_SETUPCODE: &str = "passphrase-begin"; 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. /// 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).
@@ -99,13 +164,28 @@ pub fn create_keypair(addr: impl AsRef<str>) -> Option<(Key, Key)> {
Some((Key::Public(public_key), Key::Secret(private_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. /// First, tries to use subkeys. If none of the subkeys are suitable
fn select_pk_for_encryption(key: &SignedPublicKey) -> Option<&SignedPublicSubKey> { /// for encryption, tries to use primary key. Returns `None` if the public
key.public_subkeys.iter().find(|_k| /// key cannot be used for encryption.
// TODO: check if it is an encryption subkey ///
true) /// 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` /// Encrypts `plain` text using `public_keys_for_encryption`
@@ -116,7 +196,7 @@ pub fn pk_encrypt(
private_key_for_signing: Option<&Key>, private_key_for_signing: Option<&Key>,
) -> Result<String, Error> { ) -> Result<String, Error> {
let lit_msg = Message::new_literal_bytes("", plain); 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() .keys()
.iter() .iter()
.filter_map(|key| { .filter_map(|key| {
@@ -126,6 +206,7 @@ pub fn pk_encrypt(
.and_then(select_pk_for_encryption) .and_then(select_pk_for_encryption)
}) })
.collect(); .collect();
let pkeys_refs: Vec<&SignedPublicKeyOrSubkey> = pkeys.iter().collect();
let mut rng = thread_rng(); let mut rng = thread_rng();
@@ -138,9 +219,9 @@ pub fn pk_encrypt(
lit_msg lit_msg
.sign(skey, || "".into(), Default::default()) .sign(skey, || "".into(), Default::default())
.and_then(|msg| msg.compress(CompressionAlgorithm::ZLIB)) .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 { } 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?; let msg = encrypted_msg?;