From 841b75381fc5e5377701c971be185902eb3e16fa Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Tue, 11 Jan 2022 21:57:10 -0700 Subject: [PATCH 01/22] Add Real tag Source: https://www.oss.com/asn1/resources/books-whitepapers-pubs/larmouth-asn1-book.pdf, page 174 Signed-off-by: Christopher Rabotin --- der/src/tag.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/der/src/tag.rs b/der/src/tag.rs index 8b6241524..1ed86d99e 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); From 073112b6536a894fcee8694f5db3692e399b02d9 Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Thu, 13 Jan 2022 19:48:58 -0700 Subject: [PATCH 02/22] Initial work on Real --- der/src/asn1.rs | 1 + der/src/asn1/real.rs | 231 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 232 insertions(+) create mode 100644 der/src/asn1/real.rs diff --git a/der/src/asn1.rs b/der/src/asn1.rs index e6d854d7b..8a26c3b6d 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/real.rs b/der/src/asn1/real.rs new file mode 100644 index 000000000..d41517cf2 --- /dev/null +++ b/der/src/asn1/real.rs @@ -0,0 +1,231 @@ +//! ASN.1 `NULL` support. + +use crate::{ + asn1::Any, ByteSlice, DecodeValue, Decoder, Encodable, EncodeValue, Encoder, Error, ErrorKind, + FixedTag, Length, OrdIsValueOrd, Result, Tag, +}; + +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub enum SubnormalKind { + PlusInfinity, + MinusInfinity, + NotANumber, +} + +// ASN.1 `REAL` type. +// #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +// pub enum Real { +// Normal { +// mantissa: i32, +// base: u8, +// exponent: i32, +// }, +// Subnormal(SubnormalKind), +// } + +// ASN.1 `REAL` type. +// TODO: define as Real<$t>($t) and impl for f32 and f64 +// #[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] +// pub struct Real(f64); + +impl DecodeValue<'_> for f64 { + fn decode_value(decoder: &mut Decoder<'_>, length: Length) -> Result { + let bytes = ByteSlice::decode_value(decoder, length)?.as_bytes(); + + // Implementation of section 8.5.6 + // the nth bit function is zero indexed + if is_nth_bit_one::<7>(bytes) { + // Binary encoding from section 8.5.7 applies + let sign_bit: 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, but here we allow decoding of BER, just not encoding of BER + match mnth_bits_to_u8::<5, 4>(bytes) { + 0 => (), + 1 => unimplemented!("DER only supports REAL type in base 2 (provided a base 8 REAL)"), + 2 => unimplemented!("DER only supports REAL type in base 2 (provided a base 16 REAL)"), + 3 => unimplemented!("Reserved for further editions of this Recommendation | International Standard."), + _ => unreachable!() + }; + + // Section 8.5.7.3: grab the scaling factor + let scaling_factor = mnth_bits_to_u8::<3, 2>(bytes); + + // Section 8.5.7.4: + // 1. grab the number of octets used to express the exponent; + // 2. read the exponent as an i32 + let remaining_bytes; + let exponent: i32 = match mnth_bits_to_u8::<1, 0>(bytes) { + 0 => { + remaining_bytes = bytes.len() - 2; + i8::from_be_bytes([bytes[1]]).into() + } + 1 => { + remaining_bytes = bytes.len() - 3; + i16::from_be_bytes([bytes[1], bytes[2]]).into() + } + 2 => { + remaining_bytes = bytes.len() - 4; + i32::from_be_bytes([bytes[1], bytes[2], bytes[3], 0x0]).into() + } + 3 => { + // This is where the next octet encodes this value + let exp_len = usize::from(u8::from_be_bytes([bytes[1]])); + if exp_len > 4 { + unimplemented!("Exponent is encoded in more than 4 bits, but that cannot be represented in Rust (i32 is 4 bits long)"); + } + remaining_bytes = bytes.len() - exp_len; + i32::from_be_bytes(bytes[2..2 + exp_len].try_into().unwrap()) + } + _ => unreachable!(), + }; + if exponent > 1023 || exponent < -1022 { + unimplemented!("Exponent too large to be represented as a IEEE 754"); + } + // Section 8.5.7.5: Read the remaining bytes for the mantissa + // XXX: Is this correct? I'm afraid this will not correctly pad things + let n = u64::from_be_bytes(bytes[remaining_bytes..].try_into().unwrap()); + let mantissa = n * 2_u64.pow(scaling_factor.into()); + let m_bytes = mantissa.to_be_bytes(); + if m_bytes[0] > 0x0 || m_bytes[1] > 0x0f { + // Only 52 bits can be stored + unimplemented!("Mantissa too large to be represented as IEEE 754"); + } + + // Create the f64 + // TODO: Instead of doing the math, just build it from the bits: https://en.wikipedia.org/wiki/Double-precision_floating-point_format + let mantissa_f = mantissa as f64; + let exponent_f = 2.0_f64.powf(f64::from(exponent)); + + return Ok(sign * mantissa_f * exponent_f); + } else if is_nth_bit_one::<6>(bytes) { + // This either a special value, or it's the value minus zero is encoded + } else { + // Decimal encoding from section 8.5.8 applies (both bit 8 and 7 are one) + } + + if length.is_zero() { + todo!() + // Ok(f64) + } else { + Err(decoder.error(ErrorKind::Length { tag: Self::TAG })) + } + } +} + +impl EncodeValue for f64 { + fn value_len(&self) -> Result { + Ok(Length::ZERO) + } + + fn encode_value(&self, _encoder: &mut Encoder<'_>) -> Result<()> { + Ok(()) + } +} + +impl FixedTag for f64 { + const TAG: Tag = Tag::Real; +} + +// impl OrdIsValueOrd for Real {} + +impl<'a> From for Any<'a> { + fn from(_: f64) -> Any<'a> { + Any::from_tag_and_value(Tag::Real, ByteSlice::default()) + } +} + +impl TryFrom> for f64 { + type Error = Error; + + fn try_from(any: Any<'_>) -> Result { + any.decode_into() + } +} + +// impl TryFrom> for () { +// type Error = Error; + +// fn try_from(any: Any<'_>) -> Result<()> { +// Real::try_from(any).map(|_| ()) +// } +// } + +// impl<'a> From<()> for Any<'a> { +// fn from(_: ()) -> Any<'a> { +// Real.into() +// } +// } + +// impl DecodeValue<'_> for () { +// fn decode_value(decoder: &mut Decoder<'_>, length: Length) -> Result { +// Real::decode_value(decoder, length)?; +// Ok(()) +// } +// } + +// impl Encodable for () { +// fn encoded_len(&self) -> Result { +// Real.encoded_len() +// } + +// fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> { +// Real.encode(encoder) +// } +// } + +// impl FixedTag for () { +// const TAG: Tag = Tag::Real; +// } + +/// Is the N-th bit 1 in the first octet? +fn is_nth_bit_one(bytes: &[u8]) -> bool { + let mask = match N { + 0 => 0b00000001, + 1 => 0b00000010, + 2 => 0b00000100, + 3 => 0b00001000, + 4 => 0b00010000, + 5 => 0b00100000, + 6 => 0b01000000, + 7 => 0b10000000, + _ => return false, + }; + bytes.get(0).map(|byte| byte & mask != 0).unwrap_or(false) +} + +/// Convert bits M, N into a u8, in the first octet only +fn mnth_bits_to_u8(bytes: &[u8]) -> u8 { + let bit_m = is_nth_bit_one::(bytes); + let bit_n = is_nth_bit_one::(bytes); + let data = if bit_m && bit_n { + 0b11 + } else if !bit_m && !bit_n { + 0b00 + } else if bit_n { + 0b01 + } else { + 0b10 + }; + u8::from_be_bytes([data]) +} + +#[cfg(test)] +mod tests { + use crate::{Decodable, Encodable}; + + #[test] + fn decode() { + f64::from_der(&[0x05, 0x00]).unwrap(); + } + + #[test] + fn encode() { + let mut buffer = [0u8; 2]; + assert_eq!(&[0x05, 0x00], 0.1_f64.encode_to_slice(&mut buffer).unwrap()); + assert_eq!(&[0x05, 0x00], ().encode_to_slice(&mut buffer).unwrap()); + } + + #[test] + fn reject_non_canonical() { + assert!(f64::from_der(&[0x05, 0x81, 0x00]).is_err()); + } +} From 8b66223646dd96f971d1aa3edd869695434124ae Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Mon, 17 Jan 2022 12:38:14 -0700 Subject: [PATCH 03/22] Not sure I understand the base 10 encoding, seems like a bit string Signed-off-by: Christopher Rabotin --- der/src/asn1/real.rs | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/der/src/asn1/real.rs b/der/src/asn1/real.rs index d41517cf2..e4fc98a16 100644 --- a/der/src/asn1/real.rs +++ b/der/src/asn1/real.rs @@ -36,7 +36,7 @@ impl DecodeValue<'_> for f64 { // the nth bit function is zero indexed if is_nth_bit_one::<7>(bytes) { // Binary encoding from section 8.5.7 applies - let sign_bit: u64 = if is_nth_bit_one::<6>(bytes) { 1 } else { 0 }; + 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, but here we allow decoding of BER, just not encoding of BER match mnth_bits_to_u8::<5, 4>(bytes) { 0 => (), @@ -83,23 +83,35 @@ impl DecodeValue<'_> for f64 { // Section 8.5.7.5: Read the remaining bytes for the mantissa // XXX: Is this correct? I'm afraid this will not correctly pad things let n = u64::from_be_bytes(bytes[remaining_bytes..].try_into().unwrap()); - let mantissa = n * 2_u64.pow(scaling_factor.into()); + // Multiply byt 2^F corresponds to just a left shift + let mantissa = n << scaling_factor; let m_bytes = mantissa.to_be_bytes(); if m_bytes[0] > 0x0 || m_bytes[1] > 0x0f { // Only 52 bits can be stored unimplemented!("Mantissa too large to be represented as IEEE 754"); } - + let exponent_bits: u64 = (exponent + 1032).try_into().unwrap(); // Create the f64 - // TODO: Instead of doing the math, just build it from the bits: https://en.wikipedia.org/wiki/Double-precision_floating-point_format - let mantissa_f = mantissa as f64; - let exponent_f = 2.0_f64.powf(f64::from(exponent)); - - return Ok(sign * mantissa_f * exponent_f); + let bits = sign << 63 | exponent_bits << 52 | mantissa; + return Ok(f64::from_bits(bits)); } else if is_nth_bit_one::<6>(bytes) { - // This either a special value, or it's the value minus zero is encoded + // This either a special value, or it's the value minus zero is encoded, section 8.5.9 applies + return 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 { // Decimal encoding from section 8.5.8 applies (both bit 8 and 7 are one) + match mnth_bits_to_u8::<1, 0>(bytes) { + 0 => unimplemented!("Malformed decimal real"), + 1 => unimplemented!("DER only supports REAL type in NR3 encoded (NR1 provided)"), + 2 => unimplemented!("DER only supports REAL type in NR3 encoded (NR2 provided)"), + 3 => (), + _ => unreachable!(), + }; } if length.is_zero() { From 25db001ed6fb2f4bcdf1c8fa0830e2b5618efee2 Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Tue, 18 Jan 2022 12:13:41 -0700 Subject: [PATCH 04/22] Add decode/encode of f64; Add real error Signed-off-by: Christopher Rabotin --- der/src/asn1/real.rs | 146 +++++++++++++++++++++---------------------- der/src/error.rs | 31 +++++++++ 2 files changed, 102 insertions(+), 75 deletions(-) diff --git a/der/src/asn1/real.rs b/der/src/asn1/real.rs index e4fc98a16..14c253efa 100644 --- a/der/src/asn1/real.rs +++ b/der/src/asn1/real.rs @@ -2,32 +2,9 @@ use crate::{ asn1::Any, ByteSlice, DecodeValue, Decoder, Encodable, EncodeValue, Encoder, Error, ErrorKind, - FixedTag, Length, OrdIsValueOrd, Result, Tag, + FixedTag, Length, Result, Tag, }; -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub enum SubnormalKind { - PlusInfinity, - MinusInfinity, - NotANumber, -} - -// ASN.1 `REAL` type. -// #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -// pub enum Real { -// Normal { -// mantissa: i32, -// base: u8, -// exponent: i32, -// }, -// Subnormal(SubnormalKind), -// } - -// ASN.1 `REAL` type. -// TODO: define as Real<$t>($t) and impl for f32 and f64 -// #[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] -// pub struct Real(f64); - impl DecodeValue<'_> for f64 { fn decode_value(decoder: &mut Decoder<'_>, length: Length) -> Result { let bytes = ByteSlice::decode_value(decoder, length)?.as_bytes(); @@ -38,13 +15,10 @@ impl DecodeValue<'_> for f64 { // 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, but here we allow decoding of BER, just not encoding of BER - match mnth_bits_to_u8::<5, 4>(bytes) { - 0 => (), - 1 => unimplemented!("DER only supports REAL type in base 2 (provided a base 8 REAL)"), - 2 => unimplemented!("DER only supports REAL type in base 2 (provided a base 16 REAL)"), - 3 => unimplemented!("Reserved for further editions of this Recommendation | International Standard."), - _ => unreachable!() - }; + let base = mnth_bits_to_u8::<5, 4>(bytes); + if base != 0 { + return Err(Error::new(ErrorKind::RealBaseInvalid(base), Length::ZERO)); + } // Section 8.5.7.3: grab the scaling factor let scaling_factor = mnth_bits_to_u8::<3, 2>(bytes); @@ -53,47 +27,24 @@ impl DecodeValue<'_> for f64 { // 1. grab the number of octets used to express the exponent; // 2. read the exponent as an i32 let remaining_bytes; - let exponent: i32 = match mnth_bits_to_u8::<1, 0>(bytes) { + let exponent: i16 = match mnth_bits_to_u8::<1, 0>(bytes) { 0 => { remaining_bytes = bytes.len() - 2; - i8::from_be_bytes([bytes[1]]).into() + i16::from_be_bytes([0x0, bytes[1]]).into() } 1 => { remaining_bytes = bytes.len() - 3; - i16::from_be_bytes([bytes[1], bytes[2]]).into() - } - 2 => { - remaining_bytes = bytes.len() - 4; - i32::from_be_bytes([bytes[1], bytes[2], bytes[3], 0x0]).into() + i16::from_be_bytes([bytes[1], bytes[2]]) } - 3 => { - // This is where the next octet encodes this value - let exp_len = usize::from(u8::from_be_bytes([bytes[1]])); - if exp_len > 4 { - unimplemented!("Exponent is encoded in more than 4 bits, but that cannot be represented in Rust (i32 is 4 bits long)"); - } - remaining_bytes = bytes.len() - exp_len; - i32::from_be_bytes(bytes[2..2 + exp_len].try_into().unwrap()) - } - _ => unreachable!(), + _ => return Err(Error::new(ErrorKind::RealExponentTooLong, Length::ZERO)), }; - if exponent > 1023 || exponent < -1022 { - unimplemented!("Exponent too large to be represented as a IEEE 754"); - } // Section 8.5.7.5: Read the remaining bytes for the mantissa // XXX: Is this correct? I'm afraid this will not correctly pad things let n = u64::from_be_bytes(bytes[remaining_bytes..].try_into().unwrap()); // Multiply byt 2^F corresponds to just a left shift let mantissa = n << scaling_factor; - let m_bytes = mantissa.to_be_bytes(); - if m_bytes[0] > 0x0 || m_bytes[1] > 0x0f { - // Only 52 bits can be stored - unimplemented!("Mantissa too large to be represented as IEEE 754"); - } - let exponent_bits: u64 = (exponent + 1032).try_into().unwrap(); // Create the f64 - let bits = sign << 63 | exponent_bits << 52 | mantissa; - return Ok(f64::from_bits(bits)); + return Ok(integer_encode_f64(mantissa, exponent, sign)); } 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 return match mnth_bits_to_u8::<1, 0>(bytes) { @@ -104,28 +55,50 @@ impl DecodeValue<'_> for f64 { _ => unreachable!(), }; } else { - // Decimal encoding from section 8.5.8 applies (both bit 8 and 7 are one) - match mnth_bits_to_u8::<1, 0>(bytes) { - 0 => unimplemented!("Malformed decimal real"), - 1 => unimplemented!("DER only supports REAL type in NR3 encoded (NR1 provided)"), - 2 => unimplemented!("DER only supports REAL type in NR3 encoded (NR2 provided)"), - 3 => (), - _ => unreachable!(), - }; - } - - if length.is_zero() { - todo!() - // Ok(f64) - } else { - Err(decoder.error(ErrorKind::Length { tag: Self::TAG })) + #[cfg(feature = "std")] + { + // Decimal encoding from section 8.5.8 applies (both bit 8 and 7 are one) + let iso_kind = mnth_bits_to_u8::<1, 0>(bytes); + if iso_kind != 3 { + return Err(Error::new( + ErrorKind::RealISO6093EncodingNotSupported, + Length::ZERO, + )); + } + use std::str; + let astr = str::from_utf8(&bytes[3..]).unwrap(); + let af64 = astr.parse::().unwrap(); + } + #[cfg(not(feature = "std"))] + { + return Err(Error::new( + ErrorKind::RealISO6093EncodingNotSupported, + Length::ZERO, + )); + } } } } impl EncodeValue for f64 { fn value_len(&self) -> Result { - Ok(Length::ZERO) + if self.is_nan() || self.is_infinite() { + Ok(Length::ONE) + } else { + let mut len = 0; + // Perform encoding + let (mantissa, exponent, _sign) = integer_decode_f64(*self); + if exponent.is_positive() && exponent < 255 { + // Then the exponent is encoded only on one byte + len += 1; + } else { + len += 2; + } + + let mantissa_len = mantissa.encoded_len()?; + + Length::new(len) + mantissa_len + } } fn encode_value(&self, _encoder: &mut Encoder<'_>) -> Result<()> { @@ -220,6 +193,29 @@ fn mnth_bits_to_u8(bytes: &[u8]) -> u8 { u8::from_be_bytes([data]) } +/// Decode an f64 as its mantissa, exponent (shifted by 1023!), and sign data, all in u64 +/// From num_traits, src/floats.rs +/// https://github.com/rust-num/num-traits/blob/96a89e63258762d51f80ec141fcdf88d481d8dde/src/float.rs#L2008 +fn integer_decode_f64(f: f64) -> (u64, i16, u64) { + let bits = f.to_bits(); + let sign: u64 = if bits >> 63 == 0 { 0 } else { 1 }; + let mut exponent = ((bits >> 52) & 0x7ff) as i16; + let mantissa = if exponent == 0 { + (bits & 0xfffffffffffff) << 1 + } else { + (bits & 0xfffffffffffff) | 0x10000000000000 + }; + // Exponent bias + mantissa shift + exponent -= 1023 + 52; + (mantissa, exponent, u64::from(sign)) +} + +fn integer_encode_f64(mantissa: u64, exponent: i16, sign: u64) -> f64 { + let exponent = (exponent + 1023 + 52) as u64; + let vbits = sign << 63 | exponent << 52 | mantissa; + f64::from_bits(vbits) +} + #[cfg(test)] mod tests { use crate::{Decodable, Encodable}; diff --git a/der/src/error.rs b/der/src/error.rs index 551b4cd57..2ba60660c 100644 --- a/der/src/error.rs +++ b/der/src/error.rs @@ -220,6 +220,13 @@ pub enum ErrorKind { #[cfg_attr(docsrs, doc(cfg(feature = "std")))] PermissionDenied, + /// Real related error: base is not DER compliant (base encoded in enum) + RealBaseInvalid(u8), + /// Real related error: encoded exponent cannot be represented on an IEEE-754 double + RealExponentTooLong, + /// Real related error: encoding not supported (only NR3 is supported if the std feature is enabled) + RealISO6093EncodingNotSupported, + /// Unknown tag mode. TagModeUnknown, @@ -339,6 +346,30 @@ impl fmt::Display for ErrorKind { } ErrorKind::Utf8(e) => write!(f, "{}", e), ErrorKind::Value { tag } => write!(f, "malformed ASN.1 DER value for {}", tag), + ErrorKind::RealBaseInvalid(base) => match base { + 1 => write!( + f, + "DER only supports REAL type in base 2 (provided a base {} REAL)", + base + ), + 2 => write!( + f, + "DER only supports REAL type in base 2 (provided a base {} REAL)", + base + ), + 3 => write!( + f, + "reserved for further editions of this Recommendation | International Standard" + ), + _ => unreachable!(), + }, + ErrorKind::RealExponentTooLong => write!( + f, + "exponent encoded on more than 2 bytes, but that cannot be represented on an IEEE-754", + ), + ErrorKind::RealISO6093EncodingNotSupported => { + write!(f, "provided ISO 6093 encoding not supported") + } } } } From 0b66bceea723cd37e4858ac2770cac06af870650 Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Tue, 18 Jan 2022 17:33:05 -0700 Subject: [PATCH 05/22] Fixed the decoding of an f64 Signed-off-by: Christopher Rabotin --- der/src/asn1/integer.rs | 4 +- der/src/asn1/integer/uint.rs | 2 +- der/src/asn1/real.rs | 248 ++++++++++++++++++++++++++--------- der/src/error.rs | 8 +- 4 files changed, 190 insertions(+), 72 deletions(-) diff --git a/der/src/asn1/integer.rs b/der/src/asn1/integer.rs index 83847cb53..9abfc1831 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, Length, diff --git a/der/src/asn1/integer/uint.rs b/der/src/asn1/integer/uint.rs index 75948d364..2f6d29b54 100644 --- a/der/src/asn1/integer/uint.rs +++ b/der/src/asn1/integer/uint.rs @@ -35,7 +35,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) { diff --git a/der/src/asn1/real.rs b/der/src/asn1/real.rs index 14c253efa..cefb8cafe 100644 --- a/der/src/asn1/real.rs +++ b/der/src/asn1/real.rs @@ -1,17 +1,17 @@ //! ASN.1 `NULL` support. use crate::{ - asn1::Any, ByteSlice, DecodeValue, Decoder, Encodable, EncodeValue, Encoder, Error, ErrorKind, - FixedTag, Length, Result, Tag, + asn1::Any, str_slice::StrSlice, ByteSlice, DecodeValue, Decoder, Encodable, EncodeValue, + Encoder, Error, ErrorKind, FixedTag, Length, Result, Tag, }; impl DecodeValue<'_> for f64 { fn decode_value(decoder: &mut Decoder<'_>, length: Length) -> Result { let bytes = ByteSlice::decode_value(decoder, length)?.as_bytes(); - // Implementation of section 8.5.6 - // the nth bit function is zero indexed - if is_nth_bit_one::<7>(bytes) { + if length == Length::ONE && bytes[0] == 0x0 { + 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, but here we allow decoding of BER, just not encoding of BER @@ -27,20 +27,24 @@ impl DecodeValue<'_> for f64 { // 1. grab the number of octets used to express the exponent; // 2. read the exponent as an i32 let remaining_bytes; - let exponent: i16 = match mnth_bits_to_u8::<1, 0>(bytes) { + let exponent = match mnth_bits_to_u8::<1, 0>(bytes) { 0 => { remaining_bytes = bytes.len() - 2; - i16::from_be_bytes([0x0, bytes[1]]).into() + // i16::from_be_bytes([0x0, bytes[1]]).into() + u64::decode_value(decoder, Length::new(1))? } 1 => { remaining_bytes = bytes.len() - 3; - i16::from_be_bytes([bytes[1], bytes[2]]) + // i16::from_be_bytes([bytes[1], bytes[2]]) + u64::decode_value(decoder, Length::new(2))? } _ => return Err(Error::new(ErrorKind::RealExponentTooLong, Length::ZERO)), }; // Section 8.5.7.5: Read the remaining bytes for the mantissa // XXX: Is this correct? I'm afraid this will not correctly pad things - let n = u64::from_be_bytes(bytes[remaining_bytes..].try_into().unwrap()); + let remaining_len = (length - Length::new(remaining_bytes.try_into().unwrap()))?; + let n = u64::decode_value(decoder, remaining_len)?; + // let n = u64::from_be_bytes(bytes[remaining_bytes..].try_into().unwrap()); // Multiply byt 2^F corresponds to just a left shift let mantissa = n << scaling_factor; // Create the f64 @@ -55,53 +59,102 @@ impl DecodeValue<'_> for f64 { _ => unreachable!(), }; } else { - #[cfg(feature = "std")] - { - // Decimal encoding from section 8.5.8 applies (both bit 8 and 7 are one) - let iso_kind = mnth_bits_to_u8::<1, 0>(bytes); - if iso_kind != 3 { - return Err(Error::new( - ErrorKind::RealISO6093EncodingNotSupported, - Length::ZERO, - )); - } - use std::str; - let astr = str::from_utf8(&bytes[3..]).unwrap(); - let af64 = astr.parse::().unwrap(); - } - #[cfg(not(feature = "std"))] - { - return Err(Error::new( - ErrorKind::RealISO6093EncodingNotSupported, - Length::ZERO, - )); + let astr = StrSlice::from_bytes(&bytes[3..])?; + match astr.inner.parse::() { + Ok(val) => Ok(val), + Err(_) => Err(Error::new(ErrorKind::RealISO6093Error, Length::ZERO)), } } } } +use crate::asn1::integer::uint::encode_bytes; + impl EncodeValue for f64 { fn value_len(&self) -> Result { - if self.is_nan() || self.is_infinite() { + if self.is_nan() + || self.is_infinite() + || (self.is_sign_negative() && -self < f64::EPSILON) + || (self.is_sign_positive() && (*self) < f64::EPSILON) + { Ok(Length::ONE) } else { - let mut len = 0; // Perform encoding let (mantissa, exponent, _sign) = integer_decode_f64(*self); - if exponent.is_positive() && exponent < 255 { - // Then the exponent is encoded only on one byte - len += 1; - } else { - len += 2; - } + // if exponent.is_positive() && exponent < 255 { + // // Then the exponent is encoded only on one byte + // len += 1; + // } else { + // len += 2; + // } + + let exponent_len = exponent.encoded_len()?; let mantissa_len = mantissa.encoded_len()?; - Length::new(len) + mantissa_len + exponent_len + mantissa_len } } - fn encode_value(&self, _encoder: &mut Encoder<'_>) -> Result<()> { + 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::EPSILON) + || (self.is_sign_positive() && (*self) < f64::EPSILON) + { + if self.is_sign_positive() && (*self) < f64::EPSILON { + // Zero + encoder.bytes(&[0b0000_0000])?; + } 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 + let mut first_byte = 0b1000_0000; + if self.is_sign_negative() { + first_byte |= 0b0100_0000; + } + + let (mantissa, exponent, _sign) = integer_decode_f64(*self); + // Encode the exponent as two's complement on 16 bits + let exponent_bytes = (exponent as i16).to_be_bytes(); + // If the exponent is encoded only on two bytes, add that info + if exponent_bytes[0] > 0x0 { + first_byte |= 0b0000_0001; + } + + encoder.bytes(&[first_byte])?; + + // Encode both bytes or just the last one, handled by encode_bytes directly + encode_bytes(encoder, &exponent_bytes)?; + // if exponent_bytes[0] > 0x0 { + // encode_bytes(encoder, &exponent_bytes)?; + // // encoder.bytes(&exponent_bytes)?; + // } else { + // encode_bytes(encoder, &exponent_bytes)?; + // // encoder.bytes(&exponent_bytes[1..2])?; + // } + + // Now, encode the mantissa as unsigned binary number + // mantissa.encode(encoder)?; + encode_bytes(encoder, &mantissa.to_be_bytes())?; + // encoder.bytes(&mantissa.to_be_bytes())?; + } Ok(()) } } @@ -110,8 +163,6 @@ impl FixedTag for f64 { const TAG: Tag = Tag::Real; } -// impl OrdIsValueOrd for Real {} - impl<'a> From for Any<'a> { fn from(_: f64) -> Any<'a> { Any::from_tag_and_value(Tag::Real, ByteSlice::default()) @@ -162,6 +213,7 @@ impl TryFrom> for f64 { // } /// Is the N-th bit 1 in the first octet? +/// NOTE: this function is zero indexed fn is_nth_bit_one(bytes: &[u8]) -> bool { let mask = match N { 0 => 0b00000001, @@ -193,27 +245,19 @@ fn mnth_bits_to_u8(bytes: &[u8]) -> u8 { u8::from_be_bytes([data]) } -/// Decode an f64 as its mantissa, exponent (shifted by 1023!), and sign data, all in u64 -/// From num_traits, src/floats.rs -/// https://github.com/rust-num/num-traits/blob/96a89e63258762d51f80ec141fcdf88d481d8dde/src/float.rs#L2008 -fn integer_decode_f64(f: f64) -> (u64, i16, u64) { +/// Decode an f64 as its sign, exponent, and mantissa in u64 and in that order, using bit shifts and masks +fn integer_decode_f64(f: f64) -> (u64, u64, u64) { let bits = f.to_bits(); - let sign: u64 = if bits >> 63 == 0 { 0 } else { 1 }; - let mut exponent = ((bits >> 52) & 0x7ff) as i16; - let mantissa = if exponent == 0 { - (bits & 0xfffffffffffff) << 1 - } else { - (bits & 0xfffffffffffff) | 0x10000000000000 - }; - // Exponent bias + mantissa shift - exponent -= 1023 + 52; - (mantissa, exponent, u64::from(sign)) + let sign = bits >> 63; + let exponent = bits >> 52 & 0x7ff; + let mantissa = bits & 0xfffffffffffff; + (sign, exponent, mantissa) } -fn integer_encode_f64(mantissa: u64, exponent: i16, sign: u64) -> f64 { - let exponent = (exponent + 1023 + 52) as u64; - let vbits = sign << 63 | exponent << 52 | mantissa; - f64::from_bits(vbits) +/// Encode an f64 from its sign, exponent, and mantissa using bit shifts +fn integer_encode_f64(sign: u64, exponent: u64, mantissa: u64) -> f64 { + let bits = sign << 63 | exponent << 52 | mantissa; + f64::from_bits(bits) } #[cfg(test)] @@ -221,19 +265,93 @@ mod tests { use crate::{Decodable, Encodable}; #[test] - fn decode() { - f64::from_der(&[0x05, 0x00]).unwrap(); + 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() { - let mut buffer = [0u8; 2]; - assert_eq!(&[0x05, 0x00], 0.1_f64.encode_to_slice(&mut buffer).unwrap()); - assert_eq!(&[0x05, 0x00], ().encode_to_slice(&mut buffer).unwrap()); + 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() { + { + let val = 0.0; + let expected = &[0x09, 0x01, 0x0]; + let mut buffer = [0u8; 3]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!((decoded - val).abs() < f64::EPSILON); + } + + { + let val = -1.0; + let expected = &[0x09, 0x07, 0xc1, 0x03, 0xff, 0x01]; + let mut buffer = [0u8; 6]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + // let decoded = f64::from_der(encoded).unwrap(); + // assert!((decoded - val).abs() < f64::EPSILON); + } + + { + let val = 1.0; + let expected = &[0x09, 0x07, 0x81, 0x03, 0xff, 0x01]; + let mut buffer = [0u8; 6]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + // let decoded = f64::from_der(encoded).unwrap(); + // assert!((decoded - val).abs() < f64::EPSILON); + } } #[test] fn reject_non_canonical() { assert!(f64::from_der(&[0x05, 0x81, 0x00]).is_err()); } + + #[test] + fn encdec_f64() { + use super::{integer_decode_f64, integer_encode_f64}; + // Test that the extraction and recreation works + for val in [ + 0.0, + -1.0, + 1.0, + f64::MIN_POSITIVE, + f64::MAX, + f64::MIN, + 3.1415, + 951.2357864, + ] { + let (m, e, s) = integer_decode_f64(val); + let val2 = integer_encode_f64(m, e, s); + assert!((val - val2).abs() < f64::EPSILON, "fail: {}", val); + } + } } diff --git a/der/src/error.rs b/der/src/error.rs index 2ba60660c..f97c35bc7 100644 --- a/der/src/error.rs +++ b/der/src/error.rs @@ -224,8 +224,8 @@ pub enum ErrorKind { RealBaseInvalid(u8), /// Real related error: encoded exponent cannot be represented on an IEEE-754 double RealExponentTooLong, - /// Real related error: encoding not supported (only NR3 is supported if the std feature is enabled) - RealISO6093EncodingNotSupported, + /// Real related error: encoding not supported or malformed + RealISO6093Error, /// Unknown tag mode. TagModeUnknown, @@ -367,8 +367,8 @@ impl fmt::Display for ErrorKind { f, "exponent encoded on more than 2 bytes, but that cannot be represented on an IEEE-754", ), - ErrorKind::RealISO6093EncodingNotSupported => { - write!(f, "provided ISO 6093 encoding not supported") + ErrorKind::RealISO6093Error => { + write!(f, "provided ISO 6093 encoding not supported or malformed") } } } From b9a75718f8c837da2e01bd501bbdc784dab0b49f Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Tue, 18 Jan 2022 20:35:17 -0700 Subject: [PATCH 06/22] Select examples of normal f64 work Signed-off-by: Christopher Rabotin --- der/src/asn1/integer/uint.rs | 4 +- der/src/asn1/real.rs | 119 ++++++++++++++++++++++++----------- 2 files changed, 84 insertions(+), 39 deletions(-) diff --git a/der/src/asn1/integer/uint.rs b/der/src/asn1/integer/uint.rs index 2f6d29b54..761d8dfb5 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. // @@ -47,7 +47,7 @@ pub(crate) 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 } diff --git a/der/src/asn1/real.rs b/der/src/asn1/real.rs index cefb8cafe..c163c28e5 100644 --- a/der/src/asn1/real.rs +++ b/der/src/asn1/real.rs @@ -1,8 +1,9 @@ //! ASN.1 `NULL` support. +use crate::asn1::integer::uint::{encode_bytes, encoded_len}; use crate::{ - asn1::Any, str_slice::StrSlice, ByteSlice, DecodeValue, Decoder, Encodable, EncodeValue, - Encoder, Error, ErrorKind, FixedTag, Length, Result, Tag, + asn1::Any, str_slice::StrSlice, ByteSlice, DecodeValue, Decoder, EncodeValue, Encoder, Error, + ErrorKind, FixedTag, Length, Result, Tag, }; impl DecodeValue<'_> for f64 { @@ -26,29 +27,36 @@ impl DecodeValue<'_> for f64 { // Section 8.5.7.4: // 1. grab the number of octets used to express the exponent; // 2. read the exponent as an i32 - let remaining_bytes; + let mantissa_start; let exponent = match mnth_bits_to_u8::<1, 0>(bytes) { 0 => { - remaining_bytes = bytes.len() - 2; + mantissa_start = 2; // i16::from_be_bytes([0x0, bytes[1]]).into() - u64::decode_value(decoder, Length::new(1))? + // u64::decode_value(decoder, Length::new(1))? + u64::from_be_bytes([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, bytes[1]]) } 1 => { - remaining_bytes = bytes.len() - 3; + mantissa_start = 3; // i16::from_be_bytes([bytes[1], bytes[2]]) - u64::decode_value(decoder, Length::new(2))? + // u64::decode_value(decoder, Length::new(2))? + u64::from_be_bytes([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, bytes[1], bytes[2]]) } _ => return Err(Error::new(ErrorKind::RealExponentTooLong, Length::ZERO)), }; // Section 8.5.7.5: Read the remaining bytes for the mantissa // XXX: Is this correct? I'm afraid this will not correctly pad things - let remaining_len = (length - Length::new(remaining_bytes.try_into().unwrap()))?; - let n = u64::decode_value(decoder, remaining_len)?; + // let remaining_len = (length - Length::new(remaining_bytes.try_into().unwrap()))?; + let mut n_bytes = [0x0; 8]; + for (pos, byte) in bytes[mantissa_start..].iter().rev().enumerate() { + n_bytes[7 - pos] = *byte; + } + // let n = u64::decode_value(decoder, Length::new(remaining_bytes.try_into().unwrap()))?; + let n = u64::from_be_bytes(n_bytes); // let n = u64::from_be_bytes(bytes[remaining_bytes..].try_into().unwrap()); // Multiply byt 2^F corresponds to just a left shift let mantissa = n << scaling_factor; // Create the f64 - return Ok(integer_encode_f64(mantissa, exponent, sign)); + return Ok(integer_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 return match mnth_bits_to_u8::<1, 0>(bytes) { @@ -68,31 +76,23 @@ impl DecodeValue<'_> for f64 { } } -use crate::asn1::integer::uint::encode_bytes; - impl EncodeValue for f64 { fn value_len(&self) -> Result { if self.is_nan() || self.is_infinite() - || (self.is_sign_negative() && -self < f64::EPSILON) - || (self.is_sign_positive() && (*self) < f64::EPSILON) + || (self.is_sign_negative() && -self < f64::MIN_POSITIVE) + || (self.is_sign_positive() && (*self) < f64::MIN_POSITIVE) { Ok(Length::ONE) } else { - // Perform encoding + // The length is that of the first octets plus those needed for the exponent plus those needed for the mantissa let (mantissa, exponent, _sign) = integer_decode_f64(*self); - // if exponent.is_positive() && exponent < 255 { - // // Then the exponent is encoded only on one byte - // len += 1; - // } else { - // len += 2; - // } - let exponent_len = exponent.encoded_len()?; + let exponent_len = encoded_len(&exponent.to_be_bytes())?; - let mantissa_len = mantissa.encoded_len()?; + let mantissa_len = encoded_len(&mantissa.to_be_bytes())?; - exponent_len + mantissa_len + Length::ONE + exponent_len + mantissa_len } } @@ -102,10 +102,10 @@ impl EncodeValue for f64 { // Special value from section 8.5.9 if non zero if self.is_nan() || self.is_infinite() - || (self.is_sign_negative() && -self < f64::EPSILON) - || (self.is_sign_positive() && (*self) < f64::EPSILON) + || (self.is_sign_negative() && -self < f64::MIN_POSITIVE) + || (self.is_sign_positive() && (*self) < f64::MIN_POSITIVE) { - if self.is_sign_positive() && (*self) < f64::EPSILON { + if self.is_sign_positive() && (*self) < f64::MIN_POSITIVE { // Zero encoder.bytes(&[0b0000_0000])?; } else if self.is_nan() { @@ -130,7 +130,7 @@ impl EncodeValue for f64 { first_byte |= 0b0100_0000; } - let (mantissa, exponent, _sign) = integer_decode_f64(*self); + let (_sign, exponent, mantissa) = integer_decode_f64(*self); // Encode the exponent as two's complement on 16 bits let exponent_bytes = (exponent as i16).to_be_bytes(); // If the exponent is encoded only on two bytes, add that info @@ -306,27 +306,72 @@ mod tests { let encoded = val.encode_to_slice(&mut buffer).unwrap(); assert_eq!(expected, encoded, "invalid encoding of {}", val); let decoded = f64::from_der(encoded).unwrap(); - assert!((decoded - val).abs() < f64::EPSILON); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = f64::MIN_POSITIVE; + let expected = &[0x09, 0x03, 0x80, 0x01, 0x0]; + let mut buffer = [0u8; 6]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); } { let val = -1.0; - let expected = &[0x09, 0x07, 0xc1, 0x03, 0xff, 0x01]; + let expected = &[0x09, 0x04, 0xc1, 0x03, 0xff, 0x00]; let mut buffer = [0u8; 6]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); assert_eq!(expected, encoded, "invalid encoding of {}", val); - // let decoded = f64::from_der(encoded).unwrap(); - // assert!((decoded - val).abs() < f64::EPSILON); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); } { let val = 1.0; - let expected = &[0x09, 0x07, 0x81, 0x03, 0xff, 0x01]; + let expected = &[0x09, 0x04, 0x81, 0x03, 0xff, 0x00]; + let mut buffer = [0u8; 6]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = -1.0000000000000002; + let expected = &[0x09, 0x04, 0xc1, 0x03, 0xff, 0x01]; let mut buffer = [0u8; 6]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); assert_eq!(expected, encoded, "invalid encoding of {}", val); - // let decoded = f64::from_der(encoded).unwrap(); - // assert!((decoded - val).abs() < f64::EPSILON); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); } } @@ -349,8 +394,8 @@ mod tests { 3.1415, 951.2357864, ] { - let (m, e, s) = integer_decode_f64(val); - let val2 = integer_encode_f64(m, e, s); + let (s, e, m) = integer_decode_f64(val); + let val2 = integer_encode_f64(s, e, m); assert!((val - val2).abs() < f64::EPSILON, "fail: {}", val); } } From 436cb48cf2c077a3022e8e025e24852128f7ba87 Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Tue, 18 Jan 2022 21:11:12 -0700 Subject: [PATCH 07/22] Cleanup Signed-off-by: Christopher Rabotin --- der/src/asn1/real.rs | 350 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 285 insertions(+), 65 deletions(-) diff --git a/der/src/asn1/real.rs b/der/src/asn1/real.rs index c163c28e5..e34646b02 100644 --- a/der/src/asn1/real.rs +++ b/der/src/asn1/real.rs @@ -21,38 +21,29 @@ impl DecodeValue<'_> for f64 { return Err(Error::new(ErrorKind::RealBaseInvalid(base), Length::ZERO)); } - // Section 8.5.7.3: grab the scaling factor + // Section 8.5.7.3 let scaling_factor = mnth_bits_to_u8::<3, 2>(bytes); - // Section 8.5.7.4: - // 1. grab the number of octets used to express the exponent; - // 2. read the exponent as an i32 + // Section 8.5.7.4 let mantissa_start; let exponent = match mnth_bits_to_u8::<1, 0>(bytes) { 0 => { mantissa_start = 2; - // i16::from_be_bytes([0x0, bytes[1]]).into() - // u64::decode_value(decoder, Length::new(1))? u64::from_be_bytes([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, bytes[1]]) } 1 => { mantissa_start = 3; - // i16::from_be_bytes([bytes[1], bytes[2]]) - // u64::decode_value(decoder, Length::new(2))? u64::from_be_bytes([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, bytes[1], bytes[2]]) } _ => return Err(Error::new(ErrorKind::RealExponentTooLong, Length::ZERO)), }; // Section 8.5.7.5: Read the remaining bytes for the mantissa - // XXX: Is this correct? I'm afraid this will not correctly pad things - // let remaining_len = (length - Length::new(remaining_bytes.try_into().unwrap()))?; + // FIXME: Fill backward but in a better way let mut n_bytes = [0x0; 8]; for (pos, byte) in bytes[mantissa_start..].iter().rev().enumerate() { n_bytes[7 - pos] = *byte; } - // let n = u64::decode_value(decoder, Length::new(remaining_bytes.try_into().unwrap()))?; let n = u64::from_be_bytes(n_bytes); - // let n = u64::from_be_bytes(bytes[remaining_bytes..].try_into().unwrap()); // Multiply byt 2^F corresponds to just a left shift let mantissa = n << scaling_factor; // Create the f64 @@ -86,12 +77,9 @@ impl EncodeValue for f64 { 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 (mantissa, exponent, _sign) = integer_decode_f64(*self); - + let (_sign, exponent, mantissa) = integer_decode_f64(*self); let exponent_len = encoded_len(&exponent.to_be_bytes())?; - let mantissa_len = encoded_len(&mantissa.to_be_bytes())?; - Length::ONE + exponent_len + mantissa_len } } @@ -142,18 +130,9 @@ impl EncodeValue for f64 { // Encode both bytes or just the last one, handled by encode_bytes directly encode_bytes(encoder, &exponent_bytes)?; - // if exponent_bytes[0] > 0x0 { - // encode_bytes(encoder, &exponent_bytes)?; - // // encoder.bytes(&exponent_bytes)?; - // } else { - // encode_bytes(encoder, &exponent_bytes)?; - // // encoder.bytes(&exponent_bytes[1..2])?; - // } // Now, encode the mantissa as unsigned binary number - // mantissa.encode(encoder)?; encode_bytes(encoder, &mantissa.to_be_bytes())?; - // encoder.bytes(&mantissa.to_be_bytes())?; } Ok(()) } @@ -177,44 +156,9 @@ impl TryFrom> for f64 { } } -// impl TryFrom> for () { -// type Error = Error; - -// fn try_from(any: Any<'_>) -> Result<()> { -// Real::try_from(any).map(|_| ()) -// } -// } - -// impl<'a> From<()> for Any<'a> { -// fn from(_: ()) -> Any<'a> { -// Real.into() -// } -// } - -// impl DecodeValue<'_> for () { -// fn decode_value(decoder: &mut Decoder<'_>, length: Length) -> Result { -// Real::decode_value(decoder, length)?; -// Ok(()) -// } -// } - -// impl Encodable for () { -// fn encoded_len(&self) -> Result { -// Real.encoded_len() -// } - -// fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> { -// Real.encode(encoder) -// } -// } - -// impl FixedTag for () { -// const TAG: Tag = Tag::Real; -// } - /// Is the N-th bit 1 in the first octet? /// NOTE: this function is zero indexed -fn is_nth_bit_one(bytes: &[u8]) -> bool { +pub(crate) fn is_nth_bit_one(bytes: &[u8]) -> bool { let mask = match N { 0 => 0b00000001, 1 => 0b00000010, @@ -230,7 +174,7 @@ fn is_nth_bit_one(bytes: &[u8]) -> bool { } /// Convert bits M, N into a u8, in the first octet only -fn mnth_bits_to_u8(bytes: &[u8]) -> u8 { +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); let data = if bit_m && bit_n { @@ -246,7 +190,7 @@ fn mnth_bits_to_u8(bytes: &[u8]) -> u8 { } /// Decode an f64 as its sign, exponent, and mantissa in u64 and in that order, using bit shifts and masks -fn integer_decode_f64(f: f64) -> (u64, u64, u64) { +pub(crate) fn integer_decode_f64(f: f64) -> (u64, u64, u64) { let bits = f.to_bits(); let sign = bits >> 63; let exponent = bits >> 52 & 0x7ff; @@ -255,7 +199,7 @@ fn integer_decode_f64(f: f64) -> (u64, u64, u64) { } /// Encode an f64 from its sign, exponent, and mantissa using bit shifts -fn integer_encode_f64(sign: u64, exponent: u64, mantissa: u64) -> f64 { +pub(crate) fn integer_encode_f64(sign: u64, exponent: u64, mantissa: u64) -> f64 { let bits = sign << 63 | exponent << 52 | mantissa; f64::from_bits(bits) } @@ -373,11 +317,287 @@ mod tests { decoded ); } + + { + let val = f64::MAX; + let expected = &[9, 10, 129, 7, 254, 15, 255, 255, 255, 255, 255, 255]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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 = &[9, 10, 129, 4, 0, 9, 33, 251, 84, 68, 45, 24]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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 = &[9, 10, 129, 4, 0, 5, 191, 10, 139, 20, 87, 105]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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 = &[9, 10, 129, 3, 254, 6, 46, 66, 254, 250, 57, 239]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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 + { + let val = 3221417.1584163485; + let expected = &[9, 10, 129, 4, 20, 8, 147, 212, 148, 70, 252, 166]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = 13364022.365665454; + let expected = &[9, 10, 129, 4, 22, 9, 125, 102, 203, 179, 136, 10]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = -32343.132588105735; + let expected = &[9, 10, 193, 4, 13, 15, 149, 200, 124, 82, 210, 126]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = -27084.866751869475; + let expected = &[9, 10, 193, 4, 13, 10, 115, 55, 120, 220, 213, 73]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = -252.28566647111404; + let expected = &[9, 10, 193, 4, 6, 15, 137, 36, 46, 2, 223, 244]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = -14.399709612928548; + let expected = &[9, 10, 193, 4, 2, 12, 204, 166, 189, 6, 217, 145]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = -0.08340570261832964; + let expected = &[9, 10, 193, 3, 251, 5, 90, 19, 125, 11, 174, 60]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = 0.00536851453803701; + let expected = &[9, 10, 129, 3, 247, 5, 253, 75, 165, 231, 76, 146]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = 0.00045183525648866433; + let expected = &[9, 10, 129, 3, 243, 13, 156, 137, 166, 89, 51, 56]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = 0.000033869092002682955; + let expected = &[9, 10, 129, 3, 240, 1, 193, 213, 35, 213, 84, 123]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = 0.0000011770891033600088; + let expected = &[9, 10, 129, 3, 235, 3, 191, 143, 39, 244, 98, 85]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = 0.00000005549514041997082; + let expected = &[9, 10, 129, 3, 230, 13, 203, 49, 171, 110, 184, 214]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = 0.0000000012707044685547803; + let expected = &[9, 10, 129, 3, 225, 5, 212, 158, 10, 242, 255, 30]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = 0.00000000002969611878378562; + let expected = &[9, 9, 129, 3, 220, 83, 91, 111, 151, 238, 181]; + let mut buffer = [0u8; 11]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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(&[0x05, 0x81, 0x00]).is_err()); + assert!(f64::from_der(&[0x09, 0x81, 0x00]).is_err()); } #[test] From 2da03c8a0ad2c88c69f225c259083021f868d6eb Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Thu, 13 Jan 2022 19:48:58 -0700 Subject: [PATCH 08/22] Proposed support of REAL DER type Covers sections 8.5 and 11.3 of ITU-T X.690 (02/2021) Caveats: 1. Interpretation of section 11.3.1 to be further discussed 2. Currently, all encoding is binary (no ISO 6093 NR3) Testing of arbitrarily large and small f64s shows that DER typically requires 12 bytes to encode these 8-byte IEEE-754 values. This can be explained because of the extra two bytes for the tag and length, and the fact that the exponent is encoded on 1 to 2 bytes (only 11 bits in IEEE-754), and the mantissa may require up to 8 bytes itself, hence 12 bytes total. Of note that, depending on Caveat 1 above, this information may be wrong. --- der/src/asn1.rs | 1 + der/src/asn1/integer.rs | 4 +- der/src/asn1/integer/uint.rs | 6 +- der/src/asn1/real.rs | 622 +++++++++++++++++++++++++++++++++++ der/src/error.rs | 31 ++ 5 files changed, 659 insertions(+), 5 deletions(-) create mode 100644 der/src/asn1/real.rs diff --git a/der/src/asn1.rs b/der/src/asn1.rs index e6d854d7b..8a26c3b6d 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 83847cb53..9abfc1831 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, Length, diff --git a/der/src/asn1/integer/uint.rs b/der/src/asn1/integer/uint.rs index 75948d364..761d8dfb5 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. // @@ -35,7 +35,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) { @@ -47,7 +47,7 @@ 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 } diff --git a/der/src/asn1/real.rs b/der/src/asn1/real.rs new file mode 100644 index 000000000..e34646b02 --- /dev/null +++ b/der/src/asn1/real.rs @@ -0,0 +1,622 @@ +//! ASN.1 `NULL` support. + +use crate::asn1::integer::uint::{encode_bytes, encoded_len}; +use crate::{ + asn1::Any, str_slice::StrSlice, ByteSlice, DecodeValue, Decoder, EncodeValue, Encoder, Error, + ErrorKind, FixedTag, Length, Result, Tag, +}; + +impl DecodeValue<'_> for f64 { + fn decode_value(decoder: &mut Decoder<'_>, length: Length) -> Result { + let bytes = ByteSlice::decode_value(decoder, length)?.as_bytes(); + + if length == Length::ONE && bytes[0] == 0x0 { + 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, but here we allow decoding of BER, just not encoding of BER + let base = mnth_bits_to_u8::<5, 4>(bytes); + if base != 0 { + return Err(Error::new(ErrorKind::RealBaseInvalid(base), Length::ZERO)); + } + + // 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; + u64::from_be_bytes([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, bytes[1]]) + } + 1 => { + mantissa_start = 3; + u64::from_be_bytes([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, bytes[1], bytes[2]]) + } + _ => return Err(Error::new(ErrorKind::RealExponentTooLong, Length::ZERO)), + }; + // Section 8.5.7.5: Read the remaining bytes for the mantissa + // FIXME: Fill backward but in a better way + 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 + return Ok(integer_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 + return 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[3..])?; + match astr.inner.parse::() { + Ok(val) => Ok(val), + Err(_) => Err(Error::new(ErrorKind::RealISO6093Error, Length::ZERO)), + } + } + } +} + +impl EncodeValue for f64 { + fn value_len(&self) -> Result { + if self.is_nan() + || self.is_infinite() + || (self.is_sign_negative() && -self < f64::MIN_POSITIVE) + || (self.is_sign_positive() && (*self) < f64::MIN_POSITIVE) + { + 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) = integer_decode_f64(*self); + let exponent_len = encoded_len(&exponent.to_be_bytes())?; + let mantissa_len = encoded_len(&mantissa.to_be_bytes())?; + Length::ONE + exponent_len + mantissa_len + } + } + + 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 + encoder.bytes(&[0b0000_0000])?; + } 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 + let mut first_byte = 0b1000_0000; + if self.is_sign_negative() { + first_byte |= 0b0100_0000; + } + + let (_sign, exponent, mantissa) = integer_decode_f64(*self); + // Encode the exponent as two's complement on 16 bits + let exponent_bytes = (exponent as i16).to_be_bytes(); + // If the exponent is encoded only on two bytes, add that info + if exponent_bytes[0] > 0x0 { + first_byte |= 0b0000_0001; + } + + encoder.bytes(&[first_byte])?; + + // Encode both bytes or just the last one, handled by encode_bytes directly + encode_bytes(encoder, &exponent_bytes)?; + + // Now, encode the mantissa as unsigned binary number + encode_bytes(encoder, &mantissa.to_be_bytes())?; + } + Ok(()) + } +} + +impl FixedTag for f64 { + const TAG: Tag = Tag::Real; +} + +impl<'a> From for Any<'a> { + fn from(_: f64) -> Any<'a> { + Any::from_tag_and_value(Tag::Real, ByteSlice::default()) + } +} + +impl TryFrom> for f64 { + type Error = Error; + + fn try_from(any: Any<'_>) -> Result { + any.decode_into() + } +} + +/// 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 { + let mask = match N { + 0 => 0b00000001, + 1 => 0b00000010, + 2 => 0b00000100, + 3 => 0b00001000, + 4 => 0b00010000, + 5 => 0b00100000, + 6 => 0b01000000, + 7 => 0b10000000, + _ => return false, + }; + bytes.get(0).map(|byte| byte & mask != 0).unwrap_or(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); + let data = if bit_m && bit_n { + 0b11 + } else if !bit_m && !bit_n { + 0b00 + } else if bit_n { + 0b01 + } else { + 0b10 + }; + u8::from_be_bytes([data]) +} + +/// Decode an f64 as its sign, exponent, and mantissa in u64 and in that order, using bit shifts and masks +pub(crate) fn integer_decode_f64(f: f64) -> (u64, u64, u64) { + let bits = f.to_bits(); + let sign = bits >> 63; + let exponent = bits >> 52 & 0x7ff; + let mantissa = bits & 0xfffffffffffff; + (sign, exponent, mantissa) +} + +/// Encode an f64 from its sign, exponent, and mantissa using bit shifts +pub(crate) fn integer_encode_f64(sign: u64, exponent: u64, mantissa: u64) -> f64 { + let bits = sign << 63 | exponent << 52 | mantissa; + f64::from_bits(bits) +} + +#[cfg(test)] +mod tests { + use crate::{Decodable, Encodable}; + + #[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() { + { + let val = 0.0; + let expected = &[0x09, 0x01, 0x0]; + let mut buffer = [0u8; 3]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = f64::MIN_POSITIVE; + let expected = &[0x09, 0x03, 0x80, 0x01, 0x0]; + let mut buffer = [0u8; 6]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = -1.0; + let expected = &[0x09, 0x04, 0xc1, 0x03, 0xff, 0x00]; + let mut buffer = [0u8; 6]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = 1.0; + let expected = &[0x09, 0x04, 0x81, 0x03, 0xff, 0x00]; + let mut buffer = [0u8; 6]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = -1.0000000000000002; + let expected = &[0x09, 0x04, 0xc1, 0x03, 0xff, 0x01]; + let mut buffer = [0u8; 6]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = f64::MAX; + let expected = &[9, 10, 129, 7, 254, 15, 255, 255, 255, 255, 255, 255]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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 = &[9, 10, 129, 4, 0, 9, 33, 251, 84, 68, 45, 24]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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 = &[9, 10, 129, 4, 0, 5, 191, 10, 139, 20, 87, 105]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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 = &[9, 10, 129, 3, 254, 6, 46, 66, 254, 250, 57, 239]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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 + { + let val = 3221417.1584163485; + let expected = &[9, 10, 129, 4, 20, 8, 147, 212, 148, 70, 252, 166]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = 13364022.365665454; + let expected = &[9, 10, 129, 4, 22, 9, 125, 102, 203, 179, 136, 10]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = -32343.132588105735; + let expected = &[9, 10, 193, 4, 13, 15, 149, 200, 124, 82, 210, 126]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = -27084.866751869475; + let expected = &[9, 10, 193, 4, 13, 10, 115, 55, 120, 220, 213, 73]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = -252.28566647111404; + let expected = &[9, 10, 193, 4, 6, 15, 137, 36, 46, 2, 223, 244]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = -14.399709612928548; + let expected = &[9, 10, 193, 4, 2, 12, 204, 166, 189, 6, 217, 145]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = -0.08340570261832964; + let expected = &[9, 10, 193, 3, 251, 5, 90, 19, 125, 11, 174, 60]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = 0.00536851453803701; + let expected = &[9, 10, 129, 3, 247, 5, 253, 75, 165, 231, 76, 146]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = 0.00045183525648866433; + let expected = &[9, 10, 129, 3, 243, 13, 156, 137, 166, 89, 51, 56]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = 0.000033869092002682955; + let expected = &[9, 10, 129, 3, 240, 1, 193, 213, 35, 213, 84, 123]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = 0.0000011770891033600088; + let expected = &[9, 10, 129, 3, 235, 3, 191, 143, 39, 244, 98, 85]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = 0.00000005549514041997082; + let expected = &[9, 10, 129, 3, 230, 13, 203, 49, 171, 110, 184, 214]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = 0.0000000012707044685547803; + let expected = &[9, 10, 129, 3, 225, 5, 212, 158, 10, 242, 255, 30]; + let mut buffer = [0u8; 12]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + let decoded = f64::from_der(encoded).unwrap(); + assert!( + (decoded - val).abs() < f64::EPSILON, + "wanted: {}\tgot: {}", + val, + decoded + ); + } + + { + let val = 0.00000000002969611878378562; + let expected = &[9, 9, 129, 3, 220, 83, 91, 111, 151, 238, 181]; + let mut buffer = [0u8; 11]; + let encoded = val.encode_to_slice(&mut buffer).unwrap(); + assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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::{integer_decode_f64, integer_encode_f64}; + // Test that the extraction and recreation works + for val in [ + 0.0, + -1.0, + 1.0, + f64::MIN_POSITIVE, + f64::MAX, + f64::MIN, + 3.1415, + 951.2357864, + ] { + let (s, e, m) = integer_decode_f64(val); + let val2 = integer_encode_f64(s, e, m); + assert!((val - val2).abs() < f64::EPSILON, "fail: {}", val); + } + } +} diff --git a/der/src/error.rs b/der/src/error.rs index 551b4cd57..f97c35bc7 100644 --- a/der/src/error.rs +++ b/der/src/error.rs @@ -220,6 +220,13 @@ pub enum ErrorKind { #[cfg_attr(docsrs, doc(cfg(feature = "std")))] PermissionDenied, + /// Real related error: base is not DER compliant (base encoded in enum) + RealBaseInvalid(u8), + /// Real related error: encoded exponent cannot be represented on an IEEE-754 double + RealExponentTooLong, + /// Real related error: encoding not supported or malformed + RealISO6093Error, + /// Unknown tag mode. TagModeUnknown, @@ -339,6 +346,30 @@ impl fmt::Display for ErrorKind { } ErrorKind::Utf8(e) => write!(f, "{}", e), ErrorKind::Value { tag } => write!(f, "malformed ASN.1 DER value for {}", tag), + ErrorKind::RealBaseInvalid(base) => match base { + 1 => write!( + f, + "DER only supports REAL type in base 2 (provided a base {} REAL)", + base + ), + 2 => write!( + f, + "DER only supports REAL type in base 2 (provided a base {} REAL)", + base + ), + 3 => write!( + f, + "reserved for further editions of this Recommendation | International Standard" + ), + _ => unreachable!(), + }, + ErrorKind::RealExponentTooLong => write!( + f, + "exponent encoded on more than 2 bytes, but that cannot be represented on an IEEE-754", + ), + ErrorKind::RealISO6093Error => { + write!(f, "provided ISO 6093 encoding not supported or malformed") + } } } } From c258f2bf3c9f08d25fef43ee78c70eff3c457d07 Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Tue, 18 Jan 2022 21:32:45 -0700 Subject: [PATCH 09/22] Clippy changes Signed-off-by: Christopher Rabotin --- der/src/asn1/real.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/der/src/asn1/real.rs b/der/src/asn1/real.rs index e34646b02..7baa57f1c 100644 --- a/der/src/asn1/real.rs +++ b/der/src/asn1/real.rs @@ -47,16 +47,16 @@ impl DecodeValue<'_> for f64 { // Multiply byt 2^F corresponds to just a left shift let mantissa = n << scaling_factor; // Create the f64 - return Ok(integer_encode_f64(sign, exponent, mantissa)); + Ok(integer_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 - return match mnth_bits_to_u8::<1, 0>(bytes) { + 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[3..])?; match astr.inner.parse::() { From 6e49378e6e0ae6d99b9ac683707c679ff8ca3af1 Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Sat, 22 Jan 2022 01:06:00 -0700 Subject: [PATCH 10/22] Fixed implementation of section 11.3.1 Signed-off-by: Christopher Rabotin --- der/src/asn1/real.rs | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/der/src/asn1/real.rs b/der/src/asn1/real.rs index 7baa57f1c..65a1746de 100644 --- a/der/src/asn1/real.rs +++ b/der/src/asn1/real.rs @@ -118,7 +118,14 @@ impl EncodeValue for f64 { first_byte |= 0b0100_0000; } - let (_sign, exponent, mantissa) = integer_decode_f64(*self); + let (_sign, exponent, mut mantissa) = integer_decode_f64(*self); + // Check that the mantissa is either zero or odd + if mantissa % 2 == 0 && mantissa != 0 { + // Shift by one, store that as scaling factor + mantissa >>= 1; + first_byte |= 0b0000_0100; + assert!((mantissa % 2 == 0 && mantissa != 0) || (mantissa % 2 == 1)); + } // Encode the exponent as two's complement on 16 bits let exponent_bytes = (exponent as i16).to_be_bytes(); // If the exponent is encoded only on two bytes, add that info @@ -338,7 +345,7 @@ mod tests { fn encdec_irrationals() { { let val = core::f64::consts::PI; - let expected = &[9, 10, 129, 4, 0, 9, 33, 251, 84, 68, 45, 24]; + let expected = &[9, 10, 133, 4, 0, 4, 144, 253, 170, 34, 22, 140]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); assert_eq!(expected, encoded, "invalid encoding of {}", val); @@ -386,7 +393,7 @@ mod tests { // Tests the encoding and decoding of reals with some arbitrary numbers { let val = 3221417.1584163485; - let expected = &[9, 10, 129, 4, 20, 8, 147, 212, 148, 70, 252, 166]; + let expected = &[9, 10, 133, 4, 20, 4, 73, 234, 74, 35, 126, 83]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); assert_eq!(expected, encoded, "invalid encoding of {}", val); @@ -401,7 +408,7 @@ mod tests { { let val = 13364022.365665454; - let expected = &[9, 10, 129, 4, 22, 9, 125, 102, 203, 179, 136, 10]; + let expected = &[9, 10, 133, 4, 22, 4, 190, 179, 101, 217, 196, 5]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); assert_eq!(expected, encoded, "invalid encoding of {}", val); @@ -416,7 +423,7 @@ mod tests { { let val = -32343.132588105735; - let expected = &[9, 10, 193, 4, 13, 15, 149, 200, 124, 82, 210, 126]; + let expected = &[9, 10, 197, 4, 13, 7, 202, 228, 62, 41, 105, 63]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); assert_eq!(expected, encoded, "invalid encoding of {}", val); @@ -446,7 +453,7 @@ mod tests { { let val = -252.28566647111404; - let expected = &[9, 10, 193, 4, 6, 15, 137, 36, 46, 2, 223, 244]; + let expected = &[9, 10, 197, 4, 6, 7, 196, 146, 23, 1, 111, 250]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); assert_eq!(expected, encoded, "invalid encoding of {}", val); @@ -476,7 +483,7 @@ mod tests { { let val = -0.08340570261832964; - let expected = &[9, 10, 193, 3, 251, 5, 90, 19, 125, 11, 174, 60]; + let expected = &[9, 10, 197, 3, 251, 2, 173, 9, 190, 133, 215, 30]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); assert_eq!(expected, encoded, "invalid encoding of {}", val); @@ -491,7 +498,7 @@ mod tests { { let val = 0.00536851453803701; - let expected = &[9, 10, 129, 3, 247, 5, 253, 75, 165, 231, 76, 146]; + let expected = &[9, 10, 133, 3, 247, 2, 254, 165, 210, 243, 166, 73]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); assert_eq!(expected, encoded, "invalid encoding of {}", val); @@ -506,7 +513,7 @@ mod tests { { let val = 0.00045183525648866433; - let expected = &[9, 10, 129, 3, 243, 13, 156, 137, 166, 89, 51, 56]; + let expected = &[9, 10, 133, 3, 243, 6, 206, 68, 211, 44, 153, 156]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); assert_eq!(expected, encoded, "invalid encoding of {}", val); @@ -551,7 +558,7 @@ mod tests { { let val = 0.00000005549514041997082; - let expected = &[9, 10, 129, 3, 230, 13, 203, 49, 171, 110, 184, 214]; + let expected = &[9, 10, 133, 3, 230, 6, 229, 152, 213, 183, 92, 107]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); assert_eq!(expected, encoded, "invalid encoding of {}", val); @@ -566,7 +573,7 @@ mod tests { { let val = 0.0000000012707044685547803; - let expected = &[9, 10, 129, 3, 225, 5, 212, 158, 10, 242, 255, 30]; + let expected = &[9, 10, 133, 3, 225, 2, 234, 79, 5, 121, 127, 143]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); assert_eq!(expected, encoded, "invalid encoding of {}", val); From c240f0338823621e19b34a191cf9f738801ef36a Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 30 Jan 2022 22:13:54 -0700 Subject: [PATCH 11/22] Update der/src/asn1/real.rs Co-authored-by: Tony Arcieri --- der/src/asn1/real.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/der/src/asn1/real.rs b/der/src/asn1/real.rs index 65a1746de..ed268df3d 100644 --- a/der/src/asn1/real.rs +++ b/der/src/asn1/real.rs @@ -184,16 +184,7 @@ pub(crate) fn is_nth_bit_one(bytes: &[u8]) -> bool { 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); - let data = if bit_m && bit_n { - 0b11 - } else if !bit_m && !bit_n { - 0b00 - } else if bit_n { - 0b01 - } else { - 0b10 - }; - u8::from_be_bytes([data]) + (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 From 7a2ef4707adbbdc893b840d760db6c9ee6fddf6e Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 30 Jan 2022 22:14:03 -0700 Subject: [PATCH 12/22] Update der/src/asn1/real.rs Co-authored-by: Tony Arcieri --- der/src/asn1/real.rs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/der/src/asn1/real.rs b/der/src/asn1/real.rs index ed268df3d..743c38796 100644 --- a/der/src/asn1/real.rs +++ b/der/src/asn1/real.rs @@ -166,18 +166,11 @@ impl TryFrom> for f64 { /// 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 { - let mask = match N { - 0 => 0b00000001, - 1 => 0b00000010, - 2 => 0b00000100, - 3 => 0b00001000, - 4 => 0b00010000, - 5 => 0b00100000, - 6 => 0b01000000, - 7 => 0b10000000, - _ => return false, - }; - bytes.get(0).map(|byte| byte & mask != 0).unwrap_or(false) + 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 From 042f0674a60abb05860f8e948d1859a55b799b5f Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Sun, 30 Jan 2022 23:37:31 -0700 Subject: [PATCH 13/22] Add validation tests; fix impl for 0 & NR3 decode Signed-off-by: Christopher Rabotin --- der/src/asn1/real.rs | 114 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 105 insertions(+), 9 deletions(-) diff --git a/der/src/asn1/real.rs b/der/src/asn1/real.rs index 743c38796..53de6cc95 100644 --- a/der/src/asn1/real.rs +++ b/der/src/asn1/real.rs @@ -10,7 +10,7 @@ impl DecodeValue<'_> for f64 { fn decode_value(decoder: &mut Decoder<'_>, length: Length) -> Result { let bytes = ByteSlice::decode_value(decoder, length)?.as_bytes(); - if length == Length::ONE && bytes[0] == 0x0 { + if length == Length::ZERO { Ok(0.0) } else if is_nth_bit_one::<7>(bytes) { // Binary encoding from section 8.5.7 applies @@ -58,10 +58,10 @@ impl DecodeValue<'_> for f64 { _ => unreachable!(), } } else { - let astr = StrSlice::from_bytes(&bytes[3..])?; + let astr = StrSlice::from_bytes(&bytes[1..])?; match astr.inner.parse::() { Ok(val) => Ok(val), - Err(_) => Err(Error::new(ErrorKind::RealISO6093Error, Length::ZERO)), + Err(_) => Err(Error::new(ErrorKind::RealISO6093Error, Length::ONE)), } } } @@ -69,11 +69,14 @@ impl DecodeValue<'_> for f64 { impl EncodeValue for f64 { fn value_len(&self) -> Result { - if self.is_nan() + 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) - || (self.is_sign_positive() && (*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 @@ -95,7 +98,7 @@ impl EncodeValue for f64 { { if self.is_sign_positive() && (*self) < f64::MIN_POSITIVE { // Zero - encoder.bytes(&[0b0000_0000])?; + return Ok(()); } else if self.is_nan() { // Not a number encoder.bytes(&[0b0100_0010])?; @@ -167,7 +170,10 @@ impl TryFrom> for f64 { /// 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) + bytes + .get(0) + .map(|byte| byte & (1 << N) != 0) + .unwrap_or(false) } else { false } @@ -236,8 +242,8 @@ mod tests { fn encdec_normal() { { let val = 0.0; - let expected = &[0x09, 0x01, 0x0]; - let mut buffer = [0u8; 3]; + 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 {}", val); let decoded = f64::from_der(encoded).unwrap(); @@ -610,4 +616,94 @@ mod tests { assert!((val - val2).abs() < f64::EPSILON, "fail: {}", val); } } + + #[test] + fn validation_cases() { + // Caveat: these test cases are from the ASN.1 playground: https://asn1.io/asn1playground/ . + // 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 in this case 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). + { + 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 = -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 + ); + } + { + 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 + ); + } + { + 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 + ); + } + } } From 082d21469347244561f3c4f6323c25a73542e55d Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Sun, 6 Feb 2022 20:48:41 -0700 Subject: [PATCH 14/22] Remove Real error kinds in favor of the tag error Signed-off-by: Christopher Rabotin --- der/src/asn1/real.rs | 37 +++++++++++++++++++++++++++++++------ der/src/error.rs | 31 ------------------------------- 2 files changed, 31 insertions(+), 37 deletions(-) diff --git a/der/src/asn1/real.rs b/der/src/asn1/real.rs index 53de6cc95..9d69e344d 100644 --- a/der/src/asn1/real.rs +++ b/der/src/asn1/real.rs @@ -3,7 +3,7 @@ use crate::asn1::integer::uint::{encode_bytes, encoded_len}; use crate::{ asn1::Any, str_slice::StrSlice, ByteSlice, DecodeValue, Decoder, EncodeValue, Encoder, Error, - ErrorKind, FixedTag, Length, Result, Tag, + FixedTag, Length, Result, Tag, }; impl DecodeValue<'_> for f64 { @@ -18,7 +18,8 @@ impl DecodeValue<'_> for f64 { // Section 8.5.7.2: Check the base -- the DER specs say that only base 2 should be supported in DER, but here we allow decoding of BER, just not encoding of BER let base = mnth_bits_to_u8::<5, 4>(bytes); if base != 0 { - return Err(Error::new(ErrorKind::RealBaseInvalid(base), Length::ZERO)); + // Real related error: base is not DER compliant (base encoded in enum) + return Err(Tag::Real.value_error()); } // Section 8.5.7.3 @@ -35,7 +36,10 @@ impl DecodeValue<'_> for f64 { mantissa_start = 3; u64::from_be_bytes([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, bytes[1], bytes[2]]) } - _ => return Err(Error::new(ErrorKind::RealExponentTooLong, Length::ZERO)), + _ => { + // 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 // FIXME: Fill backward but in a better way @@ -61,7 +65,10 @@ impl DecodeValue<'_> for f64 { let astr = StrSlice::from_bytes(&bytes[1..])?; match astr.inner.parse::() { Ok(val) => Ok(val), - Err(_) => Err(Error::new(ErrorKind::RealISO6093Error, Length::ONE)), + Err(_) => { + // Real related error: encoding not supported or malformed + Err(Tag::Real.value_error()) + } } } } @@ -240,7 +247,14 @@ mod tests { #[test] fn encdec_normal() { - { + // The comments correspond to the decoded value from the ASN.1 playground when the bytes are inputed. + { + /* + R REAL: tag = [UNIVERSAL 9] primitive; length = 0 + 0 + Successfully decoded 2 bytes. + rec1value R ::= 0 + */ let val = 0.0; let expected = &[0x09, 0x0]; let mut buffer = [0u8; 2]; @@ -256,6 +270,12 @@ mod tests { } { + /* + R REAL: tag = [UNIVERSAL 9] primitive; length = 3 + 0 * 2^1 + Successfully decoded 5 bytes. + rec1value R ::= 0 + */ let val = f64::MIN_POSITIVE; let expected = &[0x09, 0x03, 0x80, 0x01, 0x0]; let mut buffer = [0u8; 6]; @@ -623,7 +643,7 @@ mod tests { // 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 in this case correctly supports decoding such values. It will, however, - // systematically encode REALs in their base 2 form, with a scaling factor where needed to + // 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). { let expect = 10.0; @@ -705,5 +725,10 @@ mod tests { decoded ); } + { + use super::integer_encode_f64; + let v = integer_encode_f64(1, 1023, 1); + panic!("{}", v); + } } } diff --git a/der/src/error.rs b/der/src/error.rs index f97c35bc7..551b4cd57 100644 --- a/der/src/error.rs +++ b/der/src/error.rs @@ -220,13 +220,6 @@ pub enum ErrorKind { #[cfg_attr(docsrs, doc(cfg(feature = "std")))] PermissionDenied, - /// Real related error: base is not DER compliant (base encoded in enum) - RealBaseInvalid(u8), - /// Real related error: encoded exponent cannot be represented on an IEEE-754 double - RealExponentTooLong, - /// Real related error: encoding not supported or malformed - RealISO6093Error, - /// Unknown tag mode. TagModeUnknown, @@ -346,30 +339,6 @@ impl fmt::Display for ErrorKind { } ErrorKind::Utf8(e) => write!(f, "{}", e), ErrorKind::Value { tag } => write!(f, "malformed ASN.1 DER value for {}", tag), - ErrorKind::RealBaseInvalid(base) => match base { - 1 => write!( - f, - "DER only supports REAL type in base 2 (provided a base {} REAL)", - base - ), - 2 => write!( - f, - "DER only supports REAL type in base 2 (provided a base {} REAL)", - base - ), - 3 => write!( - f, - "reserved for further editions of this Recommendation | International Standard" - ), - _ => unreachable!(), - }, - ErrorKind::RealExponentTooLong => write!( - f, - "exponent encoded on more than 2 bytes, but that cannot be represented on an IEEE-754", - ), - ErrorKind::RealISO6093Error => { - write!(f, "provided ISO 6093 encoding not supported or malformed") - } } } } From b284a486f2d5ef99884f692d1e7ef92a3ea48a9e Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Wed, 23 Mar 2022 11:51:18 -0600 Subject: [PATCH 15/22] REAL: almost working, issue with very large numbers Signed-off-by: Christopher Rabotin --- der/src/asn1/integer/uint.rs | 2 +- der/src/asn1/real.rs | 434 +++++++++++++++++++++++++++-------- 2 files changed, 343 insertions(+), 93 deletions(-) diff --git a/der/src/asn1/integer/uint.rs b/der/src/asn1/integer/uint.rs index 761d8dfb5..d339be9cc 100644 --- a/der/src/asn1/integer/uint.rs +++ b/der/src/asn1/integer/uint.rs @@ -53,7 +53,7 @@ pub(crate) fn encoded_len(bytes: &[u8]) -> Result { } /// 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 index 9d69e344d..b7545bc5f 100644 --- a/der/src/asn1/real.rs +++ b/der/src/asn1/real.rs @@ -6,6 +6,8 @@ use crate::{ FixedTag, Length, Result, Tag, }; +use super::integer::uint::strip_leading_zeroes; + impl DecodeValue<'_> for f64 { fn decode_value(decoder: &mut Decoder<'_>, length: Length) -> Result { let bytes = ByteSlice::decode_value(decoder, length)?.as_bytes(); @@ -15,7 +17,7 @@ impl DecodeValue<'_> for f64 { } 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, but here we allow decoding of BER, just not encoding of BER + // 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) @@ -30,11 +32,14 @@ impl DecodeValue<'_> for f64 { let exponent = match mnth_bits_to_u8::<1, 0>(bytes) { 0 => { mantissa_start = 2; - u64::from_be_bytes([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, bytes[1]]) + 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; - u64::from_be_bytes([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, bytes[1], bytes[2]]) + 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]]) + // u64::from_be_bytes([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, bytes[1], bytes[2]]) } _ => { // Real related error: encoded exponent cannot be represented on an IEEE-754 double @@ -42,7 +47,6 @@ impl DecodeValue<'_> for f64 { } }; // Section 8.5.7.5: Read the remaining bytes for the mantissa - // FIXME: Fill backward but in a better way let mut n_bytes = [0x0; 8]; for (pos, byte) in bytes[mantissa_start..].iter().rev().enumerate() { n_bytes[7 - pos] = *byte; @@ -51,7 +55,7 @@ impl DecodeValue<'_> for f64 { // Multiply byt 2^F corresponds to just a left shift let mantissa = n << scaling_factor; // Create the f64 - Ok(integer_encode_f64(sign, exponent, mantissa)) + 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) { @@ -87,10 +91,22 @@ impl EncodeValue for f64 { 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) = integer_decode_f64(*self); - let exponent_len = encoded_len(&exponent.to_be_bytes())?; - let mantissa_len = encoded_len(&mantissa.to_be_bytes())?; - Length::ONE + exponent_len + mantissa_len + 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(); + encoded_len(&ebytes)? + }; + let mantissa_len = if mantissa == 0 { + Length::ONE + } else { + let mbytes = mantissa.to_be_bytes(); + encoded_len(&mbytes)? + }; + exponent_len + mantissa_len + Length::ONE } } @@ -122,31 +138,57 @@ impl EncodeValue for f64 { encoder.bytes(&[0b0100_0011])?; } } else { - // Always use binary encoding + // 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 first_byte |= 0b0100_0000; } + // Bits 6 and 5 are set to 0 to specify that binary encoding is used - let (_sign, exponent, mut mantissa) = integer_decode_f64(*self); - // Check that the mantissa is either zero or odd + let (_sign, exponent, mut mantissa) = decode_f64(*self); + // If the mantissa is even ( % 2 == 0) and it isn't zero, then + // ensure that the mantissa is either zero or odd by a + let mut scaling_factor: u8 = 0; if mantissa % 2 == 0 && mantissa != 0 { - // Shift by one, store that as scaling factor - mantissa >>= 1; - first_byte |= 0b0000_0100; - assert!((mantissa % 2 == 0 && mantissa != 0) || (mantissa % 2 == 1)); + loop { + // Shift by one, store that as scaling factor + mantissa >>= 1; + first_byte |= 0b0000_0100; + scaling_factor += 1; + if mantissa % 2 == 1 { + break; + } + if scaling_factor >= 4 { + // We can only encode a scaling of 2^3 + return Err(Tag::Real.value_error()); + } + } } - // Encode the exponent as two's complement on 16 bits - let exponent_bytes = (exponent as i16).to_be_bytes(); - // If the exponent is encoded only on two bytes, add that info - if exponent_bytes[0] > 0x0 { - first_byte |= 0b0000_0001; + // Add the scaling factor + match scaling_factor { + 0 => {} + 1 => first_byte |= 0b0000_0100, + 2 => first_byte |= 0b0000_1000, + 3 => first_byte |= 0b0000_1100, + _ => unreachable!(), + }; + // 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 - encode_bytes(encoder, &exponent_bytes)?; + // Rust already encodes the data as two's complement, so no further processing is needed + encode_bytes(encoder, &ebytes)?; // Now, encode the mantissa as unsigned binary number encode_bytes(encoder, &mantissa.to_be_bytes())?; @@ -193,18 +235,33 @@ pub(crate) fn mnth_bits_to_u8(bytes: &[u8]) -> u (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 -pub(crate) fn integer_decode_f64(f: f64) -> (u64, u64, u64) { +/// 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, mantissa) + (sign, exponent_no_bias, mantissa + 1) } -/// Encode an f64 from its sign, exponent, and mantissa using bit shifts -pub(crate) fn integer_encode_f64(sign: u64, exponent: u64, mantissa: u64) -> f64 { - let bits = sign << 63 | exponent << 52 | mantissa; +/// 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) } @@ -249,17 +306,16 @@ mod tests { fn encdec_normal() { // The comments correspond to the decoded value from the ASN.1 playground when the bytes are inputed. { - /* - R REAL: tag = [UNIVERSAL 9] primitive; length = 0 - 0 - Successfully decoded 2 bytes. - rec1value R ::= 0 - */ + // 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 {}", val); + 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, @@ -270,17 +326,16 @@ mod tests { } { - /* - R REAL: tag = [UNIVERSAL 9] primitive; length = 3 - 0 * 2^1 - Successfully decoded 5 bytes. - rec1value R ::= 0 - */ - let val = f64::MIN_POSITIVE; - let expected = &[0x09, 0x03, 0x80, 0x01, 0x0]; - let mut buffer = [0u8; 6]; + // 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 {}", val); + 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, @@ -291,11 +346,16 @@ mod tests { } { + // rec1value R ::= { mantissa -1, base 2, exponent 0 } let val = -1.0; - let expected = &[0x09, 0x04, 0xc1, 0x03, 0xff, 0x00]; - let mut buffer = [0u8; 6]; + 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 {}", val); + 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, @@ -306,11 +366,16 @@ mod tests { } { - let val = 1.0; - let expected = &[0x09, 0x04, 0x81, 0x03, 0xff, 0x00]; - let mut buffer = [0u8; 6]; + // rec1value R ::= { mantissa -1, base 2, exponent 1 } + let val = -1.0000000000000002; + let expected = &[0x09, 0x03, 0xc4, 0x00, 0x01]; + let mut buffer = [0u8; 5]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); - assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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, @@ -321,11 +386,35 @@ mod tests { } { - let val = -1.0000000000000002; - let expected = &[0x09, 0x04, 0xc1, 0x03, 0xff, 0x01]; - let mut buffer = [0u8; 6]; + let val = f64::MIN_POSITIVE; + let expected = &[0x09, 0x03, 0x80, 0x01, 0x0]; + let mut buffer = [0u8; 7]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); - assert_eq!(expected, encoded, "invalid encoding of {}", val); + // 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 = f64::MIN; + // TODO: Update expected + let expected = &[9, 10, 129, 7, 254, 15, 255, 255, 255, 255, 255, 255]; + 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, @@ -340,7 +429,11 @@ mod tests { let expected = &[9, 10, 129, 7, 254, 15, 255, 255, 255, 255, 255, 255]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); - assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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, @@ -358,7 +451,11 @@ mod tests { let expected = &[9, 10, 133, 4, 0, 4, 144, 253, 170, 34, 22, 140]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); - assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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, @@ -373,7 +470,11 @@ mod tests { let expected = &[9, 10, 129, 4, 0, 5, 191, 10, 139, 20, 87, 105]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); - assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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, @@ -387,7 +488,11 @@ mod tests { let expected = &[9, 10, 129, 3, 254, 6, 46, 66, 254, 250, 57, 239]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); - assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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, @@ -406,7 +511,11 @@ mod tests { let expected = &[9, 10, 133, 4, 20, 4, 73, 234, 74, 35, 126, 83]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); - assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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, @@ -421,7 +530,11 @@ mod tests { let expected = &[9, 10, 133, 4, 22, 4, 190, 179, 101, 217, 196, 5]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); - assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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, @@ -436,7 +549,11 @@ mod tests { let expected = &[9, 10, 197, 4, 13, 7, 202, 228, 62, 41, 105, 63]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); - assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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, @@ -451,7 +568,11 @@ mod tests { let expected = &[9, 10, 193, 4, 13, 10, 115, 55, 120, 220, 213, 73]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); - assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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, @@ -466,7 +587,11 @@ mod tests { let expected = &[9, 10, 197, 4, 6, 7, 196, 146, 23, 1, 111, 250]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); - assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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, @@ -481,7 +606,11 @@ mod tests { let expected = &[9, 10, 193, 4, 2, 12, 204, 166, 189, 6, 217, 145]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); - assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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, @@ -496,7 +625,11 @@ mod tests { let expected = &[9, 10, 197, 3, 251, 2, 173, 9, 190, 133, 215, 30]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); - assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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, @@ -511,7 +644,11 @@ mod tests { let expected = &[9, 10, 133, 3, 247, 2, 254, 165, 210, 243, 166, 73]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); - assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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, @@ -526,7 +663,11 @@ mod tests { let expected = &[9, 10, 133, 3, 243, 6, 206, 68, 211, 44, 153, 156]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); - assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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, @@ -541,7 +682,11 @@ mod tests { let expected = &[9, 10, 129, 3, 240, 1, 193, 213, 35, 213, 84, 123]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); - assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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, @@ -556,7 +701,11 @@ mod tests { let expected = &[9, 10, 129, 3, 235, 3, 191, 143, 39, 244, 98, 85]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); - assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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, @@ -571,7 +720,11 @@ mod tests { let expected = &[9, 10, 133, 3, 230, 6, 229, 152, 213, 183, 92, 107]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); - assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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, @@ -586,7 +739,11 @@ mod tests { let expected = &[9, 10, 133, 3, 225, 2, 234, 79, 5, 121, 127, 143]; let mut buffer = [0u8; 12]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); - assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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, @@ -601,7 +758,11 @@ mod tests { let expected = &[9, 9, 129, 3, 220, 83, 91, 111, 151, 238, 181]; let mut buffer = [0u8; 11]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); - assert_eq!(expected, encoded, "invalid encoding of {}", val); + 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, @@ -619,32 +780,43 @@ mod tests { #[test] fn encdec_f64() { - use super::{integer_decode_f64, integer_encode_f64}; + use super::{decode_f64, encode_f64}; // Test that the extraction and recreation works for val in [ - 0.0, - -1.0, 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) = integer_decode_f64(val); - let val2 = integer_encode_f64(s, e, m); - assert!((val - val2).abs() < f64::EPSILON, "fail: {}", val); + let (s, e, m) = decode_f64(val); + let val2 = encode_f64(s, e, m); + assert!( + (val - val2).abs() < f64::EPSILON, + "fail - want {val}\tgot {val2}" + ); } } #[test] fn validation_cases() { - // Caveat: these test cases are from the ASN.1 playground: https://asn1.io/asn1playground/ . + // 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 in this case correctly supports decoding such values. It will, however, + // 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]; @@ -679,10 +851,89 @@ mod tests { ); } { - let expect = -951.2357864; + let expect = 101.0; + let testcase = &[0x09, 0x08, 0x03, 0x31, 0x30, 0x31, 0x2E, 0x45, 0x2B, 0x30]; + // let mut buffer = [0u8; 12]; + // let encoded = expect.encode_to_slice(&mut buffer).unwrap(); + // assert_eq!( + // testcase, encoded, + // "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + // expect, encoded, testcase + // ); + 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, 0x10, 0x03, 0x2D, 0x39, 0x35, 0x31, 0x32, 0x33, 0x35, 0x37, 0x38, 0x36, 0x34, - 0x2E, 0x45, 0x2D, 0x37, + 0x09, 0x0F, 0x03, 0x39, 0x35, 0x31, 0x32, 0x33, 0x35, 0x37, 0x38, 0x36, 0x34, 0x2E, + 0x45, 0x2D, 0x37, + ]; + // let mut buffer = [0u8; 12]; + // let encoded = expect.encode_to_slice(&mut buffer).unwrap(); + // assert_eq!( + // testcase, encoded, + // "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + // expect, encoded, testcase + // ); + 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 mut buffer = [0u8; 12]; + // let encoded = expect.encode_to_slice(&mut buffer).unwrap(); + // assert_eq!( + // testcase, encoded, + // "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", + // expect, encoded, testcase + // ); + 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!( @@ -715,8 +966,12 @@ mod tests { ); } { - let expect = 0.0; - let testcase = &[0x09, 0x00]; + // 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, @@ -725,10 +980,5 @@ mod tests { decoded ); } - { - use super::integer_encode_f64; - let v = integer_encode_f64(1, 1023, 1); - panic!("{}", v); - } } } From effa7b38a59a92dcb2e99028a69d5a961f548f75 Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Wed, 23 Mar 2022 17:55:12 -0600 Subject: [PATCH 16/22] Final DER REALs implementation Signed-off-by: Christopher Rabotin --- der/src/asn1/real.rs | 163 ++++++++++++++++++++++++------------------- 1 file changed, 93 insertions(+), 70 deletions(-) diff --git a/der/src/asn1/real.rs b/der/src/asn1/real.rs index b7545bc5f..ae8763b3e 100644 --- a/der/src/asn1/real.rs +++ b/der/src/asn1/real.rs @@ -1,6 +1,4 @@ //! ASN.1 `NULL` support. - -use crate::asn1::integer::uint::{encode_bytes, encoded_len}; use crate::{ asn1::Any, str_slice::StrSlice, ByteSlice, DecodeValue, Decoder, EncodeValue, Encoder, Error, FixedTag, Length, Result, Tag, @@ -98,13 +96,14 @@ impl EncodeValue for f64 { Length::ONE } else { let ebytes = exponent.to_be_bytes(); - encoded_len(&ebytes)? + 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)? + // encoded_len(&mbytes)? + Length::try_from(strip_leading_zeroes(&mbytes).len())? }; exponent_len + mantissa_len + Length::ONE } @@ -141,57 +140,38 @@ impl EncodeValue for f64 { // 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 + // 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 - let (_sign, exponent, mut mantissa) = decode_f64(*self); - // If the mantissa is even ( % 2 == 0) and it isn't zero, then - // ensure that the mantissa is either zero or odd by a - let mut scaling_factor: u8 = 0; - if mantissa % 2 == 0 && mantissa != 0 { - loop { - // Shift by one, store that as scaling factor - mantissa >>= 1; - first_byte |= 0b0000_0100; - scaling_factor += 1; - if mantissa % 2 == 1 { - break; - } - if scaling_factor >= 4 { - // We can only encode a scaling of 2^3 - return Err(Tag::Real.value_error()); - } - } - } - // Add the scaling factor - match scaling_factor { - 0 => {} - 1 => first_byte |= 0b0000_0100, - 2 => first_byte |= 0b0000_1000, - 3 => first_byte |= 0b0000_1100, - _ => unreachable!(), - }; + // 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 => {}, + 0 | 1 => {} 2 => first_byte |= 0b0000_0001, 3 => first_byte |= 0b0000_0010, - _ => todo!("support multi octet exponent encoding") + _ => 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 - encode_bytes(encoder, &ebytes)?; + encoder.bytes(ebytes)?; // Now, encode the mantissa as unsigned binary number - encode_bytes(encoder, &mantissa.to_be_bytes())?; + let mantissa_bytes = mantissa.to_be_bytes(); + let mbytes = strip_leading_zeroes(&mantissa_bytes); + encoder.bytes(mbytes)?; } Ok(()) } @@ -368,7 +348,7 @@ mod tests { { // rec1value R ::= { mantissa -1, base 2, exponent 1 } let val = -1.0000000000000002; - let expected = &[0x09, 0x03, 0xc4, 0x00, 0x01]; + let expected = &[0x09, 0x03, 0xc0, 0x00, 0x02]; let mut buffer = [0u8; 5]; let encoded = val.encode_to_slice(&mut buffer).unwrap(); assert_eq!( @@ -386,15 +366,17 @@ mod tests { } { + // 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, 0x03, 0x80, 0x01, 0x0]; + 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 - // ); + 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, @@ -405,10 +387,10 @@ mod tests { } { - let val = f64::MIN; - // TODO: Update expected - let expected = &[9, 10, 129, 7, 254, 15, 255, 255, 255, 255, 255, 255]; - let mut buffer = [0u8; 12]; + // 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, @@ -425,9 +407,12 @@ mod tests { } { - let val = f64::MAX; - let expected = &[9, 10, 129, 7, 254, 15, 255, 255, 255, 255, 255, 255]; - let mut buffer = [0u8; 12]; + // 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, @@ -448,8 +433,10 @@ mod tests { fn encdec_irrationals() { { let val = core::f64::consts::PI; - let expected = &[9, 10, 133, 4, 0, 4, 144, 253, 170, 34, 22, 140]; - let mut buffer = [0u8; 12]; + 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, @@ -467,7 +454,9 @@ mod tests { { let val = core::f64::consts::E; - let expected = &[9, 10, 129, 4, 0, 5, 191, 10, 139, 20, 87, 105]; + 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!( @@ -485,7 +474,9 @@ mod tests { } { let val = core::f64::consts::LN_2; - let expected = &[9, 10, 129, 3, 254, 6, 46, 66, 254, 250, 57, 239]; + 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!( @@ -507,9 +498,12 @@ mod tests { 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 = &[9, 10, 133, 4, 20, 4, 73, 234, 74, 35, 126, 83]; - let mut buffer = [0u8; 12]; + 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, @@ -526,8 +520,11 @@ mod tests { } { + // rec1value R ::= { mantissa 2671155248072715, base 2, exponent 23 } let val = 13364022.365665454; - let expected = &[9, 10, 133, 4, 22, 4, 190, 179, 101, 217, 196, 5]; + 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!( @@ -545,8 +542,11 @@ mod tests { } { + // rec1value R ::= { mantissa -4386812962460287, base 2, exponent 14 } let val = -32343.132588105735; - let expected = &[9, 10, 197, 4, 13, 7, 202, 228, 62, 41, 105, 63]; + 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!( @@ -565,7 +565,9 @@ mod tests { { let val = -27084.866751869475; - let expected = &[9, 10, 193, 4, 13, 10, 115, 55, 120, 220, 213, 73]; + 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!( @@ -583,8 +585,11 @@ mod tests { } { + // rec1value R ::= { mantissa -4372913134428149, base 2, exponent 7 } let val = -252.28566647111404; - let expected = &[9, 10, 197, 4, 6, 7, 196, 146, 23, 1, 111, 250]; + 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!( @@ -603,7 +608,9 @@ mod tests { { let val = -14.399709612928548; - let expected = &[9, 10, 193, 4, 2, 12, 204, 166, 189, 6, 217, 145]; + 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!( @@ -622,7 +629,9 @@ mod tests { { let val = -0.08340570261832964; - let expected = &[9, 10, 197, 3, 251, 2, 173, 9, 190, 133, 215, 30]; + 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!( @@ -641,7 +650,9 @@ mod tests { { let val = 0.00536851453803701; - let expected = &[9, 10, 133, 3, 247, 2, 254, 165, 210, 243, 166, 73]; + 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!( @@ -660,7 +671,9 @@ mod tests { { let val = 0.00045183525648866433; - let expected = &[9, 10, 133, 3, 243, 6, 206, 68, 211, 44, 153, 156]; + 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!( @@ -679,7 +692,9 @@ mod tests { { let val = 0.000033869092002682955; - let expected = &[9, 10, 129, 3, 240, 1, 193, 213, 35, 213, 84, 123]; + 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!( @@ -698,7 +713,9 @@ mod tests { { let val = 0.0000011770891033600088; - let expected = &[9, 10, 129, 3, 235, 3, 191, 143, 39, 244, 98, 85]; + 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!( @@ -717,7 +734,9 @@ mod tests { { let val = 0.00000005549514041997082; - let expected = &[9, 10, 133, 3, 230, 6, 229, 152, 213, 183, 92, 107]; + 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!( @@ -736,7 +755,9 @@ mod tests { { let val = 0.0000000012707044685547803; - let expected = &[9, 10, 133, 3, 225, 2, 234, 79, 5, 121, 127, 143]; + 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!( @@ -755,7 +776,9 @@ mod tests { { let val = 0.00000000002969611878378562; - let expected = &[9, 9, 129, 3, 220, 83, 91, 111, 151, 238, 181]; + 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!( From 9fecabb1772681487620accf40514be010abbcfe Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Wed, 23 Mar 2022 17:59:59 -0600 Subject: [PATCH 17/22] Removed commented tests in validation These were commented because the ASN1 playground encodes in NR3 (base 10) but this impl only encodes in base 2, so the reciprocity tests would never have worked. Instead, all other cases were tested for decoding in the ASN1 playground. Signed-off-by: Christopher Rabotin --- der/src/asn1/real.rs | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/der/src/asn1/real.rs b/der/src/asn1/real.rs index ae8763b3e..73fae6f78 100644 --- a/der/src/asn1/real.rs +++ b/der/src/asn1/real.rs @@ -876,13 +876,6 @@ mod tests { { let expect = 101.0; let testcase = &[0x09, 0x08, 0x03, 0x31, 0x30, 0x31, 0x2E, 0x45, 0x2B, 0x30]; - // let mut buffer = [0u8; 12]; - // let encoded = expect.encode_to_slice(&mut buffer).unwrap(); - // assert_eq!( - // testcase, encoded, - // "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", - // expect, encoded, testcase - // ); let decoded = f64::from_der(testcase).unwrap(); assert!( (decoded - expect).abs() < f64::EPSILON, @@ -908,13 +901,6 @@ mod tests { 0x09, 0x0F, 0x03, 0x39, 0x35, 0x31, 0x32, 0x33, 0x35, 0x37, 0x38, 0x36, 0x34, 0x2E, 0x45, 0x2D, 0x37, ]; - // let mut buffer = [0u8; 12]; - // let encoded = expect.encode_to_slice(&mut buffer).unwrap(); - // assert_eq!( - // testcase, encoded, - // "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", - // expect, encoded, testcase - // ); let decoded = f64::from_der(testcase).unwrap(); assert!( (decoded - expect).abs() < f64::EPSILON, @@ -927,13 +913,6 @@ mod tests { { let expect = -10.0; let testcase = &[0x09, 0x06, 0x03, 0x2D, 0x31, 0x2E, 0x45, 0x31]; - // let mut buffer = [0u8; 12]; - // let encoded = expect.encode_to_slice(&mut buffer).unwrap(); - // assert_eq!( - // testcase, encoded, - // "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}", - // expect, encoded, testcase - // ); let decoded = f64::from_der(testcase).unwrap(); assert!( (decoded - expect).abs() < f64::EPSILON, From f7813bcd320bfa8a9d51ad90ff97b70666f0356e Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Wed, 23 Mar 2022 18:10:06 -0600 Subject: [PATCH 18/22] Updates for v0.6.0-pre.3 Signed-off-by: Christopher Rabotin --- der/src/asn1/real.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/der/src/asn1/real.rs b/der/src/asn1/real.rs index 73fae6f78..479167e9f 100644 --- a/der/src/asn1/real.rs +++ b/der/src/asn1/real.rs @@ -1,16 +1,16 @@ //! ASN.1 `NULL` support. use crate::{ asn1::Any, str_slice::StrSlice, ByteSlice, DecodeValue, Decoder, EncodeValue, Encoder, Error, - FixedTag, Length, Result, Tag, + FixedTag, Length, Result, Tag, Header, }; use super::integer::uint::strip_leading_zeroes; impl DecodeValue<'_> for f64 { - fn decode_value(decoder: &mut Decoder<'_>, length: Length) -> Result { - let bytes = ByteSlice::decode_value(decoder, length)?.as_bytes(); + fn decode_value(decoder: &mut Decoder<'_>, header: Header) -> Result { + let bytes = ByteSlice::decode_value(decoder, header)?.as_bytes(); - if length == Length::ZERO { + if header.length == Length::ZERO { Ok(0.0) } else if is_nth_bit_one::<7>(bytes) { // Binary encoding from section 8.5.7 applies @@ -247,7 +247,8 @@ pub(crate) fn encode_f64(sign: u64, exponent: u64, mantissa: u64) -> f64 { #[cfg(test)] mod tests { - use crate::{Decodable, Encodable}; + use crate::decodable::Decode; + use crate::encodable::Encode; #[test] fn decode_subnormal() { From 712e8c6bf80dd380b2152878889fbded6710356d Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Wed, 23 Mar 2022 18:14:36 -0600 Subject: [PATCH 19/22] cargo fmt Signed-off-by: Christopher Rabotin --- der/src/asn1/real.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/der/src/asn1/real.rs b/der/src/asn1/real.rs index 479167e9f..9b0102e9c 100644 --- a/der/src/asn1/real.rs +++ b/der/src/asn1/real.rs @@ -1,7 +1,7 @@ //! ASN.1 `NULL` support. use crate::{ asn1::Any, str_slice::StrSlice, ByteSlice, DecodeValue, Decoder, EncodeValue, Encoder, Error, - FixedTag, Length, Result, Tag, Header, + FixedTag, Header, Length, Result, Tag, }; use super::integer::uint::strip_leading_zeroes; From f1804bcdc295ab07742e95bb0f80426fb9241b85 Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Fri, 25 Mar 2022 10:59:48 -0600 Subject: [PATCH 20/22] Support rust 1.57 Signed-off-by: Christopher Rabotin --- der/src/asn1/real.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/der/src/asn1/real.rs b/der/src/asn1/real.rs index 9b0102e9c..378fb739b 100644 --- a/der/src/asn1/real.rs +++ b/der/src/asn1/real.rs @@ -37,7 +37,6 @@ impl DecodeValue<'_> for f64 { 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]]) - // u64::from_be_bytes([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, bytes[1], bytes[2]]) } _ => { // Real related error: encoded exponent cannot be represented on an IEEE-754 double @@ -824,7 +823,9 @@ mod tests { let val2 = encode_f64(s, e, m); assert!( (val - val2).abs() < f64::EPSILON, - "fail - want {val}\tgot {val2}" + "fail - want {}\tgot {}", + val, + val2 ); } } From a45766bfeccd09821d9ccac7f25df0be473c2f9e Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 26 Mar 2022 17:19:58 -0600 Subject: [PATCH 21/22] Update der/src/asn1/real.rs Co-authored-by: Tony Arcieri --- der/src/asn1/real.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/der/src/asn1/real.rs b/der/src/asn1/real.rs index 378fb739b..e04f979f3 100644 --- a/der/src/asn1/real.rs +++ b/der/src/asn1/real.rs @@ -246,8 +246,7 @@ pub(crate) fn encode_f64(sign: u64, exponent: u64, mantissa: u64) -> f64 { #[cfg(test)] mod tests { - use crate::decodable::Decode; - use crate::encodable::Encode; + use crate::{Decode, Encode}; #[test] fn decode_subnormal() { From ba617234c7e2b49da830723e9558d678fa8434ee Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Sun, 27 Mar 2022 09:06:56 -0600 Subject: [PATCH 22/22] Remove impl f64 from Any Signed-off-by: Christopher Rabotin --- der/src/asn1/real.rs | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/der/src/asn1/real.rs b/der/src/asn1/real.rs index e04f979f3..f69ec96a6 100644 --- a/der/src/asn1/real.rs +++ b/der/src/asn1/real.rs @@ -1,7 +1,7 @@ //! ASN.1 `NULL` support. use crate::{ - asn1::Any, str_slice::StrSlice, ByteSlice, DecodeValue, Decoder, EncodeValue, Encoder, Error, - FixedTag, Header, Length, Result, Tag, + str_slice::StrSlice, ByteSlice, DecodeValue, Decoder, EncodeValue, Encoder, FixedTag, Header, + Length, Result, Tag, }; use super::integer::uint::strip_leading_zeroes; @@ -180,20 +180,6 @@ impl FixedTag for f64 { const TAG: Tag = Tag::Real; } -impl<'a> From for Any<'a> { - fn from(_: f64) -> Any<'a> { - Any::from_tag_and_value(Tag::Real, ByteSlice::default()) - } -} - -impl TryFrom> for f64 { - type Error = Error; - - fn try_from(any: Any<'_>) -> Result { - any.decode_into() - } -} - /// 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 {