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
119#[cfg(all(test, feature = "std", target_arch = "x86_64"))]
123pub(crate) fn table_mul_adx_for_tests(scalar: &Fn) -> Option<Point> {
124 let (windows, built) = generator_table();
125 crate::nist::gentable::prepare(windows, built);
126 crate::nist::p256_x86::table_mul(windows, scalar)
127}
128
129impl Curve for P256 {
130 type Field = Fp;
131 type Scalar = Fn;
132
133 const NAME: &'static str = "P-256";
134 const FIELD_BYTES: usize = 32;
135 const SCALAR_BYTES: usize = 32;
136 const ORDER_BITS: usize = 256;
137
138 const B: Fp = Fp::to_mont_const([
140 0x3bce_3c3e_27d2_604b,
141 0x651d_06b0_cc53_b0f6,
142 0xb3eb_bd55_7698_86bc,
143 0x5ac6_35d8_aa3a_93e7,
144 ]);
145
146 const GX: Fp = Fp::to_mont_const([
147 0xf4a1_3945_d898_c296,
148 0x7703_7d81_2deb_33a0,
149 0xf8bc_e6e5_63a4_40f2,
150 0x6b17_d1f2_e12c_4247,
151 ]);
152
153 const GY: Fp = Fp::to_mont_const([
154 0xcbb6_4068_37bf_51f5,
155 0x2bce_3357_6b31_5ece,
156 0x8ee7_eb4a_7c0f_9e16,
157 0x4fe3_42e2_fe1a_7f9b,
158 ]);
159
160 fn sqrt(x: &Fp) -> Fp {
162 sqrt_p3mod4(x, Fp::MODULUS, |v, e| v.pow(e))
167 }
168
169 fn field_from_slice(bytes: &[u8]) -> Option<Fp> {
170 let mut b = [0u8; 32];
171 if bytes.len() != 32 {
172 return None;
173 }
174 b.copy_from_slice(bytes);
175 Fp::from_bytes(&b)
176 }
177
178 fn scalar_from_slice(bytes: &[u8]) -> Option<Fn> {
179 let mut b = [0u8; 32];
180 if bytes.len() != 32 {
181 return None;
182 }
183 b.copy_from_slice(bytes);
184 Fn::from_bytes(&b)
185 }
186
187 #[cfg(target_arch = "x86_64")]
188 #[inline(always)]
189 fn double_fast(x: &Fp, y: &Fp, z: &Fp) -> Option<[Fp; 3]> {
190 crate::nist::p256_x86::point_double(&x.0, &y.0, &z.0).map(|[x, y, z]| [Fp(x), Fp(y), Fp(z)])
191 }
192
193 #[cfg(target_arch = "x86_64")]
194 #[inline(always)]
195 fn scan_affine(entries: &[AffinePoint], masks: &[u64]) -> Option<AffinePoint> {
196 Some(crate::nist::p256_x86::scan_affine(entries, masks))
197 }
198
199 #[cfg(target_arch = "x86_64")]
200 #[inline(always)]
201 fn scan_point(entries: &[Point], masks: &[u64]) -> Option<[Fp; 3]> {
202 let [x, y, z] = crate::nist::p256_x86::scan_point(entries, masks);
203 Some([Fp(x), Fp(y), Fp(z)])
204 }
205
206 #[cfg(target_arch = "x86_64")]
207 #[inline(always)]
208 fn add_fast(p: [&Fp; 3], q: [&Fp; 3]) -> Option<([Fp; 3], Fp, Fp)> {
209 crate::nist::p256_x86::point_add([&p[0].0, &p[1].0, &p[2].0], [&q[0].0, &q[1].0, &q[2].0])
210 .map(|([x, y, z], h, r)| ([Fp(x), Fp(y), Fp(z)], Fp(h), Fp(r)))
211 }
212
213 #[cfg(target_arch = "x86_64")]
214 #[inline(always)]
215 fn add_affine_fast(p: [&Fp; 3], q: [&Fp; 2]) -> Option<([Fp; 3], Fp, Fp)> {
216 crate::nist::p256_x86::point_add_affine([&p[0].0, &p[1].0, &p[2].0], [&q[0].0, &q[1].0])
217 .map(|([x, y, z], h, r)| ([Fp(x), Fp(y), Fp(z)], Fp(h), Fp(r)))
218 }
219
220 #[cfg(target_arch = "x86_64")]
221 #[inline(always)]
222 fn mul_scalar_fast(p: &Point, scalar: &Fn) -> Option<Point> {
223 match crate::nist::ifma::Ifma::get() {
224 Some(t) => Some(crate::nist::ifma::mul_scalar(t, p, scalar)),
225 None => crate::nist::p256_x86::mul_scalar(p, scalar),
226 }
227 }
228
229 #[cfg(target_arch = "x86_64")]
230 #[inline(always)]
231 fn comb_mul_fast(
232 windows: &[crate::nist::gentable::AffineWindow<Self, { crate::nist::gentable::ENTRIES }>;
233 crate::nist::gentable::TEETH],
234 scalar: &Fn,
235 ) -> Option<Point> {
236 crate::nist::ifma::Ifma::get().map(|t| crate::nist::ifma::comb_mul(t, windows, scalar))
237 }
238
239 #[cfg(all(target_arch = "x86_64", feature = "std"))]
240 #[inline(always)]
241 fn table_mul_fast<const E: usize>(
242 windows: &crate::nist::gentable::Windows<Self, E>,
243 scalar: &Fn,
244 ) -> Option<Point> {
245 match crate::nist::ifma::Ifma::get() {
246 Some(t) => Some(crate::nist::ifma::table_mul(t, windows, scalar)),
247 None => crate::nist::p256_x86::table_mul(windows, scalar),
248 }
249 }
250
251 #[cfg(target_arch = "x86_64")]
254 #[inline(always)]
255 fn mul_double_vartime_fast(
256 g: &[AffinePoint],
257 q: &[AffinePoint],
258 g_naf: &[i8],
259 q_naf: &[i8],
260 ) -> Option<Point> {
261 crate::nist::verify_x86::p256(g, q, g_naf, q_naf)
262 }
263
264 fn scalar_reduce_slice(bytes: &[u8]) -> Fn {
265 let mut b = [0u8; 32];
266 let n = core::cmp::min(32, bytes.len());
267 b[32 - n..].copy_from_slice(&bytes[..n]);
270 Fn::from_bytes_reduced(&b)
271 }
272}
273
274impl ecdsa::EcdsaCurve for P256 {
275 type Digest = ic_hash::Sha256;
276 type Hmac = ic_mac::HmacSha256;
277}
278
279pub struct EcdsaP256Sha256;
281
282impl Algorithm for EcdsaP256Sha256 {
283 const ID: &'static str = "ecdsa-p256-sha256";
284 const NAME: &'static str = "ECDSA P-256 with SHA-256";
285}
286
287impl SignatureScheme for EcdsaP256Sha256 {
288 const PRIVATE_KEY_LEN: usize = 32;
289 const PUBLIC_KEY_LEN: usize = 65;
291 const SIGNATURE_LEN: usize = 64;
293
294 fn public_key(private_key: &[u8], out: &mut [u8]) -> Result<()> {
295 ecdsa::public_key::<P256>(private_key, out)
296 }
297
298 fn sign(private_key: &[u8], message: &[u8], signature: &mut [u8]) -> Result<()> {
299 ecdsa::sign::<P256>(private_key, message, signature)
300 }
301
302 fn verify(public_key: &[u8], message: &[u8], signature: &[u8]) -> Result<()> {
303 ecdsa::verify::<P256>(public_key, message, signature)
304 }
305}
306
307impl EcdsaP256Sha256 {
308 pub fn verify_prehash(public_key: &[u8], digest: &[u8], signature: &[u8]) -> Result<()> {
316 ecdsa::verify_prehash::<P256>(public_key, digest, signature)
317 }
318
319 pub fn public_key_compressed(private_key: &[u8], out: &mut [u8]) -> Result<()> {
321 ecdsa::public_key_compressed::<P256>(private_key, out)
322 }
323
324 pub fn normalize_s(signature: &mut [u8]) -> Result<()> {
333 ecdsa::normalize_s::<P256>(signature)
334 }
335
336 pub fn has_low_s(signature: &[u8]) -> Result<bool> {
338 ecdsa::has_low_s::<P256>(signature)
339 }
340}
341
342impl SelfTest for EcdsaP256Sha256 {
343 fn self_test() -> Result<()> {
344 let mut key = [0u8; 32];
346 ic_core::codec::hex_decode(
347 b"c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721",
348 &mut key,
349 )?;
350 let mut want = [0u8; 64];
351 ic_core::codec::hex_decode(
352 b"efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8",
353 &mut want,
354 )?;
355
356 let mut sig = [0u8; 64];
357 <Self as SignatureScheme>::sign(&key, b"sample", &mut sig)?;
358 ensure!(
359 ic_core::ct::verify(&want, &sig),
360 SelfTestFailed,
361 "ecdsa-p256-sha256"
362 );
363
364 let mut pk = [0u8; 65];
365 <Self as SignatureScheme>::public_key(&key, &mut pk)?;
366 <Self as SignatureScheme>::verify(&pk, b"sample", &sig)?;
367
368 sig[0] ^= 1;
370 ensure!(
371 <Self as SignatureScheme>::verify(&pk, b"sample", &sig).is_err(),
372 SelfTestFailed,
373 "ecdsa-p256-sha256"
374 );
375 Ok(())
376 }
377}
378
379pub struct EcdhP256;
381
382impl Algorithm for EcdhP256 {
383 const ID: &'static str = "ecdh-p256";
384 const NAME: &'static str = "ECDH P-256";
385}
386
387impl KeyAgreement for EcdhP256 {
388 const PRIVATE_KEY_LEN: usize = 32;
389 const PUBLIC_KEY_LEN: usize = 65;
391 const SHARED_SECRET_LEN: usize = 32;
392
393 fn public_key(private_key: &[u8], out: &mut [u8]) -> Result<()> {
394 ecdh::public_key::<P256>(private_key, out)
395 }
396
397 fn agree(private_key: &[u8], peer_public_key: &[u8], out: &mut [u8]) -> Result<()> {
398 ecdh::agree::<P256>(private_key, peer_public_key, out)
399 }
400}
401
402impl EcdhP256 {
403 pub fn public_key_compressed(private_key: &[u8], out: &mut [u8]) -> Result<()> {
408 ecdh::public_key_compressed::<P256>(private_key, out)
409 }
410}
411
412impl SelfTest for EcdhP256 {
413 fn self_test() -> Result<()> {
414 let mut d = [0u8; 32];
416 ic_core::codec::hex_decode(
417 b"7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534",
418 &mut d,
419 )?;
420 let mut peer = [0u8; 65];
421 peer[0] = 0x04;
422 ic_core::codec::hex_decode(
423 b"700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287",
424 &mut peer[1..33],
425 )?;
426 ic_core::codec::hex_decode(
427 b"db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac",
428 &mut peer[33..],
429 )?;
430 let mut want = [0u8; 32];
431 ic_core::codec::hex_decode(
432 b"46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b",
433 &mut want,
434 )?;
435
436 let mut got = [0u8; 32];
437 <Self as KeyAgreement>::agree(&d, &peer, &mut got)?;
438 ensure!(
439 ic_core::ct::verify(&want, &got),
440 SelfTestFailed,
441 "ecdh-p256"
442 );
443 Ok(())
444 }
445}
446
447pub type Point = crate::nist::point::Point<P256>;
449pub type AffinePoint = crate::nist::point::AffinePoint<P256>;
451pub type PreparedPublicKey = crate::nist::ecdh::PreparedPublicKey<P256>;
458
459#[cfg(test)]
460mod tests {
461 use super::*;
462 use ic_core::codec::{hex, unhex};
463
464 #[test]
468 #[ignore = "timing; run manually with --release"]
469 fn where_the_time_goes() {
470 use crate::nist::arith::Field;
471 use std::hint::black_box;
472 use std::time::Instant;
473 fn ns(label: &str, iters: u32, mut f: impl FnMut()) {
474 let mut best = f64::INFINITY;
475 for _ in 0..7 {
476 let t = Instant::now();
477 for _ in 0..iters {
478 f();
479 }
480 best = best.min(t.elapsed().as_secs_f64() * 1e9 / iters as f64);
481 }
482 std::println!("{label:32} {best:10.1} ns");
483 }
484 let a = Fp::to_mont([7, 11, 13, 17]);
485 let b = Fp::to_mont([19, 23, 29, 31]);
486 ns("fp mul", 2_000_000, || {
487 black_box(black_box(a).mul(&black_box(b)));
488 });
489 ns("fp square", 2_000_000, || {
490 black_box(black_box(a).square());
491 });
492 ns("fp add", 2_000_000, || {
493 black_box(black_box(a).add(&black_box(b)));
494 });
495 ns("fp sub", 2_000_000, || {
496 black_box(black_box(a).sub(&black_box(b)));
497 });
498 ns("fp invert", 2_000, || {
499 black_box(black_box(a).invert());
500 });
501 let g = Point::generator();
502 let p = g.double();
503 ns("point double", 200_000, || {
504 black_box(black_box(p).double());
505 });
506 ns("point add (complete)", 200_000, || {
507 black_box(black_box(p).add(&black_box(g)));
508 });
509 ns("point add (unequal)", 200_000, || {
510 black_box(black_box(p).add_unequal(&black_box(g)));
511 });
512 let ga = g.to_affine().unwrap();
513 ns("point add (affine, unequal)", 200_000, || {
514 black_box(black_box(p).add_affine_unequal(&black_box(ga.x), &black_box(ga.y)));
515 });
516 let k = Fn::to_mont([0x1234_5678, 0x9abc_def0, 0x1357_9bdf, 0x2468_ace0]);
517 ns("scalar invert", 2_000, || {
518 black_box(black_box(k).invert());
519 });
520 ns("mul_generator (table)", 2_000, || {
521 black_box(Point::mul_generator(&black_box(k)));
522 });
523 ns("mul_scalar (windowed)", 500, || {
524 black_box(black_box(p).mul_scalar(&black_box(k)));
525 });
526 ns("mul_scalar_vartime", 500, || {
527 black_box(black_box(p).mul_scalar_vartime(&black_box(k)));
528 });
529 ns("to_affine", 2_000, || {
530 black_box(black_box(p).to_affine());
531 });
532 let mut pk = [0u8; 65];
533 EcdhP256::public_key(&[0x5a; 32], &mut pk).unwrap();
534 let mut shared = [0u8; 32];
535 ns("ecdh agree", 500, || {
536 EcdhP256::agree(&[0x5a; 32], &pk, &mut shared).unwrap();
537 });
538 ns("decode point", 2_000, || {
539 black_box(AffinePoint::from_sec1(black_box(&pk)));
540 });
541 ns("operational", 2_000, || {
542 black_box(ic_core::module::operational()).unwrap();
543 });
544 let mut sig = [0u8; 64];
545 ns("ecdsa sign", 500, || {
546 EcdsaP256Sha256::sign(&[0x5a; 32], b"message", &mut sig).unwrap();
547 });
548 }
549
550 fn scalar(v: u64) -> Fn {
551 Fn::to_mont([v, 0, 0, 0])
552 }
553
554 fn fp(v: u64) -> Fp {
555 Fp::to_mont([v, 0, 0, 0])
556 }
557
558 #[test]
561 fn montgomery_constants_are_consistent() {
562 assert_eq!(Fp::MODULUS[0].wrapping_mul(Fp::NEG_INV), u64::MAX, "p");
563 assert_eq!(Fn::MODULUS[0].wrapping_mul(Fn::NEG_INV), u64::MAX, "n");
564 }
565
566 #[test]
567 fn small_arithmetic_matches_integers() {
568 assert_eq!(fp(2).add(&fp(3)), fp(5));
569 assert_eq!(fp(5).sub(&fp(3)), fp(2));
570 assert_eq!(fp(6).mul(&fp(7)), fp(42));
571 assert_eq!(fp(9).square(), fp(81));
572 assert_eq!(fp(5).double(), fp(10));
573 assert_eq!(fp(5).triple(), fp(15));
574 assert_eq!(Fp::ONE.from_mont(), [1, 0, 0, 0]);
575 }
576
577 #[test]
578 fn inversion_is_correct() {
579 for v in [1u64, 2, 3, 19, 65537, u32::MAX as u64] {
580 assert_eq!(fp(v).mul(&fp(v).invert()), Fp::ONE, "1/{v} in Fp");
581 assert_eq!(scalar(v).mul(&scalar(v).invert()), Fn::ONE, "1/{v} in Fn");
582 }
583 assert_eq!(Fp::ZERO.invert(), Fp::ZERO);
584 assert_eq!(Fn::ZERO.invert(), Fn::ZERO);
585 }
586
587 #[test]
591 fn inversion_matches_fermat() {
592 let mut x = fp(0x1234_5678_9abc_def1);
593 let mut k = scalar(0x0fed_cba9_8765_4321);
594 let mut fps = std::vec![
595 Fp::ONE,
596 Fp::ONE.neg(),
597 Fp::ONE.double(),
598 Fp::ONE.neg().double()
599 ];
600 let mut fns = std::vec![
601 Fn::ONE,
602 Fn::ONE.neg(),
603 Fn::ONE.double(),
604 Fn::ONE.neg().double()
605 ];
606 for _ in 0..300 {
607 x = x.square().add(&Fp::ONE);
608 k = k.square().add(&Fn::ONE);
609 fps.push(x);
610 fns.push(k);
611 }
612 for v in fps {
613 assert_eq!(v.invert().0, Fp::invert_raw(v.0), "{v:?}");
614 }
615 for v in fns {
616 assert_eq!(v.invert().0, Fn::invert_raw(v.0), "{v:?}");
617 }
618 }
619
620 #[test]
621 fn arithmetic_laws_hold_on_large_values() {
622 let a = P256::field_from_slice(&[0x3a; 32]).unwrap();
623 let b = P256::field_from_slice(&[0x91; 32]).unwrap();
624 let c = P256::field_from_slice(&[0xc7; 32]).unwrap();
625 assert_eq!(a.mul(&b).mul(&c), a.mul(&b.mul(&c)), "associativity");
626 assert_eq!(a.mul(&b), b.mul(&a), "commutativity");
627 assert_eq!(
628 a.mul(&b.add(&c)),
629 a.mul(&b).add(&a.mul(&c)),
630 "distributivity"
631 );
632 assert_eq!(a.add(&a.neg()), Fp::ZERO);
633 }
634
635 #[test]
636 fn byte_encoding_round_trips_and_rejects_non_canonical() {
637 let bytes = [0x7fu8; 32];
638 let a = P256::field_from_slice(&bytes).unwrap();
639 assert_eq!(a.to_bytes(), bytes);
640
641 let mut p_bytes = [0u8; 32];
643 for i in 0..4 {
644 let hi = 32 - i * 8;
645 p_bytes[hi - 8..hi].copy_from_slice(&Fp::MODULUS[i].to_be_bytes());
646 }
647 assert!(P256::field_from_slice(&p_bytes).is_none());
648 }
649
650 #[test]
655 fn the_base_point_is_on_the_curve() {
656 let g = Point::generator().to_affine().unwrap();
657 assert!(bool::from(g.is_on_curve()));
658 }
659
660 #[test]
662 fn the_base_point_has_order_n() {
663 let n_minus_1 = Fn::ZERO.sub(&Fn::ONE);
664 let p = Point::generator().mul_scalar(&n_minus_1);
665 assert!(
666 bool::from(p.ct_eq(&Point::generator().neg())),
667 "[n-1]G == -G"
668 );
669 assert!(
670 bool::from(p.add(&Point::generator()).is_identity()),
671 "[n]G is the identity"
672 );
673 }
674
675 #[test]
676 fn identity_and_negation_behave() {
677 let g = Point::generator();
678 assert!(bool::from(g.add(&Point::identity()).ct_eq(&g)));
679 assert!(bool::from(Point::identity().add(&g).ct_eq(&g)));
680 assert!(bool::from(Point::identity().double().is_identity()));
681 assert!(bool::from(g.add(&g.neg()).is_identity()));
682 assert!(Point::identity().to_affine().is_none());
683 }
684
685 #[test]
687 fn addition_handles_equal_inputs_as_a_doubling() {
688 let g = Point::generator();
689 assert!(bool::from(g.add(&g).ct_eq(&g.double())));
690 let p = g.mul_scalar(&scalar(5));
691 assert!(bool::from(p.add(&p).ct_eq(&p.double())));
692 }
693
694 #[test]
703 fn the_vartime_multiplication_agrees_with_the_ladder() {
704 let g = Point::generator();
705
706 let mut checked = 0;
707 for raw in [
708 [0u8; 32],
709 {
710 let mut v = [0u8; 32];
711 v[31] = 1;
712 v
713 },
714 [0xffu8; 32],
715 [0x55u8; 32],
716 [0xaau8; 32],
717 [0x9du8; 32],
718 ] {
719 let k = Fn::from_bytes_reduced(&raw);
720 assert!(
721 bool::from(g.mul_scalar_vartime(&k).ct_eq(&g.mul_scalar(&k))),
722 "vartime and ladder differ for {raw:02x?}"
723 );
724 checked += 1;
725 }
726 assert_eq!(checked, 6, "the comparison did not run");
727 }
728
729 #[test]
730 fn scalar_multiplication_matches_repeated_addition() {
731 let g = Point::generator();
732 let mut acc = Point::identity();
733 for k in 1..=10u64 {
734 acc = acc.add(&g);
735 assert!(bool::from(acc.ct_eq(&g.mul_scalar(&scalar(k)))), "[{k}]G");
736 }
737 }
738
739 #[test]
740 fn scalar_multiplication_is_linear() {
741 let g = Point::generator();
742 let a = scalar(1_234_567);
743 let b = scalar(7_654_321);
744 assert!(bool::from(
745 g.mul_scalar(&a.add(&b))
746 .ct_eq(&g.mul_scalar(&a).add(&g.mul_scalar(&b)))
747 ));
748 assert!(bool::from(
749 g.mul_scalar(&a)
750 .mul_scalar(&b)
751 .ct_eq(&g.mul_scalar(&a.mul(&b)))
752 ));
753 }
754
755 #[test]
758 fn two_g_matches_the_published_value() {
759 let two_g = Point::generator().double().to_affine().unwrap();
760 assert_eq!(
761 hex(two_g.x.to_bytes().as_ref()),
762 "7cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978"
763 );
764 assert_eq!(
765 hex(two_g.y.to_bytes().as_ref()),
766 "07775510db8ed040293d9ac69f7430dbba7dade63ce982299e04b79d227873d1"
767 );
768 }
769
770 #[test]
771 fn sec1_round_trips_in_both_forms() {
772 let g = Point::generator();
773 for k in [1u64, 2, 3, 4, 5, 6, 7, 8] {
774 let p = g.mul_scalar(&scalar(k)).to_affine().unwrap();
775 let mut unc = [0u8; 65];
776 let mut comp = [0u8; 33];
777 assert!(p.write_uncompressed(&mut unc));
778 assert!(p.write_compressed(&mut comp));
779 assert_eq!(unc[0], 0x04);
780 assert!(comp[0] == 0x02 || comp[0] == 0x03);
781
782 let a = AffinePoint::from_sec1(&unc).unwrap();
783 let b = AffinePoint::from_sec1(&comp).unwrap();
784 assert_eq!(a.x, p.x);
785 assert_eq!(a.y, p.y);
786 assert_eq!(b.x, p.x);
787 assert_eq!(b.y, p.y, "compressed y for [{k}]G");
788 }
789 }
790
791 #[test]
792 fn decoding_rejects_bad_encodings() {
793 let g = Point::generator().to_affine().unwrap();
794 let mut unc = [0u8; 65];
795 assert!(g.write_uncompressed(&mut unc));
796
797 assert!(AffinePoint::from_sec1(&[]).is_none());
798 assert!(AffinePoint::from_sec1(&[0u8; 65]).is_none(), "identity");
799 assert!(AffinePoint::from_sec1(&unc[..64]).is_none(), "truncated");
800
801 let mut bad = unc;
802 bad[0] = 0x05;
803 assert!(AffinePoint::from_sec1(&bad).is_none(), "bad tag");
804
805 let mut bad = unc;
806 bad[64] ^= 1;
807 assert!(AffinePoint::from_sec1(&bad).is_none(), "off curve");
808 }
809
810 const KEY: &str = "c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721";
813
814 #[test]
818 fn rfc6979_sample_vector() {
819 let key = unhex(KEY).unwrap();
820 let mut sig = [0u8; 64];
821 EcdsaP256Sha256::sign(&key, b"sample", &mut sig).unwrap();
822 assert_eq!(
823 hex(&sig[..32]),
824 "efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716",
825 "r"
826 );
827 assert_eq!(
828 hex(&sig[32..]),
829 "f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8",
830 "s"
831 );
832 }
833
834 #[test]
836 fn rfc6979_test_vector() {
837 let key = unhex(KEY).unwrap();
838 let mut sig = [0u8; 64];
839 EcdsaP256Sha256::sign(&key, b"test", &mut sig).unwrap();
840 assert_eq!(
841 hex(&sig[..32]),
842 "f1abb023518351cd71d881567b1ea663ed3efcf6c5132b354f28d3b0b7d38367",
843 "r"
844 );
845 assert_eq!(
846 hex(&sig[32..]),
847 "019f4113742a2b14bd25926b49c649155f267e60d3814b4c0cc84250e46f0083",
848 "s"
849 );
850 }
851
852 #[test]
853 fn rfc6979_public_key() {
854 let key = unhex(KEY).unwrap();
855 let mut pk = [0u8; 65];
856 EcdsaP256Sha256::public_key(&key, &mut pk).unwrap();
857 assert_eq!(
858 hex(&pk[1..33]),
859 "60fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6",
860 "Ux"
861 );
862 assert_eq!(
863 hex(&pk[33..]),
864 "7903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
865 "Uy"
866 );
867 }
868
869 #[test]
870 fn signing_is_deterministic_and_message_bound() {
871 let key = unhex(KEY).unwrap();
872 let mut a = [0u8; 64];
873 let mut b = [0u8; 64];
874 EcdsaP256Sha256::sign(&key, b"same", &mut a).unwrap();
875 EcdsaP256Sha256::sign(&key, b"same", &mut b).unwrap();
876 assert_eq!(a, b, "RFC 6979 signing must not depend on an RNG");
877
878 EcdsaP256Sha256::sign(&key, b"other", &mut b).unwrap();
879 assert_ne!(&a[..32], &b[..32], "r must differ between messages");
880 }
881
882 #[test]
883 fn sign_and_verify_round_trip() {
884 let key = unhex(KEY).unwrap();
885 let mut pk = [0u8; 65];
886 EcdsaP256Sha256::public_key(&key, &mut pk).unwrap();
887 for message in [&b""[..], b"short", &[0x5au8; 1000][..]] {
888 let mut sig = [0u8; 64];
889 EcdsaP256Sha256::sign(&key, message, &mut sig).unwrap();
890 EcdsaP256Sha256::verify(&pk, message, &sig).unwrap();
891 }
892 }
893
894 #[test]
895 fn verification_rejects_tampering() {
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"authentic", &mut sig).unwrap();
901
902 assert!(EcdsaP256Sha256::verify(&pk, b"forged", &sig).is_err());
903 let mut bad = sig;
904 bad[0] ^= 1;
905 assert!(EcdsaP256Sha256::verify(&pk, b"authentic", &bad).is_err());
906 let mut bad = sig;
907 bad[63] ^= 1;
908 assert!(EcdsaP256Sha256::verify(&pk, b"authentic", &bad).is_err());
909
910 let mut other = [0u8; 65];
911 EcdsaP256Sha256::public_key(&[0x11u8; 32], &mut other).unwrap();
912 assert!(EcdsaP256Sha256::verify(&other, b"authentic", &sig).is_err());
913 }
914
915 #[test]
916 fn verification_rejects_degenerate_signatures() {
917 let key = unhex(KEY).unwrap();
918 let mut pk = [0u8; 65];
919 EcdsaP256Sha256::public_key(&key, &mut pk).unwrap();
920
921 let mut zero_r = [0u8; 64];
922 zero_r[63] = 1;
923 assert!(EcdsaP256Sha256::verify(&pk, b"m", &zero_r).is_err());
924
925 let mut zero_s = [0u8; 64];
926 zero_s[31] = 1;
927 assert!(EcdsaP256Sha256::verify(&pk, b"m", &zero_s).is_err());
928
929 let n_bytes =
930 unhex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551").unwrap();
931 let mut at_n = [0u8; 64];
932 at_n[..32].copy_from_slice(&n_bytes);
933 at_n[32..].copy_from_slice(&n_bytes);
934 assert!(EcdsaP256Sha256::verify(&pk, b"m", &at_n).is_err());
935 }
936
937 #[test]
938 fn signing_rejects_invalid_private_keys() {
939 let mut sig = [0u8; 64];
940 assert!(
941 EcdsaP256Sha256::sign(&[0u8; 32], b"m", &mut sig).is_err(),
942 "zero"
943 );
944 assert!(
945 EcdsaP256Sha256::sign(&[0xffu8; 32], b"m", &mut sig).is_err(),
946 ">= n"
947 );
948 assert!(
949 EcdsaP256Sha256::sign(&[1u8; 31], b"m", &mut sig).is_err(),
950 "short"
951 );
952 }
953
954 #[test]
956 fn malleability_and_normalization() {
957 let key = unhex(KEY).unwrap();
958 let mut pk = [0u8; 65];
959 EcdsaP256Sha256::public_key(&key, &mut pk).unwrap();
960 let mut sig = [0u8; 64];
961 EcdsaP256Sha256::sign(&key, b"sample", &mut sig).unwrap();
962 assert!(
963 !EcdsaP256Sha256::has_low_s(&sig).unwrap(),
964 "RFC 6979 s is high here"
965 );
966
967 let mut flipped = sig;
968 EcdsaP256Sha256::normalize_s(&mut flipped).unwrap();
969 assert_ne!(flipped, sig);
970 EcdsaP256Sha256::verify(&pk, b"sample", &flipped).unwrap();
971 assert!(EcdsaP256Sha256::has_low_s(&flipped).unwrap());
972
973 let mut twice = flipped;
974 EcdsaP256Sha256::normalize_s(&mut twice).unwrap();
975 assert_eq!(twice, flipped, "normalization must be idempotent");
976 }
977
978 #[test]
979 fn ecdsa_self_test_passes() {
980 EcdsaP256Sha256::self_test().unwrap();
981 }
982
983 #[test]
987 fn cavp_ecc_cdh_vector() {
988 let d = unhex("7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534").unwrap();
989 let mut peer = vec![0x04u8];
990 peer.extend_from_slice(
991 &unhex("700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287").unwrap(),
992 );
993 peer.extend_from_slice(
994 &unhex("db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac").unwrap(),
995 );
996 let mut z = [0u8; 32];
997 EcdhP256::agree(&d, &peer, &mut z).unwrap();
998 assert_eq!(
999 hex(&z),
1000 "46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b"
1001 );
1002 }
1003
1004 #[test]
1005 fn both_parties_derive_the_same_secret() {
1006 let (alice, bob) = ([0x11u8; 32], [0x22u8; 32]);
1007 let mut alice_pk = [0u8; 65];
1008 let mut bob_pk = [0u8; 65];
1009 EcdhP256::public_key(&alice, &mut alice_pk).unwrap();
1010 EcdhP256::public_key(&bob, &mut bob_pk).unwrap();
1011
1012 let mut z1 = [0u8; 32];
1013 let mut z2 = [0u8; 32];
1014 EcdhP256::agree(&alice, &bob_pk, &mut z1).unwrap();
1015 EcdhP256::agree(&bob, &alice_pk, &mut z2).unwrap();
1016 assert_eq!(z1, z2);
1017 assert_ne!(z1, [0u8; 32]);
1018 }
1019
1020 #[test]
1021 fn compressed_and_uncompressed_peers_agree() {
1022 let (alice, bob) = ([0x33u8; 32], [0x44u8; 32]);
1023 let mut unc = [0u8; 65];
1024 let mut comp = [0u8; 33];
1025 EcdhP256::public_key(&bob, &mut unc).unwrap();
1026 EcdhP256::public_key_compressed(&bob, &mut comp).unwrap();
1027
1028 let mut z1 = [0u8; 32];
1029 let mut z2 = [0u8; 32];
1030 EcdhP256::agree(&alice, &unc, &mut z1).unwrap();
1031 EcdhP256::agree(&alice, &comp, &mut z2).unwrap();
1032 assert_eq!(z1, z2, "the peer key encoding must not matter");
1033 }
1034
1035 #[test]
1036 fn ecdh_rejects_invalid_inputs() {
1037 let alice = [0x11u8; 32];
1038 let mut z = [0u8; 32];
1039 assert!(EcdhP256::agree(&alice, &[0u8; 65], &mut z).is_err());
1040 assert!(EcdhP256::agree(&alice, &[], &mut z).is_err());
1041
1042 let mut bob_pk = [0u8; 65];
1043 EcdhP256::public_key(&[0x22u8; 32], &mut bob_pk).unwrap();
1044 bob_pk[64] ^= 1;
1045 assert!(
1046 EcdhP256::agree(&alice, &bob_pk, &mut z).is_err(),
1047 "off curve"
1048 );
1049
1050 let mut pk = [0u8; 65];
1051 assert!(EcdhP256::public_key(&[0u8; 32], &mut pk).is_err());
1052 assert!(EcdhP256::public_key(&[0xffu8; 32], &mut pk).is_err());
1053 }
1054
1055 #[test]
1056 fn ecdh_self_test_passes() {
1057 EcdhP256::self_test().unwrap();
1058 }
1059}