Skip to content

Commit

Permalink
x509-cert: adds a convenience helper to generate serials
Browse files Browse the repository at this point in the history
This follows the CABF ballot 164 recommendation to have serials use 64
bits from a CSPRNG.

This also provides the user with the option to prefix its values (for
use of an instance id).
  • Loading branch information
baloo committed Nov 26, 2023
1 parent 70cef1d commit 31a07a9
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions x509-cert/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ rust-version = "1.65"
[dependencies]
const-oid = { version = "0.9.3", features = ["db"] }
der = { version = "0.7.6", features = ["alloc", "derive", "flagset", "oid"] }
generic-array = { version = "0.14.7", default-features = false }
spki = { version = "0.7.2", features = ["alloc"] }

# optional dependencies
Expand Down
77 changes: 77 additions & 0 deletions x509-cert/src/serial_number.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@ use der::{
DecodeValue, EncodeValue, ErrorKind, FixedTag, Header, Length, Reader, Result, Tag, ValueOrd,
Writer,
};
#[cfg(feature = "builder")]
use {
core::ops::Add,
generic_array::{
typenum::{
consts::{U20, U8},
marker_traits::Unsigned,
type_operators::{Max, Min},
uint::UTerm,
},
ArrayLength, GenericArray,
},
signature::rand_core::CryptoRngCore,
};

use crate::certificate::{Profile, Rfc5280};

Expand Down Expand Up @@ -65,6 +79,48 @@ impl<P: Profile> SerialNumber<P> {
pub fn as_bytes(&self) -> &[u8] {
self.inner.as_bytes()
}

/// Generates a random serial number from RNG.
///
/// This follows the recommendation the CAB forum [ballot 164] and uses a minimum of 64 bits
/// of output from the CSPRNG.
///
/// [ballot 164]: https://cabforum.org/2016/03/31/ballot-164/
#[cfg(feature = "builder")]
pub fn generate<N>(rng: &mut impl CryptoRngCore) -> Result<Self>
where
N: Unsigned + ArrayLength<u8>,
N: Min<U8>, // Rand minimum is 64 bits
N: Max<U20>, // Max length is 20 bytes
{
Self::generate_with_prefix::<UTerm, N>(GenericArray::default(), rng)
}

/// Generates a random serial number from RNG. Include a prefix value.
///
/// This follows the recommendation the CAB forum [ballot 164] and uses a minimum of 64 bits
/// of output from the CSPRNG.
///
/// [ballot 164]: https://cabforum.org/2016/03/31/ballot-164/
#[cfg(feature = "builder")]
pub fn generate_with_prefix<Prefix, N>(
prefix: GenericArray<u8, Prefix>,
rng: &mut impl CryptoRngCore,
) -> Result<Self>
where
N: Unsigned,
Prefix: Unsigned + ArrayLength<u8>,
Prefix: Add<N>,
<Prefix as Add<N>>::Output: ArrayLength<u8>,
N: Min<U8>, // Rand minimum is 64 bits
<Prefix as Add<N>>::Output: Max<U20>, // Max length is 20 bytes
{
let mut buf = GenericArray::<_, <Prefix as Add<N>>::Output>::default();
buf[..Prefix::USIZE].copy_from_slice(&prefix);
rng.fill_bytes(&mut buf[Prefix::USIZE..]);

Self::new(&buf)
}
}

impl<P: Profile> EncodeValue for SerialNumber<P> {
Expand Down Expand Up @@ -151,6 +207,9 @@ impl<'a, P: Profile> arbitrary::Arbitrary<'a> for SerialNumber<P> {
mod tests {
use alloc::string::ToString;

#[cfg(feature = "builder")]
use generic_array::{arr, typenum::consts::U17};

use super::*;

#[test]
Expand Down Expand Up @@ -193,4 +252,22 @@ mod tests {
assert_eq!(sn.to_string(), "01")
}
}

#[cfg(feature = "builder")]
#[test]
fn serial_number_generate() {
let sn = SerialNumber::<Rfc5280>::generate::<U17>(&mut rand::thread_rng()).unwrap();

// Underlying storage uses signed int for compatibility reasons,
// we may need to prefix the value with 0x00 to make it an unsigned.
// in which case the length is going to be 18.
assert!(matches!(sn.as_bytes().len(), 17..=18));

let sn = SerialNumber::<Rfc5280>::generate_with_prefix::<_, U17>(
arr![u8; 1,2,3],
&mut rand::thread_rng(),
)
.unwrap();
assert_eq!(sn.as_bytes().len(), 20);
}
}

0 comments on commit 31a07a9

Please sign in to comment.