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
14mont_field!(
15 Fp,
16 4,
17 32,
18 [
19 0xffff_ffff_ffff_ffff,
20 0x0000_0000_ffff_ffff,
21 0x0000_0000_0000_0000,
22 0xffff_ffff_0000_0001,
23 ],
24 "The P-256 coordinate field, GF(p) with p = 2^256 - 2^224 + 2^192 + 2^96 - 1."
25);
26
27mont_field!(
28 Fn,
29 4,
30 32,
31 [
32 0xf3b9_cac2_fc63_2551,
33 0xbce6_faad_a717_9e84,
34 0xffff_ffff_ffff_ffff,
35 0xffff_ffff_0000_0000,
36 ],
37 "The P-256 scalar ring, Z/nZ where n is the order of the base point."
38);
39
40#[derive(Debug, Clone, Copy)]
42pub struct P256;
43
44crate::generator_table_for!(P256);
47
48impl Curve for P256 {
49 type Field = Fp;
50 type Scalar = Fn;
51
52 const NAME: &'static str = "P-256";
53 const FIELD_BYTES: usize = 32;
54 const SCALAR_BYTES: usize = 32;
55 const ORDER_BITS: usize = 256;
56
57 const B: Fp = Fp::to_mont([
59 0x3bce_3c3e_27d2_604b,
60 0x651d_06b0_cc53_b0f6,
61 0xb3eb_bd55_7698_86bc,
62 0x5ac6_35d8_aa3a_93e7,
63 ]);
64
65 const GX: Fp = Fp::to_mont([
66 0xf4a1_3945_d898_c296,
67 0x7703_7d81_2deb_33a0,
68 0xf8bc_e6e5_63a4_40f2,
69 0x6b17_d1f2_e12c_4247,
70 ]);
71
72 const GY: Fp = Fp::to_mont([
73 0xcbb6_4068_37bf_51f5,
74 0x2bce_3357_6b31_5ece,
75 0x8ee7_eb4a_7c0f_9e16,
76 0x4fe3_42e2_fe1a_7f9b,
77 ]);
78
79 fn sqrt(x: &Fp) -> Fp {
81 sqrt_p3mod4(x, Fp::MODULUS, |v, e| v.pow(e))
86 }
87
88 fn field_from_slice(bytes: &[u8]) -> Option<Fp> {
89 let mut b = [0u8; 32];
90 if bytes.len() != 32 {
91 return None;
92 }
93 b.copy_from_slice(bytes);
94 Fp::from_bytes(&b)
95 }
96
97 fn scalar_from_slice(bytes: &[u8]) -> Option<Fn> {
98 let mut b = [0u8; 32];
99 if bytes.len() != 32 {
100 return None;
101 }
102 b.copy_from_slice(bytes);
103 Fn::from_bytes(&b)
104 }
105
106 fn scalar_reduce_slice(bytes: &[u8]) -> Fn {
107 let mut b = [0u8; 32];
108 let n = core::cmp::min(32, bytes.len());
109 b[32 - n..].copy_from_slice(&bytes[..n]);
112 Fn::from_bytes_reduced(&b)
113 }
114}
115
116impl ecdsa::EcdsaCurve for P256 {
117 type Digest = ic_hash::Sha256;
118 type Hmac = ic_mac::HmacSha256;
119 const SIGNATURE_ID: &'static str = "ecdsa-p256-sha256";
120}
121
122pub struct EcdsaP256Sha256;
124
125impl Algorithm for EcdsaP256Sha256 {
126 const ID: &'static str = "ecdsa-p256-sha256";
127 const NAME: &'static str = "ECDSA P-256 with SHA-256";
128}
129
130impl SignatureScheme for EcdsaP256Sha256 {
131 const PRIVATE_KEY_LEN: usize = 32;
132 const PUBLIC_KEY_LEN: usize = 65;
134 const SIGNATURE_LEN: usize = 64;
136
137 fn public_key(private_key: &[u8], out: &mut [u8]) -> Result<()> {
138 ecdsa::public_key::<P256>(private_key, out)
139 }
140
141 fn sign(private_key: &[u8], message: &[u8], signature: &mut [u8]) -> Result<()> {
142 ecdsa::sign::<P256>(private_key, message, signature)
143 }
144
145 fn verify(public_key: &[u8], message: &[u8], signature: &[u8]) -> Result<()> {
146 ecdsa::verify::<P256>(public_key, message, signature)
147 }
148}
149
150impl EcdsaP256Sha256 {
151 pub fn public_key_compressed(private_key: &[u8], out: &mut [u8]) -> Result<()> {
153 ecdsa::public_key_compressed::<P256>(private_key, out)
154 }
155
156 pub fn normalize_s(signature: &mut [u8]) -> Result<()> {
158 ecdsa::normalize_s::<P256>(signature)
159 }
160
161 pub fn has_low_s(signature: &[u8]) -> Result<bool> {
163 ecdsa::has_low_s::<P256>(signature)
164 }
165}
166
167impl SelfTest for EcdsaP256Sha256 {
168 fn self_test() -> Result<()> {
169 let mut key = [0u8; 32];
171 ic_core::codec::hex_decode(
172 b"c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721",
173 &mut key,
174 )?;
175 let mut want = [0u8; 64];
176 ic_core::codec::hex_decode(
177 b"efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8",
178 &mut want,
179 )?;
180
181 let mut sig = [0u8; 64];
182 <Self as SignatureScheme>::sign(&key, b"sample", &mut sig)?;
183 ensure!(
184 ic_core::ct::verify(&want, &sig),
185 SelfTestFailed,
186 "ecdsa-p256-sha256"
187 );
188
189 let mut pk = [0u8; 65];
190 <Self as SignatureScheme>::public_key(&key, &mut pk)?;
191 <Self as SignatureScheme>::verify(&pk, b"sample", &sig)?;
192
193 sig[0] ^= 1;
195 ensure!(
196 <Self as SignatureScheme>::verify(&pk, b"sample", &sig).is_err(),
197 SelfTestFailed,
198 "ecdsa-p256-sha256"
199 );
200 Ok(())
201 }
202}
203
204pub struct EcdhP256;
206
207impl Algorithm for EcdhP256 {
208 const ID: &'static str = "ecdh-p256";
209 const NAME: &'static str = "ECDH P-256";
210}
211
212impl KeyAgreement for EcdhP256 {
213 const PRIVATE_KEY_LEN: usize = 32;
214 const PUBLIC_KEY_LEN: usize = 65;
216 const SHARED_SECRET_LEN: usize = 32;
217
218 fn public_key(private_key: &[u8], out: &mut [u8]) -> Result<()> {
219 ecdh::public_key::<P256>(private_key, out)
220 }
221
222 fn agree(private_key: &[u8], peer_public_key: &[u8], out: &mut [u8]) -> Result<()> {
223 ecdh::agree::<P256>(private_key, peer_public_key, out)
224 }
225}
226
227impl EcdhP256 {
228 pub fn public_key_compressed(private_key: &[u8], out: &mut [u8]) -> Result<()> {
233 ecdh::public_key_compressed::<P256>(private_key, out)
234 }
235}
236
237impl SelfTest for EcdhP256 {
238 fn self_test() -> Result<()> {
239 let mut d = [0u8; 32];
241 ic_core::codec::hex_decode(
242 b"7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534",
243 &mut d,
244 )?;
245 let mut peer = [0u8; 65];
246 peer[0] = 0x04;
247 ic_core::codec::hex_decode(
248 b"700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287",
249 &mut peer[1..33],
250 )?;
251 ic_core::codec::hex_decode(
252 b"db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac",
253 &mut peer[33..],
254 )?;
255 let mut want = [0u8; 32];
256 ic_core::codec::hex_decode(
257 b"46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b",
258 &mut want,
259 )?;
260
261 let mut got = [0u8; 32];
262 <Self as KeyAgreement>::agree(&d, &peer, &mut got)?;
263 ensure!(
264 ic_core::ct::verify(&want, &got),
265 SelfTestFailed,
266 "ecdh-p256"
267 );
268 Ok(())
269 }
270}
271
272pub type Point = crate::nist::point::Point<P256>;
274pub type AffinePoint = crate::nist::point::AffinePoint<P256>;
276
277#[cfg(test)]
278mod tests {
279 use super::*;
280 use ic_core::codec::{hex, unhex};
281
282 fn scalar(v: u64) -> Fn {
283 Fn::to_mont([v, 0, 0, 0])
284 }
285
286 fn fp(v: u64) -> Fp {
287 Fp::to_mont([v, 0, 0, 0])
288 }
289
290 #[test]
293 fn montgomery_constants_are_consistent() {
294 assert_eq!(Fp::MODULUS[0].wrapping_mul(Fp::NEG_INV), u64::MAX, "p");
295 assert_eq!(Fn::MODULUS[0].wrapping_mul(Fn::NEG_INV), u64::MAX, "n");
296 }
297
298 #[test]
299 fn small_arithmetic_matches_integers() {
300 assert_eq!(fp(2).add(&fp(3)), fp(5));
301 assert_eq!(fp(5).sub(&fp(3)), fp(2));
302 assert_eq!(fp(6).mul(&fp(7)), fp(42));
303 assert_eq!(fp(9).square(), fp(81));
304 assert_eq!(fp(5).double(), fp(10));
305 assert_eq!(fp(5).triple(), fp(15));
306 assert_eq!(Fp::ONE.from_mont(), [1, 0, 0, 0]);
307 }
308
309 #[test]
310 fn inversion_is_correct() {
311 for v in [1u64, 2, 3, 19, 65537, u32::MAX as u64] {
312 assert_eq!(fp(v).mul(&fp(v).invert()), Fp::ONE, "1/{v} in Fp");
313 assert_eq!(scalar(v).mul(&scalar(v).invert()), Fn::ONE, "1/{v} in Fn");
314 }
315 assert_eq!(Fp::ZERO.invert(), Fp::ZERO);
316 }
317
318 #[test]
319 fn arithmetic_laws_hold_on_large_values() {
320 let a = P256::field_from_slice(&[0x3a; 32]).unwrap();
321 let b = P256::field_from_slice(&[0x91; 32]).unwrap();
322 let c = P256::field_from_slice(&[0xc7; 32]).unwrap();
323 assert_eq!(a.mul(&b).mul(&c), a.mul(&b.mul(&c)), "associativity");
324 assert_eq!(a.mul(&b), b.mul(&a), "commutativity");
325 assert_eq!(
326 a.mul(&b.add(&c)),
327 a.mul(&b).add(&a.mul(&c)),
328 "distributivity"
329 );
330 assert_eq!(a.add(&a.neg()), Fp::ZERO);
331 }
332
333 #[test]
334 fn byte_encoding_round_trips_and_rejects_non_canonical() {
335 let bytes = [0x7fu8; 32];
336 let a = P256::field_from_slice(&bytes).unwrap();
337 assert_eq!(a.to_bytes(), bytes);
338
339 let mut p_bytes = [0u8; 32];
341 for i in 0..4 {
342 let hi = 32 - i * 8;
343 p_bytes[hi - 8..hi].copy_from_slice(&Fp::MODULUS[i].to_be_bytes());
344 }
345 assert!(P256::field_from_slice(&p_bytes).is_none());
346 }
347
348 #[test]
353 fn the_base_point_is_on_the_curve() {
354 let g = Point::generator().to_affine().unwrap();
355 assert!(bool::from(g.is_on_curve()));
356 }
357
358 #[test]
360 fn the_base_point_has_order_n() {
361 let n_minus_1 = Fn::ZERO.sub(&Fn::ONE);
362 let p = Point::generator().mul_scalar(&n_minus_1);
363 assert!(
364 bool::from(p.ct_eq(&Point::generator().neg())),
365 "[n-1]G == -G"
366 );
367 assert!(
368 bool::from(p.add(&Point::generator()).is_identity()),
369 "[n]G is the identity"
370 );
371 }
372
373 #[test]
374 fn identity_and_negation_behave() {
375 let g = Point::generator();
376 assert!(bool::from(g.add(&Point::identity()).ct_eq(&g)));
377 assert!(bool::from(Point::identity().add(&g).ct_eq(&g)));
378 assert!(bool::from(Point::identity().double().is_identity()));
379 assert!(bool::from(g.add(&g.neg()).is_identity()));
380 assert!(Point::identity().to_affine().is_none());
381 }
382
383 #[test]
385 fn addition_handles_equal_inputs_as_a_doubling() {
386 let g = Point::generator();
387 assert!(bool::from(g.add(&g).ct_eq(&g.double())));
388 let p = g.mul_scalar(&scalar(5));
389 assert!(bool::from(p.add(&p).ct_eq(&p.double())));
390 }
391
392 #[test]
401 fn the_vartime_multiplication_agrees_with_the_ladder() {
402 let g = Point::generator();
403
404 let mut checked = 0;
405 for raw in [
406 [0u8; 32],
407 {
408 let mut v = [0u8; 32];
409 v[31] = 1;
410 v
411 },
412 [0xffu8; 32],
413 [0x55u8; 32],
414 [0xaau8; 32],
415 [0x9du8; 32],
416 ] {
417 let k = Fn::from_bytes_reduced(&raw);
418 assert!(
419 bool::from(g.mul_scalar_vartime(&k).ct_eq(&g.mul_scalar(&k))),
420 "vartime and ladder differ for {raw:02x?}"
421 );
422 checked += 1;
423 }
424 assert_eq!(checked, 6, "the comparison did not run");
425 }
426
427 #[test]
428 fn scalar_multiplication_matches_repeated_addition() {
429 let g = Point::generator();
430 let mut acc = Point::identity();
431 for k in 1..=10u64 {
432 acc = acc.add(&g);
433 assert!(bool::from(acc.ct_eq(&g.mul_scalar(&scalar(k)))), "[{k}]G");
434 }
435 }
436
437 #[test]
438 fn scalar_multiplication_is_linear() {
439 let g = Point::generator();
440 let a = scalar(1_234_567);
441 let b = scalar(7_654_321);
442 assert!(bool::from(
443 g.mul_scalar(&a.add(&b))
444 .ct_eq(&g.mul_scalar(&a).add(&g.mul_scalar(&b)))
445 ));
446 assert!(bool::from(
447 g.mul_scalar(&a)
448 .mul_scalar(&b)
449 .ct_eq(&g.mul_scalar(&a.mul(&b)))
450 ));
451 }
452
453 #[test]
456 fn two_g_matches_the_published_value() {
457 let two_g = Point::generator().double().to_affine().unwrap();
458 assert_eq!(
459 hex(two_g.x.to_bytes().as_ref()),
460 "7cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978"
461 );
462 assert_eq!(
463 hex(two_g.y.to_bytes().as_ref()),
464 "07775510db8ed040293d9ac69f7430dbba7dade63ce982299e04b79d227873d1"
465 );
466 }
467
468 #[test]
469 fn sec1_round_trips_in_both_forms() {
470 let g = Point::generator();
471 for k in [1u64, 2, 3, 4, 5, 6, 7, 8] {
472 let p = g.mul_scalar(&scalar(k)).to_affine().unwrap();
473 let mut unc = [0u8; 65];
474 let mut comp = [0u8; 33];
475 assert!(p.write_uncompressed(&mut unc));
476 assert!(p.write_compressed(&mut comp));
477 assert_eq!(unc[0], 0x04);
478 assert!(comp[0] == 0x02 || comp[0] == 0x03);
479
480 let a = AffinePoint::from_sec1(&unc).unwrap();
481 let b = AffinePoint::from_sec1(&comp).unwrap();
482 assert_eq!(a.x, p.x);
483 assert_eq!(a.y, p.y);
484 assert_eq!(b.x, p.x);
485 assert_eq!(b.y, p.y, "compressed y for [{k}]G");
486 }
487 }
488
489 #[test]
490 fn decoding_rejects_bad_encodings() {
491 let g = Point::generator().to_affine().unwrap();
492 let mut unc = [0u8; 65];
493 assert!(g.write_uncompressed(&mut unc));
494
495 assert!(AffinePoint::from_sec1(&[]).is_none());
496 assert!(AffinePoint::from_sec1(&[0u8; 65]).is_none(), "identity");
497 assert!(AffinePoint::from_sec1(&unc[..64]).is_none(), "truncated");
498
499 let mut bad = unc;
500 bad[0] = 0x05;
501 assert!(AffinePoint::from_sec1(&bad).is_none(), "bad tag");
502
503 let mut bad = unc;
504 bad[64] ^= 1;
505 assert!(AffinePoint::from_sec1(&bad).is_none(), "off curve");
506 }
507
508 const KEY: &str = "c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721";
511
512 #[test]
516 fn rfc6979_sample_vector() {
517 let key = unhex(KEY).unwrap();
518 let mut sig = [0u8; 64];
519 EcdsaP256Sha256::sign(&key, b"sample", &mut sig).unwrap();
520 assert_eq!(
521 hex(&sig[..32]),
522 "efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716",
523 "r"
524 );
525 assert_eq!(
526 hex(&sig[32..]),
527 "f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8",
528 "s"
529 );
530 }
531
532 #[test]
534 fn rfc6979_test_vector() {
535 let key = unhex(KEY).unwrap();
536 let mut sig = [0u8; 64];
537 EcdsaP256Sha256::sign(&key, b"test", &mut sig).unwrap();
538 assert_eq!(
539 hex(&sig[..32]),
540 "f1abb023518351cd71d881567b1ea663ed3efcf6c5132b354f28d3b0b7d38367",
541 "r"
542 );
543 assert_eq!(
544 hex(&sig[32..]),
545 "019f4113742a2b14bd25926b49c649155f267e60d3814b4c0cc84250e46f0083",
546 "s"
547 );
548 }
549
550 #[test]
551 fn rfc6979_public_key() {
552 let key = unhex(KEY).unwrap();
553 let mut pk = [0u8; 65];
554 EcdsaP256Sha256::public_key(&key, &mut pk).unwrap();
555 assert_eq!(
556 hex(&pk[1..33]),
557 "60fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6",
558 "Ux"
559 );
560 assert_eq!(
561 hex(&pk[33..]),
562 "7903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
563 "Uy"
564 );
565 }
566
567 #[test]
568 fn signing_is_deterministic_and_message_bound() {
569 let key = unhex(KEY).unwrap();
570 let mut a = [0u8; 64];
571 let mut b = [0u8; 64];
572 EcdsaP256Sha256::sign(&key, b"same", &mut a).unwrap();
573 EcdsaP256Sha256::sign(&key, b"same", &mut b).unwrap();
574 assert_eq!(a, b, "RFC 6979 signing must not depend on an RNG");
575
576 EcdsaP256Sha256::sign(&key, b"other", &mut b).unwrap();
577 assert_ne!(&a[..32], &b[..32], "r must differ between messages");
578 }
579
580 #[test]
581 fn sign_and_verify_round_trip() {
582 let key = unhex(KEY).unwrap();
583 let mut pk = [0u8; 65];
584 EcdsaP256Sha256::public_key(&key, &mut pk).unwrap();
585 for message in [&b""[..], b"short", &[0x5au8; 1000][..]] {
586 let mut sig = [0u8; 64];
587 EcdsaP256Sha256::sign(&key, message, &mut sig).unwrap();
588 EcdsaP256Sha256::verify(&pk, message, &sig).unwrap();
589 }
590 }
591
592 #[test]
593 fn verification_rejects_tampering() {
594 let key = unhex(KEY).unwrap();
595 let mut pk = [0u8; 65];
596 EcdsaP256Sha256::public_key(&key, &mut pk).unwrap();
597 let mut sig = [0u8; 64];
598 EcdsaP256Sha256::sign(&key, b"authentic", &mut sig).unwrap();
599
600 assert!(EcdsaP256Sha256::verify(&pk, b"forged", &sig).is_err());
601 let mut bad = sig;
602 bad[0] ^= 1;
603 assert!(EcdsaP256Sha256::verify(&pk, b"authentic", &bad).is_err());
604 let mut bad = sig;
605 bad[63] ^= 1;
606 assert!(EcdsaP256Sha256::verify(&pk, b"authentic", &bad).is_err());
607
608 let mut other = [0u8; 65];
609 EcdsaP256Sha256::public_key(&[0x11u8; 32], &mut other).unwrap();
610 assert!(EcdsaP256Sha256::verify(&other, b"authentic", &sig).is_err());
611 }
612
613 #[test]
614 fn verification_rejects_degenerate_signatures() {
615 let key = unhex(KEY).unwrap();
616 let mut pk = [0u8; 65];
617 EcdsaP256Sha256::public_key(&key, &mut pk).unwrap();
618
619 let mut zero_r = [0u8; 64];
620 zero_r[63] = 1;
621 assert!(EcdsaP256Sha256::verify(&pk, b"m", &zero_r).is_err());
622
623 let mut zero_s = [0u8; 64];
624 zero_s[31] = 1;
625 assert!(EcdsaP256Sha256::verify(&pk, b"m", &zero_s).is_err());
626
627 let n_bytes =
628 unhex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551").unwrap();
629 let mut at_n = [0u8; 64];
630 at_n[..32].copy_from_slice(&n_bytes);
631 at_n[32..].copy_from_slice(&n_bytes);
632 assert!(EcdsaP256Sha256::verify(&pk, b"m", &at_n).is_err());
633 }
634
635 #[test]
636 fn signing_rejects_invalid_private_keys() {
637 let mut sig = [0u8; 64];
638 assert!(
639 EcdsaP256Sha256::sign(&[0u8; 32], b"m", &mut sig).is_err(),
640 "zero"
641 );
642 assert!(
643 EcdsaP256Sha256::sign(&[0xffu8; 32], b"m", &mut sig).is_err(),
644 ">= n"
645 );
646 assert!(
647 EcdsaP256Sha256::sign(&[1u8; 31], b"m", &mut sig).is_err(),
648 "short"
649 );
650 }
651
652 #[test]
654 fn malleability_and_normalization() {
655 let key = unhex(KEY).unwrap();
656 let mut pk = [0u8; 65];
657 EcdsaP256Sha256::public_key(&key, &mut pk).unwrap();
658 let mut sig = [0u8; 64];
659 EcdsaP256Sha256::sign(&key, b"sample", &mut sig).unwrap();
660 assert!(
661 !EcdsaP256Sha256::has_low_s(&sig).unwrap(),
662 "RFC 6979 s is high here"
663 );
664
665 let mut flipped = sig;
666 EcdsaP256Sha256::normalize_s(&mut flipped).unwrap();
667 assert_ne!(flipped, sig);
668 EcdsaP256Sha256::verify(&pk, b"sample", &flipped).unwrap();
669 assert!(EcdsaP256Sha256::has_low_s(&flipped).unwrap());
670
671 let mut twice = flipped;
672 EcdsaP256Sha256::normalize_s(&mut twice).unwrap();
673 assert_eq!(twice, flipped, "normalization must be idempotent");
674 }
675
676 #[test]
677 fn ecdsa_self_test_passes() {
678 EcdsaP256Sha256::self_test().unwrap();
679 }
680
681 #[test]
685 fn cavp_ecc_cdh_vector() {
686 let d = unhex("7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534").unwrap();
687 let mut peer = vec![0x04u8];
688 peer.extend_from_slice(
689 &unhex("700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287").unwrap(),
690 );
691 peer.extend_from_slice(
692 &unhex("db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac").unwrap(),
693 );
694 let mut z = [0u8; 32];
695 EcdhP256::agree(&d, &peer, &mut z).unwrap();
696 assert_eq!(
697 hex(&z),
698 "46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b"
699 );
700 }
701
702 #[test]
703 fn both_parties_derive_the_same_secret() {
704 let (alice, bob) = ([0x11u8; 32], [0x22u8; 32]);
705 let mut alice_pk = [0u8; 65];
706 let mut bob_pk = [0u8; 65];
707 EcdhP256::public_key(&alice, &mut alice_pk).unwrap();
708 EcdhP256::public_key(&bob, &mut bob_pk).unwrap();
709
710 let mut z1 = [0u8; 32];
711 let mut z2 = [0u8; 32];
712 EcdhP256::agree(&alice, &bob_pk, &mut z1).unwrap();
713 EcdhP256::agree(&bob, &alice_pk, &mut z2).unwrap();
714 assert_eq!(z1, z2);
715 assert_ne!(z1, [0u8; 32]);
716 }
717
718 #[test]
719 fn compressed_and_uncompressed_peers_agree() {
720 let (alice, bob) = ([0x33u8; 32], [0x44u8; 32]);
721 let mut unc = [0u8; 65];
722 let mut comp = [0u8; 33];
723 EcdhP256::public_key(&bob, &mut unc).unwrap();
724 EcdhP256::public_key_compressed(&bob, &mut comp).unwrap();
725
726 let mut z1 = [0u8; 32];
727 let mut z2 = [0u8; 32];
728 EcdhP256::agree(&alice, &unc, &mut z1).unwrap();
729 EcdhP256::agree(&alice, &comp, &mut z2).unwrap();
730 assert_eq!(z1, z2, "the peer key encoding must not matter");
731 }
732
733 #[test]
734 fn ecdh_rejects_invalid_inputs() {
735 let alice = [0x11u8; 32];
736 let mut z = [0u8; 32];
737 assert!(EcdhP256::agree(&alice, &[0u8; 65], &mut z).is_err());
738 assert!(EcdhP256::agree(&alice, &[], &mut z).is_err());
739
740 let mut bob_pk = [0u8; 65];
741 EcdhP256::public_key(&[0x22u8; 32], &mut bob_pk).unwrap();
742 bob_pk[64] ^= 1;
743 assert!(
744 EcdhP256::agree(&alice, &bob_pk, &mut z).is_err(),
745 "off curve"
746 );
747
748 let mut pk = [0u8; 65];
749 assert!(EcdhP256::public_key(&[0u8; 32], &mut pk).is_err());
750 assert!(EcdhP256::public_key(&[0xffu8; 32], &mut pk).is_err());
751 }
752
753 #[test]
754 fn ecdh_self_test_passes() {
755 EcdhP256::self_test().unwrap();
756 }
757}