use crate::asn1::oid::{ID_EC_PUBLIC_KEY, SM2P256V1};
use crate::asn1::{reader, writer};
use crate::sec1::{SEC1_UNCOMPRESSED_LEN, decode_uncompressed_point, encode_uncompressed_point};
use crate::sm2::point::ProjectivePoint;
use alloc::vec::Vec;
#[must_use]
#[allow(clippy::missing_panics_doc)]
pub fn encode(point: &ProjectivePoint) -> Vec<u8> {
let (x, y) = point.to_affine().expect("SPKI: point at infinity");
let pk = encode_uncompressed_point(&x, &y);
encode_uncompressed(&pk)
}
#[must_use]
pub fn encode_uncompressed(uncompressed: &[u8; SEC1_UNCOMPRESSED_LEN]) -> Vec<u8> {
let mut alg_inner = Vec::with_capacity(ID_EC_PUBLIC_KEY.len() + SM2P256V1.len() + 4);
writer::write_oid(&mut alg_inner, ID_EC_PUBLIC_KEY);
writer::write_oid(&mut alg_inner, SM2P256V1);
let mut alg_seq = Vec::with_capacity(alg_inner.len() + 4);
writer::write_sequence(&mut alg_seq, &alg_inner);
let mut bitstr = Vec::with_capacity(uncompressed.len() + 4);
writer::write_bit_string(&mut bitstr, 0, uncompressed);
let mut body = Vec::with_capacity(alg_seq.len() + bitstr.len());
body.extend_from_slice(&alg_seq);
body.extend_from_slice(&bitstr);
let mut out = Vec::with_capacity(body.len() + 4);
writer::write_sequence(&mut out, &body);
out
}
#[must_use]
pub fn decode(input: &[u8]) -> Option<ProjectivePoint> {
let (body, rest) = reader::read_sequence(input)?;
if !rest.is_empty() {
return None;
}
let (alg_inner, body) = reader::read_sequence(body)?;
let (alg_oid, alg_inner) = reader::read_oid(alg_inner)?;
if alg_oid != ID_EC_PUBLIC_KEY {
return None;
}
let (curve_oid, alg_inner) = reader::read_oid(alg_inner)?;
if curve_oid != SM2P256V1 || !alg_inner.is_empty() {
return None;
}
let (unused, pk_bytes, body) = reader::read_bit_string(body)?;
if unused != 0 || !body.is_empty() {
return None;
}
decode_uncompressed_point(pk_bytes)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trip_generator() {
let g = ProjectivePoint::generator();
let der = encode(&g);
let recovered = decode(&der).expect("decode");
let (gx, gy) = g.to_affine().expect("G finite");
let (rx, ry) = recovered.to_affine().expect("recovered finite");
assert_eq!(rx.retrieve(), gx.retrieve());
assert_eq!(ry.retrieve(), gy.retrieve());
}
#[test]
fn encoded_form_shape() {
let g = ProjectivePoint::generator();
let der = encode(&g);
assert_eq!(der[0], 0x30, "outer tag must be SEQUENCE");
assert_eq!(der.len(), 91, "SM2 SPKI is 91 bytes");
}
#[test]
fn rejects_wrong_algorithm_oid() {
let mut alg_inner = Vec::new();
writer::write_oid(
&mut alg_inner,
&[0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01],
);
writer::write_null(&mut alg_inner);
let mut alg_seq = Vec::new();
writer::write_sequence(&mut alg_seq, &alg_inner);
let mut bitstr = Vec::new();
writer::write_bit_string(&mut bitstr, 0, &[0u8; 65]);
let mut body = Vec::new();
body.extend_from_slice(&alg_seq);
body.extend_from_slice(&bitstr);
let mut der = Vec::new();
writer::write_sequence(&mut der, &body);
assert!(decode(&der).is_none());
}
#[test]
fn rejects_wrong_curve_oid() {
let mut alg_inner = Vec::new();
writer::write_oid(&mut alg_inner, ID_EC_PUBLIC_KEY);
writer::write_oid(
&mut alg_inner,
&[0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07],
);
let mut alg_seq = Vec::new();
writer::write_sequence(&mut alg_seq, &alg_inner);
let mut bitstr = Vec::new();
writer::write_bit_string(&mut bitstr, 0, &[0u8; 65]);
let mut body = Vec::new();
body.extend_from_slice(&alg_seq);
body.extend_from_slice(&bitstr);
let mut der = Vec::new();
writer::write_sequence(&mut der, &body);
assert!(decode(&der).is_none());
}
#[test]
fn rejects_off_curve_point() {
let mut alg_inner = Vec::new();
writer::write_oid(&mut alg_inner, ID_EC_PUBLIC_KEY);
writer::write_oid(&mut alg_inner, SM2P256V1);
let mut alg_seq = Vec::new();
writer::write_sequence(&mut alg_seq, &alg_inner);
let mut pt = [0u8; 65];
pt[0] = 0x04;
pt[1] = 1;
pt[33] = 1; let mut bitstr = Vec::new();
writer::write_bit_string(&mut bitstr, 0, &pt);
let mut body = Vec::new();
body.extend_from_slice(&alg_seq);
body.extend_from_slice(&bitstr);
let mut der = Vec::new();
writer::write_sequence(&mut der, &body);
assert!(decode(&der).is_none());
}
#[test]
fn rejects_trailing_bytes() {
let g = ProjectivePoint::generator();
let mut der = encode(&g);
der.push(0x00);
assert!(decode(&der).is_none());
}
}