1use crate::mont_field;
8use crate::nist::arith::{sqrt_p3mod4, Field};
9use crate::nist::point::Curve;
10use crate::nist::{ecdh, ecdsa};
11use ic_core::traits::{Algorithm, KeyAgreement, SelfTest, SignatureScheme};
12use ic_core::{ensure, Result};
13
14#[cfg(all(target_pointer_width = "64", not(ic_limb32)))]
17mont_field!(
18 Fp,
19 4,
20 32,
21 crate::nist::p256_field::P,
22 "The P-256 coordinate field, GF(p) with p = 2^256 - 2^224 + 2^192 + 2^96 - 1.",
23 mul = crate::nist::p256_field::active::mul,
24 square = crate::nist::p256_field::active::square,
25 invert = invert_field,
26 add = crate::nist::p256_field::active::add,
27 sub = crate::nist::p256_field::active::sub
28);
29
30#[cfg(not(all(target_pointer_width = "64", not(ic_limb32))))]
31mont_field!(
32 Fp,
33 4,
34 32,
35 crate::nist::p256_field::P,
36 "The P-256 coordinate field, GF(p) with p = 2^256 - 2^224 + 2^192 + 2^96 - 1."
37);
38
39const N: [u64; 4] = [
41 0xf3b9_cac2_fc63_2551,
42 0xbce6_faad_a717_9e84,
43 0xffff_ffff_ffff_ffff,
44 0xffff_ffff_0000_0000,
45];
46
47#[cfg(all(target_pointer_width = "64", not(ic_limb32)))]
50mont_field!(
51 Fn,
52 4,
53 32,
54 N,
55 "The P-256 scalar ring, Z/nZ where n is the order of the base point.",
56 mul = Self::mont_mul_raw,
57 square = Self::mont_square_raw,
58 invert = invert_scalar,
59 add = Self::add_raw,
60 sub = Self::sub_raw
61);
62
63#[cfg(not(all(target_pointer_width = "64", not(ic_limb32))))]
64mont_field!(
65 Fn,
66 4,
67 32,
68 N,
69 "The P-256 scalar ring, Z/nZ where n is the order of the base point."
70);
71
72#[cfg(all(target_pointer_width = "64", not(ic_limb32)))]
76fn invert_field(a: [u64; 4]) -> [u64; 4] {
77 const R3: [u64; 4] = Fp::mont_mul_const(Fp::R2, Fp::R2);
78 crate::nist::p256_field::active::mul(crate::nist::safegcd::invert::<FpModulus>(&a), R3)
79}
80
81#[cfg(all(target_pointer_width = "64", not(ic_limb32)))]
83pub(crate) struct FpModulus;
84
85#[cfg(all(target_pointer_width = "64", not(ic_limb32)))]
86impl crate::nist::safegcd::Modulus<5> for FpModulus {
87 const INFO: crate::nist::safegcd::ModInfo<5> =
88 crate::nist::safegcd::ModInfo::new(crate::nist::p256_field::P);
89}
90
91#[cfg(all(target_pointer_width = "64", not(ic_limb32)))]
93pub(crate) struct FnModulus;
94
95#[cfg(all(target_pointer_width = "64", not(ic_limb32)))]
96impl crate::nist::safegcd::Modulus<5> for FnModulus {
97 const INFO: crate::nist::safegcd::ModInfo<5> = crate::nist::safegcd::ModInfo::new(N);
98}
99
100#[cfg(all(target_pointer_width = "64", not(ic_limb32)))]
106fn invert_scalar(a: [u64; 4]) -> [u64; 4] {
107 const R3: [u64; 4] = Fn::mont_mul_const(Fn::R2, Fn::R2);
108 Fn::mont_mul_raw(crate::nist::safegcd::invert::<FnModulus>(&a), R3)
109}
110
111#[derive(Debug, Clone, Copy)]
113pub struct P256;
114
115crate::nist::gentable::generator_table_for!(P256, 32);
118
119impl Curve for P256 {
120 type Field = Fp;
121 type Scalar = Fn;
122
123 const NAME: &'static str = "P-256";
124 const FIELD_BYTES: usize = 32;
125 const SCALAR_BYTES: usize = 32;
126 const ORDER_BITS: usize = 256;
127
128 const B: Fp = Fp::to_mont_const([
130 0x3bce_3c3e_27d2_604b,
131 0x651d_06b0_cc53_b0f6,
132 0xb3eb_bd55_7698_86bc,
133 0x5ac6_35d8_aa3a_93e7,
134 ]);
135
136 const GX: Fp = Fp::to_mont_const([
137 0xf4a1_3945_d898_c296,
138 0x7703_7d81_2deb_33a0,
139 0xf8bc_e6e5_63a4_40f2,
140 0x6b17_d1f2_e12c_4247,
141 ]);
142
143 const GY: Fp = Fp::to_mont_const([
144 0xcbb6_4068_37bf_51f5,
145 0x2bce_3357_6b31_5ece,
146 0x8ee7_eb4a_7c0f_9e16,
147 0x4fe3_42e2_fe1a_7f9b,
148 ]);
149
150 fn sqrt(x: &Fp) -> Fp {
152 sqrt_p3mod4(x, Fp::MODULUS, |v, e| v.pow(e))
157 }
158
159 fn field_from_slice(bytes: &[u8]) -> Option<Fp> {
160 let mut b = [0u8; 32];
161 if bytes.len() != 32 {
162 return None;
163 }
164 b.copy_from_slice(bytes);
165 Fp::from_bytes(&b)
166 }
167
168 fn scalar_from_slice(bytes: &[u8]) -> Option<Fn> {
169 let mut b = [0u8; 32];
170 if bytes.len() != 32 {
171 return None;
172 }
173 b.copy_from_slice(bytes);
174 Fn::from_bytes(&b)
175 }
176
177 #[cfg(target_arch = "x86_64")]
178 #[inline(always)]
179 fn double_fast(x: &Fp, y: &Fp, z: &Fp) -> Option<[Fp; 3]> {
180 crate::nist::p256_x86::point_double(&x.0, &y.0, &z.0).map(|[x, y, z]| [Fp(x), Fp(y), Fp(z)])
181 }
182
183 #[cfg(target_arch = "x86_64")]
184 #[inline(always)]
185 fn scan_affine(entries: &[AffinePoint], masks: &[u64]) -> Option<AffinePoint> {
186 Some(crate::nist::p256_x86::scan_affine(entries, masks))
187 }
188
189 #[cfg(target_arch = "x86_64")]
190 #[inline(always)]
191 fn scan_point(entries: &[Point], masks: &[u64]) -> Option<[Fp; 3]> {
192 let [x, y, z] = crate::nist::p256_x86::scan_point(entries, masks);
193 Some([Fp(x), Fp(y), Fp(z)])
194 }
195
196 #[cfg(target_arch = "x86_64")]
197 #[inline(always)]
198 fn add_fast(p: [&Fp; 3], q: [&Fp; 3]) -> Option<([Fp; 3], Fp, Fp)> {
199 crate::nist::p256_x86::point_add([&p[0].0, &p[1].0, &p[2].0], [&q[0].0, &q[1].0, &q[2].0])
200 .map(|([x, y, z], h, r)| ([Fp(x), Fp(y), Fp(z)], Fp(h), Fp(r)))
201 }
202
203 #[cfg(target_arch = "x86_64")]
204 #[inline(always)]
205 fn add_affine_fast(p: [&Fp; 3], q: [&Fp; 2]) -> Option<([Fp; 3], Fp, Fp)> {
206 crate::nist::p256_x86::point_add_affine([&p[0].0, &p[1].0, &p[2].0], [&q[0].0, &q[1].0])
207 .map(|([x, y, z], h, r)| ([Fp(x), Fp(y), Fp(z)], Fp(h), Fp(r)))
208 }
209
210 fn scalar_reduce_slice(bytes: &[u8]) -> Fn {
211 let mut b = [0u8; 32];
212 let n = core::cmp::min(32, bytes.len());
213 b[32 - n..].copy_from_slice(&bytes[..n]);
216 Fn::from_bytes_reduced(&b)
217 }
218}
219
220impl ecdsa::EcdsaCurve for P256 {
221 type Digest = ic_hash::Sha256;
222 type Hmac = ic_mac::HmacSha256;
223}
224
225pub struct EcdsaP256Sha256;
227
228impl Algorithm for EcdsaP256Sha256 {
229 const ID: &'static str = "ecdsa-p256-sha256";
230 const NAME: &'static str = "ECDSA P-256 with SHA-256";
231}
232
233impl SignatureScheme for EcdsaP256Sha256 {
234 const PRIVATE_KEY_LEN: usize = 32;
235 const PUBLIC_KEY_LEN: usize = 65;
237 const SIGNATURE_LEN: usize = 64;
239
240 fn public_key(private_key: &[u8], out: &mut [u8]) -> Result<()> {
241 ecdsa::public_key::<P256>(private_key, out)
242 }
243
244 fn sign(private_key: &[u8], message: &[u8], signature: &mut [u8]) -> Result<()> {
245 ecdsa::sign::<P256>(private_key, message, signature)
246 }
247
248 fn verify(public_key: &[u8], message: &[u8], signature: &[u8]) -> Result<()> {
249 ecdsa::verify::<P256>(public_key, message, signature)
250 }
251}
252
253impl EcdsaP256Sha256 {
254 pub fn verify_prehash(public_key: &[u8], digest: &[u8], signature: &[u8]) -> Result<()> {
262 ecdsa::verify_prehash::<P256>(public_key, digest, signature)
263 }
264
265 pub fn public_key_compressed(private_key: &[u8], out: &mut [u8]) -> Result<()> {
267 ecdsa::public_key_compressed::<P256>(private_key, out)
268 }
269
270 pub fn normalize_s(signature: &mut [u8]) -> Result<()> {
279 ecdsa::normalize_s::<P256>(signature)
280 }
281
282 pub fn has_low_s(signature: &[u8]) -> Result<bool> {
284 ecdsa::has_low_s::<P256>(signature)
285 }
286}
287
288impl SelfTest for EcdsaP256Sha256 {
289 fn self_test() -> Result<()> {
290 let mut key = [0u8; 32];
292 ic_core::codec::hex_decode(
293 b"c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721",
294 &mut key,
295 )?;
296 let mut want = [0u8; 64];
297 ic_core::codec::hex_decode(
298 b"efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8",
299 &mut want,
300 )?;
301
302 let mut sig = [0u8; 64];
303 <Self as SignatureScheme>::sign(&key, b"sample", &mut sig)?;
304 ensure!(
305 ic_core::ct::verify(&want, &sig),
306 SelfTestFailed,
307 "ecdsa-p256-sha256"
308 );
309
310 let mut pk = [0u8; 65];
311 <Self as SignatureScheme>::public_key(&key, &mut pk)?;
312 <Self as SignatureScheme>::verify(&pk, b"sample", &sig)?;
313
314 sig[0] ^= 1;
316 ensure!(
317 <Self as SignatureScheme>::verify(&pk, b"sample", &sig).is_err(),
318 SelfTestFailed,
319 "ecdsa-p256-sha256"
320 );
321 Ok(())
322 }
323}
324
325pub struct EcdhP256;
327
328impl Algorithm for EcdhP256 {
329 const ID: &'static str = "ecdh-p256";
330 const NAME: &'static str = "ECDH P-256";
331}
332
333impl KeyAgreement for EcdhP256 {
334 const PRIVATE_KEY_LEN: usize = 32;
335 const PUBLIC_KEY_LEN: usize = 65;
337 const SHARED_SECRET_LEN: usize = 32;
338
339 fn public_key(private_key: &[u8], out: &mut [u8]) -> Result<()> {
340 ecdh::public_key::<P256>(private_key, out)
341 }
342
343 fn agree(private_key: &[u8], peer_public_key: &[u8], out: &mut [u8]) -> Result<()> {
344 ecdh::agree::<P256>(private_key, peer_public_key, out)
345 }
346}
347
348impl EcdhP256 {
349 pub fn public_key_compressed(private_key: &[u8], out: &mut [u8]) -> Result<()> {
354 ecdh::public_key_compressed::<P256>(private_key, out)
355 }
356}
357
358impl SelfTest for EcdhP256 {
359 fn self_test() -> Result<()> {
360 let mut d = [0u8; 32];
362 ic_core::codec::hex_decode(
363 b"7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534",
364 &mut d,
365 )?;
366 let mut peer = [0u8; 65];
367 peer[0] = 0x04;
368 ic_core::codec::hex_decode(
369 b"700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287",
370 &mut peer[1..33],
371 )?;
372 ic_core::codec::hex_decode(
373 b"db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac",
374 &mut peer[33..],
375 )?;
376 let mut want = [0u8; 32];
377 ic_core::codec::hex_decode(
378 b"46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b",
379 &mut want,
380 )?;
381
382 let mut got = [0u8; 32];
383 <Self as KeyAgreement>::agree(&d, &peer, &mut got)?;
384 ensure!(
385 ic_core::ct::verify(&want, &got),
386 SelfTestFailed,
387 "ecdh-p256"
388 );
389 Ok(())
390 }
391}
392
393pub type Point = crate::nist::point::Point<P256>;
395pub type AffinePoint = crate::nist::point::AffinePoint<P256>;
397
398#[cfg(test)]
399mod tests {
400 use super::*;
401 use ic_core::codec::{hex, unhex};
402
403 #[test]
407 #[ignore = "timing; run manually with --release"]
408 fn where_the_time_goes() {
409 use crate::nist::arith::Field;
410 use std::hint::black_box;
411 use std::time::Instant;
412 fn ns(label: &str, iters: u32, mut f: impl FnMut()) {
413 let mut best = f64::INFINITY;
414 for _ in 0..7 {
415 let t = Instant::now();
416 for _ in 0..iters {
417 f();
418 }
419 best = best.min(t.elapsed().as_secs_f64() * 1e9 / iters as f64);
420 }
421 std::println!("{label:32} {best:10.1} ns");
422 }
423 let a = Fp::to_mont([7, 11, 13, 17]);
424 let b = Fp::to_mont([19, 23, 29, 31]);
425 ns("fp mul", 2_000_000, || {
426 black_box(black_box(a).mul(&black_box(b)));
427 });
428 ns("fp square", 2_000_000, || {
429 black_box(black_box(a).square());
430 });
431 ns("fp add", 2_000_000, || {
432 black_box(black_box(a).add(&black_box(b)));
433 });
434 ns("fp sub", 2_000_000, || {
435 black_box(black_box(a).sub(&black_box(b)));
436 });
437 ns("fp invert", 2_000, || {
438 black_box(black_box(a).invert());
439 });
440 let g = Point::generator();
441 let p = g.double();
442 ns("point double", 200_000, || {
443 black_box(black_box(p).double());
444 });
445 ns("point add (complete)", 200_000, || {
446 black_box(black_box(p).add(&black_box(g)));
447 });
448 ns("point add (unequal)", 200_000, || {
449 black_box(black_box(p).add_unequal(&black_box(g)));
450 });
451 let ga = g.to_affine().unwrap();
452 ns("point add (affine, unequal)", 200_000, || {
453 black_box(black_box(p).add_affine_unequal(&black_box(ga.x), &black_box(ga.y)));
454 });
455 let k = Fn::to_mont([0x1234_5678, 0x9abc_def0, 0x1357_9bdf, 0x2468_ace0]);
456 ns("scalar invert", 2_000, || {
457 black_box(black_box(k).invert());
458 });
459 ns("mul_generator (table)", 2_000, || {
460 black_box(Point::mul_generator(&black_box(k)));
461 });
462 ns("mul_scalar (windowed)", 500, || {
463 black_box(black_box(p).mul_scalar(&black_box(k)));
464 });
465 ns("mul_scalar_vartime", 500, || {
466 black_box(black_box(p).mul_scalar_vartime(&black_box(k)));
467 });
468 ns("to_affine", 2_000, || {
469 black_box(black_box(p).to_affine());
470 });
471 let mut pk = [0u8; 65];
472 EcdhP256::public_key(&[0x5a; 32], &mut pk).unwrap();
473 let mut shared = [0u8; 32];
474 ns("ecdh agree", 500, || {
475 EcdhP256::agree(&[0x5a; 32], &pk, &mut shared).unwrap();
476 });
477 ns("decode point", 2_000, || {
478 black_box(AffinePoint::from_sec1(black_box(&pk)));
479 });
480 ns("operational", 2_000, || {
481 black_box(ic_core::module::operational()).unwrap();
482 });
483 let mut sig = [0u8; 64];
484 ns("ecdsa sign", 500, || {
485 EcdsaP256Sha256::sign(&[0x5a; 32], b"message", &mut sig).unwrap();
486 });
487 }
488
489 fn scalar(v: u64) -> Fn {
490 Fn::to_mont([v, 0, 0, 0])
491 }
492
493 fn fp(v: u64) -> Fp {
494 Fp::to_mont([v, 0, 0, 0])
495 }
496
497 #[test]
500 fn montgomery_constants_are_consistent() {
501 assert_eq!(Fp::MODULUS[0].wrapping_mul(Fp::NEG_INV), u64::MAX, "p");
502 assert_eq!(Fn::MODULUS[0].wrapping_mul(Fn::NEG_INV), u64::MAX, "n");
503 }
504
505 #[test]
506 fn small_arithmetic_matches_integers() {
507 assert_eq!(fp(2).add(&fp(3)), fp(5));
508 assert_eq!(fp(5).sub(&fp(3)), fp(2));
509 assert_eq!(fp(6).mul(&fp(7)), fp(42));
510 assert_eq!(fp(9).square(), fp(81));
511 assert_eq!(fp(5).double(), fp(10));
512 assert_eq!(fp(5).triple(), fp(15));
513 assert_eq!(Fp::ONE.from_mont(), [1, 0, 0, 0]);
514 }
515
516 #[test]
517 fn inversion_is_correct() {
518 for v in [1u64, 2, 3, 19, 65537, u32::MAX as u64] {
519 assert_eq!(fp(v).mul(&fp(v).invert()), Fp::ONE, "1/{v} in Fp");
520 assert_eq!(scalar(v).mul(&scalar(v).invert()), Fn::ONE, "1/{v} in Fn");
521 }
522 assert_eq!(Fp::ZERO.invert(), Fp::ZERO);
523 assert_eq!(Fn::ZERO.invert(), Fn::ZERO);
524 }
525
526 #[test]
530 fn inversion_matches_fermat() {
531 let mut x = fp(0x1234_5678_9abc_def1);
532 let mut k = scalar(0x0fed_cba9_8765_4321);
533 let mut fps = std::vec![
534 Fp::ONE,
535 Fp::ONE.neg(),
536 Fp::ONE.double(),
537 Fp::ONE.neg().double()
538 ];
539 let mut fns = std::vec![
540 Fn::ONE,
541 Fn::ONE.neg(),
542 Fn::ONE.double(),
543 Fn::ONE.neg().double()
544 ];
545 for _ in 0..300 {
546 x = x.square().add(&Fp::ONE);
547 k = k.square().add(&Fn::ONE);
548 fps.push(x);
549 fns.push(k);
550 }
551 for v in fps {
552 assert_eq!(v.invert().0, Fp::invert_raw(v.0), "{v:?}");
553 }
554 for v in fns {
555 assert_eq!(v.invert().0, Fn::invert_raw(v.0), "{v:?}");
556 }
557 }
558
559 #[test]
560 fn arithmetic_laws_hold_on_large_values() {
561 let a = P256::field_from_slice(&[0x3a; 32]).unwrap();
562 let b = P256::field_from_slice(&[0x91; 32]).unwrap();
563 let c = P256::field_from_slice(&[0xc7; 32]).unwrap();
564 assert_eq!(a.mul(&b).mul(&c), a.mul(&b.mul(&c)), "associativity");
565 assert_eq!(a.mul(&b), b.mul(&a), "commutativity");
566 assert_eq!(
567 a.mul(&b.add(&c)),
568 a.mul(&b).add(&a.mul(&c)),
569 "distributivity"
570 );
571 assert_eq!(a.add(&a.neg()), Fp::ZERO);
572 }
573
574 #[test]
575 fn byte_encoding_round_trips_and_rejects_non_canonical() {
576 let bytes = [0x7fu8; 32];
577 let a = P256::field_from_slice(&bytes).unwrap();
578 assert_eq!(a.to_bytes(), bytes);
579
580 let mut p_bytes = [0u8; 32];
582 for i in 0..4 {
583 let hi = 32 - i * 8;
584 p_bytes[hi - 8..hi].copy_from_slice(&Fp::MODULUS[i].to_be_bytes());
585 }
586 assert!(P256::field_from_slice(&p_bytes).is_none());
587 }
588
589 #[test]
594 fn the_base_point_is_on_the_curve() {
595 let g = Point::generator().to_affine().unwrap();
596 assert!(bool::from(g.is_on_curve()));
597 }
598
599 #[test]
601 fn the_base_point_has_order_n() {
602 let n_minus_1 = Fn::ZERO.sub(&Fn::ONE);
603 let p = Point::generator().mul_scalar(&n_minus_1);
604 assert!(
605 bool::from(p.ct_eq(&Point::generator().neg())),
606 "[n-1]G == -G"
607 );
608 assert!(
609 bool::from(p.add(&Point::generator()).is_identity()),
610 "[n]G is the identity"
611 );
612 }
613
614 #[test]
615 fn identity_and_negation_behave() {
616 let g = Point::generator();
617 assert!(bool::from(g.add(&Point::identity()).ct_eq(&g)));
618 assert!(bool::from(Point::identity().add(&g).ct_eq(&g)));
619 assert!(bool::from(Point::identity().double().is_identity()));
620 assert!(bool::from(g.add(&g.neg()).is_identity()));
621 assert!(Point::identity().to_affine().is_none());
622 }
623
624 #[test]
626 fn addition_handles_equal_inputs_as_a_doubling() {
627 let g = Point::generator();
628 assert!(bool::from(g.add(&g).ct_eq(&g.double())));
629 let p = g.mul_scalar(&scalar(5));
630 assert!(bool::from(p.add(&p).ct_eq(&p.double())));
631 }
632
633 #[test]
642 fn the_vartime_multiplication_agrees_with_the_ladder() {
643 let g = Point::generator();
644
645 let mut checked = 0;
646 for raw in [
647 [0u8; 32],
648 {
649 let mut v = [0u8; 32];
650 v[31] = 1;
651 v
652 },
653 [0xffu8; 32],
654 [0x55u8; 32],
655 [0xaau8; 32],
656 [0x9du8; 32],
657 ] {
658 let k = Fn::from_bytes_reduced(&raw);
659 assert!(
660 bool::from(g.mul_scalar_vartime(&k).ct_eq(&g.mul_scalar(&k))),
661 "vartime and ladder differ for {raw:02x?}"
662 );
663 checked += 1;
664 }
665 assert_eq!(checked, 6, "the comparison did not run");
666 }
667
668 #[test]
669 fn scalar_multiplication_matches_repeated_addition() {
670 let g = Point::generator();
671 let mut acc = Point::identity();
672 for k in 1..=10u64 {
673 acc = acc.add(&g);
674 assert!(bool::from(acc.ct_eq(&g.mul_scalar(&scalar(k)))), "[{k}]G");
675 }
676 }
677
678 #[test]
679 fn scalar_multiplication_is_linear() {
680 let g = Point::generator();
681 let a = scalar(1_234_567);
682 let b = scalar(7_654_321);
683 assert!(bool::from(
684 g.mul_scalar(&a.add(&b))
685 .ct_eq(&g.mul_scalar(&a).add(&g.mul_scalar(&b)))
686 ));
687 assert!(bool::from(
688 g.mul_scalar(&a)
689 .mul_scalar(&b)
690 .ct_eq(&g.mul_scalar(&a.mul(&b)))
691 ));
692 }
693
694 #[test]
697 fn two_g_matches_the_published_value() {
698 let two_g = Point::generator().double().to_affine().unwrap();
699 assert_eq!(
700 hex(two_g.x.to_bytes().as_ref()),
701 "7cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978"
702 );
703 assert_eq!(
704 hex(two_g.y.to_bytes().as_ref()),
705 "07775510db8ed040293d9ac69f7430dbba7dade63ce982299e04b79d227873d1"
706 );
707 }
708
709 #[test]
710 fn sec1_round_trips_in_both_forms() {
711 let g = Point::generator();
712 for k in [1u64, 2, 3, 4, 5, 6, 7, 8] {
713 let p = g.mul_scalar(&scalar(k)).to_affine().unwrap();
714 let mut unc = [0u8; 65];
715 let mut comp = [0u8; 33];
716 assert!(p.write_uncompressed(&mut unc));
717 assert!(p.write_compressed(&mut comp));
718 assert_eq!(unc[0], 0x04);
719 assert!(comp[0] == 0x02 || comp[0] == 0x03);
720
721 let a = AffinePoint::from_sec1(&unc).unwrap();
722 let b = AffinePoint::from_sec1(&comp).unwrap();
723 assert_eq!(a.x, p.x);
724 assert_eq!(a.y, p.y);
725 assert_eq!(b.x, p.x);
726 assert_eq!(b.y, p.y, "compressed y for [{k}]G");
727 }
728 }
729
730 #[test]
731 fn decoding_rejects_bad_encodings() {
732 let g = Point::generator().to_affine().unwrap();
733 let mut unc = [0u8; 65];
734 assert!(g.write_uncompressed(&mut unc));
735
736 assert!(AffinePoint::from_sec1(&[]).is_none());
737 assert!(AffinePoint::from_sec1(&[0u8; 65]).is_none(), "identity");
738 assert!(AffinePoint::from_sec1(&unc[..64]).is_none(), "truncated");
739
740 let mut bad = unc;
741 bad[0] = 0x05;
742 assert!(AffinePoint::from_sec1(&bad).is_none(), "bad tag");
743
744 let mut bad = unc;
745 bad[64] ^= 1;
746 assert!(AffinePoint::from_sec1(&bad).is_none(), "off curve");
747 }
748
749 const KEY: &str = "c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721";
752
753 #[test]
757 fn rfc6979_sample_vector() {
758 let key = unhex(KEY).unwrap();
759 let mut sig = [0u8; 64];
760 EcdsaP256Sha256::sign(&key, b"sample", &mut sig).unwrap();
761 assert_eq!(
762 hex(&sig[..32]),
763 "efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716",
764 "r"
765 );
766 assert_eq!(
767 hex(&sig[32..]),
768 "f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8",
769 "s"
770 );
771 }
772
773 #[test]
775 fn rfc6979_test_vector() {
776 let key = unhex(KEY).unwrap();
777 let mut sig = [0u8; 64];
778 EcdsaP256Sha256::sign(&key, b"test", &mut sig).unwrap();
779 assert_eq!(
780 hex(&sig[..32]),
781 "f1abb023518351cd71d881567b1ea663ed3efcf6c5132b354f28d3b0b7d38367",
782 "r"
783 );
784 assert_eq!(
785 hex(&sig[32..]),
786 "019f4113742a2b14bd25926b49c649155f267e60d3814b4c0cc84250e46f0083",
787 "s"
788 );
789 }
790
791 #[test]
792 fn rfc6979_public_key() {
793 let key = unhex(KEY).unwrap();
794 let mut pk = [0u8; 65];
795 EcdsaP256Sha256::public_key(&key, &mut pk).unwrap();
796 assert_eq!(
797 hex(&pk[1..33]),
798 "60fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6",
799 "Ux"
800 );
801 assert_eq!(
802 hex(&pk[33..]),
803 "7903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
804 "Uy"
805 );
806 }
807
808 #[test]
809 fn signing_is_deterministic_and_message_bound() {
810 let key = unhex(KEY).unwrap();
811 let mut a = [0u8; 64];
812 let mut b = [0u8; 64];
813 EcdsaP256Sha256::sign(&key, b"same", &mut a).unwrap();
814 EcdsaP256Sha256::sign(&key, b"same", &mut b).unwrap();
815 assert_eq!(a, b, "RFC 6979 signing must not depend on an RNG");
816
817 EcdsaP256Sha256::sign(&key, b"other", &mut b).unwrap();
818 assert_ne!(&a[..32], &b[..32], "r must differ between messages");
819 }
820
821 #[test]
822 fn sign_and_verify_round_trip() {
823 let key = unhex(KEY).unwrap();
824 let mut pk = [0u8; 65];
825 EcdsaP256Sha256::public_key(&key, &mut pk).unwrap();
826 for message in [&b""[..], b"short", &[0x5au8; 1000][..]] {
827 let mut sig = [0u8; 64];
828 EcdsaP256Sha256::sign(&key, message, &mut sig).unwrap();
829 EcdsaP256Sha256::verify(&pk, message, &sig).unwrap();
830 }
831 }
832
833 #[test]
834 fn verification_rejects_tampering() {
835 let key = unhex(KEY).unwrap();
836 let mut pk = [0u8; 65];
837 EcdsaP256Sha256::public_key(&key, &mut pk).unwrap();
838 let mut sig = [0u8; 64];
839 EcdsaP256Sha256::sign(&key, b"authentic", &mut sig).unwrap();
840
841 assert!(EcdsaP256Sha256::verify(&pk, b"forged", &sig).is_err());
842 let mut bad = sig;
843 bad[0] ^= 1;
844 assert!(EcdsaP256Sha256::verify(&pk, b"authentic", &bad).is_err());
845 let mut bad = sig;
846 bad[63] ^= 1;
847 assert!(EcdsaP256Sha256::verify(&pk, b"authentic", &bad).is_err());
848
849 let mut other = [0u8; 65];
850 EcdsaP256Sha256::public_key(&[0x11u8; 32], &mut other).unwrap();
851 assert!(EcdsaP256Sha256::verify(&other, b"authentic", &sig).is_err());
852 }
853
854 #[test]
855 fn verification_rejects_degenerate_signatures() {
856 let key = unhex(KEY).unwrap();
857 let mut pk = [0u8; 65];
858 EcdsaP256Sha256::public_key(&key, &mut pk).unwrap();
859
860 let mut zero_r = [0u8; 64];
861 zero_r[63] = 1;
862 assert!(EcdsaP256Sha256::verify(&pk, b"m", &zero_r).is_err());
863
864 let mut zero_s = [0u8; 64];
865 zero_s[31] = 1;
866 assert!(EcdsaP256Sha256::verify(&pk, b"m", &zero_s).is_err());
867
868 let n_bytes =
869 unhex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551").unwrap();
870 let mut at_n = [0u8; 64];
871 at_n[..32].copy_from_slice(&n_bytes);
872 at_n[32..].copy_from_slice(&n_bytes);
873 assert!(EcdsaP256Sha256::verify(&pk, b"m", &at_n).is_err());
874 }
875
876 #[test]
877 fn signing_rejects_invalid_private_keys() {
878 let mut sig = [0u8; 64];
879 assert!(
880 EcdsaP256Sha256::sign(&[0u8; 32], b"m", &mut sig).is_err(),
881 "zero"
882 );
883 assert!(
884 EcdsaP256Sha256::sign(&[0xffu8; 32], b"m", &mut sig).is_err(),
885 ">= n"
886 );
887 assert!(
888 EcdsaP256Sha256::sign(&[1u8; 31], b"m", &mut sig).is_err(),
889 "short"
890 );
891 }
892
893 #[test]
895 fn malleability_and_normalization() {
896 let key = unhex(KEY).unwrap();
897 let mut pk = [0u8; 65];
898 EcdsaP256Sha256::public_key(&key, &mut pk).unwrap();
899 let mut sig = [0u8; 64];
900 EcdsaP256Sha256::sign(&key, b"sample", &mut sig).unwrap();
901 assert!(
902 !EcdsaP256Sha256::has_low_s(&sig).unwrap(),
903 "RFC 6979 s is high here"
904 );
905
906 let mut flipped = sig;
907 EcdsaP256Sha256::normalize_s(&mut flipped).unwrap();
908 assert_ne!(flipped, sig);
909 EcdsaP256Sha256::verify(&pk, b"sample", &flipped).unwrap();
910 assert!(EcdsaP256Sha256::has_low_s(&flipped).unwrap());
911
912 let mut twice = flipped;
913 EcdsaP256Sha256::normalize_s(&mut twice).unwrap();
914 assert_eq!(twice, flipped, "normalization must be idempotent");
915 }
916
917 #[test]
918 fn ecdsa_self_test_passes() {
919 EcdsaP256Sha256::self_test().unwrap();
920 }
921
922 #[test]
926 fn cavp_ecc_cdh_vector() {
927 let d = unhex("7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534").unwrap();
928 let mut peer = vec![0x04u8];
929 peer.extend_from_slice(
930 &unhex("700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287").unwrap(),
931 );
932 peer.extend_from_slice(
933 &unhex("db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac").unwrap(),
934 );
935 let mut z = [0u8; 32];
936 EcdhP256::agree(&d, &peer, &mut z).unwrap();
937 assert_eq!(
938 hex(&z),
939 "46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b"
940 );
941 }
942
943 #[test]
944 fn both_parties_derive_the_same_secret() {
945 let (alice, bob) = ([0x11u8; 32], [0x22u8; 32]);
946 let mut alice_pk = [0u8; 65];
947 let mut bob_pk = [0u8; 65];
948 EcdhP256::public_key(&alice, &mut alice_pk).unwrap();
949 EcdhP256::public_key(&bob, &mut bob_pk).unwrap();
950
951 let mut z1 = [0u8; 32];
952 let mut z2 = [0u8; 32];
953 EcdhP256::agree(&alice, &bob_pk, &mut z1).unwrap();
954 EcdhP256::agree(&bob, &alice_pk, &mut z2).unwrap();
955 assert_eq!(z1, z2);
956 assert_ne!(z1, [0u8; 32]);
957 }
958
959 #[test]
960 fn compressed_and_uncompressed_peers_agree() {
961 let (alice, bob) = ([0x33u8; 32], [0x44u8; 32]);
962 let mut unc = [0u8; 65];
963 let mut comp = [0u8; 33];
964 EcdhP256::public_key(&bob, &mut unc).unwrap();
965 EcdhP256::public_key_compressed(&bob, &mut comp).unwrap();
966
967 let mut z1 = [0u8; 32];
968 let mut z2 = [0u8; 32];
969 EcdhP256::agree(&alice, &unc, &mut z1).unwrap();
970 EcdhP256::agree(&alice, &comp, &mut z2).unwrap();
971 assert_eq!(z1, z2, "the peer key encoding must not matter");
972 }
973
974 #[test]
975 fn ecdh_rejects_invalid_inputs() {
976 let alice = [0x11u8; 32];
977 let mut z = [0u8; 32];
978 assert!(EcdhP256::agree(&alice, &[0u8; 65], &mut z).is_err());
979 assert!(EcdhP256::agree(&alice, &[], &mut z).is_err());
980
981 let mut bob_pk = [0u8; 65];
982 EcdhP256::public_key(&[0x22u8; 32], &mut bob_pk).unwrap();
983 bob_pk[64] ^= 1;
984 assert!(
985 EcdhP256::agree(&alice, &bob_pk, &mut z).is_err(),
986 "off curve"
987 );
988
989 let mut pk = [0u8; 65];
990 assert!(EcdhP256::public_key(&[0u8; 32], &mut pk).is_err());
991 assert!(EcdhP256::public_key(&[0xffu8; 32], &mut pk).is_err());
992 }
993
994 #[test]
995 fn ecdh_self_test_passes() {
996 EcdhP256::self_test().unwrap();
997 }
998}