Skip to content

der, x509-cert: support for signed bigints, negative serial numbers #823

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Dec 30, 2022
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
22 changes: 11 additions & 11 deletions der/src/asn1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,24 @@ mod utc_time;
mod utf8_string;
mod videotex_string;

#[cfg(feature = "oid")]
#[cfg_attr(docsrs, doc(cfg(feature = "oid")))]
pub use const_oid::ObjectIdentifier;

#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub use self::{
any::Any, bit_string::BitString, integer::bigint::Int, integer::bigint::Uint,
octet_string::OctetString, set_of::SetOfVec,
};
pub use self::{
any::AnyRef,
bit_string::{BitStringIter, BitStringRef},
choice::Choice,
context_specific::{ContextSpecific, ContextSpecificRef},
generalized_time::GeneralizedTime,
ia5_string::Ia5StringRef,
integer::bigint::IntRef,
integer::bigint::UintRef,
null::Null,
octet_string::OctetStringRef,
Expand All @@ -44,14 +55,3 @@ pub use self::{
utf8_string::Utf8StringRef,
videotex_string::VideotexStringRef,
};

#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
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")))]
pub use const_oid::ObjectIdentifier;
3 changes: 2 additions & 1 deletion der/src/asn1/integer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ pub(super) mod bigint;
pub(super) mod int;
pub(super) mod uint;

use core::{cmp::Ordering, mem};

use crate::{
asn1::AnyRef, ByteSlice, DecodeValue, EncodeValue, Error, FixedTag, Header, Length, Reader,
Result, SliceWriter, Tag, ValueOrd, Writer,
};
use core::{cmp::Ordering, mem};

macro_rules! impl_int_encoding {
($($int:ty => $uint:ty),+) => {
Expand Down
256 changes: 252 additions & 4 deletions der/src/asn1/integer/bigint.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,94 @@
//! "Big" ASN.1 `INTEGER` types.

use super::uint;
use super::{int, uint};
use crate::{
asn1::AnyRef, ord::OrdIsValueOrd, ByteSlice, DecodeValue, EncodeValue, Error, ErrorKind,
FixedTag, Header, Length, Reader, Result, Tag, Writer,
};

/// "Big" signed ASN.1 `INTEGER` type.
///
/// Provides direct access to the underlying big endian bytes which comprise
/// an signed integer value.
///
/// Intended for use cases like very large integers that are used in
/// cryptographic applications (e.g. keys, signatures).
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct IntRef<'a> {
/// Inner value
inner: ByteSlice<'a>,
}

impl<'a> IntRef<'a> {
/// Create a new [`IntRef`] from a byte slice.
pub fn new(bytes: &'a [u8]) -> Result<Self> {
let inner = ByteSlice::new(int::strip_leading_ones(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 ones stripped.
pub fn as_bytes(&self) -> &'a [u8] {
self.inner.as_slice()
}

/// Get the length of this [`IntRef`] 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 IntRef<'a> {
fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
let bytes = ByteSlice::decode_value(reader, header)?.as_slice();
let result = Self::new(int::decode_to_slice(bytes)?)?;

// 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<'a> EncodeValue for IntRef<'a> {
fn value_len(&self) -> Result<Length> {
int::encoded_len(self.inner.as_slice())
}

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

impl<'a> From<&IntRef<'a>> for IntRef<'a> {
fn from(value: &IntRef<'a>) -> IntRef<'a> {
*value
}
}

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

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

impl<'a> FixedTag for IntRef<'a> {
const TAG: Tag = Tag::Integer;
}

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

/// "Big" unsigned ASN.1 `INTEGER` type.
///
/// Provides direct access to the underlying big endian bytes which comprise an
Expand Down Expand Up @@ -95,11 +178,11 @@ impl<'a> FixedTag for UintRef<'a> {
impl<'a> OrdIsValueOrd for UintRef<'a> {}

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

#[cfg(feature = "alloc")]
mod allocating {
use super::{super::uint, UintRef};
use super::{super::int, super::uint, IntRef, UintRef};
use crate::{
asn1::AnyRef,
ord::OrdIsValueOrd,
Expand All @@ -108,6 +191,108 @@ mod allocating {
Result, Tag, Writer,
};

/// "Big" signed ASN.1 `INTEGER` type.
///
/// Provides direct storage for the big endian bytes which comprise an
/// signed 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 Int {
/// Inner value
inner: Bytes,
}

impl Int {
/// Create a new [`Int`] from a byte slice.
pub fn new(bytes: &[u8]) -> Result<Self> {
let inner = Bytes::new(int::strip_leading_ones(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 ones stripped.
pub fn as_bytes(&self) -> &[u8] {
self.inner.as_slice()
}

/// Get the length of this [`Int`] 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 Int {
fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
let bytes = Bytes::decode_value(reader, header)?;
let result = Self::new(int::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 Int {
fn value_len(&self) -> Result<Length> {
int::encoded_len(self.inner.as_slice())
}

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

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

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

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

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

impl OrdIsValueOrd for Int {}

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

Int { inner }
}
}

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

IntRef { inner }
}
}

/// "Big" unsigned ASN.1 `INTEGER` type.
///
/// Provides direct storage for the big endian bytes which comprise an
Expand Down Expand Up @@ -220,10 +405,73 @@ mod allocating {
mod tests {
use super::UintRef;
use crate::{
asn1::{integer::tests::*, AnyRef},
asn1::{integer::tests::*, AnyRef, IntRef},
Decode, Encode, ErrorKind, SliceWriter, Tag,
};

#[test]
fn decode_int_bytes() {
// Positive numbers decode, but have zero extensions as necessary
// (to distinguish them from negative representations).
assert_eq!(&[0], IntRef::from_der(I0_BYTES).unwrap().as_bytes());
assert_eq!(&[127], IntRef::from_der(I127_BYTES).unwrap().as_bytes());
assert_eq!(&[0, 128], IntRef::from_der(I128_BYTES).unwrap().as_bytes());
assert_eq!(&[0, 255], IntRef::from_der(I255_BYTES).unwrap().as_bytes());

assert_eq!(
&[0x01, 0x00],
IntRef::from_der(I256_BYTES).unwrap().as_bytes()
);

assert_eq!(
&[0x7F, 0xFF],
IntRef::from_der(I32767_BYTES).unwrap().as_bytes()
);

// Negative integers decode.
assert_eq!(&[128], IntRef::from_der(INEG128_BYTES).unwrap().as_bytes());
assert_eq!(
&[255, 127],
IntRef::from_der(INEG129_BYTES).unwrap().as_bytes()
);
assert_eq!(
&[128, 0],
IntRef::from_der(INEG32768_BYTES).unwrap().as_bytes()
);
}

#[test]
fn encode_int_bytes() {
for &example in &[
I0_BYTES,
I127_BYTES,
I128_BYTES,
I255_BYTES,
I256_BYTES,
I32767_BYTES,
] {
let uint = IntRef::from_der(example).unwrap();

let mut buf = [0u8; 128];
let mut encoder = SliceWriter::new(&mut buf);
uint.encode(&mut encoder).unwrap();

let result = encoder.finish().unwrap();
assert_eq!(example, result);
}

for &example in &[INEG128_BYTES, INEG129_BYTES, INEG32768_BYTES] {
let uint = IntRef::from_der(example).unwrap();

let mut buf = [0u8; 128];
let mut encoder = SliceWriter::new(&mut buf);
uint.encode(&mut encoder).unwrap();

let result = encoder.finish().unwrap();
assert_eq!(example, result);
}
}

#[test]
fn decode_uint_bytes() {
assert_eq!(&[0], UintRef::from_der(I0_BYTES).unwrap().as_bytes());
Expand Down
Loading