diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2023-02-10 13:20:58 +0100 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2023-10-10 11:16:07 +0200 |
commit | f03b2b721fa8b2fc9c0e7cd41d9f7686303dd1a7 (patch) | |
tree | 97fb77363d0abcfc2056d0c8cc89bb6e71f944f4 | |
parent | a4c2b9f50a9f6e6539154eb3bcef8a0a062b331f (diff) |
openpgp: Add fuzz targets.
-rw-r--r-- | openpgp/fuzz/.gitignore | 4 | ||||
-rw-r--r-- | openpgp/fuzz/Cargo.toml | 47 | ||||
-rw-r--r-- | openpgp/fuzz/fuzz_targets/cert_from_bytes.rs | 16 | ||||
-rw-r--r-- | openpgp/fuzz/fuzz_targets/csf_verify_from_bytes.rs | 77 | ||||
-rw-r--r-- | openpgp/fuzz/fuzz_targets/decrypt_from_bytes.rs | 90 | ||||
-rw-r--r-- | openpgp/fuzz/fuzz_targets/inline_verify_from_bytes.rs | 72 |
6 files changed, 306 insertions, 0 deletions
diff --git a/openpgp/fuzz/.gitignore b/openpgp/fuzz/.gitignore new file mode 100644 index 00000000..1a45eee7 --- /dev/null +++ b/openpgp/fuzz/.gitignore @@ -0,0 +1,4 @@ +target +corpus +artifacts +coverage diff --git a/openpgp/fuzz/Cargo.toml b/openpgp/fuzz/Cargo.toml new file mode 100644 index 00000000..55609cf1 --- /dev/null +++ b/openpgp/fuzz/Cargo.toml @@ -0,0 +1,47 @@ +[package] +name = "sequoia-openpgp-fuzz" +version = "0.0.0" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +anyhow = "1" +lazy_static = "1" +libfuzzer-sys = "0.4" + +[dependencies.sequoia-openpgp] +path = ".." +default-features = false +features = ["crypto-fuzzing", "allow-experimental-crypto", "allow-variable-time-crypto"] + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[profile.release] +debug = 1 + +[[bin]] +name = "cert_from_bytes" +path = "fuzz_targets/cert_from_bytes.rs" +test = false +doc = false + +[[bin]] +name = "inline_verify_from_bytes" +path = "fuzz_targets/inline_verify_from_bytes.rs" + +[[bin]] +name = "csf_verify_from_bytes" +path = "fuzz_targets/csf_verify_from_bytes.rs" +test = false +doc = false + +[[bin]] +name = "decrypt_from_bytes" +path = "fuzz_targets/decrypt_from_bytes.rs" +test = false +doc = false diff --git a/openpgp/fuzz/fuzz_targets/cert_from_bytes.rs b/openpgp/fuzz/fuzz_targets/cert_from_bytes.rs new file mode 100644 index 00000000..905095d2 --- /dev/null +++ b/openpgp/fuzz/fuzz_targets/cert_from_bytes.rs @@ -0,0 +1,16 @@ +#![no_main] + +use libfuzzer_sys::{Corpus, fuzz_target}; + +use sequoia_openpgp as openpgp; +use openpgp::{ + Cert, + parse::Parse, +}; + +fuzz_target!(|data: &[u8]| -> Corpus { + match Cert::from_bytes(data) { + Ok(_) => Corpus::Keep, + Err(_) => Corpus::Reject, + } +}); diff --git a/openpgp/fuzz/fuzz_targets/csf_verify_from_bytes.rs b/openpgp/fuzz/fuzz_targets/csf_verify_from_bytes.rs new file mode 100644 index 00000000..a2025c44 --- /dev/null +++ b/openpgp/fuzz/fuzz_targets/csf_verify_from_bytes.rs @@ -0,0 +1,77 @@ +#![no_main] + +use libfuzzer_sys::{Corpus, fuzz_target}; + +use sequoia_openpgp as openpgp; +use openpgp::{ + Cert, + KeyHandle, + parse::{Parse, stream::*}, + policy::StandardPolicy, +}; + +const P: &StandardPolicy = &StandardPolicy::new(); + +lazy_static::lazy_static! { + /// This is an example for using doc comment attributes + static ref CERT: Cert = Cert::from_bytes(b" +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEWlNvABYJKwYBBAHaRw8BAQdA+EC2pvebpEbzPA9YplVgVXzkIG5eK+7wEAez +lcBgLJq0MVRlc3R5IE1jVGVzdGZhY2UgKG15IG5ldyBrZXkpIDx0ZXN0eUBleGFt +cGxlLm9yZz6IkAQTFggAOBYhBDnRAKtn1b2MBAECBfs3UfFYfa7xBQJaU28AAhsD +BQsJCAcCBhUICQoLAgQWAgMBAh4BAheAAAoJEPs3UfFYfa7xJHQBAO4/GABMWUcJ +5D/DZ9b+6YiFnysSjCT/gILJgxMgl7uoAPwJherI1pAAh49RnPHBR1IkWDtwzX65 +CJG8sDyO2FhzDrg4BFpTbwASCisGAQQBl1UBBQEBB0B+A0GRHuBgdDX50T1nePjb +mKQ5PeqXJbWEtVrUtVJaPwMBCAeIeAQYFggAIBYhBDnRAKtn1b2MBAECBfs3UfFY +fa7xBQJaU28AAhsMAAoJEPs3UfFYfa7xzjIBANX2/FgDX3WkmvwpEHg/sn40zACM +W2hrBY5x0sZ8H7JlAP47mCfCuRVBqyaePuzKbxLJeLe2BpDdc0n2izMVj8t9Cg== +=bbbT +-----END PGP PUBLIC KEY BLOCK----- +").unwrap(); +} + + +fuzz_target!(|data: &[u8]| -> Corpus { + const NEEDLE: &[u8] = b"-----BEGIN PGP SIGNED MESSAGE-----"; + if data.len() < NEEDLE.len() || &data[..NEEDLE.len()] != NEEDLE { + return Corpus::Reject; + } + + struct Helper {} + impl VerificationHelper for Helper { + fn get_certs(&mut self, _ids: &[KeyHandle]) + -> openpgp::Result<Vec<Cert>> { + Ok(vec![CERT.clone()]) + } + + fn check(&mut self, structure: MessageStructure) + -> openpgp::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 ! results.iter().any(|r| r.is_ok()) { + return Err(anyhow::anyhow!( + "No valid signature")); + } + } + _ => return Err(anyhow::anyhow!( + "Unexpected message structure")), + } + } + Ok(()) + } + } + + let h = Helper {}; + if let Ok(mut v) = VerifierBuilder::from_bytes(data) + .and_then(|b| b.with_policy(P, None, h)) + { + let _ = std::io::copy(&mut v, &mut std::io::sink()); + Corpus::Keep + } else { + Corpus::Keep + } +}); diff --git a/openpgp/fuzz/fuzz_targets/decrypt_from_bytes.rs b/openpgp/fuzz/fuzz_targets/decrypt_from_bytes.rs new file mode 100644 index 00000000..517a936c --- /dev/null +++ b/openpgp/fuzz/fuzz_targets/decrypt_from_bytes.rs @@ -0,0 +1,90 @@ +#![no_main] + +use libfuzzer_sys::{Corpus, fuzz_target}; + +use sequoia_openpgp as openpgp; +use openpgp::{ + Cert, + Fingerprint, + KeyHandle, + Result, + crypto::SessionKey, + packet::prelude::*, + parse::{Parse, stream::*}, + policy::StandardPolicy, + types::*, +}; + +const P: &StandardPolicy = &StandardPolicy::new(); +const CERT_BYTES: &[u8; 665] = b" +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEWlNvABYJKwYBBAHaRw8BAQdA+EC2pvebpEbzPA9YplVgVXzkIG5eK+7wEAez +lcBgLJq0MVRlc3R5IE1jVGVzdGZhY2UgKG15IG5ldyBrZXkpIDx0ZXN0eUBleGFt +cGxlLm9yZz6IkAQTFggAOBYhBDnRAKtn1b2MBAECBfs3UfFYfa7xBQJaU28AAhsD +BQsJCAcCBhUICQoLAgQWAgMBAh4BAheAAAoJEPs3UfFYfa7xJHQBAO4/GABMWUcJ +5D/DZ9b+6YiFnysSjCT/gILJgxMgl7uoAPwJherI1pAAh49RnPHBR1IkWDtwzX65 +CJG8sDyO2FhzDrg4BFpTbwASCisGAQQBl1UBBQEBB0B+A0GRHuBgdDX50T1nePjb +mKQ5PeqXJbWEtVrUtVJaPwMBCAeIeAQYFggAIBYhBDnRAKtn1b2MBAECBfs3UfFY +fa7xBQJaU28AAhsMAAoJEPs3UfFYfa7xzjIBANX2/FgDX3WkmvwpEHg/sn40zACM +W2hrBY5x0sZ8H7JlAP47mCfCuRVBqyaePuzKbxLJeLe2BpDdc0n2izMVj8t9Cg== +=bbbT +-----END PGP PUBLIC KEY BLOCK----- +"; + +lazy_static::lazy_static! { + /// This is an example for using doc comment attributes + static ref CERT: Cert = Cert::from_bytes(&CERT_BYTES[..]).unwrap(); + static ref FP: Fingerprint = CERT.fingerprint(); + static ref SK: SessionKey = vec![].into(); +} + + +fuzz_target!(|data: &[u8]| -> Corpus { + struct Helper {} + impl VerificationHelper for Helper { + fn get_certs(&mut self, _ids: &[KeyHandle]) + -> openpgp::Result<Vec<Cert>> { + Ok(vec![CERT.clone()]) + } + + fn check(&mut self, structure: MessageStructure) + -> openpgp::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 ! results.iter().any(|r| r.is_ok()) { + return Err(anyhow::anyhow!( + "No valid signature")); + } + } + _ => return Err(anyhow::anyhow!( + "Unexpected message structure")), + } + } + Ok(()) + } + } + impl DecryptionHelper for Helper { + fn decrypt<D>(&mut self, _: &[PKESK], _: &[SKESK], + _sym_algo: Option<SymmetricAlgorithm>, + mut decrypt: D) -> Result<Option<openpgp::Fingerprint>> + where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool + { + decrypt(SymmetricAlgorithm::AES128, &SK); + Ok(Some(FP.clone())) + } + } + + let h = Helper {}; + if let Ok(mut v) = DecryptorBuilder::from_bytes(data) + .and_then(|b| b.with_policy(P, None, h)) + { + let _ = std::io::copy(&mut v, &mut std::io::sink()); + Corpus::Keep + } else { + Corpus::Keep + } +}); diff --git a/openpgp/fuzz/fuzz_targets/inline_verify_from_bytes.rs b/openpgp/fuzz/fuzz_targets/inline_verify_from_bytes.rs new file mode 100644 index 00000000..5ade8f8d --- /dev/null +++ b/openpgp/fuzz/fuzz_targets/inline_verify_from_bytes.rs @@ -0,0 +1,72 @@ +#![no_main] + +use libfuzzer_sys::{Corpus, fuzz_target}; + +use sequoia_openpgp as openpgp; +use openpgp::{ + Cert, + KeyHandle, + parse::{Parse, stream::*}, + policy::StandardPolicy, +}; + +const P: &StandardPolicy = &StandardPolicy::new(); + +lazy_static::lazy_static! { + /// This is an example for using doc comment attributes + static ref CERT: Cert = Cert::from_bytes(b" +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEWlNvABYJKwYBBAHaRw8BAQdA+EC2pvebpEbzPA9YplVgVXzkIG5eK+7wEAez +lcBgLJq0MVRlc3R5IE1jVGVzdGZhY2UgKG15IG5ldyBrZXkpIDx0ZXN0eUBleGFt +cGxlLm9yZz6IkAQTFggAOBYhBDnRAKtn1b2MBAECBfs3UfFYfa7xBQJaU28AAhsD +BQsJCAcCBhUICQoLAgQWAgMBAh4BAheAAAoJEPs3UfFYfa7xJHQBAO4/GABMWUcJ +5D/DZ9b+6YiFnysSjCT/gILJgxMgl7uoAPwJherI1pAAh49RnPHBR1IkWDtwzX65 +CJG8sDyO2FhzDrg4BFpTbwASCisGAQQBl1UBBQEBB0B+A0GRHuBgdDX50T1nePjb +mKQ5PeqXJbWEtVrUtVJaPwMBCAeIeAQYFggAIBYhBDnRAKtn1b2MBAECBfs3UfFY +fa7xBQJaU28AAhsMAAoJEPs3UfFYfa7xzjIBANX2/FgDX3WkmvwpEHg/sn40zACM +W2hrBY5x0sZ8H7JlAP47mCfCuRVBqyaePuzKbxLJeLe2BpDdc0n2izMVj8t9Cg== +=bbbT +-----END PGP PUBLIC KEY BLOCK----- +").unwrap(); +} + + +fuzz_target!(|data: &[u8]| -> Corpus { + struct Helper {} + impl VerificationHelper for Helper { + fn get_certs(&mut self, _ids: &[KeyHandle]) + -> openpgp::Result<Vec<Cert>> { + Ok(vec![CERT.clone()]) + } + + fn check(&mut self, structure: MessageStructure) + -> openpgp::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 ! results.iter().any(|r| r.is_ok()) { + return Err(anyhow::anyhow!( + "No valid signature")); + } + } + _ => return Err(anyhow::anyhow!( + "Unexpected message structure")), + } + } + Ok(()) + } + } + + let h = Helper {}; + if let Ok(mut v) = VerifierBuilder::from_bytes(data) + .and_then(|b| b.with_policy(P, None, h)) + { + let _ = std::io::copy(&mut v, &mut std::io::sink()); + Corpus::Keep + } else { + Corpus::Keep + } +}); |