diff options
author | Nora Widdecke <nora@sequoia-pgp.org> | 2021-01-20 22:07:31 +0100 |
---|---|---|
committer | Nora Widdecke <nora@sequoia-pgp.org> | 2021-04-13 12:38:16 +0200 |
commit | f8bbf6f27f259662bbb9bfca6b399445d44019c0 (patch) | |
tree | 081f6ddf6824ea7791d1ce3ae33568f8dc7e84e5 /openpgp | |
parent | eebd9c6661a82f01646f993d54d696982c68cf6c (diff) |
bench: Add decrypt benchmark.
- Prevent the compiler from optimizing for the inputs by using
bench_with_input.
Diffstat (limited to 'openpgp')
-rw-r--r-- | openpgp/Cargo.toml | 4 | ||||
-rw-r--r-- | openpgp/benches/common/decrypt.rs | 168 | ||||
-rw-r--r-- | openpgp/benches/common/mod.rs | 1 | ||||
-rw-r--r-- | openpgp/benches/decrypt_message.rs | 65 |
4 files changed, 238 insertions, 0 deletions
diff --git a/openpgp/Cargo.toml b/openpgp/Cargo.toml index d36022f8..1710ba31 100644 --- a/openpgp/Cargo.toml +++ b/openpgp/Cargo.toml @@ -96,3 +96,7 @@ harness = false [[bench]] name = "encrypt_message" harness = false + +[[bench]] +name = "decrypt_message" +harness = false diff --git a/openpgp/benches/common/decrypt.rs b/openpgp/benches/common/decrypt.rs new file mode 100644 index 00000000..48f157c5 --- /dev/null +++ b/openpgp/benches/common/decrypt.rs @@ -0,0 +1,168 @@ +use sequoia_openpgp::cert::Cert; +use sequoia_openpgp::crypto::{Password, SessionKey}; +use sequoia_openpgp::packet::prelude::*; +use sequoia_openpgp::packet::{PKESK, SKESK}; +use sequoia_openpgp::parse::stream::{ + DecryptionHelper, DecryptorBuilder, MessageStructure, VerificationHelper, +}; +use sequoia_openpgp::parse::Parse; +use sequoia_openpgp::policy::StandardPolicy; +use sequoia_openpgp::types::SymmetricAlgorithm; +use sequoia_openpgp::{Fingerprint, KeyHandle, Result}; + +use std::io::Write; + +// Borrowed from the examples at +// sequoia_openpgp::parse::stream::DecryptionHelper +// sequoia_openpgp::parse::stream::Decryptor +struct PasswordHelper { + password: Password, +} + +impl VerificationHelper for PasswordHelper { + fn get_certs(&mut self, _ids: &[KeyHandle]) -> Result<Vec<Cert>> { + Ok(Vec::new()) + } + fn check(&mut self, _structure: MessageStructure) -> Result<()> { + Ok(()) + } +} + +impl DecryptionHelper for PasswordHelper { + fn decrypt<D>( + &mut self, + _pkesks: &[PKESK], + skesks: &[SKESK], + _sym_algo: Option<SymmetricAlgorithm>, + mut decrypt: D, + ) -> Result<Option<Fingerprint>> + where + D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool, + { + // Finally, try to decrypt using the SKESKs. + for skesk in skesks { + if skesk + .decrypt(&self.password) + .map(|(algo, sk)| decrypt(algo, &sk)) + .unwrap_or(false) + { + return Ok(None); + } + } + + Err(anyhow::anyhow!("Wrong password!")) + } +} + +// This is marked as dead_code. Seems that using a function only from within +// a benchmark loop hides it from the compiler. +#[allow(dead_code)] +// Decrypts the given message using the given password. +pub fn decrypt_with_password( + sink: &mut dyn Write, + ciphertext: &[u8], + password: &str, +) -> sequoia_openpgp::Result<()> { + let password = password.into(); + // Make a helper that that feeds the password to the decryptor. + let helper = PasswordHelper { password }; + + // Now, create a decryptor with a helper using the given Certs. + let p = &StandardPolicy::new(); + let mut decryptor = DecryptorBuilder::from_bytes(ciphertext)? + .with_policy(p, None, helper)?; + + // Decrypt the data. + std::io::copy(&mut decryptor, sink)?; + + Ok(()) +} + +// Borrowed from the examples at +// sequoia_openpgp::parse::stream::DecryptionHelper +// sequoia_openpgp::parse::stream::Decryptor +struct CertHelper<'a> { + cert: &'a Cert, +} + +impl VerificationHelper for CertHelper<'_> { + fn get_certs(&mut self, _ids: &[KeyHandle]) -> Result<Vec<Cert>> { + Ok(Vec::new()) + } + fn check(&mut self, _structure: MessageStructure) -> Result<()> { + Ok(()) + } +} + +impl DecryptionHelper for CertHelper<'_> { + fn decrypt<D>( + &mut self, + pkesks: &[PKESK], + _skesks: &[SKESK], + sym_algo: Option<SymmetricAlgorithm>, + mut decrypt: D, + ) -> Result<Option<Fingerprint>> + where + D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool, + { + let p = &StandardPolicy::new(); + + // check that pkesk has right recipient + // if yes, use decrypt function + let keys: Vec<Key<key::SecretParts, key::UnspecifiedRole>> = self + .cert + .keys() + .with_policy(p, None) + .for_transport_encryption() + .for_storage_encryption() + .secret() + .map(|amalgamation| amalgamation.key().clone().into()) + .collect(); + + let successful_key = keys + .iter() + .cloned() + .filter_map(|key| { + pkesks + .into_iter() + .find(|pkesk| pkesk.recipient() == &key.keyid()) + .map(|pkesk| (pkesk, key)) + }) + .find(|(pkesk, key)| { + let mut keypair = key.clone().into_keypair().unwrap(); + pkesk + .decrypt(&mut keypair, sym_algo) + .map(|(algo, sk)| decrypt(algo, &sk)) + .unwrap_or(false) + }) + .map(|(_, key)| key.fingerprint()); + + match successful_key { + Some(key) => Ok(Some(key)), + None => Err(anyhow::anyhow!("Wrong cert!")), + } + } +} + +// This is marked as dead_code. Seems that using a function only from within +// a benchmark loop hides it from the compiler. +#[allow(dead_code)] +// Decrypts the given message using the given password. +pub fn decrypt_with_cert( + sink: &mut dyn Write, + ciphertext: &[u8], + cert: &Cert, +) -> sequoia_openpgp::Result<()> { + // Make a helper that that feeds the password to the decryptor. + let helper = CertHelper { cert }; + + // Now, create a decryptor with a helper using the given Certs. + let p = &StandardPolicy::new(); + let mut decryptor = DecryptorBuilder::from_bytes(ciphertext)? + .with_policy(p, None, helper)?; + + // Decrypt the data. + std::io::copy(&mut decryptor, sink)?; + + Ok(()) +} diff --git a/openpgp/benches/common/mod.rs b/openpgp/benches/common/mod.rs index b9c16b39..760539e6 100644 --- a/openpgp/benches/common/mod.rs +++ b/openpgp/benches/common/mod.rs @@ -1 +1,2 @@ +pub(super) mod decrypt; pub(super) mod encrypt; diff --git a/openpgp/benches/decrypt_message.rs b/openpgp/benches/decrypt_message.rs new file mode 100644 index 00000000..b59fb550 --- /dev/null +++ b/openpgp/benches/decrypt_message.rs @@ -0,0 +1,65 @@ +use criterion::{ + criterion_group, criterion_main, BenchmarkId, Criterion, Throughput, +}; + +use sequoia_openpgp::cert::Cert; +use sequoia_openpgp::parse::Parse; + +mod common; +use common::decrypt; +use common::encrypt; + +static PASSWORD: &'static str = "password"; + +lazy_static::lazy_static! { + static ref TESTY: Cert = + Cert::from_bytes(&include_bytes!("../tests/data/keys/testy-private.pgp")[..]) + .unwrap(); + static ref ZEROS_1_MB: Vec<u8> = vec![0; 1 * 1024 * 1024]; + static ref ZEROS_10_MB: Vec<u8> = vec![0; 10 * 1024 * 1024]; +} + +fn decrypt_cert(bytes: &[u8], cert: &Cert) { + let mut sink = Vec::new(); + decrypt::decrypt_with_cert(&mut sink, &bytes, cert).unwrap(); +} + +fn decrypt_password(bytes: &[u8]) { + let mut sink = Vec::new(); + decrypt::decrypt_with_password(&mut sink, &bytes, PASSWORD).unwrap(); +} + +fn bench_decrypt(c: &mut Criterion) { + let mut group = c.benchmark_group("decrypt message"); + + // Encrypt a very short, medium and very long message, + // and then benchmark decryption. + let messages = &[b"Hello world.", &ZEROS_1_MB[..], &ZEROS_10_MB[..]]; + + // Encrypt and decrypt with password + messages.iter().for_each(|m| { + let encrypted = encrypt::encrypt_with_password(m, PASSWORD).unwrap(); + group.throughput(Throughput::Bytes(encrypted.len() as u64)); + group.bench_with_input( + BenchmarkId::new("password", encrypted.len()), + &encrypted, + |b, e| b.iter(|| decrypt_password(&e)), + ); + }); + + // Encrypt and decrypt with a cert + messages.iter().for_each(|m| { + let encrypted = encrypt::encrypt_to_cert(m, &TESTY).unwrap(); + group.throughput(Throughput::Bytes(encrypted.len() as u64)); + group.bench_with_input( + BenchmarkId::new("cert", m.len()), + &encrypted, + |b, e| b.iter(|| decrypt_cert(&e, &TESTY)), + ); + }); + + group.finish(); +} + +criterion_group!(benches, bench_decrypt); +criterion_main!(benches); |