diff --git a/der/src/asn1.rs b/der/src/asn1.rs index fd9a753df..0dbed1bb3 100644 --- a/der/src/asn1.rs +++ b/der/src/asn1.rs @@ -15,6 +15,7 @@ mod octet_string; mod oid; mod optional; mod printable_string; +mod real; mod sequence; mod sequence_of; mod set_of; diff --git a/der/src/asn1/integer.rs b/der/src/asn1/integer.rs index 2d93575a1..39ace21b3 100644 --- a/der/src/asn1/integer.rs +++ b/der/src/asn1/integer.rs @@ -1,8 +1,8 @@ //! ASN.1 `INTEGER` support. pub(super) mod bigint; -mod int; -mod uint; +pub(super) mod int; +pub(super) mod uint; use crate::{ asn1::Any, ByteSlice, DecodeValue, Decoder, EncodeValue, Encoder, Error, FixedTag, Header, diff --git a/der/src/asn1/integer/uint.rs b/der/src/asn1/integer/uint.rs index e49a02b05..4b3812066 100644 --- a/der/src/asn1/integer/uint.rs +++ b/der/src/asn1/integer/uint.rs @@ -6,7 +6,7 @@ use crate::{Encoder, Length, Result, Tag}; /// zeroes removed. /// /// Returns a byte array of the requested size containing a big endian integer. -pub(super) fn decode_to_slice(bytes: &[u8]) -> Result<&[u8]> { +pub(crate) fn decode_to_slice(bytes: &[u8]) -> Result<&[u8]> { // The `INTEGER` type always encodes a signed value, so for unsigned // values the leading `0x00` byte may need to be removed. // @@ -40,7 +40,7 @@ pub(super) fn decode_to_array(bytes: &[u8]) -> Result<[u8; N]> { } /// Encode the given big endian bytes representing an integer as ASN.1 DER. -pub(super) fn encode_bytes(encoder: &mut Encoder<'_>, bytes: &[u8]) -> Result<()> { +pub(crate) fn encode_bytes(encoder: &mut Encoder<'_>, bytes: &[u8]) -> Result<()> { let bytes = strip_leading_zeroes(bytes); if needs_leading_zero(bytes) { @@ -52,13 +52,13 @@ pub(super) fn encode_bytes(encoder: &mut Encoder<'_>, bytes: &[u8]) -> Result<() /// Get the encoded length for the given unsigned integer serialized as bytes. #[inline] -pub(super) fn encoded_len(bytes: &[u8]) -> Result { +pub(crate) fn encoded_len(bytes: &[u8]) -> Result { let bytes = strip_leading_zeroes(bytes); Length::try_from(bytes.len())? + needs_leading_zero(bytes) as u8 } /// Strip the leading zeroes from the given byte slice -pub(super) fn strip_leading_zeroes(mut bytes: &[u8]) -> &[u8] { +pub(crate) fn strip_leading_zeroes(mut bytes: &[u8]) -> &[u8] { while let Some((byte, rest)) = bytes.split_first() { if *byte == 0 && !rest.is_empty() { bytes = rest; diff --git a/der/src/asn1/real.rs b/der/src/asn1/real.rs new file mode 100644 index 000000000..f69ec96a6 --- /dev/null +++ b/der/src/asn1/real.rs @@ -0,0 +1,973 @@ +//! ASN.1 `NULL` support. +use crate::{ + str_slice::StrSlice, ByteSlice, DecodeValue, Decoder, EncodeValue, Encoder, FixedTag, Header, + Length, Result, Tag, +}; + +use super::integer::uint::strip_leading_zeroes; + +impl DecodeValue<'_> for f64 { + fn decode_value(decoder: &mut Decoder<'_>, header: Header) -> Result { + let bytes = ByteSlice::decode_value(decoder, header)?.as_bytes(); + + if header.length == Length::ZERO { + Ok(0.0) + } else if is_nth_bit_one::<7>(bytes) { + // Binary encoding from section 8.5.7 applies + let sign: u64 = if is_nth_bit_one::<6>(bytes) { 1 } else { 0 }; + // Section 8.5.7.2: Check the base -- the DER specs say that only base 2 should be supported in DER + let base = mnth_bits_to_u8::<5, 4>(bytes); + if base != 0 { + // Real related error: base is not DER compliant (base encoded in enum) + return Err(Tag::Real.value_error()); + } + + // Section 8.5.7.3 + let scaling_factor = mnth_bits_to_u8::<3, 2>(bytes); + + // Section 8.5.7.4 + let mantissa_start; + let exponent = match mnth_bits_to_u8::<1, 0>(bytes) { + 0 => { + mantissa_start = 2; + let ebytes = (i16::from_be_bytes([0x0, bytes[1]])).to_be_bytes(); + u64::from_be_bytes([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ebytes[0], ebytes[1]]) + } + 1 => { + mantissa_start = 3; + let ebytes = (i16::from_be_bytes([bytes[1], bytes[2]])).to_be_bytes(); + u64::from_be_bytes([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ebytes[0], ebytes[1]]) + } + _ => { + // Real related error: encoded exponent cannot be represented on an IEEE-754 double + return Err(Tag::Real.value_error()); + } + }; + // Section 8.5.7.5: Read the remaining bytes for the mantissa + let mut n_bytes = [0x0; 8]; + for (pos, byte) in bytes[mantissa_start..].iter().rev().enumerate() { + n_bytes[7 - pos] = *byte; + } + let n = u64::from_be_bytes(n_bytes); + // Multiply byt 2^F corresponds to just a left shift + let mantissa = n << scaling_factor; + // Create the f64 + Ok(encode_f64(sign, exponent, mantissa)) + } else if is_nth_bit_one::<6>(bytes) { + // This either a special value, or it's the value minus zero is encoded, section 8.5.9 applies + match mnth_bits_to_u8::<1, 0>(bytes) { + 0 => Ok(f64::INFINITY), + 1 => Ok(f64::NEG_INFINITY), + 2 => Ok(f64::NAN), + 3 => Ok(-0.0_f64), + _ => unreachable!(), + } + } else { + let astr = StrSlice::from_bytes(&bytes[1..])?; + match astr.inner.parse::() { + Ok(val) => Ok(val), + Err(_) => { + // Real related error: encoding not supported or malformed + Err(Tag::Real.value_error()) + } + } + } + } +} + +impl EncodeValue for f64 { + fn value_len(&self) -> Result { + if self.is_sign_positive() && (*self) < f64::MIN_POSITIVE { + // Zero: positive yet smaller than the minimum positive number + Ok(Length::ZERO) + } else if self.is_nan() + || self.is_infinite() + || (self.is_sign_negative() && -self < f64::MIN_POSITIVE) + { + // NaN, infinite (positive or negative), or negative zero (negative but its negative is less than the min positive number) + Ok(Length::ONE) + } else { + // The length is that of the first octets plus those needed for the exponent plus those needed for the mantissa + let (_sign, exponent, mantissa) = decode_f64(*self); + let exponent_len = if exponent == 0 { + // Section 8.5.7.4: there must be at least one octet for exponent encoding + // But, if the exponent is zero, it'll be skipped, so we make sure force it to 1 + Length::ONE + } else { + let ebytes = exponent.to_be_bytes(); + Length::try_from(strip_leading_zeroes(&ebytes).len())? + }; + let mantissa_len = if mantissa == 0 { + Length::ONE + } else { + let mbytes = mantissa.to_be_bytes(); + // encoded_len(&mbytes)? + Length::try_from(strip_leading_zeroes(&mbytes).len())? + }; + exponent_len + mantissa_len + Length::ONE + } + } + + fn encode_value(&self, encoder: &mut Encoder<'_>) -> Result<()> { + // Check if special value + // Encode zero first, if it's zero + // Special value from section 8.5.9 if non zero + if self.is_nan() + || self.is_infinite() + || (self.is_sign_negative() && -self < f64::MIN_POSITIVE) + || (self.is_sign_positive() && (*self) < f64::MIN_POSITIVE) + { + if self.is_sign_positive() && (*self) < f64::MIN_POSITIVE { + // Zero + return Ok(()); + } else if self.is_nan() { + // Not a number + encoder.bytes(&[0b0100_0010])?; + } else if self.is_infinite() { + if self.is_sign_negative() { + // Negative infinity + encoder.bytes(&[0b0100_0001])?; + } else { + // Plus infinity + encoder.bytes(&[0b0100_0000])?; + } + } else { + // Minus zero + encoder.bytes(&[0b0100_0011])?; + } + } else { + // Always use binary encoding, set bit 8 to 1 + let mut first_byte = 0b1000_0000; + if self.is_sign_negative() { + // Section 8.5.7.1: set bit 7 to 1 if negative + first_byte |= 0b0100_0000; + } + // Bits 6 and 5 are set to 0 to specify that binary encoding is used + + // NOTE: the scaling factor is only used to align the implicit point of the mantissa. + // This is unnecessary in DER because the base is 2, and therefore necessarily aligned. + // Therefore, we do not modify the mantissa in anyway after this function call, which + // already adds the implicit one of the IEEE 754 representation. + let (_sign, exponent, mantissa) = decode_f64(*self); + + // Encode the exponent as two's complement on 16 bits and remove the bias + let exponent_bytes = exponent.to_be_bytes(); + let ebytes = strip_leading_zeroes(&exponent_bytes); + + match ebytes.len() { + 0 | 1 => {} + 2 => first_byte |= 0b0000_0001, + 3 => first_byte |= 0b0000_0010, + _ => todo!("support multi octet exponent encoding"), + } + + encoder.bytes(&[first_byte])?; + + // Encode both bytes or just the last one, handled by encode_bytes directly + // Rust already encodes the data as two's complement, so no further processing is needed + encoder.bytes(ebytes)?; + + // Now, encode the mantissa as unsigned binary number + let mantissa_bytes = mantissa.to_be_bytes(); + let mbytes = strip_leading_zeroes(&mantissa_bytes); + encoder.bytes(mbytes)?; + } + Ok(()) + } +} + +impl FixedTag for f64 { + const TAG: Tag = Tag::Real; +} + +/// Is the N-th bit 1 in the first octet? +/// NOTE: this function is zero indexed +pub(crate) fn is_nth_bit_one(bytes: &[u8]) -> bool { + if N < 8 { + bytes + .get(0) + .map(|byte| byte & (1 << N) != 0) + .unwrap_or(false) + } else { + false + } +} + +/// Convert bits M, N into a u8, in the first octet only +pub(crate) fn mnth_bits_to_u8(bytes: &[u8]) -> u8 { + let bit_m = is_nth_bit_one::(bytes); + let bit_n = is_nth_bit_one::(bytes); + (bit_m as u8) << 1 | bit_n as u8 +} + +/// Decode an f64 as its sign, exponent, and mantissa in u64 and in that order, using bit shifts and masks. +/// Note: this function **removes** the 1023 bias from the exponent and adds the implicit 1 +pub(crate) fn decode_f64(f: f64) -> (u64, u64, u64) { + let bits = f.to_bits(); + let sign = bits >> 63; + let exponent = bits >> 52 & 0x7ff; + let exponent_bytes_no_bias = (exponent as i16 - 1023).to_be_bytes(); + let exponent_no_bias = u64::from_be_bytes([ + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + exponent_bytes_no_bias[0], + exponent_bytes_no_bias[1], + ]); + let mantissa = bits & 0xfffffffffffff; + (sign, exponent_no_bias, mantissa + 1) +} + +/// Encode an f64 from its sign, exponent (**without** the 1023 bias), and (mantissa - 1) using bit shifts as received by ASN1 +pub(crate) fn encode_f64(sign: u64, exponent: u64, mantissa: u64) -> f64 { + // Add the bias to the exponent + let exponent_with_bias = + (i16::from_be_bytes([exponent.to_be_bytes()[6], exponent.to_be_bytes()[7]]) + 1023) as u64; + let bits = sign << 63 | exponent_with_bias << 52 | (mantissa - 1); + f64::from_bits(bits) +} + +#[cfg(test)] +mod tests { + use crate::{Decode, Encode}; + + #[test] + fn decode_subnormal() { + assert!(f64::from_der(&[0x09, 0x01, 0b0100_0010]).unwrap().is_nan()); + let plus_infty = f64::from_der(&[0x09, 0x01, 0b0100_0000]).unwrap(); + assert!(plus_infty.is_infinite() && plus_infty.is_sign_positive()); + let neg_infty = f64::from_der(&[0x09, 0x01, 0b0100_0001]).unwrap(); + assert!(neg_infty.is_infinite() && neg_infty.is_sign_negative()); + let neg_zero = f64::from_der(&[0x09, 0x01, 0b0100_0011]).unwrap(); + assert!(neg_zero.is_sign_negative() && neg_zero.abs() < f64::EPSILON); + } + + #[test] + fn encode_subnormal() { + // All subnormal fit in three bytes + let mut buffer = [0u8; 3]; + assert_eq!( + &[0x09, 0x01, 0b0100_0010], + f64::NAN.encode_to_slice(&mut buffer).unwrap() + ); + assert_eq!( + &[0x09, 0x01, 0b0100_0000], + f64::INFINITY.encode_to_slice(&mut buffer).unwrap() + ); + assert_eq!( + &[0x09, 0x01, 0b0100_0001], + f64::NEG_INFINITY.encode_to_slice(&mut buffer).unwrap() + ); + assert_eq!( + &[0x09, 0x01, 0b0100_0011], + (-0.0_f64).encode_to_slice(&mut buffer).unwrap() + ); + } + + #[test] + fn encdec_normal() { + // The comments correspond to the decoded value from the ASN.1 playground when the bytes are inputed. + { + // rec1value R ::= 0 + let val = 0.0; + let expected = &[0x09, 0x0]; + let mut buffer = [0u8; 2]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!( + expected, encoded, + "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + val, encoded, expected + ); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + // rec1value R ::= { mantissa 1, base 2, exponent 0 } + let val = 1.0; + let expected = &[0x09, 0x03, 0x80, 0x00, 0x01]; + let mut buffer = [0u8; 5]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!( + expected, encoded, + "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + val, encoded, expected + ); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + // rec1value R ::= { mantissa -1, base 2, exponent 0 } + let val = -1.0; + let expected = &[0x09, 0x03, 0xc0, 0x00, 0x01]; + let mut buffer = [0u8; 5]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!( + expected, encoded, + "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + val, encoded, expected + ); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + // rec1value R ::= { mantissa -1, base 2, exponent 1 } + let val = -1.0000000000000002; + let expected = &[0x09, 0x03, 0xc0, 0x00, 0x02]; + let mut buffer = [0u8; 5]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!( + expected, encoded, + "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + val, encoded, expected + ); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + // rec1value R ::= { mantissa 1, base 2, exponent -1022 } + // NOTE: f64::MIN_EXP == -1021 so the exponent decoded by ASN.1 is what we expect + let val = f64::MIN_POSITIVE; + let expected = &[0x09, 0x04, 0x81, 0xfc, 0x02, 0x01]; + let mut buffer = [0u8; 7]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!( + expected, encoded, + "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + val, encoded, expected + ); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + // rec4value R ::= { mantissa 1, base 2, exponent 3 } + let val = 1.0000000000000016; + let expected = &[0x09, 0x03, 0x80, 0x00, 0x08]; + let mut buffer = [0u8; 5]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!( + expected, encoded, + "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + val, encoded, expected + ); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + // rec5value R ::= { mantissa 4222124650659841, base 2, exponent 4 } + let val = 31.0; + let expected = &[ + 0x9, 0x9, 0x80, 0x04, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + ]; + let mut buffer = [0u8; 11]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!( + expected, encoded, + "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + val, encoded, expected + ); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + } + + #[test] + fn encdec_irrationals() { + { + let val = core::f64::consts::PI; + let expected = &[ + 0x09, 0x09, 0x80, 0x01, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x19, + ]; + let mut buffer = [0u8; 11]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!( + expected, encoded, + "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + val, encoded, expected + ); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = core::f64::consts::E; + let expected = &[ + 0x09, 0x09, 0x80, 0x01, 0x05, 0xbf, 0x0a, 0x8b, 0x14, 0x57, 0x6a, + ]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!( + expected, encoded, + "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + val, encoded, expected + ); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + { + let val = core::f64::consts::LN_2; + let expected = &[ + 0x09, 0x0a, 0x81, 0xff, 0xff, 0x6, 0x2e, 0x42, 0xfe, 0xfa, 0x39, 0xf0, + ]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!( + expected, encoded, + "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + val, encoded, expected + ); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + } + + #[test] + fn encdec_reasonable_f64() { + // Tests the encoding and decoding of reals with some arbitrary numbers + { + // rec1value R ::= { mantissa 2414341043715239, base 2, exponent 21 } + let val = 3221417.1584163485; + let expected = &[ + 0x9, 0x9, 0x80, 0x15, 0x8, 0x93, 0xd4, 0x94, 0x46, 0xfc, 0xa7, + ]; + let mut buffer = [0u8; 11]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!( + expected, encoded, + "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + val, encoded, expected + ); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + // rec1value R ::= { mantissa 2671155248072715, base 2, exponent 23 } + let val = 13364022.365665454; + let expected = &[ + 0x09, 0x09, 0x80, 0x17, 0x09, 0x7d, 0x66, 0xcb, 0xb3, 0x88, 0x0b, + ]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!( + expected, encoded, + "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + val, encoded, expected + ); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + // rec1value R ::= { mantissa -4386812962460287, base 2, exponent 14 } + let val = -32343.132588105735; + let expected = &[ + 0x09, 0x09, 0xc0, 0x0e, 0x0f, 0x95, 0xc8, 0x7c, 0x52, 0xd2, 0x7f, + ]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!( + expected, encoded, + "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + val, encoded, expected + ); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = -27084.866751869475; + let expected = &[ + 0x09, 0x09, 0xc0, 0x0e, 0x0a, 0x73, 0x37, 0x78, 0xdc, 0xd5, 0x4a, + ]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!( + expected, encoded, + "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + val, encoded, expected + ); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + // rec1value R ::= { mantissa -4372913134428149, base 2, exponent 7 } + let val = -252.28566647111404; + let expected = &[ + 0x09, 0x09, 0xc0, 0x07, 0x0f, 0x89, 0x24, 0x2e, 0x02, 0xdf, 0xf5, + ]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!( + expected, encoded, + "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + val, encoded, expected + ); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = -14.399709612928548; + let expected = &[ + 0x09, 0x09, 0xc0, 0x03, 0x0c, 0xcc, 0xa6, 0xbd, 0x06, 0xd9, 0x92, + ]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!( + expected, encoded, + "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + val, encoded, expected + ); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = -0.08340570261832964; + let expected = &[ + 0x09, 0x0a, 0xc1, 0xff, 0xfc, 0x05, 0x5a, 0x13, 0x7d, 0x0b, 0xae, 0x3d, + ]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!( + expected, encoded, + "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + val, encoded, expected + ); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = 0.00536851453803701; + let expected = &[ + 0x09, 0x0a, 0x81, 0xff, 0xf8, 0x05, 0xfd, 0x4b, 0xa5, 0xe7, 0x4c, 0x93, + ]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!( + expected, encoded, + "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + val, encoded, expected + ); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = 0.00045183525648866433; + let expected = &[ + 0x09, 0x0a, 0x81, 0xff, 0xf4, 0x0d, 0x9c, 0x89, 0xa6, 0x59, 0x33, 0x39, + ]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!( + expected, encoded, + "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + val, encoded, expected + ); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = 0.000033869092002682955; + let expected = &[ + 0x09, 0x0a, 0x81, 0xff, 0xf1, 0x01, 0xc1, 0xd5, 0x23, 0xd5, 0x54, 0x7c, + ]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!( + expected, encoded, + "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + val, encoded, expected + ); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = 0.0000011770891033600088; + let expected = &[ + 0x09, 0x0a, 0x81, 0xff, 0xec, 0x03, 0xbf, 0x8f, 0x27, 0xf4, 0x62, 0x56, + ]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!( + expected, encoded, + "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + val, encoded, expected + ); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = 0.00000005549514041997082; + let expected = &[ + 0x09, 0x0a, 0x81, 0xff, 0xe7, 0x0d, 0xcb, 0x31, 0xab, 0x6e, 0xb8, 0xd7, + ]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!( + expected, encoded, + "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + val, encoded, expected + ); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = 0.0000000012707044685547803; + let expected = &[ + 0x09, 0x0a, 0x81, 0xff, 0xe2, 0x05, 0xd4, 0x9e, 0x0a, 0xf2, 0xff, 0x1f, + ]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!( + expected, encoded, + "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + val, encoded, expected + ); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = 0.00000000002969611878378562; + let expected = &[ + 0x09, 0x09, 0x81, 0xff, 0xdd, 0x53, 0x5b, 0x6f, 0x97, 0xee, 0xb6, + ]; + let mut buffer = [0u8; 11]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!( + expected, encoded, + "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + val, encoded, expected + ); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + } + + #[test] + fn reject_non_canonical() { + assert!(f64::from_der(&[0x09, 0x81, 0x00]).is_err()); + } + + #[test] + fn encdec_f64() { + use super::{decode_f64, encode_f64}; + // Test that the extraction and recreation works + for val in [ + 1.0, + 0.1, + -0.1, + -1.0, + 0.0, + f64::MIN_POSITIVE, + f64::MAX, + f64::MIN, + 3.1415, + 951.2357864, + -3.1415, + -951.2357864, + ] { + let (s, e, m) = decode_f64(val); + let val2 = encode_f64(s, e, m); + assert!( + (val - val2).abs() < f64::EPSILON, + "fail - want {}\tgot {}", + val, + val2 + ); + } + } + + #[test] + fn validation_cases() { + // Caveat: these test cases are validated on the ASN.1 playground: https://asn1.io/asn1playground/ . + // The test case consists in inputing the bytes in the "decode" field and checking that the decoded + // value corresponds to the one encoded here. + // This tool encodes _all_ values that are non-zero in the ISO 6093 NR3 representation. + // This does not seem to perfectly adhere to the ITU specifications, Special Cases section. + // The implementation of this crate correctly supports decoding such values. It will, however, + // systematically encode REALs in their base 2 form, with a scaling factor where needed to + // ensure that the mantissa is either odd or zero (as per section 11.3.1). + + // Positive trivial numbers + { + let expect = 10.0; + let testcase = &[0x09, 0x05, 0x03, 0x31, 0x2E, 0x45, 0x31]; + let decoded = f64::from_der(testcase).unwrap(); + assert!( + (decoded - expect).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + expect, + decoded + ); + } + { + let expect = 100.0; + let testcase = &[0x09, 0x05, 0x03, 0x31, 0x2E, 0x45, 0x32]; + let decoded = f64::from_der(testcase).unwrap(); + assert!( + (decoded - expect).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + expect, + decoded + ); + } + { + let expect = 101.0; + let testcase = &[0x09, 0x08, 0x03, 0x31, 0x30, 0x31, 0x2E, 0x45, 0x2B, 0x30]; + let decoded = f64::from_der(testcase).unwrap(); + assert!( + (decoded - expect).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + expect, + decoded + ); + } + { + let expect = 101.0; + let testcase = &[0x09, 0x08, 0x03, 0x31, 0x30, 0x31, 0x2E, 0x45, 0x2B, 0x30]; + let decoded = f64::from_der(testcase).unwrap(); + assert!( + (decoded - expect).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + expect, + decoded + ); + } + { + let expect = 0.0; + let testcase = &[0x09, 0x00]; + let decoded = f64::from_der(testcase).unwrap(); + assert!( + (decoded - expect).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + expect, + decoded + ); + } + { + let expect = 951.2357864; + let testcase = &[ + 0x09, 0x0F, 0x03, 0x39, 0x35, 0x31, 0x32, 0x33, 0x35, 0x37, 0x38, 0x36, 0x34, 0x2E, + 0x45, 0x2D, 0x37, + ]; + let decoded = f64::from_der(testcase).unwrap(); + assert!( + (decoded - expect).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + expect, + decoded + ); + } + // Negative trivial numbers + { + let expect = -10.0; + let testcase = &[0x09, 0x06, 0x03, 0x2D, 0x31, 0x2E, 0x45, 0x31]; + let decoded = f64::from_der(testcase).unwrap(); + assert!( + (decoded - expect).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + expect, + decoded + ); + } + { + let expect = -100.0; + let testcase = &[0x09, 0x06, 0x03, 0x2D, 0x31, 0x2E, 0x45, 0x32]; + let decoded = f64::from_der(testcase).unwrap(); + assert!( + (decoded - expect).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + expect, + decoded + ); + } + { + let expect = -101.0; + let testcase = &[ + 0x09, 0x09, 0x03, 0x2D, 0x31, 0x30, 0x31, 0x2E, 0x45, 0x2B, 0x30, + ]; + let decoded = f64::from_der(testcase).unwrap(); + assert!( + (decoded - expect).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + expect, + decoded + ); + } + { + let expect = -0.5; + let testcase = &[0x09, 0x07, 0x03, 0x2D, 0x35, 0x2E, 0x45, 0x2D, 0x31]; + let decoded = f64::from_der(testcase).unwrap(); + assert!( + (decoded - expect).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + expect, + decoded + ); + } + { + let expect = -0.0; + let testcase = &[0x09, 0x03, 0x01, 0x2D, 0x30]; + let decoded = f64::from_der(testcase).unwrap(); + assert!( + (decoded - expect).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + expect, + decoded + ); + } + { + // Test NR3 decoding + let expect = -951.2357864; + let testcase = &[ + 0x09, 0x10, 0x03, 0x2D, 0x39, 0x35, 0x31, 0x32, 0x33, 0x35, 0x37, 0x38, 0x36, 0x34, + 0x2E, 0x45, 0x2D, 0x37, + ]; + let decoded = f64::from_der(testcase).unwrap(); + assert!( + (decoded - expect).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + expect, + decoded + ); + } + } +} diff --git a/der/src/tag.rs b/der/src/tag.rs index b9cefade0..7597da6b7 100644 --- a/der/src/tag.rs +++ b/der/src/tag.rs @@ -67,6 +67,9 @@ pub enum Tag { /// `OBJECT IDENTIFIER` tag: `6`. ObjectIdentifier, + /// `REAL` tag: `9`. + Real, + /// `ENUMERATED` tag: `10`. Enumerated, @@ -189,6 +192,7 @@ impl Tag { Tag::OctetString => 0x04, Tag::Null => 0x05, Tag::ObjectIdentifier => 0x06, + Tag::Real => 0x09, Tag::Enumerated => 0x0A, Tag::Utf8String => 0x0C, Tag::Sequence => 0x10 | CONSTRUCTED_FLAG, @@ -257,6 +261,7 @@ impl TryFrom for Tag { 0x04 => Ok(Tag::OctetString), 0x05 => Ok(Tag::Null), 0x06 => Ok(Tag::ObjectIdentifier), + 0x09 => Ok(Tag::Real), 0x0A => Ok(Tag::Enumerated), 0x0C => Ok(Tag::Utf8String), 0x12 => Ok(Tag::NumericString), @@ -330,6 +335,7 @@ impl fmt::Display for Tag { Tag::OctetString => f.write_str("OCTET STRING"), Tag::Null => f.write_str("NULL"), Tag::ObjectIdentifier => f.write_str("OBJECT IDENTIFIER"), + Tag::Real => f.write_str("REAL"), Tag::Enumerated => f.write_str("ENUMERATED"), Tag::Utf8String => f.write_str("UTF8String"), Tag::Set => f.write_str("SET"), @@ -388,6 +394,7 @@ mod tests { assert_eq!(Tag::OctetString.class(), Class::Universal); assert_eq!(Tag::Null.class(), Class::Universal); assert_eq!(Tag::ObjectIdentifier.class(), Class::Universal); + assert_eq!(Tag::Real.class(), Class::Universal); assert_eq!(Tag::Enumerated.class(), Class::Universal); assert_eq!(Tag::Utf8String.class(), Class::Universal); assert_eq!(Tag::Set.class(), Class::Universal);