1use crate::asn1::oid::SM2P256V1;
35use crate::asn1::{reader, writer};
36use crate::sm2::curve::{Fn, Fp};
37use crate::sm2::encrypt::{point_on_curve, projective_from_affine};
38use crate::sm2::point::ProjectivePoint;
39use alloc::vec::Vec;
40use crypto_bigint::U256;
41use subtle::ConstantTimeLess;
42use zeroize::Zeroize;
43
44pub(crate) const SEC1_TAG_UNCOMPRESSED: u8 = 0x04;
46pub(crate) const SEC1_UNCOMPRESSED_LEN: usize = 65;
48const ECPRIVKEY_VER1: u8 = 1;
50
51#[must_use]
56pub(crate) fn encode_uncompressed_point(x: &Fp, y: &Fp) -> [u8; SEC1_UNCOMPRESSED_LEN] {
57 let mut out = [0u8; SEC1_UNCOMPRESSED_LEN];
58 out[0] = SEC1_TAG_UNCOMPRESSED;
59 out[1..33].copy_from_slice(&x.retrieve().to_be_bytes());
60 out[33..65].copy_from_slice(&y.retrieve().to_be_bytes());
61 out
62}
63
64#[must_use]
77pub(crate) fn decode_uncompressed_point(input: &[u8]) -> Option<ProjectivePoint> {
78 if input.len() != SEC1_UNCOMPRESSED_LEN {
79 return None;
80 }
81 if input[0] != SEC1_TAG_UNCOMPRESSED {
82 return None;
83 }
84 let x_be = &input[1..33];
85 let y_be = &input[33..65];
86 let x_u = U256::from_be_slice(x_be);
87 let y_u = U256::from_be_slice(y_be);
88 let p = *Fp::MODULUS.as_ref();
89 if !bool::from(x_u.ct_lt(&p)) || !bool::from(y_u.ct_lt(&p)) {
90 return None;
91 }
92 let x = Fp::new(&x_u);
93 let y = Fp::new(&y_u);
94 if !point_on_curve(&x, &y) {
95 return None;
96 }
97 Some(projective_from_affine(x, y))
98}
99
100#[must_use]
111pub fn encode(
112 scalar_be: &[u8; 32],
113 public_uncompressed: Option<&[u8; SEC1_UNCOMPRESSED_LEN]>,
114) -> Vec<u8> {
115 let mut body = Vec::with_capacity(120);
116 writer::write_integer(&mut body, &[ECPRIVKEY_VER1]);
118 writer::write_octet_string(&mut body, scalar_be);
121 let mut params_inner = Vec::with_capacity(SM2P256V1.len() + 2);
123 writer::write_oid(&mut params_inner, SM2P256V1);
124 writer::write_context_tagged_explicit(&mut body, 0, ¶ms_inner);
125 if let Some(pk) = public_uncompressed {
127 let mut pk_inner = Vec::with_capacity(SEC1_UNCOMPRESSED_LEN + 4);
128 writer::write_bit_string(&mut pk_inner, 0, pk);
129 writer::write_context_tagged_explicit(&mut body, 1, &pk_inner);
130 }
131 let mut out = Vec::with_capacity(body.len() + 4);
132 writer::write_sequence(&mut out, &body);
133 body.zeroize();
134 out
135}
136
137#[derive(Debug, Clone)]
143pub struct EcPrivateKey {
144 pub scalar_be: [u8; 32],
148 pub public: Option<ProjectivePoint>,
151}
152
153impl Drop for EcPrivateKey {
154 fn drop(&mut self) {
155 self.scalar_be.zeroize();
156 }
157}
158
159#[must_use]
173pub fn decode(input: &[u8]) -> Option<EcPrivateKey> {
174 let (body, rest) = reader::read_sequence(input)?;
175 if !rest.is_empty() {
176 return None;
177 }
178
179 let (version, body) = reader::read_integer(body)?;
181 if version != [ECPRIVKEY_VER1] {
182 return None;
183 }
184 let (scalar_bytes, mut body) = reader::read_octet_string(body)?;
186 if scalar_bytes.len() != 32 {
187 return None;
188 }
189 let mut scalar_be = [0u8; 32];
190 scalar_be.copy_from_slice(scalar_bytes);
191
192 let mut public: Option<ProjectivePoint> = None;
193
194 if let Some((params_inner, after)) = reader::read_context_tagged_explicit(body, 0) {
196 let (oid, params_rest) = reader::read_oid(params_inner)?;
197 if !params_rest.is_empty() || oid != SM2P256V1 {
198 scalar_be.zeroize();
199 return None;
200 }
201 body = after;
202 }
203
204 if let Some((pk_inner, after)) = reader::read_context_tagged_explicit(body, 1) {
206 let (unused, pk_bytes, pk_rest) = reader::read_bit_string(pk_inner)?;
207 if unused != 0 || !pk_rest.is_empty() {
208 scalar_be.zeroize();
209 return None;
210 }
211 if let Some(p) = decode_uncompressed_point(pk_bytes) {
212 public = Some(p);
213 } else {
214 scalar_be.zeroize();
215 return None;
216 }
217 body = after;
218 }
219
220 if !body.is_empty() {
221 scalar_be.zeroize();
222 return None;
223 }
224
225 Some(EcPrivateKey { scalar_be, public })
226}
227
228#[must_use]
234#[allow(dead_code)]
235pub(crate) fn validate_scalar(scalar_be: &[u8; 32]) -> Option<Fn> {
236 let d = U256::from_be_slice(scalar_be);
237 let n = *Fn::MODULUS.as_ref();
238 if d == U256::ZERO {
239 return None;
240 }
241 if !bool::from(d.ct_lt(&n)) {
242 return None;
243 }
244 Some(Fn::new(&d))
245}
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250 use crate::sm2::point::ProjectivePoint;
251
252 #[test]
254 fn uncompressed_point_round_trip_generator() {
255 let g = ProjectivePoint::generator();
256 let (x, y) = g.to_affine().expect("G finite");
257 let bytes = encode_uncompressed_point(&x, &y);
258 assert_eq!(bytes[0], 0x04);
259 let recovered = decode_uncompressed_point(&bytes).expect("decode");
260 let (rx, ry) = recovered.to_affine().expect("recovered finite");
261 assert_eq!(rx.retrieve(), x.retrieve());
262 assert_eq!(ry.retrieve(), y.retrieve());
263 }
264
265 #[test]
266 fn uncompressed_point_rejects_wrong_length() {
267 assert!(decode_uncompressed_point(&[0x04]).is_none());
268 assert!(decode_uncompressed_point(&[0x04; 64]).is_none());
269 assert!(decode_uncompressed_point(&[0x04; 66]).is_none());
270 }
271
272 #[test]
273 fn uncompressed_point_rejects_compressed_tag() {
274 let mut bytes = [0u8; 65];
275 bytes[0] = 0x02;
276 assert!(decode_uncompressed_point(&bytes).is_none());
277 bytes[0] = 0x03;
278 assert!(decode_uncompressed_point(&bytes).is_none());
279 }
280
281 #[test]
282 fn uncompressed_point_rejects_off_curve() {
283 let mut bytes = [0u8; 65];
284 bytes[0] = 0x04;
285 bytes[1] = 1;
286 bytes[33] = 1;
287 assert!(decode_uncompressed_point(&bytes).is_none());
289 }
290
291 #[test]
292 fn uncompressed_point_rejects_x_at_or_above_p() {
293 let g = ProjectivePoint::generator();
294 let (_x, y) = g.to_affine().expect("G finite");
295 let p = *Fp::MODULUS.as_ref();
296 let mut bytes = [0u8; SEC1_UNCOMPRESSED_LEN];
297 bytes[0] = 0x04;
298 bytes[1..33].copy_from_slice(&p.to_be_bytes());
300 bytes[33..65].copy_from_slice(&y.retrieve().to_be_bytes());
301 assert!(
302 decode_uncompressed_point(&bytes).is_none(),
303 "X = p must be rejected"
304 );
305 }
306
307 #[test]
309 fn ecprivatekey_round_trip_with_public() {
310 let scalar_be: [u8; 32] = [
311 0x39, 0x45, 0x20, 0x8F, 0x7B, 0x21, 0x44, 0xB1, 0x3F, 0x36, 0xE3, 0x8A, 0xC6, 0xD3,
312 0x9F, 0x95, 0x88, 0x93, 0x93, 0x69, 0x28, 0x60, 0xB5, 0x1A, 0x42, 0xFB, 0x81, 0xEF,
313 0x4D, 0xF7, 0xC5, 0xB8,
314 ];
315 let d = U256::from_be_slice(&scalar_be);
317 let key = crate::sm2::Sm2PrivateKey::from_scalar_inner(d).expect("valid d");
318 let (x, y) = key.public_key().to_affine().expect("finite");
319 let pk = encode_uncompressed_point(&x, &y);
320 let der = encode(&scalar_be, Some(&pk));
321
322 let recovered = decode(&der).expect("decode");
323 assert_eq!(recovered.scalar_be, scalar_be);
324 assert!(recovered.public.is_some());
325 let (rx, ry) = recovered.public.unwrap().to_affine().expect("finite");
326 assert_eq!(rx.retrieve(), x.retrieve());
327 assert_eq!(ry.retrieve(), y.retrieve());
328 }
329
330 #[test]
332 fn ecprivatekey_round_trip_minimal() {
333 let scalar_be: [u8; 32] = [0x42; 32];
334 let der = encode(&scalar_be, None);
335 let recovered = decode(&der).expect("decode");
336 assert_eq!(recovered.scalar_be, scalar_be);
337 assert!(recovered.public.is_none());
338 }
339
340 #[test]
342 fn ecprivatekey_round_trip_params_only() {
343 let scalar_be: [u8; 32] = [0x11; 32];
344 let der = encode(&scalar_be, None);
345 let recovered = decode(&der).expect("decode");
347 let der2 = encode(
348 &recovered.scalar_be,
349 recovered
350 .public
351 .as_ref()
352 .map(|p| {
353 let (x, y) = p.to_affine().expect("finite");
354 encode_uncompressed_point(&x, &y)
355 })
356 .as_ref(),
357 );
358 assert_eq!(der, der2);
359 }
360
361 #[test]
362 fn ecprivatekey_rejects_wrong_version() {
363 let bad = [
364 0x30, 0x05, 0x02, 0x01, 0x02, 0x04, 0x00, ];
368 assert!(decode(&bad).is_none());
369 }
370
371 #[test]
372 fn ecprivatekey_rejects_short_scalar() {
373 let bad = [
374 0x30, 0x06, 0x02, 0x01, 0x01, 0x04, 0x01, 0xAB, ];
378 assert!(decode(&bad).is_none());
379 }
380
381 #[test]
382 fn ecprivatekey_rejects_wrong_curve_oid() {
383 let mut body = Vec::new();
385 writer::write_integer(&mut body, &[1]);
386 let scalar = [0u8; 32];
387 writer::write_octet_string(&mut body, &scalar);
388 let p256_oid = &[0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07];
391 let mut params = Vec::new();
392 writer::write_oid(&mut params, p256_oid);
393 writer::write_context_tagged_explicit(&mut body, 0, ¶ms);
394 let mut der = Vec::new();
395 writer::write_sequence(&mut der, &body);
396 assert!(
397 decode(&der).is_none(),
398 "non-SM2 namedCurve must be rejected"
399 );
400 }
401
402 #[test]
403 fn ecprivatekey_rejects_trailing_bytes() {
404 let scalar_be: [u8; 32] = [0x42; 32];
405 let mut der = encode(&scalar_be, None);
406 der.push(0x00);
407 assert!(decode(&der).is_none(), "trailing byte must be rejected");
408 }
409
410 #[test]
411 fn validate_scalar_rejects_zero() {
412 let zero = [0u8; 32];
413 assert!(validate_scalar(&zero).is_none());
414 }
415
416 #[test]
417 fn validate_scalar_rejects_n() {
418 let n = *Fn::MODULUS.as_ref();
419 let n_bytes = n.to_be_bytes();
420 let mut buf = [0u8; 32];
421 buf.copy_from_slice(&n_bytes);
422 assert!(validate_scalar(&buf).is_none());
423 }
424}