diff --git a/der/src/asn1.rs b/der/src/asn1.rs index 12fe46b4b..4b1bcf52e 100644 --- a/der/src/asn1.rs +++ b/der/src/asn1.rs @@ -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")))] diff --git a/der/src/asn1/integer/bigint.rs b/der/src/asn1/integer/bigint.rs index 6e69636d3..79dd55e5a 100644 --- a/der/src/asn1/integer/bigint.rs +++ b/der/src/asn1/integer/bigint.rs @@ -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 { + 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>(reader: &mut R, header: Header) -> Result { + 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 { + 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> for Uint { + type Error = Error; + + fn try_from(any: AnyRef<'a>) -> Result { + 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; diff --git a/der/src/byte_slice.rs b/der/src/byte_slice.rs index 9b86d7dcc..f5edddded 100644 --- a/der/src/byte_slice.rs +++ b/der/src/byte_slice.rs @@ -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) + } + } +} diff --git a/der/src/bytes.rs b/der/src/bytes.rs index 39bbac550..5f9167c90 100644 --- a/der/src/bytes.rs +++ b/der/src/bytes.rs @@ -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; @@ -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 { @@ -90,6 +95,16 @@ impl From> 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> for Bytes { fn from(s: ByteSlice<'_>) -> Bytes { Bytes { diff --git a/x509-cert/src/certificate.rs b/x509-cert/src/certificate.rs index 0c2faf982..f58817a6d 100644 --- a/x509-cert/src/certificate.rs +++ b/x509-cert/src/certificate.rs @@ -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}; @@ -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, diff --git a/x509-cert/src/crl.rs b/x509-cert/src/crl.rs index 98bef1c81..3e83c57a0 100644 --- a/x509-cert/src/crl.rs +++ b/x509-cert/src/crl.rs @@ -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; @@ -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>, } diff --git a/x509-cert/src/ext/pkix/authkeyid.rs b/x509-cert/src/ext/pkix/authkeyid.rs index 1f504c0f2..b9a2be04b 100644 --- a/x509-cert/src/ext/pkix/authkeyid.rs +++ b/x509-cert/src/ext/pkix/authkeyid.rs @@ -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]. @@ -28,7 +29,7 @@ pub struct AuthorityKeyIdentifier<'a> { pub authority_cert_issuer: Option>, #[asn1(context_specific = "2", tag_mode = "IMPLICIT", optional = "true")] - pub authority_cert_serial_number: Option>, + pub authority_cert_serial_number: Option, } impl<'a> AssociatedOid for AuthorityKeyIdentifier<'a> { diff --git a/x509-cert/src/lib.rs b/x509-cert/src/lib.rs index ea0718375..c70fa4b1a 100644 --- a/x509-cert/src/lib.rs +++ b/x509-cert/src/lib.rs @@ -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}; diff --git a/x509-cert/src/serial_number.rs b/x509-cert/src/serial_number.rs new file mode 100644 index 000000000..87120e308 --- /dev/null +++ b/x509-cert/src/serial_number.rs @@ -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 { + 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 { + 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>(reader: &mut R, header: Header) -> Result { + 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 = ::TAG; +} diff --git a/x509-cert/tests/certificate.rs b/x509-cert/tests/certificate.rs index 467885754..8775f82bb 100644 --- a/x509-cert/tests/certificate.rs +++ b/x509-cert/tests/certificate.rs @@ -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::*; @@ -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(), diff --git a/x509-cert/tests/pkix_extensions.rs b/x509-cert/tests/pkix_extensions.rs index fbaf3b44f..538403fd2 100644 --- a/x509-cert/tests/pkix_extensions.rs +++ b/x509-cert/tests/pkix_extensions.rs @@ -1,6 +1,6 @@ //! Certificate tests use const_oid::AssociatedOid; -use der::asn1::{Ia5StringRef, PrintableStringRef, UintRef, Utf8StringRef}; +use der::asn1::{Ia5StringRef, PrintableStringRef, Utf8StringRef}; use der::{Decode, Encode, ErrorKind, Length, Tag, Tagged}; use hex_literal::hex; use x509_cert::ext::pkix::crl::dp::{DistributionPoint, ReasonFlags, Reasons}; @@ -8,7 +8,7 @@ use x509_cert::ext::pkix::name::{DistributionPointName, GeneralName, GeneralName use x509_cert::ext::pkix::*; use x509_cert::ext::Extensions; use x509_cert::name::Name; -use x509_cert::{Certificate, Version}; +use x509_cert::{serial_number::SerialNumber, Certificate, Version}; use const_oid::db::rfc5280::*; use const_oid::db::rfc5912::ID_CE_CERTIFICATE_POLICIES; @@ -486,7 +486,7 @@ fn decode_cert() { let target_serial: [u8; 1] = [2]; 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(), diff --git a/x509-ocsp/src/lib.rs b/x509-ocsp/src/lib.rs index e9218328b..6b93d91f4 100644 --- a/x509-ocsp/src/lib.rs +++ b/x509-ocsp/src/lib.rs @@ -11,6 +11,7 @@ use x509_cert::ext::pkix::name::GeneralName; use x509_cert::ext::pkix::{AuthorityInfoAccessSyntax, CrlReason}; use x509_cert::ext::Extensions; use x509_cert::name::Name; +use x509_cert::serial_number::SerialNumber; use x509_cert::Certificate; use alloc::vec::Vec; @@ -152,7 +153,7 @@ pub struct CertId<'a> { pub hash_algorithm: AlgorithmIdentifierRef<'a>, pub issuer_name_hash: OctetStringRef<'a>, pub issuer_key_hash: OctetStringRef<'a>, - pub serial_number: UintRef<'a>, + pub serial_number: SerialNumber, } /// OCSPResponse structure as defined in [RFC 6960 Section 4.2.1].