Skip to content

Commit

Permalink
Merge pull request #761 from stormshield-guillaumed/arbitrary
Browse files Browse the repository at this point in the history
Implement Arbitrary for Certificate
  • Loading branch information
tarcieri committed Dec 22, 2022
2 parents 48e2d01 + 517adb7 commit 6082a98
Show file tree
Hide file tree
Showing 31 changed files with 253 additions and 28 deletions.
11 changes: 6 additions & 5 deletions .github/workflows/const-oid.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,13 @@ jobs:
target: ${{ matrix.target }}
override: true
- uses: RustCrypto/actions/cargo-hack-install@master
- run: cargo hack build --target ${{ matrix.target }} --feature-powerset --exclude-features std
- run: cargo hack build --target ${{ matrix.target }} --feature-powerset --exclude-features std,arbitrary

minimal-versions:
uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master
with:
working-directory: ${{ github.workflow }}
# FIXME: Temporary disabled until https://github.com/rust-fuzz/arbitrary/issues/134 is fixed
# minimal-versions:
# uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master
# with:
# working-directory: ${{ github.workflow }}

test:
runs-on: ubuntu-latest
Expand Down
11 changes: 6 additions & 5 deletions .github/workflows/der.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,13 @@ jobs:
target: ${{ matrix.target }}
override: true
- uses: RustCrypto/actions/cargo-hack-install@master
- run: cargo hack build --target ${{ matrix.target }} --feature-powerset --exclude-features std
- run: cargo hack build --target ${{ matrix.target }} --feature-powerset --exclude-features std,arbitrary

minimal-versions:
uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master
with:
working-directory: ${{ github.workflow }}
# FIXME: Temporary disabled until https://github.com/rust-fuzz/arbitrary/issues/134 is fixed
# minimal-versions:
# uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master
# with:
# working-directory: ${{ github.workflow }}

test:
strategy:
Expand Down
11 changes: 6 additions & 5 deletions .github/workflows/spki.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,13 @@ jobs:
target: ${{ matrix.target }}
override: true
- uses: RustCrypto/actions/cargo-hack-install@master
- run: cargo hack build --target ${{ matrix.target }} --feature-powerset --exclude-features std
- run: cargo hack build --target ${{ matrix.target }} --feature-powerset --exclude-features std,arbitrary

minimal-versions:
uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master
with:
working-directory: ${{ github.workflow }}
# FIXME: Temporary disabled until https://github.com/rust-fuzz/arbitrary/issues/134 is fixed
# minimal-versions:
# uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master
# with:
# working-directory: ${{ github.workflow }}

test:
runs-on: ubuntu-latest
Expand Down
11 changes: 6 additions & 5 deletions .github/workflows/x509-cert.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,13 @@ jobs:
target: ${{ matrix.target }}
override: true
- uses: RustCrypto/actions/cargo-hack-install@master
- run: cargo hack build --target ${{ matrix.target }} --feature-powerset --exclude-features std
- run: cargo hack build --target ${{ matrix.target }} --feature-powerset --exclude-features std,arbitrary

minimal-versions:
uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master
with:
working-directory: ${{ github.workflow }}
# FIXME: Temporary disabled until https://github.com/rust-fuzz/arbitrary/issues/134 is fixed
# minimal-versions:
# uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master
# with:
# working-directory: ${{ github.workflow }}

test:
runs-on: ubuntu-latest
Expand Down
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions const-oid/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,18 @@ categories = ["cryptography", "data-structures", "encoding", "no-std", "parser-i
keywords = ["iso", "iec", "itu", "oid"]
readme = "README.md"
edition = "2021"
rust-version = "1.60"
rust-version = "1.65"

[dependencies]
arbitrary = { version = "1.2.0", features = ["derive"], optional = true }

[dev-dependencies]
hex-literal = "0.3"

[features]
std = []
arbitrary = ["std", "dep:arbitrary"]
db = []
std = []

[package.metadata.docs.rs]
all-features = true
Expand Down
4 changes: 2 additions & 2 deletions const-oid/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ well as a runtime OID library.

## Minimum Supported Rust Version

This crate requires **Rust 1.60** at a minimum.
This crate requires **Rust 1.65** at a minimum.

We may change the MSRV in the future, but it will be accompanied by a minor
version bump.
Expand All @@ -84,7 +84,7 @@ dual licensed as above, without any additional terms or conditions.
[docs-image]: https://docs.rs/const-oid/badge.svg
[docs-link]: https://docs.rs/const-oid/
[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg
[rustc-image]: https://img.shields.io/badge/rustc-1.60+-blue.svg
[rustc-image]: https://img.shields.io/badge/rustc-1.65+-blue.svg
[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg
[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/300570-formats
[build-image]: https://github.com/RustCrypto/formats/workflows/const-oid/badge.svg?branch=master&event=push
Expand Down
26 changes: 26 additions & 0 deletions const-oid/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,3 +271,29 @@ impl fmt::Display for ObjectIdentifier {
Ok(())
}
}

// Implement by hand because the derive would create invalid values.
// Use the constructor to create a valid oid with at least 3 arcs.
#[cfg(feature = "arbitrary")]
impl<'a> arbitrary::Arbitrary<'a> for ObjectIdentifier {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let first = u.int_in_range(0..=arcs::ARC_MAX_FIRST)?;
let second = u.int_in_range(0..=arcs::ARC_MAX_SECOND)?;
let third = u.arbitrary()?;

let mut oid = Self::from_arcs([first, second, third])
.map_err(|_| arbitrary::Error::IncorrectFormat)?;

for arc in u.arbitrary_iter()? {
oid = oid
.push_arc(arc?)
.map_err(|_| arbitrary::Error::IncorrectFormat)?;
}

Ok(oid)
}

fn size_hint(depth: usize) -> (usize, Option<usize>) {
(Arc::size_hint(depth).0.saturating_mul(3), None)
}
}
2 changes: 2 additions & 0 deletions der/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ edition = "2021"
rust-version = "1.65"

[dependencies]
arbitrary = { version = "1.2.0", features = ["derive"], optional = true }
const-oid = { version = "=0.10.0-pre", optional = true, path = "../const-oid" }
der_derive = { version = "=0.7.0-pre", optional = true, path = "derive" }
flagset = { version = "0.4.3", optional = true }
Expand All @@ -29,6 +30,7 @@ proptest = "1"

[features]
alloc = []
arbitrary = ["std", "dep:arbitrary", "const-oid?/arbitrary"]
derive = ["der_derive"]
oid = ["const-oid"]
pem = ["alloc", "pem-rfc7468/alloc", "zeroize"]
Expand Down
3 changes: 3 additions & 0 deletions der/src/asn1/any.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! ASN.1 `ANY` type.
#![cfg_attr(feature = "arbitrary", allow(clippy::integer_arithmetic))]

use crate::{
asn1::*, ByteSlice, Choice, Decode, DecodeValue, DerOrd, EncodeValue, Error, ErrorKind,
Expand All @@ -23,6 +24,7 @@ use crate::asn1::ObjectIdentifier;
/// Nevertheless, this crate defines an `ANY` type as it remains a familiar
/// and useful concept which is still extensively used in things like
/// PKI-related RFCs.
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct AnyRef<'a> {
/// Tag representing the type of the encoded value.
Expand Down Expand Up @@ -202,6 +204,7 @@ impl<'a> TryFrom<&'a [u8]> for AnyRef<'a> {
/// backing data.
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct Any {
/// Tag representing the type of the encoded value.
Expand Down
17 changes: 17 additions & 0 deletions der/src/asn1/bit_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,23 @@ impl<'a> FixedTag for BitStringRef<'a> {
const TAG: Tag = Tag::BitString;
}

// Implement by hand because the derive would create invalid values.
// Use the constructor to create a valid value.
#[cfg(feature = "arbitrary")]
impl<'a> arbitrary::Arbitrary<'a> for BitStringRef<'a> {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Self::new(
u.int_in_range(0..=Self::MAX_UNUSED_BITS)?,
ByteSlice::arbitrary(u)?.as_slice(),
)
.map_err(|_| arbitrary::Error::IncorrectFormat)
}

fn size_hint(depth: usize) -> (usize, Option<usize>) {
arbitrary::size_hint::and(u8::size_hint(depth), ByteSlice::size_hint(depth))
}
}

/// Owned form of ASN.1 `BIT STRING` type.
///
/// This type provides the same functionality as [`BitStringRef`] but owns the
Expand Down
2 changes: 2 additions & 0 deletions der/src/asn1/generalized_time.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! ASN.1 `GeneralizedTime` support.
#![cfg_attr(feature = "arbitrary", allow(clippy::integer_arithmetic))]

use crate::{
asn1::AnyRef,
Expand Down Expand Up @@ -26,6 +27,7 @@ use time::PrimitiveDateTime;
/// > is zero. GeneralizedTime values MUST NOT include fractional seconds.
///
/// [1]: https://tools.ietf.org/html/rfc5280#section-4.1.2.5.2
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct GeneralizedTime(DateTime);

Expand Down
4 changes: 2 additions & 2 deletions der/src/asn1/integer/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ mod allocating {
}

impl Uint {
/// Create a new [`UintRef`] from a byte slice.
/// Create a new [`Uint`] from a byte slice.
pub fn new(bytes: &[u8]) -> Result<Self> {
let inner = Bytes::new(uint::strip_leading_zeroes(bytes))
.map_err(|_| ErrorKind::Length { tag: Self::TAG })?;
Expand All @@ -136,7 +136,7 @@ mod allocating {
self.inner.as_slice()
}

/// Get the length of this [`UintRef`] in bytes.
/// Get the length of this [`Uint`] in bytes.
pub fn len(&self) -> Length {
self.inner.len()
}
Expand Down
20 changes: 20 additions & 0 deletions der/src/asn1/set_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,26 @@ where
}
}

// Implement by hand because the derive would create invalid values.
// Use the conversion from Vec to create a valid value.
#[cfg(feature = "arbitrary")]
impl<'a, T> arbitrary::Arbitrary<'a> for SetOfVec<T>
where
T: DerOrd + arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Self::try_from(
u.arbitrary_iter()?
.collect::<std::result::Result<Vec<_>, _>>()?,
)
.map_err(|_| arbitrary::Error::IncorrectFormat)
}

fn size_hint(_depth: usize) -> (usize, Option<usize>) {
(0, None)
}
}

/// Sort a mut slice according to its [`DerOrd`], returning any errors which
/// might occur during the comparison.
///
Expand Down
34 changes: 34 additions & 0 deletions der/src/asn1/utc_time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,40 @@ impl TryFrom<AnyRef<'_>> for UtcTime {
}
}

// Implement by hand because the derive would create invalid values.
// Use the conversion from DateTime to create a valid value.
// The DateTime type has a way bigger range of valid years than UtcTime,
// so the DateTime year is mapped into a valid range to throw away less inputs.
#[cfg(feature = "arbitrary")]
impl<'a> arbitrary::Arbitrary<'a> for UtcTime {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
const MIN_YEAR: u16 = 1970;
const VALID_YEAR_COUNT: u16 = MAX_YEAR - MIN_YEAR + 1;
const AVERAGE_SECONDS_IN_YEAR: u64 = 31_556_952;

let datetime = DateTime::arbitrary(u)?;
let year = datetime.year();
let duration = datetime.unix_duration();

// Clamp the year into a valid range to not throw away too much input
let valid_year = (year.saturating_sub(MIN_YEAR))
.rem_euclid(VALID_YEAR_COUNT)
.saturating_add(MIN_YEAR);
let year_to_remove = year.saturating_sub(valid_year);
let valid_duration = duration
- Duration::from_secs(
u64::from(year_to_remove).saturating_mul(AVERAGE_SECONDS_IN_YEAR),
);

Self::from_date_time(DateTime::from_unix_duration(valid_duration).expect("supported range"))
.map_err(|_| arbitrary::Error::IncorrectFormat)
}

fn size_hint(depth: usize) -> (usize, Option<usize>) {
DateTime::size_hint(depth)
}
}

#[cfg(test)]
mod tests {
use super::UtcTime;
Expand Down
17 changes: 17 additions & 0 deletions der/src/byte_slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,23 @@ impl<'a> TryFrom<&'a [u8]> for ByteSlice<'a> {
}
}

// Implement by hand because the derive would create invalid values.
// Make sure the length and the inner.len matches.
#[cfg(feature = "arbitrary")]
impl<'a> arbitrary::Arbitrary<'a> for ByteSlice<'a> {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let length = u.arbitrary()?;
Ok(Self {
length,
inner: u.bytes(u32::from(length) as usize)?,
})
}

fn size_hint(depth: usize) -> (usize, Option<usize>) {
arbitrary::size_hint::and(Length::size_hint(depth), (0, None))
}
}

#[cfg(feature = "alloc")]
mod allocating {
use super::ByteSlice;
Expand Down
17 changes: 17 additions & 0 deletions der/src/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,20 @@ impl TryFrom<&[u8]> for Bytes {
Self::new(slice)
}
}

// Implement by hand because the derive would create invalid values.
// Make sure the length and the inner.len matches.
#[cfg(feature = "arbitrary")]
impl<'a> arbitrary::Arbitrary<'a> for Bytes {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let length = u.arbitrary()?;
Ok(Self {
length,
inner: Box::from(u.bytes(u32::from(length) as usize)?),
})
}

fn size_hint(depth: usize) -> (usize, Option<usize>) {
arbitrary::size_hint::and(Length::size_hint(depth), (0, None))
}
}
Loading

0 comments on commit 6082a98

Please sign in to comment.