use crate::{
Any, ByteSlice, Encodable, Encoder, Error, ErrorKind, Header, Length, Result, Tag, Tagged,
};
use core::{convert::TryFrom, marker::PhantomData};
use typenum::{NonZero, Unsigned};
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd)]
#[cfg_attr(docsrs, doc(cfg(feature = "big-uint")))]
pub struct BigUInt<'a, N: Unsigned + NonZero> {
inner: ByteSlice<'a>,
size: PhantomData<N>,
}
impl<'a, N> BigUInt<'a, N>
where
N: Unsigned + NonZero,
{
pub fn new(mut bytes: &'a [u8]) -> Result<Self> {
while bytes.get(0).cloned() == Some(0) {
bytes = &bytes[1..];
}
if bytes.len() > N::to_usize() {
return Err(ErrorKind::Length { tag: Self::TAG }.into());
}
ByteSlice::new(bytes)
.map(|inner| Self {
inner,
size: PhantomData,
})
.map_err(|_| ErrorKind::Length { tag: Self::TAG }.into())
}
pub fn as_bytes(&self) -> &'a [u8] {
self.inner.as_bytes()
}
pub fn len(&self) -> Length {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
fn inner_len(self) -> Result<Length> {
self.len()
+ match self.inner.as_ref().get(0).cloned() {
Some(n) if n >= 0x80 => 1u8, None => 1u8, _ => 0u8, }
}
}
impl<'a, N> From<&BigUInt<'a, N>> for BigUInt<'a, N>
where
N: Unsigned + NonZero,
{
fn from(value: &BigUInt<'a, N>) -> BigUInt<'a, N> {
*value
}
}
impl<'a, N> TryFrom<Any<'a>> for BigUInt<'a, N>
where
N: Unsigned + NonZero,
{
type Error = Error;
fn try_from(any: Any<'a>) -> Result<BigUInt<'a, N>> {
any.tag().assert_eq(Tag::Integer)?;
let mut bytes = any.as_bytes();
if let Some(byte) = bytes.get(0).cloned() {
if byte > 0x80 {
return Err(ErrorKind::Value { tag: Self::TAG }.into());
}
}
if bytes.len() > N::to_usize() {
if bytes.len() != N::to_usize().checked_add(1).unwrap() {
return Err(ErrorKind::Length { tag: Self::TAG }.into());
}
if bytes.get(0).cloned() != Some(0) {
return Err(ErrorKind::Value { tag: Self::TAG }.into());
}
bytes = &bytes[1..];
}
Self::new(bytes)
}
}
impl<'a, N> Encodable for BigUInt<'a, N>
where
N: Unsigned + NonZero,
{
fn encoded_len(&self) -> Result<Length> {
self.inner_len()?.for_tlv()
}
fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> {
Header::new(Self::TAG, self.inner_len()?)?.encode(encoder)?;
if self.inner_len()? > self.len() {
encoder.byte(0)?;
}
encoder.bytes(self.as_bytes())
}
}
impl<'a, N> Tagged for BigUInt<'a, N>
where
N: Unsigned + NonZero,
{
const TAG: Tag = Tag::Integer;
}
#[cfg(test)]
mod tests {
use super::BigUInt;
use crate::{asn1::integer::tests::*, Any, Decodable, ErrorKind, Result, Tag};
use core::convert::TryInto;
type BigU8<'a> = BigUInt<'a, typenum::U1>;
type BigU16<'a> = BigUInt<'a, typenum::U2>;
fn parse_bigu8_from_any(bytes: &[u8]) -> Result<BigU8<'_>> {
Any::new(Tag::Integer, bytes)?.try_into()
}
#[test]
fn decode_empty() {
let x = parse_bigu8_from_any(&[]).unwrap();
assert_eq!(x.as_bytes(), &[]);
}
#[test]
fn decode_bigu8() {
assert!(BigU8::from_der(I0_BYTES).unwrap().is_empty());
assert_eq!(&[127], BigU8::from_der(I127_BYTES).unwrap().as_bytes());
assert_eq!(&[128], BigU8::from_der(I128_BYTES).unwrap().as_bytes());
assert_eq!(&[255], BigU8::from_der(I255_BYTES).unwrap().as_bytes());
}
#[test]
fn decode_bigu16() {
assert!(BigU16::from_der(I0_BYTES).unwrap().is_empty());
assert_eq!(&[127], BigU16::from_der(I127_BYTES).unwrap().as_bytes());
assert_eq!(&[128], BigU16::from_der(I128_BYTES).unwrap().as_bytes());
assert_eq!(&[255], BigU16::from_der(I255_BYTES).unwrap().as_bytes());
assert_eq!(
&[0x01, 0x00],
BigU16::from_der(I256_BYTES).unwrap().as_bytes()
);
assert_eq!(
&[0x7F, 0xFF],
BigU16::from_der(I32767_BYTES).unwrap().as_bytes()
);
}
#[test]
fn reject_oversize_without_extra_zero() {
let err = parse_bigu8_from_any(&[0x81]).err().unwrap();
assert_eq!(err.kind(), ErrorKind::Value { tag: Tag::Integer });
}
}