1use crate::asn1::oid::{ID_EC_PUBLIC_KEY, SM2P256V1};
29use crate::asn1::{reader, writer};
30use crate::sec1::{SEC1_UNCOMPRESSED_LEN, decode_uncompressed_point, encode_uncompressed_point};
31use alloc::vec::Vec;
32
33#[must_use]
45#[allow(clippy::missing_panics_doc)]
46pub fn encode(key: &crate::sm2::Sm2PublicKey) -> Vec<u8> {
47 let (x, y) = key.point().to_affine().expect("SPKI: point at infinity");
48 let pk = encode_uncompressed_point(&x, &y);
49 encode_uncompressed(&pk)
50}
51
52#[must_use]
56pub fn encode_uncompressed(uncompressed: &[u8; SEC1_UNCOMPRESSED_LEN]) -> Vec<u8> {
57 let mut alg_inner = Vec::with_capacity(ID_EC_PUBLIC_KEY.len() + SM2P256V1.len() + 4);
59 writer::write_oid(&mut alg_inner, ID_EC_PUBLIC_KEY);
60 writer::write_oid(&mut alg_inner, SM2P256V1);
61
62 let mut alg_seq = Vec::with_capacity(alg_inner.len() + 4);
63 writer::write_sequence(&mut alg_seq, &alg_inner);
64
65 let mut bitstr = Vec::with_capacity(uncompressed.len() + 4);
67 writer::write_bit_string(&mut bitstr, 0, uncompressed);
68
69 let mut body = Vec::with_capacity(alg_seq.len() + bitstr.len());
70 body.extend_from_slice(&alg_seq);
71 body.extend_from_slice(&bitstr);
72
73 let mut out = Vec::with_capacity(body.len() + 4);
74 writer::write_sequence(&mut out, &body);
75 out
76}
77
78#[must_use]
92pub fn decode(input: &[u8]) -> Option<crate::sm2::Sm2PublicKey> {
93 let (body, rest) = reader::read_sequence(input)?;
94 if !rest.is_empty() {
95 return None;
96 }
97
98 let (alg_inner, body) = reader::read_sequence(body)?;
100 let (alg_oid, alg_inner) = reader::read_oid(alg_inner)?;
101 if alg_oid != ID_EC_PUBLIC_KEY {
102 return None;
103 }
104 let (curve_oid, alg_inner) = reader::read_oid(alg_inner)?;
106 if curve_oid != SM2P256V1 || !alg_inner.is_empty() {
107 return None;
108 }
109
110 let (unused, pk_bytes, body) = reader::read_bit_string(body)?;
112 if unused != 0 || !body.is_empty() {
113 return None;
114 }
115 Some(crate::sm2::Sm2PublicKey::from_point(
116 decode_uncompressed_point(pk_bytes)?,
117 ))
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123 use crate::sm2::point::ProjectivePoint;
124
125 #[test]
127 fn round_trip_generator() {
128 let g = ProjectivePoint::generator();
129 let der = encode(&crate::sm2::Sm2PublicKey::from_point(g));
130 let recovered = decode(&der).expect("decode");
131 let (gx, gy) = g.to_affine().expect("G finite");
132 let (rx, ry) = recovered.point().to_affine().expect("recovered finite");
133 assert_eq!(rx.retrieve(), gx.retrieve());
134 assert_eq!(ry.retrieve(), gy.retrieve());
135 }
136
137 #[test]
140 fn encoded_form_shape() {
141 let g = ProjectivePoint::generator();
142 let der = encode(&crate::sm2::Sm2PublicKey::from_point(g));
143 assert_eq!(der[0], 0x30, "outer tag must be SEQUENCE");
144 assert_eq!(der.len(), 91, "SM2 SPKI is 91 bytes");
148 }
149
150 #[test]
151 fn rejects_wrong_algorithm_oid() {
152 let mut alg_inner = Vec::new();
155 writer::write_oid(
156 &mut alg_inner,
157 &[0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01],
158 );
159 writer::write_null(&mut alg_inner);
160 let mut alg_seq = Vec::new();
161 writer::write_sequence(&mut alg_seq, &alg_inner);
162 let mut bitstr = Vec::new();
163 writer::write_bit_string(&mut bitstr, 0, &[0u8; 65]);
164 let mut body = Vec::new();
165 body.extend_from_slice(&alg_seq);
166 body.extend_from_slice(&bitstr);
167 let mut der = Vec::new();
168 writer::write_sequence(&mut der, &body);
169 assert!(decode(&der).is_none());
170 }
171
172 #[test]
173 fn rejects_wrong_curve_oid() {
174 let mut alg_inner = Vec::new();
176 writer::write_oid(&mut alg_inner, ID_EC_PUBLIC_KEY);
177 writer::write_oid(
178 &mut alg_inner,
179 &[0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07],
180 );
181 let mut alg_seq = Vec::new();
182 writer::write_sequence(&mut alg_seq, &alg_inner);
183 let mut bitstr = Vec::new();
184 writer::write_bit_string(&mut bitstr, 0, &[0u8; 65]);
185 let mut body = Vec::new();
186 body.extend_from_slice(&alg_seq);
187 body.extend_from_slice(&bitstr);
188 let mut der = Vec::new();
189 writer::write_sequence(&mut der, &body);
190 assert!(decode(&der).is_none());
191 }
192
193 #[test]
194 fn rejects_off_curve_point() {
195 let mut alg_inner = Vec::new();
196 writer::write_oid(&mut alg_inner, ID_EC_PUBLIC_KEY);
197 writer::write_oid(&mut alg_inner, SM2P256V1);
198 let mut alg_seq = Vec::new();
199 writer::write_sequence(&mut alg_seq, &alg_inner);
200 let mut pt = [0u8; 65];
201 pt[0] = 0x04;
202 pt[1] = 1;
203 pt[33] = 1; let mut bitstr = Vec::new();
205 writer::write_bit_string(&mut bitstr, 0, &pt);
206 let mut body = Vec::new();
207 body.extend_from_slice(&alg_seq);
208 body.extend_from_slice(&bitstr);
209 let mut der = Vec::new();
210 writer::write_sequence(&mut der, &body);
211 assert!(decode(&der).is_none());
212 }
213
214 #[test]
215 fn rejects_trailing_bytes() {
216 let g = ProjectivePoint::generator();
217 let mut der = encode(&crate::sm2::Sm2PublicKey::from_point(g));
218 der.push(0x00);
219 assert!(decode(&der).is_none());
220 }
221}