1use big_number::Uint;
8
9use super::p_curves::{self, Curve, UintOps, field_pow};
10use crate::{EllipticCurveError, Hasher, RandomError, hmac::Hmac, sha2::Sha256};
11
12pub const SECRET_KEY_SIZE: usize = 28;
14pub const PUBLIC_KEY_COMPRESSED_SIZE: usize = 29;
16pub const PUBLIC_KEY_UNCOMPRESSED_SIZE: usize = 57;
18pub const SIGNATURE_SIZE: usize = 56;
20pub const ECDH_SHARED_SECRET_SIZE: usize = 28;
23
24#[derive(Clone, PartialEq, Eq)]
55#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop))]
56pub struct SecretKey(p_curves::SecretKey<P224>);
57
58impl SecretKey {
59 #[cfg(feature = "random")]
61 pub fn generate() -> Result<SecretKey, EllipticCurveError> {
62 Ok(SecretKey(p_curves::SecretKey::generate()?))
63 }
64
65 pub fn from_bytes(key: &[u8; SECRET_KEY_SIZE]) -> Result<SecretKey, EllipticCurveError> {
70 Ok(SecretKey(p_curves::SecretKey::from_bytes(key)?))
71 }
72
73 pub fn public_key(&self) -> PublicKey {
75 PublicKey(self.0.public_key())
76 }
77
78 pub fn sign(&self, message: &[u8]) -> Result<[u8; SIGNATURE_SIZE], EllipticCurveError> {
90 self.0.sign(message)
91 }
92
93 pub fn ecdh(&self, peer_public: &PublicKey) -> Result<[u8; ECDH_SHARED_SECRET_SIZE], EllipticCurveError> {
100 self.0.ecdh(&peer_public.0)
101 }
102
103 pub fn to_bytes(&self) -> [u8; SECRET_KEY_SIZE] {
105 self.0.to_bytes()
106 }
107}
108
109#[derive(Clone, Copy, Debug, PartialEq, Eq)]
125pub struct PublicKey(p_curves::PublicKey<P224>);
126
127impl PublicKey {
128 #[inline]
133 pub fn from_bytes(key: &[u8]) -> Result<PublicKey, EllipticCurveError> {
134 Ok(PublicKey(p_curves::PublicKey::from_bytes(key)?))
135 }
136
137 #[inline]
144 pub fn from_x_y(x_bytes: &[u8; 28], y_bytes: &[u8; 28]) -> Result<PublicKey, EllipticCurveError> {
145 Ok(PublicKey(p_curves::PublicKey::from_x_y(x_bytes, y_bytes)?))
146 }
147
148 pub fn verify(&self, message: &[u8], signature: &[u8; SIGNATURE_SIZE]) -> Result<(), EllipticCurveError> {
155 self.0.verify(message, signature)
156 }
157
158 pub fn verify_strict(&self, message: &[u8], signature: &[u8; SIGNATURE_SIZE]) -> Result<(), EllipticCurveError> {
165 self.0.verify_strict(message, signature)
166 }
167
168 #[inline]
170 pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_UNCOMPRESSED_SIZE] {
171 self.0.to_bytes()
172 }
173
174 #[inline]
176 pub fn to_compressed_bytes(&self) -> [u8; PUBLIC_KEY_COMPRESSED_SIZE] {
177 self.0.to_compressed_bytes()
178 }
179
180 #[inline]
182 pub fn x_y(&self) -> ([u8; 28], [u8; 28]) {
183 self.0.x_y()
184 }
185}
186
187#[derive(Clone, Copy, Debug, PartialEq, Eq)]
189struct P224;
190
191type U224 = Uint<224, 4>;
192type CurveUint = U224;
193
194const MODULUS_P: CurveUint = CurveUint::from_limbs([
195 0x0000_0000_0000_0001,
196 0xffff_ffff_0000_0000,
197 0xffff_ffff_ffff_ffff,
198 0x0000_0000_ffff_ffff,
199]);
200
201const MODULUS_N: CurveUint = CurveUint::from_limbs([
202 0x13dd_2945_5c5c_2a3d,
203 0xffff_16a2_e0b8_f03e,
204 0xffff_ffff_ffff_ffff,
205 0x0000_0000_ffff_ffff,
206]);
207
208const N_HALF: CurveUint = CurveUint::from_limbs([
211 0x09ee_94a2_ae2e_151e,
212 0xffff_8b51_705c_781f,
213 0xffff_ffff_ffff_ffff,
214 0x0000_0000_7fff_ffff,
215]);
216
217const P_MINUS_TWO: CurveUint = CurveUint::from_limbs([
218 0xffff_ffff_ffff_ffff,
219 0xffff_fffe_ffff_ffff,
220 0xffff_ffff_ffff_ffff,
221 0x0000_0000_ffff_ffff,
222]);
223
224const N_MINUS_TWO: CurveUint = CurveUint::from_limbs([
225 0x13dd_2945_5c5c_2a3b,
226 0xffff_16a2_e0b8_f03e,
227 0xffff_ffff_ffff_ffff,
228 0x0000_0000_ffff_ffff,
229]);
230
231const TONELLI_S: usize = 96;
235const TONELLI_Q_MINUS_ONE_OVER_TWO: CurveUint = CurveUint::from_limbs([
237 0xffff_ffff_ffff_ffff,
238 0x7fff_ffff_ffff_ffff,
239 0x0000_0000_0000_0000,
240 0x0000_0000_0000_0000,
241]);
242const TONELLI_ROOT_OF_UNITY: CurveUint = CurveUint::from_limbs([
246 0xf3fb_3632_dc69_1b74,
247 0x0b2d_6ffb_bea3_d8ce,
248 0x8598_a792_0c55_b2d4,
249 0x0000_0000_6a0f_ec67,
250]);
251
252const P_MU: [u64; 5] = [
255 0x0000_0000_ffff_ffff,
256 0xffff_ffff_ffff_ffff,
257 0x0000_0000_ffff_ffff,
258 0x0000_0000_0000_0000,
259 0x0000_0001_0000_0000,
260];
261
262const N_MU: [u64; 5] = [
263 0xd4ba_a4cf_1822_bc47,
264 0xec22_d6ba_a3a3_d5c3,
265 0x0000_e95d_1f47_0fc1,
266 0x0000_0000_0000_0000,
267 0x0000_0001_0000_0000,
268];
269
270const CURVE_B: CurveUint = CurveUint::from_limbs([
271 0x270b_3943_2355_ffb4,
272 0x5044_b0b7_d7bf_d8ba,
273 0x0c04_b3ab_f541_3256,
274 0x0000_0000_b405_0a85,
275]);
276
277const GENERATOR_X: CurveUint = CurveUint::from_limbs([
278 0x3432_80d6_115c_1d21,
279 0x4a03_c1d3_56c2_1122,
280 0x6bb4_bf7f_3213_90b9,
281 0x0000_0000_b70e_0cbd,
282]);
283
284const GENERATOR_Y: CurveUint = CurveUint::from_limbs([
285 0x44d5_8199_8500_7e34,
286 0xcd43_75a0_5a07_4764,
287 0xb5f7_23fb_4c22_dfe6,
288 0x0000_0000_bd37_6388,
289]);
290
291#[inline]
293fn reduce_mod(value: CurveUint) -> CurveUint {
294 let (sub_value, _) = value.sub_raw(&MODULUS_N);
295 CurveUint::ct_select(&sub_value, &value, value.ct_ge(&MODULUS_N))
296}
297
298fn p224_field_sqrt(a: CurveUint) -> Option<CurveUint> {
308 let w = field_pow::<P224>(a, &TONELLI_Q_MINUS_ONE_OVER_TWO);
310 let mut v = TONELLI_S;
311 let mut x = curve_field_mul(&w, &a);
312 let mut b = curve_field_mul(&x, &w);
313
314 let mut z = TONELLI_ROOT_OF_UNITY;
316
317 for max_v in (1..=TONELLI_S).rev() {
318 let mut k = 1usize;
319 let mut b2k = curve_field_mul(&b, &b);
320 let mut j_less_than_v = true;
321
322 for j in 2..max_v {
323 let b2k_is_one = b2k.ct_eq(&CurveUint::ONE);
324 let squared = curve_field_mul(
326 &CurveUint::ct_select(&z, &b2k, b2k_is_one),
327 &CurveUint::ct_select(&z, &b2k, b2k_is_one),
328 );
329 b2k = CurveUint::ct_select(&b2k, &squared, b2k_is_one);
330 let new_z = CurveUint::ct_select(&squared, &z, b2k_is_one);
331 j_less_than_v &= j != v;
332 k = ct_select_usize(k, j, b2k_is_one);
333 z = CurveUint::ct_select(&new_z, &z, j_less_than_v);
334 }
335
336 let result = curve_field_mul(&x, &z);
337 x = CurveUint::ct_select(&x, &result, b.ct_eq(&CurveUint::ONE));
338 z = curve_field_mul(&z, &z);
339 b = curve_field_mul(&b, &z);
340 v = k;
341 }
342
343 if curve_field_mul(&x, &x).ct_eq(&a) {
344 Some(x)
345 } else {
346 None
347 }
348}
349
350#[inline]
352fn ct_select_usize(a: usize, b: usize, choice: bool) -> usize {
353 let mask = (choice as usize).wrapping_neg();
354 (a & mask) | (b & !mask)
355}
356
357#[inline]
358fn curve_field_mul(a: &CurveUint, b: &CurveUint) -> CurveUint {
359 a.mul_mod_barrett_w::<10>(b, &MODULUS_P, &P_MU)
360}
361
362impl Curve for P224 {
363 type U = CurveUint;
364 type FieldBytes = [u8; SECRET_KEY_SIZE];
365 type DigestBytes = [u8; 32];
366 type CompressedBytes = [u8; PUBLIC_KEY_COMPRESSED_SIZE];
367 type UncompressedBytes = [u8; PUBLIC_KEY_UNCOMPRESSED_SIZE];
368 type SignatureBytes = [u8; SIGNATURE_SIZE];
369
370 const FIELD_BITS: usize = 224;
371 const FIELD_BYTES: usize = SECRET_KEY_SIZE;
372 const DIGEST_BYTES: usize = 32;
373 const COMPRESSED_BYTES: usize = PUBLIC_KEY_COMPRESSED_SIZE;
374 const UNCOMPRESSED_BYTES: usize = PUBLIC_KEY_UNCOMPRESSED_SIZE;
375
376 const MODULUS_P: CurveUint = MODULUS_P;
377 const MODULUS_N: CurveUint = MODULUS_N;
378 const N_HALF: CurveUint = N_HALF;
379 const P_MINUS_TWO: CurveUint = P_MINUS_TWO;
380 const N_MINUS_TWO: CurveUint = N_MINUS_TWO;
381 const CURVE_B: CurveUint = CURVE_B;
382 const GENERATOR_X: CurveUint = GENERATOR_X;
383 const GENERATOR_Y: CurveUint = GENERATOR_Y;
384
385 #[inline]
386 fn field_mul(a: &Self::U, b: &Self::U) -> Self::U {
387 a.mul_mod_barrett_w::<10>(b, &MODULUS_P, &P_MU)
388 }
389
390 #[inline]
391 fn scalar_mul(a: &Self::U, b: &Self::U) -> Self::U {
392 a.mul_mod_barrett_w::<10>(b, &MODULUS_N, &N_MU)
393 }
394
395 #[inline]
396 fn field_sqrt(a: Self::U) -> Option<Self::U> {
397 p224_field_sqrt(a)
398 }
399
400 #[inline]
401 fn hash(data: &[u8]) -> Self::DigestBytes {
402 Sha256::hash(data).as_ref().try_into().unwrap()
403 }
404
405 #[inline]
406 fn hmac(key: &[u8], data: &[u8]) -> Self::DigestBytes {
407 Hmac::<Sha256>::mac(key, data).as_ref().try_into().unwrap()
408 }
409
410 #[inline]
413 fn scalar_from_digest(digest: &Self::DigestBytes) -> Self::U {
414 let truncated: [u8; SECRET_KEY_SIZE] = digest.as_ref()[..SECRET_KEY_SIZE].try_into().unwrap();
415 reduce_mod(CurveUint::read_be(&truncated))
416 }
417
418 #[inline]
419 fn scalar_from_field_bytes(bytes: &Self::FieldBytes) -> Self::U {
420 reduce_mod(CurveUint::read_be(bytes.as_ref()))
421 }
422
423 #[cfg(feature = "random")]
424 #[inline]
425 fn random_secret_key() -> Result<Self::FieldBytes, RandomError> {
426 crate::random::bytes()
427 }
428}
429
430pub fn is_valid_public_key(public_key: &[u8]) -> bool {
433 p_curves::is_valid_public_key::<P224>(public_key)
434}
435
436#[cfg(test)]
437mod tests {
438 #![allow(dead_code)]
439 use super::*;
440 use crate::p_curves::p_curves;
441
442 type FieldElement = p_curves::FieldElement<P224>;
443 type Scalar = p_curves::Scalar<P224>;
444 type AffinePoint = p_curves::AffinePoint<P224>;
445 type ProjectivePoint = p_curves::ProjectivePoint<P224>;
446 type Rfc6979 = p_curves::Rfc6979<P224>;
447
448 fn hash_message(message: &[u8]) -> [u8; 32] {
450 p_curves::hash_message::<P224>(message)
451 }
452
453 fn hmac_digest(key: &[u8], data: &[u8]) -> [u8; 32] {
454 p_curves::hmac_digest::<P224>(key, data)
455 }
456
457 fn bits2octets(hash: &[u8; 32]) -> [u8; SECRET_KEY_SIZE] {
458 p_curves::bits2octets::<P224>(hash)
459 }
460
461 fn derive_public_key_uncompressed(
462 private_key: &[u8; SECRET_KEY_SIZE],
463 ) -> Result<[u8; PUBLIC_KEY_UNCOMPRESSED_SIZE], crate::EllipticCurveError> {
464 p_curves::derive_public_key_uncompressed::<P224>(private_key)
465 }
466
467 fn derive_public_key_compressed(
468 private_key: &[u8; SECRET_KEY_SIZE],
469 ) -> Result<[u8; PUBLIC_KEY_COMPRESSED_SIZE], crate::EllipticCurveError> {
470 p_curves::derive_public_key_compressed::<P224>(private_key)
471 }
472
473 fn ecdsa_sign_inner_impl(
474 scalar: &Scalar,
475 message: &[u8],
476 force_first_retry: bool,
477 ) -> Result<[u8; SIGNATURE_SIZE], crate::EllipticCurveError> {
478 p_curves::ecdsa_sign_inner_impl::<P224>(scalar, message, force_first_retry)
479 }
480
481 fn ecdsa_verify_inner(
482 public_point: &AffinePoint,
483 message: &[u8],
484 signature: &[u8; SIGNATURE_SIZE],
485 ) -> Result<(), crate::EllipticCurveError> {
486 p_curves::ecdsa_verify_inner::<P224>(public_point, message, signature)
487 }
488
489 fn parse_public_key(public_key: &[u8]) -> Result<AffinePoint, crate::EllipticCurveError> {
490 p_curves::parse_public_key::<P224>(public_key)
491 }
492
493 fn scalar_mul_generator(scalar: &Scalar) -> ProjectivePoint {
494 p_curves::scalar_mul_generator::<P224>(scalar)
495 }
496
497 fn scalar_mul_affine(base: &AffinePoint, scalar: &Scalar) -> ProjectivePoint {
498 p_curves::scalar_mul_affine::<P224>(base, scalar)
499 }
500
501 fn ecdh(
503 secret_key: &[u8; SECRET_KEY_SIZE],
504 peer_public_key: &[u8],
505 ) -> Result<[u8; ECDH_SHARED_SECRET_SIZE], crate::EllipticCurveError> {
506 SecretKey::from_bytes(secret_key)?.ecdh(&PublicKey::from_bytes(peer_public_key)?)
507 }
508
509 #[cfg(feature = "zeroize")]
510 #[test]
511 fn secret_key_zeroize_clears_scalar() {
512 use zeroize::Zeroize;
513
514 let mut key = SecretKey::from_bytes(&[1u8; SECRET_KEY_SIZE]).unwrap();
515 assert_ne!(key.to_bytes(), [0u8; SECRET_KEY_SIZE]);
516 key.zeroize();
517 assert_eq!(key.to_bytes(), [0u8; SECRET_KEY_SIZE]);
518 }
519
520 fn decode_hex<const N: usize>(hex_bytes: &str) -> [u8; N] {
521 let bytes = hex::decode(hex_bytes).unwrap();
522 assert_eq!(bytes.len(), N);
523 let mut out = [0u8; N];
524 out.copy_from_slice(&bytes);
525 out
526 }
527
528 fn der_read_tlv<'a>(data: &'a [u8], offset: &mut usize) -> Option<(u8, &'a [u8])> {
530 if *offset >= data.len() {
531 return None;
532 }
533 let tag = data[*offset];
534 *offset += 1;
535 if *offset >= data.len() {
536 return None;
537 }
538 let len_byte = data[*offset];
539 *offset += 1;
540 let (len, _) = if len_byte & 0x80 != 0 {
541 let num_bytes = (len_byte & 0x7f) as usize;
542 if num_bytes == 0 || num_bytes > core::mem::size_of::<usize>() || *offset + num_bytes > data.len() {
543 return None;
544 }
545 if num_bytes > 1 && data[*offset] == 0 {
546 return None;
547 }
548 let mut l = 0usize;
549 for i in 0..num_bytes {
550 l = (l << 8) | data[*offset + i] as usize;
551 }
552 if l < 128 {
553 return None;
554 }
555 *offset += num_bytes;
556 (l, num_bytes + 1)
557 } else {
558 (len_byte as usize, 1)
559 };
560 if (*offset).checked_add(len).map_or(true, |sum| sum > data.len()) {
561 return None;
562 }
563 let value = &data[*offset..*offset + len];
564 *offset = (*offset).checked_add(len)?;
565 Some((tag, value))
566 }
567
568 fn der_ecdsa_sig_to_p1363(der: &[u8]) -> Option<[u8; 56]> {
571 let mut offset = 0;
572 let (tag, inner) = der_read_tlv(der, &mut offset)?;
573 if tag != 0x30 {
574 return None;
575 }
576 if offset != der.len() {
577 return None;
578 }
579 let mut inner_offset = 0;
580 let (rtag, rval) = der_read_tlv(inner, &mut inner_offset)?;
581 if rtag != 0x02 || rval.is_empty() || rval.len() > 29 {
582 return None;
583 }
584 let (stag, sval) = der_read_tlv(inner, &mut inner_offset)?;
585 if stag != 0x02 || sval.is_empty() || sval.len() > 29 {
586 return None;
587 }
588 if inner_offset != inner.len() {
589 return None;
590 }
591 let r_valid = if rval.len() == 28 && rval[0] >= 0x80 {
592 false
593 } else if rval.len() == 29 && rval[0] != 0 {
594 false
595 } else if rval.len() == 29 && rval[0] == 0 && rval[1] < 0x80 {
596 false
597 } else {
598 rval.len() <= 29
599 };
600 let s_valid = if sval.len() == 28 && sval[0] >= 0x80 {
601 false
602 } else if sval.len() == 29 && sval[0] != 0 {
603 false
604 } else if sval.len() == 29 && sval[0] == 0 && sval[1] < 0x80 {
605 false
606 } else {
607 sval.len() <= 29
608 };
609 if !r_valid || !s_valid {
610 return None;
611 }
612
613 let r_trimmed = if rval.len() == 29 && rval[0] == 0 {
614 &rval[1..]
615 } else {
616 rval
617 };
618 let s_trimmed = if sval.len() == 29 && sval[0] == 0 {
619 &sval[1..]
620 } else {
621 sval
622 };
623 if r_trimmed.len() > 28 || s_trimmed.len() > 28 {
624 return None;
625 }
626 let mut sig = [0u8; 56];
627 sig[28 - r_trimmed.len()..28].copy_from_slice(r_trimmed);
628 sig[56 - s_trimmed.len()..56].copy_from_slice(s_trimmed);
629 Some(sig)
630 }
631
632 fn spki_to_sec1_point(spki: &[u8]) -> Option<Vec<u8>> {
635 let ec_public_key_oid: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01];
636 let secp224r1_oid: &[u8] = &[0x2b, 0x81, 0x04, 0x00, 0x21];
637 let mut offset = 0;
638 let (_tag, outer) = der_read_tlv(spki, &mut offset)?;
639 let mut inner = 0;
640 let (_alg_tag, alg_content) = der_read_tlv(outer, &mut inner)?;
641 if _alg_tag != 0x30 {
642 return None;
643 }
644 let mut ai = 0;
645 let (oid1_tag, oid1) = der_read_tlv(alg_content, &mut ai)?;
646 if oid1_tag != 0x06 || oid1 != ec_public_key_oid {
647 return None;
648 }
649 let (oid2_tag, oid2) = der_read_tlv(alg_content, &mut ai)?;
650 if oid2_tag != 0x06 || oid2 != secp224r1_oid {
651 return None;
652 }
653 let (_bs_tag, bs_val) = der_read_tlv(outer, &mut inner)?;
654 if _bs_tag != 0x03 || bs_val.is_empty() {
655 return None;
656 }
657 Some(bs_val[1..].to_vec())
658 }
659
660 const RFC6979_PRIVATE_KEY: &str = "f220266e1105bfe3083e03ec7a3a654651f45e37167e88600bf257c1";
662 const RFC6979_PUBLIC_X: &str = "00cf08da5ad719e42707fa431292dea11244d64fc51610d94b130d6c";
663 const RFC6979_PUBLIC_Y: &str = "eeab6f3debe455e3dbf85416f7030cbd94f34f2d6f232c69f3c1385a";
664
665 #[test]
666 fn derive_public_key_generator_matches_sec1_base_point() {
667 let mut private_key = [0u8; SECRET_KEY_SIZE];
668 private_key[27] = 1;
669 let derived = derive_public_key_uncompressed(&private_key).unwrap();
670 let expected = decode_hex::<57>(
671 "04b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21\
672 bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34",
673 );
674 assert_eq!(derived, expected);
675 }
676
677 #[test]
678 fn derive_public_key_matches_rfc6979_vector() {
679 let private_key = decode_hex::<28>(RFC6979_PRIVATE_KEY);
680 let expected = decode_hex::<57>(
681 "0400cf08da5ad719e42707fa431292dea11244d64fc51610d94b130d6c\
682 eeab6f3debe455e3dbf85416f7030cbd94f34f2d6f232c69f3c1385a",
683 );
684 assert_eq!(derive_public_key_uncompressed(&private_key).unwrap(), expected);
685 assert_eq!(
686 PublicKey::from_x_y(&decode_hex::<28>(RFC6979_PUBLIC_X), &decode_hex::<28>(RFC6979_PUBLIC_Y),)
687 .unwrap()
688 .to_bytes(),
689 expected,
690 );
691
692 let compressed = derive_public_key_compressed(&private_key).unwrap();
694 assert_eq!(&compressed[1..], &decode_hex::<28>(RFC6979_PUBLIC_X));
695 }
696
697 #[test]
698 fn ecdsa_sign_matches_rfc6979_vectors() {
699 let private_key = decode_hex::<28>(RFC6979_PRIVATE_KEY);
700 let key = SecretKey::from_bytes(&private_key).unwrap();
701
702 let sample_signature = key.sign(b"sample").unwrap();
705 let expected_sample = decode_hex::<56>(
706 "61aa3da010e8e8406c656bc477a7a7189895e7e840cdfe8ff42307ba\
707 437ebfaf254a2dc88f786b6b061e7022049df927fa4b6b5ec9ab293c",
708 );
709 assert_eq!(sample_signature, expected_sample);
710
711 let test_signature = key.sign(b"test").unwrap();
712 let expected_test = decode_hex::<56>(
713 "ad04dde87b84747a243a631ea47a1ba6d1faa059149ad2440de6fba6\
714 178d49b1ae90e3d8b629be3db5683915f4e8c99fdf6e666cf37adcfd",
715 );
716 assert_eq!(test_signature, expected_test);
717 }
718
719 #[test]
720 fn ecdsa_sign_emits_canonical_low_s() {
721 let private_key = decode_hex::<28>(RFC6979_PRIVATE_KEY);
722 let key = SecretKey::from_bytes(&private_key).unwrap();
723 for msg in [&b"sample"[..], b"test", b"", b"another message"] {
724 let sig = key.sign(msg).unwrap();
725 let s = Scalar::from_bytes(sig[28..].try_into().unwrap()).unwrap();
726 assert!(!s.is_high(), "sign produced a high-s signature for {:?}", msg);
727 }
728 }
729
730 #[test]
731 fn ecdsa_verify_strict_rejects_high_s() {
732 let private_key = decode_hex::<28>(RFC6979_PRIVATE_KEY);
733 let key = SecretKey::from_bytes(&private_key).unwrap();
734 let public_key = key.public_key();
735 let signature = key.sign(b"sample").unwrap();
736
737 assert!(public_key.verify(b"sample", &signature).is_ok());
739 assert!(public_key.verify_strict(b"sample", &signature).is_ok());
740
741 let r = Scalar::from_bytes(signature[..28].try_into().unwrap()).unwrap();
743 let s = Scalar::from_bytes(signature[28..].try_into().unwrap()).unwrap();
744 let high_s = Scalar::ZERO.sub(s);
745 assert!(high_s.is_high());
746
747 let mut malleable = [0u8; SIGNATURE_SIZE];
748 malleable[..28].copy_from_slice(&r.to_bytes());
749 malleable[28..].copy_from_slice(&high_s.to_bytes());
750
751 assert!(public_key.verify(b"sample", &malleable).is_ok());
752 assert!(public_key.verify_strict(b"sample", &malleable).is_err());
753 }
754
755 #[test]
756 fn rfc6979_nonce_generation_matches_known_value() {
757 let private_key = Scalar::from_bytes(&decode_hex::<28>(RFC6979_PRIVATE_KEY)).unwrap();
758 let hash = hash_message(b"sample");
759 assert_eq!(
760 Rfc6979::new(&private_key, &hash).generate().to_bytes(),
761 decode_hex::<28>("ad3029e0278f80643de33917ce6908c70a8ff50a411f06e41dedfcdc")
762 );
763 }
764
765 #[test]
766 fn rfc6979_test_message_nonce_matches_known_value() {
767 let private_key = Scalar::from_bytes(&decode_hex::<28>(RFC6979_PRIVATE_KEY)).unwrap();
768 let hash = hash_message(b"test");
769 assert_eq!(
770 Rfc6979::new(&private_key, &hash).generate().to_bytes(),
771 decode_hex::<28>("ff86f57924da248d6e44e8154eb69f0ae2aebaee9931d0b5a969f904")
772 );
773 }
774
775 #[test]
776 fn rfc6979_retry_advances_drbg() {
777 let private_key = Scalar::from_bytes(&decode_hex::<28>(RFC6979_PRIVATE_KEY)).unwrap();
778 let hash = hash_message(b"sample");
779
780 let mut drbg = Rfc6979::new(&private_key, &hash);
781 let k1 = drbg.generate();
782 assert_eq!(
783 k1.to_bytes(),
784 decode_hex::<28>("ad3029e0278f80643de33917ce6908c70a8ff50a411f06e41dedfcdc")
785 );
786
787 drbg.retry();
790 let k2 = drbg.generate();
791 assert_ne!(k1.to_bytes(), k2.to_bytes());
792 assert_eq!(
793 k2.to_bytes(),
794 decode_hex::<28>("e651536d86136a3b2a48606e067796dd9b8586698a271d594aeb0255")
795 );
796 }
797
798 #[test]
799 fn ecdsa_forced_retry_uses_fresh_nonce() {
800 let private_key = decode_hex::<28>(RFC6979_PRIVATE_KEY);
801 let key = SecretKey::from_bytes(&private_key).unwrap();
802 let scalar = Scalar::from_bytes(&private_key).unwrap();
803
804 let normal = key.sign(b"sample").unwrap();
805 let retried = ecdsa_sign_inner_impl(&scalar, b"sample", true).unwrap();
808
809 assert_ne!(normal, retried);
810 assert!(key.public_key().verify(b"sample", &retried).is_ok());
811 }
812
813 #[test]
814 fn rfc6979_bits2octets_truncates_and_reduces() {
815 let hash = hash_message(b"sample");
817 let expected = decode_hex::<28>("af2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e9891562113d8a");
818 assert_eq!(bits2octets(&hash), expected);
819
820 let n_times = decode_hex::<32>("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d00000000");
823 assert_eq!(bits2octets(&n_times), [0u8; 28]);
824 let n_plus_one = decode_hex::<32>("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3e00000000");
825 assert_eq!(
826 bits2octets(&n_plus_one),
827 decode_hex::<28>("00000000000000000000000000000000000000000000000000000001")
828 );
829 }
830
831 #[test]
832 fn ecdsa_verify_accepts_compressed_and_uncompressed_public_keys() {
833 let private_key = decode_hex::<28>(RFC6979_PRIVATE_KEY);
834 let key = SecretKey::from_bytes(&private_key).unwrap();
835 let uncompressed = key.public_key();
836 let compressed = derive_public_key_compressed(&private_key).unwrap();
837 let signature = key.sign(b"sample").unwrap();
838
839 assert!(uncompressed.verify(b"sample", &signature).is_ok());
840 let point = AffinePoint::from_sec1_bytes(&compressed).unwrap();
841 assert!(ecdsa_verify_inner(&point, b"sample", &signature).is_ok());
842 }
843
844 #[test]
845 fn verify_rejects_tampering_and_invalid_points() {
846 let private_key = decode_hex::<28>(RFC6979_PRIVATE_KEY);
847 let key = SecretKey::from_bytes(&private_key).unwrap();
848 let pub_key = key.public_key();
849 let mut off_curve = [0u8; 57];
850 off_curve.copy_from_slice(&pub_key.to_bytes());
851 let signature = key.sign(b"sample").unwrap();
852
853 assert!(pub_key.verify(b"tampered", &signature).is_err());
854
855 let mut bad_signature = signature;
856 bad_signature[10] ^= 0x80;
857 assert!(pub_key.verify(b"sample", &bad_signature).is_err());
858
859 off_curve[56] ^= 0x01;
860 assert!(!is_valid_public_key(&off_curve));
861 assert!(PublicKey::from_bytes(&off_curve).is_err());
862 }
863
864 #[test]
865 fn invalid_inputs_are_rejected() {
866 let invalid_private_key = [0u8; SECRET_KEY_SIZE];
867 assert!(SecretKey::from_bytes(&invalid_private_key).is_err());
868 assert!(derive_public_key_uncompressed(&invalid_private_key).is_err());
869 assert!(derive_public_key_compressed(&invalid_private_key).is_err());
870
871 let private_key = decode_hex::<28>(RFC6979_PRIVATE_KEY);
872 let key = SecretKey::from_bytes(&private_key).unwrap();
873 let signature = key.sign(b"msg").unwrap();
874 let mut zero_r = signature;
875 zero_r[..28].fill(0);
876 assert!(key.public_key().verify(b"msg", &zero_r).is_err());
877 }
878
879 #[test]
880 fn public_key_validation_accepts_known_good_points() {
881 assert!(is_valid_public_key(&decode_hex::<57>(
882 "04b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21\
883 bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34"
884 )));
885 }
886
887 #[test]
888 fn scalar_from_bytes_rejects_boundary_values() {
889 assert!(Scalar::from_bytes(&[0u8; 28]).is_none());
890
891 let n_bytes = decode_hex::<28>("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d");
893 assert!(Scalar::from_bytes(&n_bytes).is_none());
894
895 let n_minus_1 = decode_hex::<28>("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3c");
897 assert!(Scalar::from_bytes(&n_minus_1).is_some());
898
899 let one = decode_hex::<28>("00000000000000000000000000000000000000000000000000000001");
900 assert!(Scalar::from_bytes(&one).is_some());
901 }
902
903 #[test]
904 fn field_element_from_bytes_rejects_boundary_values() {
905 let p_bytes = decode_hex::<28>("ffffffffffffffffffffffffffffffff000000000000000000000001");
907 assert!(FieldElement::from_bytes(&p_bytes).is_none());
908
909 let p_minus_1 = decode_hex::<28>("ffffffffffffffffffffffffffffffff000000000000000000000000");
911 assert!(FieldElement::from_bytes(&p_minus_1).is_some());
912
913 assert!(FieldElement::from_bytes(&[0u8; 28]).is_some());
914 }
915
916 #[test]
917 fn point_decompression_round_trip() {
918 let keys: &[&str] = &[
919 "00000000000000000000000000000000000000000000000000000001",
920 "00000000000000000000000000000000000000000000000000000002",
921 RFC6979_PRIVATE_KEY,
922 "a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7",
923 ];
924
925 for key_hex in keys {
926 let private_key = decode_hex::<28>(key_hex);
927 let key = SecretKey::from_bytes(&private_key).unwrap();
928 let uncompressed = key.public_key();
929 let compressed = derive_public_key_compressed(&private_key).unwrap();
930
931 let sig = key.sign(b"round-trip").unwrap();
932 assert!(uncompressed.verify(b"round-trip", &sig).is_ok());
933 let point = AffinePoint::from_sec1_bytes(&compressed).unwrap();
934 assert!(ecdsa_verify_inner(&point, b"round-trip", &sig).is_ok());
935
936 let point = AffinePoint::from_sec1_bytes(&compressed).unwrap();
937 assert_eq!(point.to_uncompressed_bytes(), uncompressed.to_bytes());
938 }
939 }
940
941 #[test]
942 fn public_key_compressed_round_trip() {
943 for _ in 0..5 {
944 let key = SecretKey::generate().unwrap();
945 let pub_key = key.public_key();
946 let compressed = pub_key.to_compressed_bytes();
947 assert_eq!(compressed.len(), PUBLIC_KEY_COMPRESSED_SIZE);
948
949 let decoded = PublicKey::from_bytes(&compressed).unwrap();
950 assert_eq!(decoded, pub_key);
951 assert_eq!(decoded.to_bytes(), pub_key.to_bytes());
952 }
953 }
954
955 #[test]
956 fn sqrt_matches_squares_and_rejects_non_residues() {
957 for _ in 0..500 {
960 let bytes: [u8; 28] = rand::random();
961 let Some(x) = FieldElement::from_bytes(&bytes) else {
962 continue;
963 };
964 let square = x.square();
965 let root = square.sqrt().expect("square should have a square root");
966 assert_eq!(root.square(), square);
967 assert!(root == x || root == x.negate());
968
969 assert_eq!(root, square.sqrt().unwrap());
971 }
972
973 let zero = FieldElement::from_uint(U224::from_u64(0));
975 assert_eq!(zero.sqrt().unwrap(), zero);
976
977 let non_residue = FieldElement::from_uint(U224::from_u64(11));
979 assert!(non_residue.sqrt().is_none());
980 }
981
982 #[test]
983 fn scalar_inversion_correctness() {
984 let k =
985 Scalar::from_bytes(&decode_hex::<28>("ad3029e0278f80643de33917ce6908c70a8ff50a411f06e41dedfcdc")).unwrap();
986 let k_inv = k.invert().unwrap();
987 assert_eq!(k.mul(k_inv), Scalar::ONE);
988 }
989
990 #[test]
991 fn field_element_inversion_correctness() {
992 let x = FieldElement::from_bytes(&decode_hex::<28>("b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21"))
993 .unwrap();
994 let x_inv = x.invert().unwrap();
995 assert_eq!(x.mul(x_inv), FieldElement::ONE);
996 }
997
998 #[test]
999 fn barrett_mul_matches_generic() {
1000 for _ in 0..1000 {
1003 let a_bytes: [u8; 28] = rand::random();
1004 let b_bytes: [u8; 28] = rand::random();
1005 let (Some(a), Some(b)) = (FieldElement::from_bytes(&a_bytes), FieldElement::from_bytes(&b_bytes)) else {
1006 continue;
1007 };
1008 assert_eq!(a.mul(b).0, a.0.mul_mod(&b.0, &MODULUS_P), "field mul mismatch");
1009
1010 let (Some(c), Some(d)) = (Scalar::from_bytes(&a_bytes), Scalar::from_bytes(&b_bytes)) else {
1011 continue;
1012 };
1013 assert_eq!(c.mul(d).0, c.0.mul_mod(&d.0, &MODULUS_N), "scalar mul mismatch");
1014 }
1015 }
1016
1017 #[test]
1018 fn scalar_mul_generator_n_gives_identity() {
1019 let n_minus_1 =
1021 Scalar::from_bytes(&decode_hex::<28>("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3c")).unwrap();
1022 let result = scalar_mul_generator(&n_minus_1).to_affine().unwrap();
1023 assert_eq!(result.x, FieldElement::from_uint(GENERATOR_X));
1024 assert_eq!(result.y, FieldElement::from_uint(GENERATOR_Y).negate());
1025 }
1026
1027 #[test]
1028 fn ecdh_deterministic_vector_against_generator() {
1029 let private_key = decode_hex::<28>(RFC6979_PRIVATE_KEY);
1032 let generator = decode_hex::<57>(
1033 "04b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21\
1034 bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34",
1035 );
1036
1037 let key = SecretKey::from_bytes(&private_key).unwrap();
1038 let shared = ecdh(&private_key, &generator).unwrap();
1039 assert_eq!(shared, key.public_key().x_y().0);
1040 assert_eq!(shared, decode_hex::<28>(RFC6979_PUBLIC_X));
1041 }
1042
1043 #[test]
1044 fn ecdh_with_compressed_public_key() {
1045 let alice = SecretKey::generate().unwrap();
1046 let bob = SecretKey::generate().unwrap();
1047 let bob_compressed = derive_public_key_compressed(&bob.to_bytes()).unwrap();
1048
1049 assert!(is_valid_public_key(&bob_compressed));
1050 let shared = alice.ecdh(&PublicKey::from_bytes(&bob_compressed).unwrap()).unwrap();
1051 let expected = bob.ecdh(&alice.public_key()).unwrap();
1052 assert_eq!(shared, expected);
1053 }
1054
1055 #[test]
1056 fn ecdh_rejects_invalid_private_key_zero_and_order() {
1057 let zero_key = [0u8; 28];
1058 assert!(SecretKey::from_bytes(&zero_key).is_err());
1059 let bob = SecretKey::generate().unwrap();
1060 assert!(ecdh(&zero_key, &bob.public_key().to_bytes()).is_err());
1061
1062 let n_bytes = decode_hex::<28>("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d");
1063 assert!(SecretKey::from_bytes(&n_bytes).is_err());
1064 }
1065
1066 #[test]
1067 fn ecdh_standalone_function_matches_method() {
1068 let alice = SecretKey::generate().unwrap();
1069 let bob = SecretKey::generate().unwrap();
1070
1071 let method_result = alice.ecdh(&bob.public_key()).unwrap();
1072 let standalone_result = ecdh(&alice.to_bytes(), &bob.public_key().to_bytes()).unwrap();
1073 assert_eq!(method_result, standalone_result);
1074 }
1075
1076 #[test]
1077 fn ecdsa_sign_verify_round_trip_multiple_messages() {
1078 let key = SecretKey::generate().unwrap();
1079 let pub_key = key.public_key();
1080
1081 let messages: &[&[u8]] = &[
1082 b"",
1083 b"hello world",
1084 b"The quick brown fox jumps over the lazy dog",
1085 &[0xffu8; 100],
1086 b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
1087 ];
1088
1089 for msg in messages {
1090 let sig = key.sign(msg).unwrap();
1091 assert!(pub_key.verify(msg, &sig).is_ok(), "round-trip failed for message {msg:?}");
1092 let mut wrong_msg = msg.to_vec();
1093 wrong_msg.push(0x42);
1094 assert!(pub_key.verify(&wrong_msg, &sig).is_err());
1095 }
1096 }
1097
1098 #[test]
1099 fn ecdsa_verify_wrong_public_key_rejects() {
1100 let key1 = SecretKey::generate().unwrap();
1101 let key2 = SecretKey::generate().unwrap();
1102
1103 let sig = key1.sign(b"message").unwrap();
1104 assert!(key2.public_key().verify(b"message", &sig).is_err());
1105 }
1106
1107 #[test]
1108 fn ecdsa_rejects_non_canonical_r_and_s() {
1109 let key = SecretKey::generate().unwrap();
1110 let mut bad_r = key.sign(b"msg").unwrap();
1111 bad_r[..28].copy_from_slice(&decode_hex::<28>("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3e"));
1112 assert!(key.public_key().verify(b"msg", &bad_r).is_err());
1113
1114 let mut bad_s = key.sign(b"msg").unwrap();
1115 bad_s[28..].copy_from_slice(&decode_hex::<28>("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3e"));
1116 assert!(key.public_key().verify(b"msg", &bad_s).is_err());
1117 }
1118
1119 #[test]
1120 fn from_x_y_matches_generator() {
1121 let key = PublicKey::from_x_y(
1122 &decode_hex::<28>("b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21"),
1123 &decode_hex::<28>("bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34"),
1124 )
1125 .unwrap();
1126 let from_sec1 = PublicKey::from_bytes(&key.to_bytes()).unwrap();
1127 assert_eq!(key, from_sec1);
1128 }
1129
1130 #[test]
1131 fn x_y_round_trip() {
1132 let key = SecretKey::generate().unwrap();
1133 let pub_key = key.public_key();
1134 let (x, y) = pub_key.x_y();
1135 let pub_key2 = PublicKey::from_x_y(&x, &y).unwrap();
1136 assert_eq!(pub_key.to_bytes(), pub_key2.to_bytes());
1137 }
1138
1139 #[test]
1140 fn field_element_add_sub_mul_consistency() {
1141 let a = FieldElement::from_bytes(&decode_hex::<28>("b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21"))
1142 .unwrap();
1143 let b = FieldElement::from_bytes(&decode_hex::<28>("bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34"))
1144 .unwrap();
1145
1146 assert_eq!(a.add(b).sub(b), a);
1147 assert_eq!(a.add(b), b.add(a));
1148 assert_eq!(a.mul(b), b.mul(a));
1149
1150 let c = FieldElement::from_bytes(&decode_hex::<28>("270b39432355ffb45044b0b7d7bfd8ba0c04b3abf5413256b4050a85"))
1151 .unwrap();
1152 assert_eq!(a.add(b).mul(c), a.mul(c).add(b.mul(c)));
1153 }
1154
1155 #[test]
1156 fn scalar_add_sub_mul_consistency() {
1157 let a =
1158 Scalar::from_bytes(&decode_hex::<28>("ad3029e0278f80643de33917ce6908c70a8ff50a411f06e41dedfcdc")).unwrap();
1159 let one =
1160 Scalar::from_bytes(&decode_hex::<28>("00000000000000000000000000000000000000000000000000000001")).unwrap();
1161
1162 assert_eq!(a.add(one).sub(one), a);
1163 assert_eq!(a.mul(one), a);
1164
1165 let b =
1166 Scalar::from_bytes(&decode_hex::<28>("178d49b1ae90e3d8b629be3db5683915f4e8c99fdf6e666cf37adcfd")).unwrap();
1167 assert_eq!(a.mul(b), b.mul(a));
1168 assert_eq!(a.add(b), b.add(a));
1169 }
1170
1171 #[test]
1172 fn field_element_negate_round_trip() {
1173 let x = FieldElement::from_bytes(&decode_hex::<28>("b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21"))
1174 .unwrap();
1175 let neg = x.negate();
1176 assert_eq!(neg.negate(), x);
1177 assert_eq!(x.add(neg), FieldElement::ZERO);
1178 }
1179
1180 #[test]
1181 fn field_element_pow_correctness() {
1182 let x = FieldElement::from_bytes(&decode_hex::<28>("00000000000000000000000000000000000000000000000000000002"))
1183 .unwrap();
1184 assert_eq!(x.pow(&U224::from_u64(3)), x.mul(x).mul(x));
1185 assert_eq!(x.pow(&U224::ZERO), FieldElement::ONE);
1186 }
1187
1188 #[test]
1189 fn wycheproof_ecdsa_p224_sha256_p1363() {
1190 let data: serde_json::Value = serde_json::from_str(include_str!(
1191 "../../testdata/wycheproof/testvectors_v1/ecdsa_secp224r1_sha256_p1363_test.json"
1192 ))
1193 .unwrap();
1194 let mut valid_tested = 0u64;
1195 let mut invalid_tested = 0u64;
1196 for group in data["testGroups"].as_array().unwrap() {
1197 let uncompressed_hex = group["publicKey"]["uncompressed"].as_str().unwrap();
1198 let pubkey_bytes = hex::decode(uncompressed_hex).unwrap();
1199 let pk = PublicKey::from_bytes(&pubkey_bytes).unwrap();
1200
1201 for test in group["tests"].as_array().unwrap() {
1202 let msg = hex::decode(test["msg"].as_str().unwrap()).unwrap();
1203 let sig_hex = test["sig"].as_str().unwrap();
1204 let result = test["result"].as_str().unwrap();
1205
1206 if sig_hex.len() != SIGNATURE_SIZE * 2 {
1207 continue;
1208 }
1209 let sig = decode_hex::<SIGNATURE_SIZE>(sig_hex);
1210 let verify_result = pk.verify(&msg, &sig);
1211
1212 if result == "valid" {
1213 assert!(verify_result.is_ok(), "wycheproof ECDSA P1363 tcId={}", test["tcId"]);
1214 valid_tested += 1;
1215 } else {
1216 assert!(verify_result.is_err(), "wycheproof ECDSA P1363 tcId={}", test["tcId"]);
1217 invalid_tested += 1;
1218 }
1219 }
1220 }
1221 assert!(valid_tested > 0, "no valid ECDSA P1363 wycheproof tests were run");
1222 assert!(invalid_tested > 0, "no invalid ECDSA P1363 wycheproof tests were run");
1223 }
1224
1225 #[test]
1226 fn wycheproof_ecdsa_p224_sha256_der() {
1227 let data: serde_json::Value = serde_json::from_str(include_str!(
1228 "../../testdata/wycheproof/testvectors_v1/ecdsa_secp224r1_sha256_test.json"
1229 ))
1230 .unwrap();
1231 let mut valid_tested = 0u64;
1232 let mut invalid_tested = 0u64;
1233 for group in data["testGroups"].as_array().unwrap() {
1234 let uncompressed_hex = group["publicKey"]["uncompressed"].as_str().unwrap();
1235 let pubkey_bytes = hex::decode(uncompressed_hex).unwrap();
1236 let pk = PublicKey::from_bytes(&pubkey_bytes).unwrap();
1237
1238 for test in group["tests"].as_array().unwrap() {
1239 let msg = hex::decode(test["msg"].as_str().unwrap()).unwrap();
1240 let der_sig = hex::decode(test["sig"].as_str().unwrap()).unwrap();
1241 let result = test["result"].as_str().unwrap();
1242
1243 let Some(sig) = der_ecdsa_sig_to_p1363(&der_sig) else {
1244 continue;
1245 };
1246
1247 let verify_result = pk.verify(&msg, &sig);
1248 if result == "valid" {
1249 assert!(
1250 verify_result.is_ok(),
1251 "wycheproof ECDSA DER tcId={} expected valid but failed",
1252 test["tcId"]
1253 );
1254 valid_tested += 1;
1255 } else {
1256 assert!(
1257 verify_result.is_err(),
1258 "wycheproof ECDSA DER tcId={} expected invalid but passed",
1259 test["tcId"]
1260 );
1261 invalid_tested += 1;
1262 }
1263 }
1264 }
1265 assert!(valid_tested > 0, "no valid ECDSA DER wycheproof tests were run");
1266 assert!(invalid_tested > 0, "no invalid ECDSA DER wycheproof tests were run");
1267 }
1268
1269 fn wycheproof_ecdh_case(file: &str, asn: bool) {
1270 let data: serde_json::Value = serde_json::from_str(file).unwrap();
1271 let mut valid_tested = 0u64;
1272 let mut invalid_tested = 0u64;
1273 for group in data["testGroups"].as_array().unwrap() {
1274 for test in group["tests"].as_array().unwrap() {
1275 let public_hex = test["public"].as_str().unwrap();
1276 let private_hex = test["private"].as_str().unwrap();
1277 let expected_shared_hex = test["shared"].as_str().unwrap();
1278 let result = test["result"].as_str().unwrap();
1279
1280 let public_bytes = hex::decode(public_hex).unwrap();
1281 let public_key = if asn {
1282 match spki_to_sec1_point(&public_bytes) {
1283 Some(point) => point,
1284 None => {
1285 if result == "valid" {
1286 panic!("wycheproof ECDH ASN tcId={}: failed to parse valid SPKI", test["tcId"]);
1287 }
1288 invalid_tested += 1;
1289 continue;
1290 }
1291 }
1292 } else {
1293 public_bytes
1294 };
1295
1296 let private_bytes = hex::decode(private_hex).unwrap();
1298 let mut private_key = [0u8; SECRET_KEY_SIZE];
1299 let effective_len = private_bytes.len().min(SECRET_KEY_SIZE);
1300 let skip = private_bytes.len().saturating_sub(SECRET_KEY_SIZE);
1301 private_key[SECRET_KEY_SIZE - effective_len..]
1302 .copy_from_slice(&private_bytes[skip..skip + effective_len]);
1303
1304 let shared = ecdh(&private_key, &public_key);
1305
1306 if result == "valid" {
1307 let shared = shared.unwrap();
1308 assert_eq!(
1309 hex::encode(shared),
1310 expected_shared_hex,
1311 "wycheproof ECDH tcId={}",
1312 test["tcId"]
1313 );
1314 valid_tested += 1;
1315 } else if result == "invalid" {
1316 assert!(
1317 shared.is_err(),
1318 "wycheproof ECDH tcId={} expected invalid but passed",
1319 test["tcId"]
1320 );
1321 invalid_tested += 1;
1322 }
1323 }
1324 }
1325 assert!(valid_tested > 0, "no valid ECDH wycheproof tests were run");
1326 assert!(invalid_tested > 0, "no invalid ECDH wycheproof tests were run");
1327 }
1328
1329 #[test]
1330 fn wycheproof_ecdh_p224_ecpoint() {
1331 wycheproof_ecdh_case(
1332 include_str!("../../testdata/wycheproof/testvectors_v1/ecdh_secp224r1_ecpoint_test.json"),
1333 false,
1334 );
1335 }
1336
1337 #[test]
1338 fn wycheproof_ecdh_p224_asn() {
1339 wycheproof_ecdh_case(
1340 include_str!("../../testdata/wycheproof/testvectors_v1/ecdh_secp224r1_test.json"),
1341 true,
1342 );
1343 }
1344
1345 #[test]
1347 fn generator_point_is_on_curve() {
1348 p_curves::test_support::generator_point_is_on_curve::<P224>();
1349 }
1350
1351 #[test]
1352 fn from_x_y_rejects_off_curve() {
1353 p_curves::test_support::from_x_y_rejects_off_curve::<P224>();
1354 }
1355
1356 #[test]
1357 fn ecdh_round_trip_alice_bob() {
1358 p_curves::test_support::ecdh_round_trip_alice_bob::<P224>();
1359 }
1360
1361 #[test]
1362 fn ecdh_rejects_off_curve_peer_public_key() {
1363 p_curves::test_support::ecdh_rejects_off_curve_peer_public_key::<P224>();
1364 }
1365
1366 #[test]
1367 fn ecdh_rejects_infinity_peer_public_key() {
1368 p_curves::test_support::ecdh_rejects_infinity_peer_public_key::<P224>();
1369 }
1370
1371 #[test]
1372 fn ecdh_rejects_bad_length_peer_public_key() {
1373 p_curves::test_support::ecdh_rejects_bad_length_peer_public_key::<P224>();
1374 }
1375
1376 #[test]
1377 fn ecdh_multiple_exchanges_consistency() {
1378 p_curves::test_support::ecdh_multiple_exchanges_consistency::<P224>();
1379 }
1380
1381 #[test]
1382 fn private_key_round_trip_bytes() {
1383 p_curves::test_support::private_key_round_trip_bytes::<P224>();
1384 }
1385
1386 #[test]
1387 fn public_key_round_trip_bytes() {
1388 p_curves::test_support::public_key_round_trip_bytes::<P224>();
1389 }
1390
1391 #[test]
1392 fn point_double_and_add_consistency() {
1393 p_curves::test_support::point_double_and_add_consistency::<P224>();
1394 }
1395
1396 #[test]
1397 fn compressed_public_key_has_correct_prefix() {
1398 p_curves::test_support::compressed_public_key_has_correct_prefix::<P224>();
1399 }
1400
1401 #[cfg(feature = "zeroize")]
1402 #[test]
1403 fn rfc6979_state_zeroize_clears_drbg() {
1404 p_curves::test_support::rfc6979_state_zeroize_clears_drbg::<P224>();
1405 }
1406}