const RAW_POINT_LENGTHS: &[usize] = &[
32, 33, 49, 57, 65, 67, 97, 133, ];
const DER_OCTET_STRING: u8 = 0x04;
pub fn encode_der(raw: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(raw.len() + 4);
out.push(DER_OCTET_STRING);
let len = raw.len();
if len < 0x80 {
out.push(len as u8);
} else if len <= 0xff {
out.extend_from_slice(&[0x81, len as u8]);
} else {
out.extend_from_slice(&[0x82, (len >> 8) as u8, len as u8]);
}
out.extend_from_slice(raw);
out
}
pub fn decode(value: &[u8]) -> &[u8] {
match unwrap_der(value) {
Some(inner) if RAW_POINT_LENGTHS.contains(&inner.len()) => inner,
_ => value,
}
}
fn unwrap_der(value: &[u8]) -> Option<&[u8]> {
let (&tag, rest) = value.split_first()?;
if tag != DER_OCTET_STRING {
return None;
}
let (&first, rest) = rest.split_first()?;
let (len, rest) = match first {
0x00..=0x7f => (first as usize, rest),
0x81 => {
let (&l, rest) = rest.split_first()?;
if l < 0x80 {
return None;
}
(l as usize, rest)
}
0x82 => {
if rest.len() < 2 {
return None;
}
let l = ((rest[0] as usize) << 8) | rest[1] as usize;
if l <= 0xff {
return None;
}
(l, &rest[2..])
}
_ => return None,
};
(rest.len() == len).then_some(rest)
}
#[cfg(test)]
mod tests {
use super::*;
fn uncompressed(len: usize, fill: u8) -> Vec<u8> {
let mut p = vec![fill; len];
p[0] = 0x04;
p
}
#[test]
fn encode_p256_uses_short_form() {
let raw = uncompressed(65, 0xab);
let der = encode_der(&raw);
assert_eq!(&der[..3], &[0x04, 0x41, 0x04]);
assert_eq!(der.len(), 67);
}
#[test]
fn encode_p384_uses_short_form() {
let raw = uncompressed(97, 0xe7);
let der = encode_der(&raw);
assert_eq!(&der[..3], &[0x04, 0x61, 0x04]);
assert_eq!(der.len(), 99);
}
#[test]
fn encode_p521_uses_long_form() {
let raw = uncompressed(133, 0x01);
let der = encode_der(&raw);
assert_eq!(&der[..4], &[0x04, 0x81, 0x85, 0x04]);
assert_eq!(der.len(), 136);
}
#[test]
fn decode_round_trips_all_supported_lengths() {
for &len in RAW_POINT_LENGTHS {
let raw = uncompressed(len, 0x5a);
assert_eq!(decode(&encode_der(&raw)), raw.as_slice(), "len {len}");
}
}
#[test]
fn decode_passes_raw_points_through() {
for &len in RAW_POINT_LENGTHS {
let raw = uncompressed(len, 0x5a);
assert_eq!(decode(&raw), raw.as_slice(), "len {len}");
}
}
#[test]
fn decode_does_not_misread_raw_point_that_looks_like_der_header() {
let mut raw = uncompressed(65, 0x11);
raw[1] = 0x3f;
assert_eq!(decode(&raw), raw.as_slice());
let mut ed = vec![0x22; 32];
ed[0] = 0x04;
ed[1] = 0x1e;
assert_eq!(decode(&ed), ed.as_slice());
}
#[test]
fn decode_rejects_non_minimal_length() {
let raw = uncompressed(65, 0x33);
let mut ber = vec![0x04, 0x81, 0x41];
ber.extend_from_slice(&raw);
assert_eq!(decode(&ber), ber.as_slice());
}
}