use crate::{
Asn1DerError, DerObject, Sink, error::ErrorChain, rust::mem,
typed::{ DerTypeView, DerDecodable, DerEncodable }
};
#[derive(Copy, Clone)]
pub struct Integer<'a> {
object: DerObject<'a>
}
impl<'a> Integer<'a> {
#[cfg_attr(feature = "no_panic", no_panic::no_panic)]
pub fn new<S: Sink + Into<&'a[u8]>>(be_value: &[u8], is_negative: bool, mut sink: S)
-> Result<Self, Asn1DerError>
{
Self::write(be_value, is_negative, &mut sink).propagate(e!("Failed to construct integer"))?;
let object = DerObject::decode(sink.into())
.propagate(e!("Failed to load constructed integer"))?;
Ok(Self{ object })
}
#[cfg_attr(feature = "no_panic", no_panic::no_panic)]
pub fn is_negative(&self) -> bool {
match self.object.value().get(0) {
Some(first) => first & 0b1000_0000 != 0,
None => false
}
}
#[cfg_attr(feature = "no_panic", no_panic::no_panic)]
pub fn get_numbytes(&self) -> &[u8] {
let slice = self.object.value();
match slice.len() {
len if len >= 1 && slice[0] == 0 => &slice[1..],
_ => slice
}
}
#[cfg_attr(feature = "no_panic", no_panic::no_panic)]
pub fn copy_numbytes<T: AsMut<[u8]>>(&self, mut buf: T) -> Result<T, Asn1DerError> {
let (slice, buf_slice) = (self.get_numbytes(), buf.as_mut());
let to_skip = match slice.len() {
len if len > buf_slice.len() => Err(eunsupported!("The numeric value is too large"))?,
len => buf_slice.len() - len
};
buf_slice.iter_mut().skip(to_skip).zip(slice.iter()).for_each(|(t, b)| *t = *b);
Ok(buf)
}
#[cfg_attr(feature = "no_panic", no_panic::no_panic)] #[inline(always)]
pub fn write<S: Sink>(value: &[u8], is_negative: bool, sink: &mut S)
-> Result<(), Asn1DerError>
{
let to_skip = value.iter().take_while(|b| **b == 0).count();
let len = value.iter().skip(to_skip).count();
let value_prefix = match value.get(to_skip) {
None => b"\x00".as_ref(),
Some(first) if first & 0b1000_0000 != 0 && !is_negative => b"\x00".as_ref(),
_ => b"".as_ref()
};
let len = match len.checked_add(value_prefix.len()) {
Some(len) => len,
None => Err(eunsupported!("The number length would exceed `usize::max_value()`"))?
};
let mut value = value_prefix.iter()
.chain(value.iter().skip(to_skip));
DerObject::write(Self::TAG, len, &mut value, sink).propagate(e!("Failed to write integer"))
}
}
impl<'a> DerTypeView<'a> for Integer<'a> {
const TAG: u8 = b'\x02';
#[cfg_attr(feature = "no_panic", no_panic::no_panic)]
fn object(&self) -> DerObject<'a> {
self.object
}
}
impl<'a> DerDecodable<'a> for Integer<'a> {
#[cfg_attr(feature = "no_panic", no_panic::no_panic)]
fn load(object: DerObject<'a>) -> Result<Self, Asn1DerError> {
match object.value() {
_ if object.tag() != Self::TAG => Err(einval!("DER object is not an integer"))?,
value if value.is_empty() => Err(einval!("DER object is not a valid integer"))?,
value if value.len() >= 2 && value[0] == b'\x00' && value[1] & 0b1000_0000 == 0 =>
Err(einval!("DER object is not a valid integer")),
value if value.len() >= 2 && value[0] == b'\xff' && value[1] & 0b1000_0000 != 0 =>
Err(einval!("DER object is not a valid integer")),
_ => Ok(Self{ object }),
}
}
}
impl<'a> DerEncodable for Integer<'a> {
#[cfg_attr(feature = "no_panic", no_panic::no_panic)]
fn encode<S: Sink>(&self, sink: &mut S) -> Result<(), Asn1DerError> {
self.object().encode(sink).propagate(e!("Failed to encode integer"))
}
}
macro_rules! impl_dercodable {
(unsigned: $num:ty) => {
impl<'a> DerDecodable<'a> for $num {
#[cfg_attr(feature = "no_panic", no_panic::no_panic)]
fn load(object: DerObject<'a>) -> Result<Self, Asn1DerError> {
let integer = Integer::load(object).propagate(e!("Failed to load integer"))?;
let buf = integer.copy_numbytes([0; mem::size_of::<Self>()])
.propagate(e!("The numeric value is too large"))?;
match Self::from_be_bytes(buf) {
_ if integer.is_negative() =>
Err(eunsupported!("The numeric value is negative")),
num => Ok(num)
}
}
}
impl DerEncodable for $num {
#[cfg_attr(feature = "no_panic", no_panic::no_panic)]
fn encode<S: Sink>(&self, sink: &mut S) -> Result<(), Asn1DerError> {
Integer::write(&self.to_be_bytes(), false, sink)
}
}
};
(unsigned: $($num:ty),+) => ($( impl_dercodable!(unsigned: $num); )+);
}
impl_dercodable!(unsigned: u8, u16, u32, u64, u128, usize);