Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion der/src/asn1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ pub use self::{

#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub use self::{any::Any, bit_string::BitString, octet_string::OctetString, set_of::SetOfVec};
pub use self::{
any::Any, bit_string::BitString, integer::bigint::Uint, octet_string::OctetString,
set_of::SetOfVec,
};

#[cfg(feature = "oid")]
#[cfg_attr(docsrs, doc(cfg(feature = "oid")))]
Expand Down
122 changes: 122 additions & 0 deletions der/src/asn1/integer/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,128 @@ impl<'a> FixedTag for UintRef<'a> {

impl<'a> OrdIsValueOrd for UintRef<'a> {}

#[cfg(feature = "alloc")]
pub use self::allocating::Uint;

#[cfg(feature = "alloc")]
mod allocating {
use super::{super::uint, UintRef};
use crate::{
asn1::AnyRef,
ord::OrdIsValueOrd,
referenced::{OwnedToRef, RefToOwned},
Bytes, DecodeValue, EncodeValue, Error, ErrorKind, FixedTag, Header, Length, Reader,
Result, Tag, Writer,
};

/// "Big" unsigned ASN.1 `INTEGER` type.
///
/// Provides direct storage for the big endian bytes which comprise an
/// unsigned integer value.
///
/// Intended for use cases like very large integers that are used in
/// cryptographic applications (e.g. keys, signatures).
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct Uint {
/// Inner value
inner: Bytes,
}

impl Uint {
/// Create a new [`UintRef`] 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 })?;

Ok(Self { inner })
}

/// Borrow the inner byte slice which contains the least significant bytes
/// of a big endian integer value with all leading zeros stripped.
pub fn as_bytes(&self) -> &[u8] {
self.inner.as_slice()
}

/// Get the length of this [`UintRef`] in bytes.
pub fn len(&self) -> Length {
self.inner.len()
}

/// Is the inner byte slice empty?
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
}

impl<'a> DecodeValue<'a> for Uint {
fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
let bytes = Bytes::decode_value(reader, header)?;
let result = Self::new(uint::decode_to_slice(bytes.as_slice())?)?;

// Ensure we compute the same encoded length as the original any value.
if result.value_len()? != header.length {
return Err(Self::TAG.non_canonical_error());
}

Ok(result)
}
}

impl EncodeValue for Uint {
fn value_len(&self) -> Result<Length> {
uint::encoded_len(self.inner.as_slice())
}

fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
// Add leading `0x00` byte if required
if self.value_len()? > self.len() {
writer.write_byte(0)?;
}

writer.write(self.as_bytes())
}
}

impl<'a> From<&UintRef<'a>> for Uint {
fn from(value: &UintRef<'a>) -> Uint {
let inner = Bytes::new(value.as_bytes()).expect("Invalid Uint");
Uint { inner }
}
}

impl<'a> TryFrom<AnyRef<'a>> for Uint {
type Error = Error;

fn try_from(any: AnyRef<'a>) -> Result<Uint> {
any.decode_into()
}
}

impl FixedTag for Uint {
const TAG: Tag = Tag::Integer;
}

impl OrdIsValueOrd for Uint {}

impl<'a> RefToOwned<'a> for UintRef<'a> {
type Owned = Uint;
fn to_owned(&self) -> Self::Owned {
let inner = self.inner.to_owned();

Uint { inner }
}
}

impl OwnedToRef for Uint {
type Borrowed<'a> = UintRef<'a>;
fn to_ref(&self) -> Self::Borrowed<'_> {
let inner = self.inner.to_ref();

UintRef { inner }
}
}
}

#[cfg(test)]
mod tests {
use super::UintRef;
Expand Down
13 changes: 13 additions & 0 deletions der/src/byte_slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,16 @@ impl<'a> TryFrom<&'a [u8]> for ByteSlice<'a> {
Self::new(slice)
}
}

#[cfg(feature = "alloc")]
mod allocating {
use super::ByteSlice;
use crate::{referenced::RefToOwned, Bytes};

impl<'a> RefToOwned<'a> for ByteSlice<'a> {
type Owned = Bytes;
fn to_owned(&self) -> Self::Owned {
Bytes::from(*self)
}
}
}
19 changes: 17 additions & 2 deletions der/src/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
//! library-level length limitation i.e. `Length::max()`.

use crate::{
str_slice::StrSlice, ByteSlice, DecodeValue, DerOrd, EncodeValue, Error, Header, Length,
Reader, Result, Writer,
referenced::OwnedToRef, str_slice::StrSlice, ByteSlice, DecodeValue, DerOrd, EncodeValue,
Error, Header, Length, Reader, Result, Writer,
};
use alloc::boxed::Box;
use core::cmp::Ordering;
Expand Down Expand Up @@ -39,6 +39,11 @@ impl Bytes {
pub fn len(&self) -> Length {
self.length
}

/// Is this [`Bytes`] empty?
pub fn is_empty(&self) -> bool {
self.len() == Length::ZERO
}
}

impl AsRef<[u8]> for Bytes {
Expand Down Expand Up @@ -90,6 +95,16 @@ impl From<StrSlice<'_>> for Bytes {
}
}

impl OwnedToRef for Bytes {
type Borrowed<'a> = ByteSlice<'a>;
fn to_ref(&self) -> Self::Borrowed<'_> {
ByteSlice {
length: self.length,
inner: self.inner.as_ref(),
}
}
}

impl From<ByteSlice<'_>> for Bytes {
fn from(s: ByteSlice<'_>) -> Bytes {
Bytes {
Expand Down
6 changes: 3 additions & 3 deletions x509-cert/src/certificate.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
//! Certificate types

use crate::{name::Name, time::Validity};
use crate::{name::Name, serial_number::SerialNumber, time::Validity};

use alloc::vec::Vec;
use core::cmp::Ordering;

use const_oid::AssociatedOid;
use der::asn1::{BitStringRef, UintRef};
use der::asn1::BitStringRef;
use der::{Decode, Enumerated, Error, ErrorKind, Sequence, ValueOrd};
use spki::{AlgorithmIdentifierRef, SubjectPublicKeyInfoRef};

Expand Down Expand Up @@ -83,7 +83,7 @@ pub struct TbsCertificate<'a> {
#[asn1(context_specific = "0", default = "Default::default")]
pub version: Version,

pub serial_number: UintRef<'a>,
pub serial_number: SerialNumber,
pub signature: AlgorithmIdentifierRef<'a>,
pub issuer: Name,
pub validity: Validity,
Expand Down
5 changes: 3 additions & 2 deletions x509-cert/src/crl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

use crate::ext::Extensions;
use crate::name::Name;
use crate::serial_number::SerialNumber;
use crate::time::Time;
use crate::Version;

use alloc::vec::Vec;

use der::asn1::{BitStringRef, UintRef};
use der::asn1::BitStringRef;
use der::{Sequence, ValueOrd};
use spki::AlgorithmIdentifierRef;

Expand Down Expand Up @@ -47,7 +48,7 @@ pub struct CertificateList<'a> {
#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
#[allow(missing_docs)]
pub struct RevokedCert<'a> {
pub serial_number: UintRef<'a>,
pub serial_number: SerialNumber,
pub revocation_date: Time,
pub crl_entry_extensions: Option<Extensions<'a>>,
}
Expand Down
5 changes: 3 additions & 2 deletions x509-cert/src/ext/pkix/authkeyid.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use super::name::GeneralNames;
use crate::serial_number::SerialNumber;

use const_oid::db::rfc5280::ID_CE_AUTHORITY_KEY_IDENTIFIER;
use const_oid::{AssociatedOid, ObjectIdentifier};
use der::asn1::{OctetStringRef, UintRef};
use der::asn1::OctetStringRef;
use der::Sequence;

/// AuthorityKeyIdentifier as defined in [RFC 5280 Section 4.2.1.1].
Expand All @@ -28,7 +29,7 @@ pub struct AuthorityKeyIdentifier<'a> {
pub authority_cert_issuer: Option<GeneralNames<'a>>,

#[asn1(context_specific = "2", tag_mode = "IMPLICIT", optional = "true")]
pub authority_cert_serial_number: Option<UintRef<'a>>,
pub authority_cert_serial_number: Option<SerialNumber>,
}

impl<'a> AssociatedOid for AuthorityKeyIdentifier<'a> {
Expand Down
1 change: 1 addition & 0 deletions x509-cert/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub mod crl;
pub mod ext;
pub mod name;
pub mod request;
pub mod serial_number;
pub mod time;

pub use certificate::{Certificate, PkiPath, TbsCertificate, Version};
Expand Down
75 changes: 75 additions & 0 deletions x509-cert/src/serial_number.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//! X.509 serial number

use der::{
asn1::Uint, DecodeValue, EncodeValue, ErrorKind, FixedTag, Header, Length, Reader, Result, Tag,
ValueOrd, Writer,
};

/// [RFC 5280 Section 4.1.2.2.] Serial Number
///
/// The serial number MUST be a positive integer assigned by the CA to
/// each certificate. It MUST be unique for each certificate issued by a
/// given CA (i.e., the issuer name and serial number identify a unique
/// certificate). CAs MUST force the serialNumber to be a non-negative
/// integer.
///
/// Given the uniqueness requirements above, serial numbers can be
/// expected to contain long integers. Certificate users MUST be able to
/// handle serialNumber values up to 20 octets. Conforming CAs MUST NOT
/// use serialNumber values longer than 20 octets.
///
/// Note: Non-conforming CAs may issue certificates with serial numbers
/// that are negative or zero. Certificate users SHOULD be prepared to
/// gracefully handle such certificates.
#[derive(Clone, Debug, Eq, PartialEq, ValueOrd, PartialOrd, Ord)]
pub struct SerialNumber {
inner: Uint,
}

impl SerialNumber {
/// Maximum length in bytes for a [`SerialNumber`]
pub const MAX_LEN: Length = Length::new(20);

/// Create a new [`SerialNumber`] from a byte slice.
pub fn new(bytes: &[u8]) -> Result<Self> {
let inner = Uint::new(bytes)?;

if inner.len() > SerialNumber::MAX_LEN {
return Err(ErrorKind::Overlength.into());
}

Ok(Self { inner })
}

/// Borrow the inner byte slice which contains the least significant bytes
/// of a big endian integer value with all leading zeros stripped.
pub fn as_bytes(&self) -> &[u8] {
self.inner.as_bytes()
}
}

impl EncodeValue for SerialNumber {
fn value_len(&self) -> Result<Length> {
self.inner.value_len()
}

fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
self.inner.encode_value(writer)
}
}

impl<'a> DecodeValue<'a> for SerialNumber {
fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
let inner = Uint::decode_value(reader, header)?;

if inner.len() > SerialNumber::MAX_LEN {
return Err(ErrorKind::Overlength.into());
}

Ok(Self { inner })
}
}

impl FixedTag for SerialNumber {
const TAG: Tag = <Uint as FixedTag>::TAG;
}
7 changes: 3 additions & 4 deletions x509-cert/tests/certificate.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
//! Certificate tests

use der::{
asn1::{
BitStringRef, ContextSpecific, ObjectIdentifier, PrintableStringRef, UintRef, Utf8StringRef,
},
asn1::{BitStringRef, ContextSpecific, ObjectIdentifier, PrintableStringRef, Utf8StringRef},
Decode, DecodeValue, Encode, FixedTag, Header, Reader, Tag, Tagged,
};
use hex_literal::hex;
use spki::AlgorithmIdentifierRef;
use x509_cert::serial_number::SerialNumber;
use x509_cert::Certificate;
use x509_cert::*;

Expand Down Expand Up @@ -209,7 +208,7 @@ fn decode_cert() {
];
assert_eq!(
cert.tbs_certificate.serial_number,
UintRef::new(&target_serial).unwrap()
SerialNumber::new(&target_serial).unwrap()
);
assert_eq!(
cert.tbs_certificate.signature.oid.to_string(),
Expand Down
Loading