summaryrefslogtreecommitdiffstats
path: root/openpgp/benches
diff options
context:
space:
mode:
authorNora Widdecke <nora@sequoia-pgp.org>2021-01-20 22:07:31 +0100
committerNora Widdecke <nora@sequoia-pgp.org>2021-04-13 12:38:16 +0200
commitf8bbf6f27f259662bbb9bfca6b399445d44019c0 (patch)
tree081f6ddf6824ea7791d1ce3ae33568f8dc7e84e5 /openpgp/benches
parenteebd9c6661a82f01646f993d54d696982c68cf6c (diff)
bench: Add decrypt benchmark.
- Prevent the compiler from optimizing for the inputs by using bench_with_input.
Diffstat (limited to 'openpgp/benches')
-rw-r--r--openpgp/benches/common/decrypt.rs168
-rw-r--r--openpgp/benches/common/mod.rs1
-rw-r--r--openpgp/benches/decrypt_message.rs65
3 files changed, 234 insertions, 0 deletions
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);