From d9c64350e210b3d9fde378029285bbdb6848a475 Mon Sep 17 00:00:00 2001 From: Nora Widdecke Date: Tue, 9 Feb 2021 14:58:46 +0100 Subject: bench: Add encrypt+sign and decrypt+verify. --- openpgp/benches/common/decrypt.rs | 114 +++++++++++++++++++++++------- openpgp/benches/common/encrypt.rs | 44 +++++++++++- openpgp/benches/decrypt_message.rs | 5 +- openpgp/benches/decrypt_verify_message.rs | 51 +++++++++++++ openpgp/benches/encrypt_sign_message.rs | 47 ++++++++++++ 5 files changed, 233 insertions(+), 28 deletions(-) create mode 100644 openpgp/benches/decrypt_verify_message.rs create mode 100644 openpgp/benches/encrypt_sign_message.rs (limited to 'openpgp/benches') diff --git a/openpgp/benches/common/decrypt.rs b/openpgp/benches/common/decrypt.rs index 48f157c5..fa507aa7 100644 --- a/openpgp/benches/common/decrypt.rs +++ b/openpgp/benches/common/decrypt.rs @@ -1,20 +1,22 @@ -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 as openpgp; +use openpgp::cert::Cert; +use openpgp::crypto::{Password, SessionKey}; +use openpgp::packet::prelude::*; +use openpgp::packet::{PKESK, SKESK}; +use openpgp::parse::stream::{ + DecryptionHelper, DecryptorBuilder, MessageLayer, MessageStructure, + VerificationHelper, VerificationError }; -use sequoia_openpgp::parse::Parse; -use sequoia_openpgp::policy::StandardPolicy; -use sequoia_openpgp::types::SymmetricAlgorithm; -use sequoia_openpgp::{Fingerprint, KeyHandle, Result}; +use openpgp::parse::Parse; +use openpgp::policy::StandardPolicy; +use openpgp::types::SymmetricAlgorithm; +use openpgp::{Fingerprint, KeyHandle, Result}; use std::io::Write; // Borrowed from the examples at -// sequoia_openpgp::parse::stream::DecryptionHelper -// sequoia_openpgp::parse::stream::Decryptor +// openpgp::parse::stream::DecryptionHelper +// openpgp::parse::stream::Decryptor struct PasswordHelper { password: Password, } @@ -62,7 +64,7 @@ pub fn decrypt_with_password( sink: &mut dyn Write, ciphertext: &[u8], password: &str, -) -> sequoia_openpgp::Result<()> { +) -> openpgp::Result<()> { let password = password.into(); // Make a helper that that feeds the password to the decryptor. let helper = PasswordHelper { password }; @@ -79,17 +81,49 @@ pub fn decrypt_with_password( } // Borrowed from the examples at -// sequoia_openpgp::parse::stream::DecryptionHelper -// sequoia_openpgp::parse::stream::Decryptor +// openpgp::parse::stream::DecryptionHelper +// openpgp::parse::stream::Decryptor struct CertHelper<'a> { - cert: &'a Cert, + sender: Option<&'a Cert>, + recipient: &'a Cert, } impl VerificationHelper for CertHelper<'_> { + // get candidates for having created the signature fn get_certs(&mut self, _ids: &[KeyHandle]) -> Result> { - Ok(Vec::new()) + let mut certs = Vec::new(); + // maybe check that the cert matches (one of the) ids + if let Some(sender) = self.sender { + certs.push(sender.clone()); + } + Ok(certs) } - fn check(&mut self, _structure: MessageStructure) -> Result<()> { + // does the signature match the policy + // e.g. am I the intended recipient + fn check(&mut self, structure: MessageStructure) -> Result<()> { + for (i, layer) in structure.into_iter().enumerate() { + match layer { + MessageLayer::Encryption { .. } if i == 0 => (), + MessageLayer::Compression { .. } if i == 1 => (), + MessageLayer::SignatureGroup { ref results } + if i == 1 || i == 2 => + { + if !results.iter().any(|r| r.is_ok()) { + for result in results { + let error = result.as_ref().err().unwrap(); + println!("{:?}", error); + } + return Err(anyhow::anyhow!("No valid signature")); + } + } + _ => { + return Err(anyhow::anyhow!( + "Unexpected message structure {:?}", + layer + )) + } + } + } Ok(()) } } @@ -107,10 +141,8 @@ impl DecryptionHelper for CertHelper<'_> { { let p = &StandardPolicy::new(); - // check that pkesk has right recipient - // if yes, use decrypt function - let keys: Vec> = self - .cert + let cand_secret_keys: Vec> = self + .recipient .keys() .with_policy(p, None) .for_transport_encryption() @@ -119,7 +151,9 @@ impl DecryptionHelper for CertHelper<'_> { .map(|amalgamation| amalgamation.key().clone().into()) .collect(); - let successful_key = keys + // check that pkesk has right recipient + // if yes, use decrypt function + let successful_key = cand_secret_keys .iter() .cloned() .filter_map(|key| { @@ -152,9 +186,39 @@ pub fn decrypt_with_cert( sink: &mut dyn Write, ciphertext: &[u8], cert: &Cert, -) -> sequoia_openpgp::Result<()> { +) -> openpgp::Result<()> { + // Make a helper that that feeds the password to the decryptor. + let helper = CertHelper { + sender: None, + recipient: 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(()) +} + +// 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_and_verify( + sink: &mut dyn Write, + ciphertext: &[u8], + sender: &Cert, + recipient: &Cert, +) -> openpgp::Result<()> { // Make a helper that that feeds the password to the decryptor. - let helper = CertHelper { cert }; + let helper = CertHelper { + sender: Some(sender), + recipient, + }; // Now, create a decryptor with a helper using the given Certs. let p = &StandardPolicy::new(); diff --git a/openpgp/benches/common/encrypt.rs b/openpgp/benches/common/encrypt.rs index 0b5c94d7..edd898b5 100644 --- a/openpgp/benches/common/encrypt.rs +++ b/openpgp/benches/common/encrypt.rs @@ -1,7 +1,9 @@ use sequoia_openpgp as openpgp; use openpgp::cert::Cert; use openpgp::policy::StandardPolicy; -use openpgp::serialize::stream::{Encryptor, LiteralWriter, Message}; +use openpgp::serialize::stream::{ + padding::Padder, Armorer, Encryptor, LiteralWriter, Message, Signer, +}; use std::io::Write; @@ -27,6 +29,46 @@ pub fn encrypt_to_cert( Ok(sink) } +// naively encrypt and sign, without caring for revocation or expiration +pub fn encrypt_to_cert_and_sign( + bytes: &[u8], + sender: &Cert, + recipient: &Cert, +) -> openpgp::Result> { + let mut sink = vec![]; + + let p = &StandardPolicy::new(); + let signing_keypair = sender + .keys() + .with_policy(p, None) + .secret() + .for_signing() + .nth(0) + .unwrap() + .key() + .clone() + .into_keypair()?; + + let recipients = recipient + .keys() + .with_policy(p, None) + .supported() + .for_transport_encryption() + .for_storage_encryption(); + + let message = Message::new(&mut sink); + let message = Armorer::new(message).build()?; + let message = Encryptor::for_recipients(message, recipients).build()?; + let message = Padder::new(message).build()?; + let message = Signer::new(message, signing_keypair) + //.add_intended_recipient(&recipient) + .build()?; + let mut w = LiteralWriter::new(message).build()?; + w.write_all(bytes)?; + w.finalize()?; + Ok(sink) +} + pub fn encrypt_with_password( bytes: &[u8], password: &str, diff --git a/openpgp/benches/decrypt_message.rs b/openpgp/benches/decrypt_message.rs index b59fb550..23987fc3 100644 --- a/openpgp/benches/decrypt_message.rs +++ b/openpgp/benches/decrypt_message.rs @@ -2,8 +2,9 @@ use criterion::{ criterion_group, criterion_main, BenchmarkId, Criterion, Throughput, }; -use sequoia_openpgp::cert::Cert; -use sequoia_openpgp::parse::Parse; +use sequoia_openpgp as openpgp; +use openpgp::cert::Cert; +use openpgp::parse::Parse; mod common; use common::decrypt; diff --git a/openpgp/benches/decrypt_verify_message.rs b/openpgp/benches/decrypt_verify_message.rs new file mode 100644 index 00000000..33f15c3e --- /dev/null +++ b/openpgp/benches/decrypt_verify_message.rs @@ -0,0 +1,51 @@ +use criterion::{ + criterion_group, criterion_main, BenchmarkId, Criterion, Throughput, +}; + +use sequoia_openpgp as openpgp; +use openpgp::cert::Cert; +use openpgp::parse::Parse; + +mod common; +use common::decrypt; +use common::encrypt; + +lazy_static::lazy_static! { + static ref SENDER: Cert = + Cert::from_bytes(&include_bytes!("../tests/data/keys/sender.pgp")[..]) + .unwrap(); + static ref RECIPIENT: Cert = + Cert::from_bytes(&include_bytes!("../tests/data/keys/recipient.pgp")[..]) + .unwrap(); + static ref ZEROS_1_MB: Vec = vec![0; 1 * 1024 * 1024]; + static ref ZEROS_10_MB: Vec = vec![0; 10 * 1024 * 1024]; +} + +fn decrypt_and_verify(bytes: &[u8], sender: &Cert, recipient: &Cert) { + let mut sink = Vec::new(); + decrypt::decrypt_and_verify(&mut sink, &bytes, sender, recipient).unwrap(); +} + +fn bench_decrypt_verify(c: &mut Criterion) { + let mut group = c.benchmark_group("decrypt and verify 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[..]]; + + messages.iter().for_each(|m| { + let encrypted = + encrypt::encrypt_to_cert_and_sign(m, &SENDER, &RECIPIENT).unwrap(); + group.throughput(Throughput::Bytes(encrypted.len() as u64)); + group.bench_with_input( + BenchmarkId::new("decrypt and verify", m.len()), + &encrypted, + |b, e| b.iter(|| decrypt_and_verify(&e, &SENDER, &RECIPIENT)), + ); + }); + + group.finish(); +} + +criterion_group!(benches, bench_decrypt_verify); +criterion_main!(benches); diff --git a/openpgp/benches/encrypt_sign_message.rs b/openpgp/benches/encrypt_sign_message.rs new file mode 100644 index 00000000..03916f71 --- /dev/null +++ b/openpgp/benches/encrypt_sign_message.rs @@ -0,0 +1,47 @@ +use criterion::{ + criterion_group, criterion_main, BenchmarkId, Criterion, Throughput, +}; + +use sequoia_openpgp as openpgp; +use openpgp::cert::Cert; +use openpgp::parse::Parse; + +mod common; +use common::encrypt; + +lazy_static::lazy_static! { + static ref ZEROS_1_MB: Vec = vec![0; 1 * 1024 * 1024]; + static ref ZEROS_10_MB: Vec = vec![0; 10 * 1024 * 1024]; +} + +pub fn encrypt_to_donald_sign_by_ivanka(bytes: &[u8]) { + let sender = Cert::from_bytes( + &include_bytes!("../tests/data/keys/ivanka-private.gpg")[..], + ) + .unwrap(); + let recipient = Cert::from_bytes( + &include_bytes!("../tests/data/keys/the-donald-private.gpg")[..], + ) + .unwrap(); + encrypt::encrypt_to_cert_and_sign(bytes, &sender, &recipient).unwrap(); +} + +fn bench_encrypt_sign(c: &mut Criterion) { + let mut group = c.benchmark_group("encrypt and sign message"); + + // Encrypt a very short, medium and very long message. + let messages = &[b"Hello world.", &ZEROS_1_MB[..], &ZEROS_10_MB[..]]; + + for message in messages { + group.throughput(Throughput::Bytes(message.len() as u64)); + group.bench_with_input( + BenchmarkId::new("encrypt and sign", message.len()), + &message, + |b, m| b.iter(|| encrypt_to_donald_sign_by_ivanka(&m)), + ); + } + group.finish(); +} + +criterion_group!(benches, bench_encrypt_sign); +criterion_main!(benches); -- cgit v1.2.3