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<crate::sm2::Sm2PublicKey>,
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<crate::sm2::Sm2PublicKey> = 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(crate::sm2::Sm2PublicKey::from_point(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().point().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
326 .public
327 .unwrap()
328 .point()
329 .to_affine()
330 .expect("finite");
331 assert_eq!(rx.retrieve(), x.retrieve());
332 assert_eq!(ry.retrieve(), y.retrieve());
333 }
334
335 #[test]
337 fn ecprivatekey_round_trip_minimal() {
338 let scalar_be: [u8; 32] = [0x42; 32];
339 let der = encode(&scalar_be, None);
340 let recovered = decode(&der).expect("decode");
341 assert_eq!(recovered.scalar_be, scalar_be);
342 assert!(recovered.public.is_none());
343 }
344
345 #[test]
347 fn ecprivatekey_round_trip_params_only() {
348 let scalar_be: [u8; 32] = [0x11; 32];
349 let der = encode(&scalar_be, None);
350 let recovered = decode(&der).expect("decode");
352 let der2 = encode(
353 &recovered.scalar_be,
354 recovered
355 .public
356 .as_ref()
357 .map(|p| {
358 let (x, y) = p.point().to_affine().expect("finite");
359 encode_uncompressed_point(&x, &y)
360 })
361 .as_ref(),
362 );
363 assert_eq!(der, der2);
364 }
365
366 #[test]
367 fn ecprivatekey_rejects_wrong_version() {
368 let bad = [
369 0x30, 0x05, 0x02, 0x01, 0x02, 0x04, 0x00, ];
373 assert!(decode(&bad).is_none());
374 }
375
376 #[test]
377 fn ecprivatekey_rejects_short_scalar() {
378 let bad = [
379 0x30, 0x06, 0x02, 0x01, 0x01, 0x04, 0x01, 0xAB, ];
383 assert!(decode(&bad).is_none());
384 }
385
386 #[test]
387 fn ecprivatekey_rejects_wrong_curve_oid() {
388 let mut body = Vec::new();
390 writer::write_integer(&mut body, &[1]);
391 let scalar = [0u8; 32];
392 writer::write_octet_string(&mut body, &scalar);
393 let p256_oid = &[0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07];
396 let mut params = Vec::new();
397 writer::write_oid(&mut params, p256_oid);
398 writer::write_context_tagged_explicit(&mut body, 0, ¶ms);
399 let mut der = Vec::new();
400 writer::write_sequence(&mut der, &body);
401 assert!(
402 decode(&der).is_none(),
403 "non-SM2 namedCurve must be rejected"
404 );
405 }
406
407 #[test]
408 fn ecprivatekey_rejects_trailing_bytes() {
409 let scalar_be: [u8; 32] = [0x42; 32];
410 let mut der = encode(&scalar_be, None);
411 der.push(0x00);
412 assert!(decode(&der).is_none(), "trailing byte must be rejected");
413 }
414
415 #[test]
416 fn validate_scalar_rejects_zero() {
417 let zero = [0u8; 32];
418 assert!(validate_scalar(&zero).is_none());
419 }
420
421 #[test]
422 fn validate_scalar_rejects_n() {
423 let n = *Fn::MODULUS.as_ref();
424 let n_bytes = n.to_be_bytes();
425 let mut buf = [0u8; 32];
426 buf.copy_from_slice(&n_bytes);
427 assert!(validate_scalar(&buf).is_none());
428 }
429}