use crate::buffer::Buf;
use arrayvec::ArrayVec;
use nom::bytes::complete::take;
use nom::error::{Error, ErrorKind};
use nom::{Err, IResult, number::complete::be_u8};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[allow(unused)]
pub enum ECPointFormat {
#[default]
Uncompressed = 0x00,
AnsiX962CompressedPrime = 0x01,
AnsiX962CompressedChar2 = 0x02,
}
impl ECPointFormat {
#[allow(unused)]
pub fn parse(input: &[u8]) -> IResult<&[u8], ECPointFormat> {
let (input, value) = be_u8(input)?;
let format = match value {
0x00 => ECPointFormat::Uncompressed,
0x01 => ECPointFormat::AnsiX962CompressedPrime,
0x02 => ECPointFormat::AnsiX962CompressedChar2,
_ => {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::Switch,
)));
}
};
Ok((input, format))
}
pub fn as_u8(&self) -> u8 {
*self as u8
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ECPointFormatsExtension {
pub formats: ArrayVec<ECPointFormat, 3>,
}
impl ECPointFormatsExtension {
pub fn default() -> Self {
let mut formats = ArrayVec::new();
formats.push(ECPointFormat::Uncompressed);
ECPointFormatsExtension { formats }
}
#[allow(unused)]
pub fn parse(input: &[u8]) -> IResult<&[u8], ECPointFormatsExtension> {
let (input, list_len) = be_u8(input)?;
let (input, formats_data) = take(list_len)(input)?;
if !input.is_empty() {
return Err(Err::Failure(Error::new(input, ErrorKind::LengthValue)));
}
let mut formats = ArrayVec::new();
let mut current_input = formats_data;
while !current_input.is_empty() {
let format_input = current_input;
let (rest, format) = be_u8(current_input)?;
let Some(format) = (match format {
0x00 => Some(ECPointFormat::Uncompressed),
0x01 => Some(ECPointFormat::AnsiX962CompressedPrime),
0x02 => Some(ECPointFormat::AnsiX962CompressedChar2),
_ => None,
}) else {
current_input = rest;
continue;
};
formats
.try_push(format)
.map_err(|_| Err::Failure(Error::new(format_input, ErrorKind::LengthValue)))?;
current_input = rest;
}
Ok((current_input, ECPointFormatsExtension { formats }))
}
pub fn serialize(&self, output: &mut Buf) {
output.push(self.formats.len() as u8);
for format in &self.formats {
output.push(format.as_u8());
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::buffer::Buf;
#[test]
fn test_ec_point_formats_extension() {
let mut formats = ArrayVec::new();
formats.push(ECPointFormat::Uncompressed);
formats.push(ECPointFormat::AnsiX962CompressedPrime);
let ext = ECPointFormatsExtension { formats };
let mut serialized = Buf::new();
ext.serialize(&mut serialized);
let expected = [
0x02, 0x00, 0x01, ];
assert_eq!(&*serialized, expected);
let (_, parsed) = ECPointFormatsExtension::parse(&serialized).unwrap();
assert_eq!(parsed.formats.as_slice(), ext.formats.as_slice());
}
#[test]
fn too_many_ec_point_formats_are_rejected() {
let bytes = [
0x04, 0x00, 0x00, 0x00, 0x00, ];
let err = ECPointFormatsExtension::parse(&bytes).unwrap_err();
assert!(matches!(
err,
Err::Failure(Error {
code: ErrorKind::LengthValue,
..
})
));
}
#[test]
fn unknown_ec_point_formats_are_ignored() {
let (rest, parsed) = ECPointFormatsExtension::parse(&[
0x03, 0x02, 0x00, 0xFF, ])
.unwrap();
assert!(rest.is_empty());
assert_eq!(
parsed.formats.as_slice(),
&[
ECPointFormat::AnsiX962CompressedChar2,
ECPointFormat::Uncompressed,
]
);
}
#[test]
fn trailing_ec_point_format_bytes_are_rejected() {
let result = ECPointFormatsExtension::parse(&[
0x01, 0x00, 0xFF, ]);
assert!(matches!(
result,
Err(Err::Failure(Error {
code: ErrorKind::LengthValue,
..
}))
));
}
}