diff --git a/Cargo.lock b/Cargo.lock index 7553abce..fae1945d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1217,17 +1217,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "picky" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +version = "4.5.0" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-bigint-dig 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "oid 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "picky-asn1 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "picky-asn1-der 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "picky-asn1-der 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "rsa 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "snafu 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1236,21 +1240,17 @@ dependencies = [ [[package]] name = "picky" -version = "4.4.2" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "num-bigint-dig 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "oid 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "picky-asn1 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "picky-asn1-der 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "picky-asn1-der 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "rsa 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "snafu 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1263,7 +1263,7 @@ version = "0.1.0" dependencies = [ "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "oid 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "picky-asn1-der 0.1.0", + "picky-asn1-der 0.2.0", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_bytes 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1281,7 +1281,7 @@ dependencies = [ [[package]] name = "picky-asn1-der" -version = "0.1.0" +version = "0.2.0" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1295,7 +1295,7 @@ dependencies = [ [[package]] name = "picky-asn1-der" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "picky-asn1 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1304,7 +1304,7 @@ dependencies = [ [[package]] name = "picky-server" -version = "4.2.0" +version = "4.2.1" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "bson 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1317,7 +1317,7 @@ dependencies = [ "log4rs 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", "mongodb_cwal 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "multihash 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "picky 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "picky 4.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "picky-asn1 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "portpicker 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "r2d2 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2694,9 +2694,9 @@ dependencies = [ "checksum pbkdf2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0c09cddfbfc98de7f76931acf44460972edb4023eb14d0c6d4018800e552d8e0" "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 picky 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b55d2009b7b69f6710e95c9f8042e79192633536f7a5d883e0f3f8542fe33ed8" +"checksum picky 4.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c9d7aa6ad4f8b4b667514e2af5f8d9583e1f346416c8fcb0395548a564c05f87" "checksum picky-asn1 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7e3797d15d9e7b6e6c46b6f971f13f5fd120578643a4e4416309606ebcfc048" -"checksum picky-asn1-der 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "651cd390619d725d5bee51b716b47a6c7086a1cdbfe0aedb4c6808bc1736af35" +"checksum picky-asn1-der 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "affcd14dea3ac5a55852b4247a341c092a79de129a8816b2d5f697b63fd6d486" "checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" "checksum portpicker 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b497d05c16fe00939445c00a4fe2fa4f3d3dfc9c0401a3ab5c577afda2debb9" "checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" diff --git a/picky-asn1-der/CHANGELOG.md b/picky-asn1-der/CHANGELOG.md new file mode 100644 index 00000000..b7c81c2b --- /dev/null +++ b/picky-asn1-der/CHANGELOG.md @@ -0,0 +1,16 @@ +# Changelog + +## [0.2.0] 2019-12-23 + +### Added + +- Add `from_reader_with_max_len` deserialization function to limit how many bytes can be read at most. + +### Changed + +- `from_reader` function has a default limit of 10240 bytes before returning a truncated data error. Uses `from_reader_with_max_len` to change the limit. + +### Fixed + +- Fix various crash found by fuzzing. + diff --git a/picky-asn1-der/Cargo.toml b/picky-asn1-der/Cargo.toml index 28209439..b1733ef4 100644 --- a/picky-asn1-der/Cargo.toml +++ b/picky-asn1-der/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "picky-asn1-der" -version = "0.1.0" +version = "0.2.0" edition = "2018" authors = [ "KizzyCode Software Labs./Keziah Biermann ", diff --git a/picky-asn1-der/src/de/mod.rs b/picky-asn1-der/src/de/mod.rs index 0d6718eb..a11902f0 100644 --- a/picky-asn1-der/src/de/mod.rs +++ b/picky-asn1-der/src/de/mod.rs @@ -13,6 +13,8 @@ use picky_asn1::{tag::Tag, wrapper::*, Asn1Type}; use serde::{de::Visitor, Deserialize}; use std::io::{Cursor, Read}; +const DEFAULT_MAX_LEN: usize = 10240; + /// Deserializes `T` from `bytes` pub fn from_bytes<'a, T: Deserialize<'a>>(bytes: &'a [u8]) -> Result { debug_log!("deserialization using `from_bytes`"); @@ -22,8 +24,16 @@ pub fn from_bytes<'a, T: Deserialize<'a>>(bytes: &'a [u8]) -> Result { /// Deserializes `T` from `reader` pub fn from_reader<'a, T: Deserialize<'a>>(reader: impl Read + 'a) -> Result { - debug_log!("deserialization using `from_reader`"); - let mut deserializer = Deserializer::new_from_reader(reader); + from_reader_with_max_len(reader, DEFAULT_MAX_LEN) +} + +/// Deserializes `T` from `reader` reading at most n bytes. +pub fn from_reader_with_max_len<'a, T: Deserialize<'a>>(reader: impl Read + 'a, max_len: usize) -> Result { + debug_log!( + "deserialization using `from_reader_with_max_len`, max_len = {}", + max_len + ); + let mut deserializer = Deserializer::new_from_reader(reader, max_len); T::deserialize(&mut deserializer) } @@ -33,20 +43,22 @@ pub struct Deserializer<'de> { buf: Vec, encapsulator_tag_stack: Vec, header_only: bool, + max_len: usize, } impl<'de> Deserializer<'de> { /// Creates a new deserializer over `bytes` pub fn new_from_bytes(bytes: &'de [u8]) -> Self { - Self::new_from_reader(Cursor::new(bytes)) + Self::new_from_reader(Cursor::new(bytes), bytes.len()) } /// Creates a new deserializer for `reader` - pub fn new_from_reader(reader: impl Read + 'de) -> Self { + pub fn new_from_reader(reader: impl Read + 'de, max_len: usize) -> Self { Self { reader: PeekableReader::new(Box::new(reader)), buf: Vec::new(), encapsulator_tag_stack: Vec::with_capacity(3), header_only: false, + max_len, } } @@ -74,8 +86,13 @@ impl<'de> Deserializer<'de> { (tag, len) }; + if len > self.max_len { + debug_log!("TRUNCATED DATA (invalid len: found {}, max is {})", len, self.max_len); + return Err(Asn1DerError::TruncatedData); + } + self.buf.resize(len, 0); - self.reader.read_exact(&mut self.buf)?; + self.reader.read_exact(self.buf.as_mut_slice())?; Ok(tag) } @@ -122,7 +139,7 @@ impl<'de> Deserializer<'de> { }; } - if peeked.len() < cursor { + if peeked.len() <= cursor { debug_log!("peek_object: TRUNCATED DATA (couldn't read object tag)"); return Err(Asn1DerError::TruncatedData); } diff --git a/picky-asn1-der/src/de/sequence.rs b/picky-asn1-der/src/de/sequence.rs index 05aac808..e49e6efe 100644 --- a/picky-asn1-der/src/de/sequence.rs +++ b/picky-asn1-der/src/de/sequence.rs @@ -6,12 +6,14 @@ pub struct Sequence<'a, 'de> { de: &'a mut Deserializer<'de>, len: usize, } + impl<'a, 'de> Sequence<'a, 'de> { /// Creates a lazy deserializer that can walk through the sequence's sub-elements pub fn deserialize_lazy(de: &'a mut Deserializer<'de>, len: usize) -> Self { Self { de, len } } } + impl<'a, 'de> SeqAccess<'de> for Sequence<'a, 'de> { type Error = Asn1DerError; @@ -24,7 +26,13 @@ impl<'a, 'de> SeqAccess<'de> for Sequence<'a, 'de> { // Deserialize the element let pos = self.de.reader.pos(); let element = seed.deserialize(&mut *self.de)?; - self.len -= self.de.reader.pos() - pos; + + let read = self.de.reader.pos() - pos; + if self.len < read { + debug_log!("TRUNCATED DATA (read more than necessary??)"); + return Err(Asn1DerError::TruncatedData); + } + self.len -= read; Ok(Some(element)) } diff --git a/picky-asn1-der/src/lib.rs b/picky-asn1-der/src/lib.rs index 04cb3559..789bacac 100644 --- a/picky-asn1-der/src/lib.rs +++ b/picky-asn1-der/src/lib.rs @@ -77,7 +77,7 @@ mod misc; mod ser; pub use crate::{ - de::{from_bytes, from_reader, Deserializer}, + de::{from_bytes, from_reader, from_reader_with_max_len, Deserializer}, ser::{to_byte_buf, to_bytes, to_vec, to_writer, Serializer}, }; diff --git a/picky-asn1-der/src/ser/sequence.rs b/picky-asn1-der/src/ser/sequence.rs index 715ae5bf..dd3c0b0e 100644 --- a/picky-asn1-der/src/ser/sequence.rs +++ b/picky-asn1-der/src/ser/sequence.rs @@ -13,6 +13,7 @@ pub struct Sequence<'a, 'se> { buf: Cursor>, tag: Tag, } + impl<'a, 'se> Sequence<'a, 'se> { /// Creates a lazy serializer that will serialize the sequence's sub-elements to `writer` pub fn serialize_lazy(ser: &'a mut Serializer<'se>, tag: Tag) -> Self { @@ -28,6 +29,7 @@ impl<'a, 'se> Sequence<'a, 'se> { to_writer(value, &mut self.buf)?; Ok(()) } + /// Finalizes the sequence fn finalize(self) -> Result { // Reclaim buffer @@ -39,6 +41,7 @@ impl<'a, 'se> Sequence<'a, 'se> { Ok(written) } } + impl<'a, 'se> serde::ser::SerializeSeq for Sequence<'a, 'se> { type Ok = usize; type Error = Asn1DerError; @@ -50,6 +53,7 @@ impl<'a, 'se> serde::ser::SerializeSeq for Sequence<'a, 'se> { self.finalize() } } + impl<'a, 'se> serde::ser::SerializeTuple for Sequence<'a, 'se> { type Ok = usize; type Error = Asn1DerError; @@ -61,6 +65,7 @@ impl<'a, 'se> serde::ser::SerializeTuple for Sequence<'a, 'se> { self.finalize() } } + impl<'a, 'se> serde::ser::SerializeStruct for Sequence<'a, 'se> { type Ok = usize; type Error = Asn1DerError; @@ -72,6 +77,7 @@ impl<'a, 'se> serde::ser::SerializeStruct for Sequence<'a, 'se> { self.finalize() } } + impl<'a, 'se> serde::ser::SerializeTupleStruct for Sequence<'a, 'se> { type Ok = usize; type Error = Asn1DerError; diff --git a/picky-server/Cargo.toml b/picky-server/Cargo.toml index 734d60af..177260e2 100644 --- a/picky-server/Cargo.toml +++ b/picky-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "picky-server" -version = "4.2.0" +version = "4.2.1" authors = [ "jtrepanier-devolutions ", "BenoĂŽt CORTIER ", @@ -11,7 +11,7 @@ license = "MIT OR Apache-2.0" repository = "https://github.com/Devolutions/picky-rs" [dependencies] -picky = { version = "4.2", default-features = false, features = ["x509", "chrono_conversion"] } +picky = { version = "4.5", default-features = false, features = ["x509", "chrono_conversion"] } picky-asn1 = "0.1" mongodb = { package = "mongodb_cwal", version = "0.6", features = ["ssl"] } curl = { git = "https://github.com/Devolutions/curl-rust", branch = "wayk" } diff --git a/picky-server/src/picky_controller.rs b/picky-server/src/picky_controller.rs index fe060a35..734bc513 100644 --- a/picky-server/src/picky_controller.rs +++ b/picky-server/src/picky_controller.rs @@ -1,5 +1,5 @@ use picky::{ - key::{KeyError, OwnedPublicKey, PrivateKey}, + key::{KeyError, PrivateKey, PublicKey}, oids, pem::PemError, signature::SignatureHashType, @@ -83,7 +83,7 @@ impl Picky { pub fn generate_intermediate( intermediate_name: &str, - intermediate_key: OwnedPublicKey, + intermediate_key: PublicKey, issuer_cert: &Cert, issuer_key: &PrivateKey, signature_hash_type: SignatureHashType, diff --git a/picky/Cargo.toml b/picky/Cargo.toml index 05f5108e..cb5f121b 100644 --- a/picky/Cargo.toml +++ b/picky/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "picky" -version = "4.4.2" +version = "4.5.0" authors = [ "jtrepanier-devolutions ", "BenoĂŽt CORTIER ", @@ -12,7 +12,7 @@ repository = "https://github.com/Devolutions/picky-rs" [dependencies] picky-asn1 = "0.1" -picky-asn1-der = "0.1" +picky-asn1-der = "0.2" serde = { version = "1.0", features = ["derive"] } oid = { version = "^0.1.1", features = ["serde_support"] } base64 = "0.10" diff --git a/picky/fuzz/.gitignore b/picky/fuzz/.gitignore new file mode 100644 index 00000000..775bf2ea --- /dev/null +++ b/picky/fuzz/.gitignore @@ -0,0 +1,4 @@ +target +corpus +artifacts +Cargo.lock \ No newline at end of file diff --git a/picky/fuzz/Cargo.toml b/picky/fuzz/Cargo.toml new file mode 100644 index 00000000..e77ca871 --- /dev/null +++ b/picky/fuzz/Cargo.toml @@ -0,0 +1,23 @@ + +[package] +name = "picky-fuzz" +version = "0.0.0" +authors = ["Automatically generated"] +publish = false +edition = "2018" + +[package.metadata] +cargo-fuzz = true + +[dependencies.picky] +path = ".." +[dependencies.libfuzzer-sys] +git = "https://github.com/rust-fuzz/libfuzzer-sys.git" + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[[bin]] +name = "fuzz_target_1" +path = "fuzz_targets/fuzz_target_1.rs" diff --git a/picky/fuzz/fuzz_targets/fuzz_target_1.rs b/picky/fuzz/fuzz_targets/fuzz_target_1.rs new file mode 100644 index 00000000..42b674eb --- /dev/null +++ b/picky/fuzz/fuzz_targets/fuzz_target_1.rs @@ -0,0 +1,39 @@ +#![no_main] +use libfuzzer_sys::fuzz_target; +use picky::{ + jose::{ + jwk::Jwk, + jwt::{Jwt, JwtDate, JwtValidator}, + }, + key::{PrivateKey, PublicKey}, + pem::{parse_pem, Pem}, + x509::{certificate::Cert, csr::Csr}, +}; + +fuzz_target!(|data: &[u8]| { + // pem + let _ = parse_pem(data); + let pem = Pem::new("HEADER", data); + let _ = parse_pem(&pem.to_string()); + + // keys + let _ = PrivateKey::from_pkcs8(data); + let _ = PublicKey::from_der(data); + + // x509 + let _ = Csr::from_der(data); + let _ = Cert::from_der(data); + + // jose + if let Ok(s) = std::str::from_utf8(data) { + if data.len() >= 4 { + let numeric_date = + (data[0] as i64) + (data[1] as i64) * 2_i64.pow(8) + (data[2] as i64) * 2_i64.pow(16) + (data[3] as i64) * 2_i64.pow(24); + let date = JwtDate::new(numeric_date); + let validator = JwtValidator::dangerous().current_date(&date); + let _ = Jwt::<()>::decode(s, &validator); + } + + let _ = Jwk::from_json(s); + } +}); diff --git a/picky/src/algorithm_identifier.rs b/picky/src/algorithm_identifier.rs index 9c1bb77a..725a3458 100644 --- a/picky/src/algorithm_identifier.rs +++ b/picky/src/algorithm_identifier.rs @@ -129,7 +129,7 @@ impl<'de> de::Deserialize<'de> for AlgorithmIdentifier { where A: de::SeqAccess<'de>, { - let oid: ObjectIdentifierAsn1 = seq.next_element()?.unwrap(); + let oid: ObjectIdentifierAsn1 = seq_next_element!(seq, AlgorithmIdentifier, "algorithm oid"); let args = match Into::::into(&oid.0).as_str() { oids::RSA_ENCRYPTION @@ -138,17 +138,20 @@ impl<'de> de::Deserialize<'de> for AlgorithmIdentifier { | oids::SHA256_WITH_RSA_ENCRYPTION | oids::SHA384_WITH_RSA_ENCRYPTION | oids::SHA512_WITH_RSA_ENCRYPTION => { - seq.next_element::<()>()?.unwrap(); + seq_next_element!(seq, AlgorithmIdentifier, "algorithm identifier parameters (null)"); AlgorithmIdentifierParameters::Null } oids::ECDSA_WITH_SHA384 | oids::ECDSA_WITH_SHA256 => AlgorithmIdentifierParameters::None, - oids::EC_PUBLIC_KEY => { - AlgorithmIdentifierParameters::EC(seq.next_element::()?.unwrap()) - } + oids::EC_PUBLIC_KEY => AlgorithmIdentifierParameters::EC(seq_next_element!( + seq, + AlgorithmIdentifier, + "elliptic curves parameters" + )), _ => { - return Err(de::Error::invalid_value( - de::Unexpected::Other("[AlgorithmIdentifier] unsupported algorithm (unknown oid)"), - &"a supported algorithm", + return Err(serde_invalid_value!( + AlgorithmIdentifier, + "unsupported algorithm (unknown oid)", + "a supported algorithm" )); } }; @@ -226,16 +229,21 @@ impl<'de> de::Deserialize<'de> for ECParameters { where A: de::SeqAccess<'de>, { - // cannot panic with DER deserializer - match seq.next_element::()?.unwrap().next_tag { - Tag::OID => Ok(ECParameters::NamedCurve(seq.next_element()?.unwrap())), + let tag_peeker: TagPeeker = seq_next_element!(seq, ECParameters, "choice tag"); + match tag_peeker.next_tag { + Tag::OID => Ok(ECParameters::NamedCurve(seq_next_element!( + seq, + ECParameters, + "Object Identifier" + ))), Tag::NULL => { - seq.next_element::<()>()?.unwrap(); + seq.next_element::<()>()?.expect("should not panic"); Ok(ECParameters::ImplicitCurve) } - _ => Err(de::Error::invalid_value( - de::Unexpected::Other("[ECParameters] unsupported or unknown elliptic curve parameter"), - &"a supported elliptic curve parameter", + _ => Err(serde_invalid_value!( + ECParameters, + "unsupported or unknown elliptic curve parameter", + "a supported elliptic curve parameter" )), } } diff --git a/picky/src/jose/jwk.rs b/picky/src/jose/jwk.rs index 569f89a5..ae5619f2 100644 --- a/picky/src/jose/jwk.rs +++ b/picky/src/jose/jwk.rs @@ -1,8 +1,4 @@ -use crate::{ - key::PublicKey, - private::SubjectPublicKeyInfo, - signature::SignatureHashType, -}; +use crate::{key::PublicKey, private::SubjectPublicKeyInfo, signature::SignatureHashType}; use base64::DecodeError; use serde::{Deserialize, Serialize}; use snafu::Snafu; diff --git a/picky/src/macros.rs b/picky/src/macros.rs index aeb6228d..113dab64 100644 --- a/picky/src/macros.rs +++ b/picky/src/macros.rs @@ -1,3 +1,34 @@ +macro_rules! serde_invalid_value { + ($typ:ident, $unexp:literal, $exp:literal) => {{ + const _: Option<$typ> = None; + de::Error::invalid_value( + serde::de::Unexpected::Other(concat!("[", stringify!($typ), "] ", $unexp)), + &$exp, + ) + }}; +} + +macro_rules! seq_next_element { + ($seq:ident, $typ:ident, $missing_elem:literal) => {{ + const _: Option<$typ> = None; + $seq.next_element()?.ok_or_else(|| { + de::Error::invalid_value( + serde::de::Unexpected::Other(concat!("[", stringify!($typ), "] ", $missing_elem, " is missing")), + &concat!("valid ", $missing_elem), + ) + })? + }}; + ($seq:ident, $typ_hint:path, $typ:ident, $missing_elem:literal) => {{ + const _: Option<$typ> = None; + $seq.next_element::<$typ_hint>()?.ok_or_else(|| { + de::Error::invalid_value( + serde::de::Unexpected::Other(concat!("[", stringify!($typ), "] ", $missing_elem, " is missing")), + &concat!("valid ", $missing_elem), + ) + })? + }}; +} + #[cfg(test)] #[macro_use] mod tests { diff --git a/picky/src/private/private_key_info.rs b/picky/src/private/private_key_info.rs index a305a0bc..5cc0f2fd 100644 --- a/picky/src/private/private_key_info.rs +++ b/picky/src/private/private_key_info.rs @@ -88,22 +88,24 @@ impl<'de> de::Deserialize<'de> for PrivateKeyInfo { where A: de::SeqAccess<'de>, { - let version = seq.next_element()?.unwrap(); + let version = seq_next_element!(seq, PrivateKeyInfo, "version"); if version != 0 { - return Err(de::Error::invalid_value( - de::Unexpected::Other("[PrivateKeyInfo] unsupported version (valid version number: 0)"), - &"a supported PrivateKeyInfo", + return Err(serde_invalid_value!( + PrivateKeyInfo, + "unsupported version (valid version number: 0)", + "a supported PrivateKeyInfo" )); } - let private_key_algorithm: AlgorithmIdentifier = seq.next_element()?.unwrap(); - + let private_key_algorithm: AlgorithmIdentifier = + seq_next_element!(seq, PrivateKeyInfo, "private key algorithm"); let private_key = if private_key_algorithm.is_a(oids::rsa_encryption()) { - PrivateKeyValue::RSA(seq.next_element()?.unwrap()) + PrivateKeyValue::RSA(seq_next_element!(seq, PrivateKeyInfo, "rsa oid")) } else { - return Err(de::Error::invalid_value( - de::Unexpected::Other("[PrivateKeyInfo] unsupported algorithm"), - &"a PrivateKeyInfo using a supported algorithm", + return Err(serde_invalid_value!( + PrivateKeyInfo, + "unsupported algorithm", + "a supported algorithm" )); }; @@ -157,8 +159,11 @@ impl RSAPrivateKey { pub fn into_public_components(self) -> (IntegerAsn1, IntegerAsn1) { let mut iter = (self.0).0.into_iter(); - iter.next().unwrap(); - (iter.next().unwrap(), iter.next().unwrap()) + iter.next().expect("should not panic"); + ( + iter.next().expect("should not panic"), + iter.next().expect("should not panic"), + ) } } diff --git a/picky/src/private/subject_public_key_info.rs b/picky/src/private/subject_public_key_info.rs index 6dfd738d..01d6f959 100644 --- a/picky/src/private/subject_public_key_info.rs +++ b/picky/src/private/subject_public_key_info.rs @@ -74,15 +74,18 @@ impl<'de> de::Deserialize<'de> for SubjectPublicKeyInfo { where A: de::SeqAccess<'de>, { - let algorithm: AlgorithmIdentifier = seq.next_element()?.unwrap(); + let algorithm: AlgorithmIdentifier = seq_next_element!(seq, AlgorithmIdentifier, "algorithm oid"); let subject_public_key = match Into::::into(algorithm.oid()).as_str() { - oids::RSA_ENCRYPTION => PublicKey::RSA(seq.next_element()?.unwrap()), - oids::EC_PUBLIC_KEY => PublicKey::EC(seq.next_element()?.unwrap()), + oids::RSA_ENCRYPTION => PublicKey::RSA(seq_next_element!(seq, SubjectPublicKeyInfo, "rsa key")), + oids::EC_PUBLIC_KEY => { + PublicKey::EC(seq_next_element!(seq, SubjectPublicKeyInfo, "elliptic curves key")) + } _ => { - return Err(de::Error::invalid_value( - de::Unexpected::Other("[SubjectPublicKeyInfo] unsupported algorithm (unknown oid)"), - &"a supported algorithm", + return Err(serde_invalid_value!( + SubjectPublicKeyInfo, + "unsupported algorithm (unknown oid)", + "a supported algorithm" )); } }; diff --git a/picky/src/x509/certificate.rs b/picky/src/x509/certificate.rs index 7f3c6da4..a1d1a22d 100644 --- a/picky/src/x509/certificate.rs +++ b/picky/src/x509/certificate.rs @@ -387,10 +387,7 @@ impl Cert { #[derive(Clone, Debug)] enum SubjectInfos { Csr(Csr), - NameAndPublicKey { - name: DirectoryName, - public_key: PublicKey, - }, + NameAndPublicKey { name: DirectoryName, public_key: PublicKey }, } #[derive(Clone, Debug)] diff --git a/picky/src/x509/directory_string.rs b/picky/src/x509/directory_string.rs index d74ccf26..84fc6852 100644 --- a/picky/src/x509/directory_string.rs +++ b/picky/src/x509/directory_string.rs @@ -107,25 +107,37 @@ impl<'de> de::Deserialize<'de> for DirectoryString { where A: de::SeqAccess<'de>, { - // cannot panic with DER deserializer - match seq.next_element::()?.unwrap().next_tag { - Tag::UTF8_STRING => Ok(DirectoryString::Utf8String(seq.next_element()?.unwrap())), - Tag::PRINTABLE_STRING => Ok(DirectoryString::PrintableString(seq.next_element()?.unwrap())), - Tag::TELETEX_STRING => Err(de::Error::invalid_value( - de::Unexpected::Other("[DirectoryString] TeletexString not supported"), - &"a supported string type", + let tag_peeker: TagPeeker = seq_next_element!(seq, DirectoryString, "choice tag"); + match tag_peeker.next_tag { + Tag::UTF8_STRING => Ok(DirectoryString::Utf8String(seq_next_element!( + seq, + DirectoryString, + "Utf8String" + ))), + Tag::PRINTABLE_STRING => Ok(DirectoryString::PrintableString(seq_next_element!( + seq, + DirectoryString, + "PrintableString" + ))), + Tag::TELETEX_STRING => Err(serde_invalid_value!( + DirectoryString, + "TeletexString not supported", + "a supported string type" )), - Tag::VIDEOTEX_STRING => Err(de::Error::invalid_value( - de::Unexpected::Other("[DirectoryString] VideotexString not supported"), - &"a supported string type", + Tag::VIDEOTEX_STRING => Err(serde_invalid_value!( + DirectoryString, + "VideotexString not supported", + "a supported string type" )), - Tag::IA5_STRING => Err(de::Error::invalid_value( - de::Unexpected::Other("[DirectoryString] IA5String not supported"), - &"a supported string type", + Tag::IA5_STRING => Err(serde_invalid_value!( + DirectoryString, + "IA5String not supported", + "a supported string type" )), - _ => Err(de::Error::invalid_value( - de::Unexpected::Other("[DirectoryString] unknown string type"), - &"a known supported string type", + _ => Err(serde_invalid_value!( + DirectoryString, + "unknown string type", + "a known supported string type" )), } } diff --git a/picky/src/x509/extension.rs b/picky/src/x509/extension.rs index 4f87b5a9..bb35b43e 100644 --- a/picky/src/x509/extension.rs +++ b/picky/src/x509/extension.rs @@ -207,19 +207,31 @@ impl<'de> de::Deserialize<'de> for Extension { where A: de::SeqAccess<'de>, { - let id: ObjectIdentifierAsn1 = seq.next_element()?.unwrap(); - let critical: Implicit = seq.next_element()?.unwrap(); + let id: ObjectIdentifierAsn1 = seq_next_element!(seq, Extension, "id"); + let critical: Implicit = seq_next_element!(seq, Extension, "critical"); let value = match Into::::into(&id.0).as_str() { - oids::AUTHORITY_KEY_IDENTIFIER => { - ExtensionValue::AuthorityKeyIdentifier(seq.next_element()?.unwrap()) + oids::AUTHORITY_KEY_IDENTIFIER => ExtensionValue::AuthorityKeyIdentifier(seq_next_element!( + seq, + Extension, + "AuthorityKeyIdentifier" + )), + oids::SUBJECT_KEY_IDENTIFIER => { + ExtensionValue::SubjectKeyIdentifier(seq_next_element!(seq, Extension, "SubjectKeyIdentifier")) } - oids::SUBJECT_KEY_IDENTIFIER => ExtensionValue::SubjectKeyIdentifier(seq.next_element()?.unwrap()), - oids::KEY_USAGE => ExtensionValue::KeyUsage(seq.next_element()?.unwrap()), - oids::SUBJECT_ALTERNATIVE_NAME => ExtensionValue::SubjectAltName(seq.next_element()?.unwrap()), - oids::ISSUER_ALTERNATIVE_NAME => ExtensionValue::IssuerAltName(seq.next_element()?.unwrap()), - oids::BASIC_CONSTRAINTS => ExtensionValue::BasicConstraints(seq.next_element()?.unwrap()), - oids::EXTENDED_KEY_USAGE => ExtensionValue::ExtendedKeyUsage(seq.next_element()?.unwrap()), - _ => ExtensionValue::Generic(seq.next_element()?.unwrap()), + oids::KEY_USAGE => ExtensionValue::KeyUsage(seq_next_element!(seq, Extension, "KeyUsage")), + oids::SUBJECT_ALTERNATIVE_NAME => { + ExtensionValue::SubjectAltName(seq_next_element!(seq, Extension, "SubjectAltName")) + } + oids::ISSUER_ALTERNATIVE_NAME => { + ExtensionValue::IssuerAltName(seq_next_element!(seq, Extension, "IssuerAltName")) + } + oids::BASIC_CONSTRAINTS => { + ExtensionValue::BasicConstraints(seq_next_element!(seq, Extension, "BasicConstraints")) + } + oids::EXTENDED_KEY_USAGE => { + ExtensionValue::ExtendedKeyUsage(seq_next_element!(seq, Extension, "ExtendedKeyUsage")) + } + _ => ExtensionValue::Generic(seq_next_element!(seq, Extension, "Generic")), }; Ok(Extension { diff --git a/picky/src/x509/private/attribute_type_and_value.rs b/picky/src/x509/private/attribute_type_and_value.rs index 8f0ee77a..a399f991 100644 --- a/picky/src/x509/private/attribute_type_and_value.rs +++ b/picky/src/x509/private/attribute_type_and_value.rs @@ -146,61 +146,65 @@ impl<'de> de::Deserialize<'de> for AttributeTypeAndValue { where A: de::SeqAccess<'de>, { - let ty: ObjectIdentifierAsn1 = seq.next_element()?.unwrap(); // cannot panic with DER deserializer - - let value = match Into::::into(&ty.0).as_str() { - oids::AT_COMMON_NAME => { - // cannot panic with DER deserializer - AttributeTypeAndValueParameters::CommonName(seq.next_element()?.unwrap()) - } - oids::AT_SURNAME => { - // cannot panic with DER deserializer - AttributeTypeAndValueParameters::Surname(seq.next_element()?.unwrap()) - } - oids::AT_SERIAL_NUMBER => { - // cannot panic with DER deserializer - AttributeTypeAndValueParameters::SerialNumber(seq.next_element()?.unwrap()) - } - oids::AT_COUNTRY_NAME => { - // cannot panic with DER deserializer - AttributeTypeAndValueParameters::CountryName(seq.next_element()?.unwrap()) - } - oids::AT_LOCALITY_NAME => { - // cannot panic with DER deserializer - AttributeTypeAndValueParameters::LocalityName(seq.next_element()?.unwrap()) - } - oids::AT_STATE_OR_PROVINCE_NAME => { - // cannot panic with DER deserializer - AttributeTypeAndValueParameters::StateOrProvinceName(seq.next_element()?.unwrap()) - } - oids::AT_STREET_NAME => { - // cannot panic with DER deserializer - AttributeTypeAndValueParameters::StreetName(seq.next_element()?.unwrap()) - } - oids::AT_ORGANISATION_NAME => { - // cannot panic with DER deserializer - AttributeTypeAndValueParameters::OrganisationName(seq.next_element()?.unwrap()) - } - oids::AT_ORGANISATIONAL_UNIT_NAME => { - // cannot panic with DER deserializer - AttributeTypeAndValueParameters::OrganisationalUnitName(seq.next_element()?.unwrap()) - } - oids::EMAIL_ADDRESS => { - return Err(de::Error::invalid_value( - de::Unexpected::Other( - "[AttributeTypeAndValue] 1.2.840.113549.1.9.1 (e-mailAddress) \ + let ty: ObjectIdentifierAsn1 = seq_next_element!(seq, AttributeTypeAndValue, "type oid"); + + let value = + match Into::::into(&ty.0).as_str() { + oids::AT_COMMON_NAME => AttributeTypeAndValueParameters::CommonName(seq_next_element!( + seq, + AttributeTypeAndValue, + "at common name" + )), + oids::AT_SURNAME => AttributeTypeAndValueParameters::Surname(seq_next_element!( + seq, + AttributeTypeAndValue, + "at surname" + )), + oids::AT_SERIAL_NUMBER => AttributeTypeAndValueParameters::SerialNumber(seq_next_element!( + seq, + AttributeTypeAndValue, + "at serial number" + )), + oids::AT_COUNTRY_NAME => AttributeTypeAndValueParameters::CountryName(seq_next_element!( + seq, + AttributeTypeAndValue, + "at country name" + )), + oids::AT_LOCALITY_NAME => AttributeTypeAndValueParameters::LocalityName(seq_next_element!( + seq, + AttributeTypeAndValue, + "at locality name" + )), + oids::AT_STATE_OR_PROVINCE_NAME => AttributeTypeAndValueParameters::StateOrProvinceName( + seq_next_element!(seq, AttributeTypeAndValue, "at state or province name"), + ), + oids::AT_STREET_NAME => AttributeTypeAndValueParameters::StreetName(seq_next_element!( + seq, + AttributeTypeAndValue, + "at street name" + )), + oids::AT_ORGANISATION_NAME => AttributeTypeAndValueParameters::OrganisationName( + seq_next_element!(seq, AttributeTypeAndValue, "at organisation name"), + ), + oids::AT_ORGANISATIONAL_UNIT_NAME => AttributeTypeAndValueParameters::OrganisationalUnitName( + seq_next_element!(seq, AttributeTypeAndValue, "at organisational unit name"), + ), + oids::EMAIL_ADDRESS => { + return Err(serde_invalid_value!( + AttributeTypeAndValue, + "1.2.840.113549.1.9.1 (e-mailAddress) \ attribute is deprecated and won't be supported", - ), - &"a supported type", - )); - } - _ => { - return Err(de::Error::invalid_value( - de::Unexpected::Other("[AttributeTypeAndValue] unsupported type (unknown oid)"), - &"a supported type", - )); - } - }; + "a supported type" + )); + } + _ => { + return Err(serde_invalid_value!( + AttributeTypeAndValue, + "unsupported type (unknown oid)", + "a supported type" + )); + } + }; Ok(AttributeTypeAndValue { ty, value }) } diff --git a/picky/src/x509/private/name.rs b/picky/src/x509/private/name.rs index b471b6bd..719de5d4 100644 --- a/picky/src/x509/private/name.rs +++ b/picky/src/x509/private/name.rs @@ -149,61 +149,64 @@ impl<'de> de::Deserialize<'de> for GeneralName { where A: de::SeqAccess<'de>, { - // cannot panic with DER deserializer - match seq.next_element::()?.unwrap().next_tag { - Tag::CTX_0 | Tag::APP_0 => Err(de::Error::invalid_value( - de::Unexpected::Other("[GeneralName] OtherName not supported"), - &"a supported GeneraleName choice", + let tag_peeker: TagPeeker = seq_next_element!(seq, DirectoryString, "choice tag"); + match tag_peeker.next_tag { + Tag::CTX_0 | Tag::APP_0 => Err(serde_invalid_value!( + GeneralName, + "OtherName not supported", + "a supported choice" )), Tag::CTX_1 => Ok(GeneralName::RFC822Name( - seq.next_element::>()?.unwrap().0, + seq_next_element!(seq, ContextTag1, GeneralName, "RFC822Name").0, )), Tag::APP_1 => Ok(GeneralName::RFC822Name( - seq.next_element::>()?.unwrap().0, + seq_next_element!(seq, ApplicationTag1, GeneralName, "RFC822Name").0, )), Tag::CTX_2 => Ok(GeneralName::DNSName( - seq.next_element::>()?.unwrap().0, + seq_next_element!(seq, ContextTag2, GeneralName, "DNSName").0, )), Tag::APP_2 => Ok(GeneralName::DNSName( - seq.next_element::>()?.unwrap().0, + seq_next_element!(seq, ApplicationTag2, GeneralName, "DNSName").0, )), - Tag::CTX_3 | Tag::APP_3 => Err(de::Error::invalid_value( - de::Unexpected::Other("[GeneralName] X400Address not supported"), - &"a supported GeneraleName choice", + Tag::CTX_3 | Tag::APP_3 => Err(serde_invalid_value!( + GeneralName, + "X400Address not supported", + "a supported choice" )), Tag::CTX_4 => Ok(GeneralName::DirectoryName( - seq.next_element::>()?.unwrap().0, + seq_next_element!(seq, ContextTag4, GeneralName, "DirectoryName").0, )), Tag::APP_4 => Ok(GeneralName::DirectoryName( - seq.next_element::>()?.unwrap().0, + seq_next_element!(seq, ApplicationTag4, GeneralName, "DirectoryName").0, )), Tag::CTX_5 => Ok(GeneralName::EDIPartyName( - seq.next_element::>()?.unwrap().0, + seq_next_element!(seq, ContextTag5, GeneralName, "EDIPartyName").0, )), Tag::APP_5 => Ok(GeneralName::EDIPartyName( - seq.next_element::>()?.unwrap().0, + seq_next_element!(seq, ApplicationTag5, GeneralName, "EDIPartyName").0, )), Tag::CTX_6 => Ok(GeneralName::URI( - seq.next_element::>()?.unwrap().0, + seq_next_element!(seq, ContextTag6, GeneralName, "URI").0, )), Tag::APP_6 => Ok(GeneralName::URI( - seq.next_element::>()?.unwrap().0, + seq_next_element!(seq, ApplicationTag6, GeneralName, "URI").0, )), Tag::CTX_7 => Ok(GeneralName::IpAddress( - seq.next_element::>()?.unwrap().0, + seq_next_element!(seq, ContextTag7, GeneralName, "IpAddress").0, )), Tag::APP_7 => Ok(GeneralName::IpAddress( - seq.next_element::>()?.unwrap().0, + seq_next_element!(seq, ApplicationTag7, GeneralName, "IpAddress").0, )), Tag::CTX_8 => Ok(GeneralName::RegisteredId( - seq.next_element::>()?.unwrap().0, + seq_next_element!(seq, ContextTag8, GeneralName, "RegisteredId").0, )), Tag::APP_8 => Ok(GeneralName::RegisteredId( - seq.next_element::>()?.unwrap().0, + seq_next_element!(seq, ApplicationTag8, GeneralName, "RegisteredId").0, )), - _ => Err(de::Error::invalid_value( - de::Unexpected::Other("[GeneralName] unknown choice value"), - &"a supported GeneralName choice", + _ => Err(serde_invalid_value!( + GeneralName, + "unknown choice value", + "a supported GeneralName choice" )), } } diff --git a/picky/src/x509/private/validity.rs b/picky/src/x509/private/validity.rs index 60dcea4e..4ddf4fe1 100644 --- a/picky/src/x509/private/validity.rs +++ b/picky/src/x509/private/validity.rs @@ -73,13 +73,14 @@ impl<'de> de::Deserialize<'de> for Time { where A: de::SeqAccess<'de>, { - // cannot panic with DER deserializer - match seq.next_element::()?.unwrap().next_tag { - UTCTimeAsn1::TAG => Ok(Time::UTC(seq.next_element()?.unwrap())), - GeneralizedTimeAsn1::TAG => Ok(Time::Generalized(seq.next_element()?.unwrap())), - _ => Err(de::Error::invalid_value( - de::Unexpected::Other("[Time] invalid variant"), - &"either UTCTime or GeneralizedTime", + let tag_peeker: TagPeeker = seq_next_element!(seq, Time, "choice tag"); + match tag_peeker.next_tag { + UTCTimeAsn1::TAG => Ok(Time::UTC(seq_next_element!(seq, Time, "UTCTime"))), + GeneralizedTimeAsn1::TAG => Ok(Time::Generalized(seq_next_element!(seq, Time, "GeneralizedTime"))), + _ => Err(serde_invalid_value!( + Time, + "invalid variant", + "either UTCTime or GeneralizedTime" )), } } diff --git a/picky/tests/artifacts_vector/crash-155f132f5f648ee37fecc1de689fdc7443fb30a9 b/picky/tests/artifacts_vector/crash-155f132f5f648ee37fecc1de689fdc7443fb30a9 new file mode 100644 index 00000000..b3d4fd98 --- /dev/null +++ b/picky/tests/artifacts_vector/crash-155f132f5f648ee37fecc1de689fdc7443fb30a9 @@ -0,0 +1 @@ +€Š€ \ No newline at end of file diff --git a/picky/tests/artifacts_vector/crash-7fd87f0f47cd3de4a7d20a3ba6102f3eb6e82427 b/picky/tests/artifacts_vector/crash-7fd87f0f47cd3de4a7d20a3ba6102f3eb6e82427 new file mode 100644 index 00000000..cc2203c3 --- /dev/null +++ b/picky/tests/artifacts_vector/crash-7fd87f0f47cd3de4a7d20a3ba6102f3eb6e82427 @@ -0,0 +1 @@ + ˆ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙‚ \ No newline at end of file diff --git a/picky/tests/artifacts_vector/crash-aa736175d07af8e970ad8ba87e299bf065d26f30 b/picky/tests/artifacts_vector/crash-aa736175d07af8e970ad8ba87e299bf065d26f30 new file mode 100644 index 00000000..e8e32976 --- /dev/null +++ b/picky/tests/artifacts_vector/crash-aa736175d07af8e970ad8ba87e299bf065d26f30 @@ -0,0 +1 @@ +€€ \ No newline at end of file diff --git a/picky/tests/artifacts_vector/oom-4858a62f60be11cf4a60eef4cdad042ccb30927c b/picky/tests/artifacts_vector/oom-4858a62f60be11cf4a60eef4cdad042ccb30927c new file mode 100644 index 00000000..2538b8c4 --- /dev/null +++ b/picky/tests/artifacts_vector/oom-4858a62f60be11cf4a60eef4cdad042ccb30927c @@ -0,0 +1 @@ +‹2„„˙ŞŞŞ \ No newline at end of file diff --git a/picky/tests/artifacts_vector/oom-b4720b734896445daffae45d5e3363f8c61785da b/picky/tests/artifacts_vector/oom-b4720b734896445daffae45d5e3363f8c61785da new file mode 100644 index 00000000..0c4dd4aa --- /dev/null +++ b/picky/tests/artifacts_vector/oom-b4720b734896445daffae45d5e3363f8c61785da @@ -0,0 +1 @@ +„"„„„"„„1 \ No newline at end of file diff --git a/picky/tests/artifacts_vector/slow-unit-4763febe11286919c41b2a8b07ae03e5923f0834 b/picky/tests/artifacts_vector/slow-unit-4763febe11286919c41b2a8b07ae03e5923f0834 new file mode 100644 index 00000000..61c935fd --- /dev/null +++ b/picky/tests/artifacts_vector/slow-unit-4763febe11286919c41b2a8b07ae03e5923f0834 @@ -0,0 +1,2 @@ +0 „„. +z. \ No newline at end of file diff --git a/picky/tests/artifacts_vector/slow-unit-8b8b4392d9590538c156a51eaf100b9d3f014d0f b/picky/tests/artifacts_vector/slow-unit-8b8b4392d9590538c156a51eaf100b9d3f014d0f new file mode 100644 index 00000000..f4b3c393 --- /dev/null +++ b/picky/tests/artifacts_vector/slow-unit-8b8b4392d9590538c156a51eaf100b9d3f014d0f @@ -0,0 +1 @@ +1$„„VVVV \ No newline at end of file diff --git a/picky/tests/artifacts_vector/slow-unit-eee10772d15bae083a6a56283cc6e5295427f61f b/picky/tests/artifacts_vector/slow-unit-eee10772d15bae083a6a56283cc6e5295427f61f new file mode 100644 index 00000000..3a11f514 --- /dev/null +++ b/picky/tests/artifacts_vector/slow-unit-eee10772d15bae083a6a56283cc6e5295427f61f @@ -0,0 +1 @@ +‹1„„"ŞŞŞ \ No newline at end of file diff --git a/picky/tests/fuzzer_regressions.rs b/picky/tests/fuzzer_regressions.rs new file mode 100644 index 00000000..0a9c46e5 --- /dev/null +++ b/picky/tests/fuzzer_regressions.rs @@ -0,0 +1,84 @@ +#![cfg(feature = "x509")] +#![cfg(feature = "jose")] +//! This test run problematic artifacts found by fuzzing + +use picky::{ + jose::{ + jwk::Jwk, + jwt::{Jwt, JwtDate, JwtValidator}, + }, + key::{PrivateKey, PublicKey}, + pem::{parse_pem, Pem}, + x509::{certificate::Cert, csr::Csr}, +}; +use std::{fs, io::Read, path::PathBuf, time::Instant}; + +fn read_vector() -> Vec> { + let dir = PathBuf::from("tests/artifacts_vector"); + let mut vector = Vec::new(); + for entry in fs::read_dir(dir).expect("couldn't read dir") { + let entry = entry.expect("couldn't read entry"); + let path = entry.path(); + if !path.is_dir() { + let mut file = fs::File::open(path).expect("couldn't open file"); + let mut test = Vec::new(); + file.read_to_end(&mut test).expect("couldn't read file"); + vector.push(test); + } + } + vector +} + +fn fuzz_target(data: &[u8]) { + println!("PEM..."); + let _ = parse_pem(data); + let pem = Pem::new("HEADER", data); + let _ = parse_pem(&pem.to_string()); + + println!("Keys..."); + let _ = PrivateKey::from_pkcs8(data); + let _ = PublicKey::from_der(data); + + println!("X509..."); + let _ = Csr::from_der(data); + let _ = Cert::from_der(data); + + println!("JOSE..."); + if let Ok(s) = std::str::from_utf8(data) { + if data.len() >= 4 { + let numeric_date = (data[0] as i64) + + (data[1] as i64) * 2_i64.pow(8) + + (data[2] as i64) * 2_i64.pow(16) + + (data[3] as i64) * 2_i64.pow(24); + let date = JwtDate::new(numeric_date); + let validator = JwtValidator::dangerous().current_date(&date); + let _ = Jwt::<()>::decode(s, &validator); + } + + let _ = Jwk::from_json(s); + } + + println!("Done!"); +} + +#[test] +fn fuzz_artifacts() { + let mut success = true; + + for test in read_vector() { + println!("==> Test: {:?}", test); + + let now = Instant::now(); + + fuzz_target(&test); + + let duration = Instant::now().duration_since(now); + println!("Duration: {:?}", duration); + if duration.as_secs() > 1 { + println!("/!!!!\\ WAY TOO SLOW"); + success = false; + } + } + + assert!(success); +}