1use constant_time_eq::constant_time_eq;
2
3use super::curve25519::{FieldElement, U256};
4use crate::{EllipticCurveError, Hasher, sha2::Sha512};
5
6pub const SECRET_KEY_SIZE: usize = 32;
7pub const PUBLIC_KEY_SIZE: usize = 32;
8pub const SIGNATURE_SIZE: usize = 64;
9
10const MODULUS_L: U256 = U256::from_limbs([
11 0x5812_631a_5cf5_d3ed,
12 0x14de_f9de_a2f7_9cd6,
13 0x0000_0000_0000_0000,
14 0x1000_0000_0000_0000,
15]);
16
17const EDWARDS_D: FieldElement = FieldElement(U256::from_limbs([
25 0x75eb_4dca_1359_78a3,
26 0x0070_0a4d_4141_d8ab,
27 0x8cc7_4079_7779_e898,
28 0x5203_6cee_2b6f_fe73,
29]));
30
31const R: U256 = U256::from_limbs([
33 0xd6ec_3174_8d98_951d,
34 0xc6ef_5bf4_737d_cf70,
35 0xffff_ffff_ffff_fffe,
36 0x0fff_ffff_ffff_ffff,
37]);
38
39const BAR_MU: [u64; 5] = [
41 0xed9c_e5a3_0a2c_131b,
42 0x2106_215d_0863_29a7,
43 0xffff_ffff_ffff_ffeb,
44 0xffff_ffff_ffff_ffff,
45 0x0000_0000_0000_000f,
46];
47
48const EDWARDS_2D: FieldElement = FieldElement(U256::from_limbs([
49 0xebd6_9b94_26b2_f159,
50 0x00e0_149a_8283_b156,
51 0x198e_80f2_eef3_d130,
52 0x2406_d9dc_56df_fce7,
53]));
54
55const SQRT_M1: FieldElement = FieldElement(U256::from_limbs([
56 0xc4ee_1b27_4a0e_a0b0,
57 0x2f43_1806_ad2f_e478,
58 0x2b4d_0099_3dfb_d7a7,
59 0x2b83_2480_4fc1_df0b,
60]));
61
62const BASEPOINT: EdwardsPoint = EdwardsPoint {
63 x: FieldElement(U256::from_limbs([
64 0xc9562d608f25d51a,
65 0x692cc7609525a7b2,
66 0xc0a4e231fdd6dc5c,
67 0x216936d3cd6e53fe,
68 ])),
69 y: FieldElement(U256::from_limbs([
70 0x6666666666666658,
71 0x6666666666666666,
72 0x6666666666666666,
73 0x6666666666666666,
74 ])),
75 z: FieldElement(U256::from_limbs([
76 0x0000000000000001,
77 0x0000000000000000,
78 0x0000000000000000,
79 0x0000000000000000,
80 ])),
81 t: FieldElement(U256::from_limbs([
82 0x6dde8ab3a5b7dda3,
83 0x20f09f80775152f5,
84 0x66ea4e8e64abe37d,
85 0x67875f0fd78b7665,
86 ])),
87};
88
89#[derive(Clone, Debug, PartialEq, Eq)]
112pub struct SecretKey {
113 seed: [u8; SECRET_KEY_SIZE],
114 scalar: Scalar,
115 prefix: [u8; 32],
116 public_point: EdwardsPoint,
117 public_bytes: [u8; PUBLIC_KEY_SIZE],
118}
119
120impl SecretKey {
121 #[cfg(feature = "random")]
122 pub fn generate() -> SecretKey {
123 let seed: [u8; SECRET_KEY_SIZE] = crate::random::random_bytes();
124 SecretKey::from_bytes(&seed)
125 }
126
127 pub fn from_bytes(seed: &[u8; SECRET_KEY_SIZE]) -> SecretKey {
128 let (scalar, prefix) = expand_secret(seed);
129 let public_point = scalar_mul_base(&scalar);
130 let public_bytes = public_point
131 .to_bytes()
132 .expect("basepoint multiplication must produce a valid point");
133 SecretKey {
134 seed: *seed,
135 scalar,
136 prefix,
137 public_point,
138 public_bytes,
139 }
140 }
141
142 pub fn sign(&self, message: &[u8]) -> [u8; SIGNATURE_SIZE] {
147 let r = hash_to_scalar(&[&self.prefix, message]);
148 let r_point = scalar_mul_base(&r)
149 .to_bytes()
150 .expect("basepoint multiplication must produce a valid point");
151
152 let k = hash_to_scalar(&[&r_point, &self.public_bytes, message]);
153 let s = r.add(k.mul(self.scalar));
154
155 let mut signature = [0u8; SIGNATURE_SIZE];
156 signature[..32].copy_from_slice(&r_point);
157 signature[32..].copy_from_slice(&s.to_bytes());
158 signature
159 }
160
161 #[inline]
162 pub fn to_bytes(&self) -> [u8; SECRET_KEY_SIZE] {
163 self.seed
164 }
165
166 #[inline]
167 pub fn public_key(&self) -> PublicKey {
168 PublicKey {
169 point: self.public_point,
170 bytes: self.public_bytes,
171 }
172 }
173}
174
175impl From<&[u8; SECRET_KEY_SIZE]> for SecretKey {
176 fn from(bytes: &[u8; SECRET_KEY_SIZE]) -> Self {
177 Self::from_bytes(bytes)
178 }
179}
180
181impl TryFrom<&[u8]> for SecretKey {
182 type Error = EllipticCurveError;
183
184 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
185 Ok(Self::from_bytes(bytes.try_into().map_err(|_| EllipticCurveError::InvalidKey)?))
186 }
187}
188
189#[derive(Clone, Debug, PartialEq, Eq)]
211pub struct PublicKey {
212 point: EdwardsPoint,
213 bytes: [u8; PUBLIC_KEY_SIZE],
214}
215
216impl PublicKey {
217 pub fn from_bytes(key: &[u8; PUBLIC_KEY_SIZE]) -> Result<PublicKey, EllipticCurveError> {
218 let point = EdwardsPoint::from_bytes(key.try_into().unwrap()).ok_or(EllipticCurveError::InvalidKey)?;
219 Ok(PublicKey {
220 point,
221 bytes: *key,
222 })
223 }
224
225 pub fn verify(&self, message: &[u8], signature: &[u8; SIGNATURE_SIZE]) -> Result<(), EllipticCurveError> {
226 ed25519_verify(&self.point, message, signature)
227 }
228
229 #[inline]
230 pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_SIZE] {
231 self.bytes
232 }
233
234 pub fn to_montgomery_u(&self) -> Option<FieldElement> {
235 let inv_z = self.point.z.invert()?;
236 let y = self.point.y.mul(inv_z);
237 let one = FieldElement::ONE;
238 let u = (one.add(y)).mul((one.sub(y)).invert()?);
239 Some(u)
240 }
241}
242
243impl TryFrom<&[u8]> for PublicKey {
244 type Error = EllipticCurveError;
245
246 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
247 Self::from_bytes(bytes.try_into().map_err(|_| EllipticCurveError::InvalidKey)?)
248 }
249}
250
251#[derive(Clone, Copy, Debug, PartialEq, Eq)]
252struct Scalar(U256);
253
254impl Scalar {
255 fn from_canonical_bytes(bytes: &[u8; 32]) -> Option<Self> {
256 let value = U256::from_le_slice(bytes);
257 if value.ct_ge(&MODULUS_L) {
258 None
259 } else {
260 Some(Self(value))
261 }
262 }
263
264 fn reduce_bytes_mod_l(bytes: &[u8]) -> Self {
273 let len = bytes.len();
274 if len <= 32 {
275 let mut padded = [0u8; 32];
276 padded[..len].copy_from_slice(bytes);
277 let val = U256::from_le_slice(&padded);
278 let mut result = val;
279 let mut i = 16;
280 while i > 0 {
281 let (diff, borrow) = result.sub_raw(&MODULUS_L);
282 result = U256::ct_select(&diff, &result, borrow == 0);
283 i -= 1;
284 }
285 Self(result)
286 } else {
287 let chunk_count = (len + 31) / 32;
288 let mut acc = U256::ZERO;
289 let mut chunk_idx = chunk_count;
290 while chunk_idx > 0 {
291 chunk_idx -= 1;
292 let chunk_start = chunk_idx * 32;
293 let chunk_end = usize::min(chunk_start + 32, len);
294 let chunk_len = chunk_end - chunk_start;
295
296 if !acc.is_zero() {
297 acc = acc.mul_mod_barrett(&R, &MODULUS_L, &BAR_MU);
298 }
299
300 let mut padded = [0u8; 32];
301 padded[..chunk_len].copy_from_slice(&bytes[chunk_start..chunk_end]);
302 let mut chunk_reduced = U256::from_le_slice(&padded);
303 let mut j = 16;
304 while j > 0 {
305 let (diff, borrow) = chunk_reduced.sub_raw(&MODULUS_L);
306 chunk_reduced = U256::ct_select(&diff, &chunk_reduced, borrow == 0);
307 j -= 1;
308 }
309
310 acc = acc.add_mod(&chunk_reduced, &MODULUS_L);
311 }
312 Self(acc)
313 }
314 }
315
316 #[inline]
317 fn to_bytes(self) -> [u8; 32] {
318 self.0.to_le_bytes_fixed::<32>()
319 }
320
321 #[inline]
322 fn add(self, rhs: Self) -> Self {
323 Self(self.0.add_mod(&rhs.0, &MODULUS_L))
324 }
325
326 #[inline]
327 fn mul(self, rhs: Self) -> Self {
328 Self(self.0.mul_mod_barrett(&rhs.0, &MODULUS_L, &BAR_MU))
329 }
330}
331
332#[derive(Clone, Copy, Debug, PartialEq, Eq)]
333struct EdwardsPoint {
334 x: FieldElement,
335 y: FieldElement,
336 z: FieldElement,
337 t: FieldElement,
338}
339
340impl EdwardsPoint {
341 #[inline]
342 fn identity() -> Self {
343 Self {
344 x: FieldElement::ZERO,
345 y: FieldElement::ONE,
346 z: FieldElement::ONE,
347 t: FieldElement::ZERO,
348 }
349 }
350
351 #[inline]
352 fn from_affine(x: FieldElement, y: FieldElement) -> Self {
353 Self {
354 x,
355 y,
356 z: FieldElement::ONE,
357 t: x.mul(y),
358 }
359 }
360
361 fn from_bytes(bytes: &[u8; 32]) -> Option<Self> {
362 let sign = (bytes[31] >> 7) == 1;
363 let mut y_bytes = *bytes;
364 y_bytes[31] &= 0x7f;
365 let y = FieldElement::from_canonical_bytes(&y_bytes)?;
366 let y2 = y.square();
367 let u = y2.sub(FieldElement::ONE);
368 let v = EDWARDS_D.mul(y2).add(FieldElement::ONE);
369 let x2 = u.mul(v.invert()?);
370 let mut x = sqrt(&x2)?;
371 if x.is_zero() && sign {
372 return None;
373 }
374 if x.is_odd() != sign {
375 x = x.negate();
376 }
377 Some(Self::from_affine(x, y))
378 }
379
380 fn to_bytes(self) -> Option<[u8; 32]> {
381 let inv_z = self.z.invert()?;
382 let x = self.x.mul(inv_z);
383 let y = self.y.mul(inv_z);
384 let mut out = y.to_bytes();
385 if x.is_odd() {
386 out[31] |= 0x80;
387 }
388 Some(out)
389 }
390
391 #[inline]
392 fn add(&self, rhs: &Self) -> Self {
393 let a = self.y.sub(self.x).mul(rhs.y.sub(rhs.x));
394 let b = self.y.add(self.x).mul(rhs.y.add(rhs.x));
395 let c = self.t.mul(rhs.t).mul(EDWARDS_2D);
396 let z1z2 = self.z.mul(rhs.z);
397 let d = z1z2.add(z1z2);
398 let e = b.sub(a);
399 let f = d.sub(c);
400 let g = d.add(c);
401 let h = b.add(a);
402 Self {
403 x: e.mul(f),
404 y: g.mul(h),
405 t: e.mul(h),
406 z: f.mul(g),
407 }
408 }
409
410 #[inline]
411 fn double(&self) -> Self {
412 let a = self.x.square();
413 let b = self.y.square();
414 let z2 = self.z.square();
415 let c = z2.add(z2);
416 let d = a.negate();
417 let e = self.x.add(self.y).square().sub(a).sub(b);
418 let g = d.add(b);
419 let f = g.sub(c);
420 let h = d.sub(b);
421 Self {
422 x: e.mul(f),
423 y: g.mul(h),
424 t: e.mul(h),
425 z: f.mul(g),
426 }
427 }
428
429 #[inline]
430 fn select(a: &Self, b: &Self, choice: bool) -> Self {
431 Self {
432 x: FieldElement::select(&a.x, &b.x, choice),
433 y: FieldElement::select(&a.y, &b.y, choice),
434 z: FieldElement::select(&a.z, &b.z, choice),
435 t: FieldElement::select(&a.t, &b.t, choice),
436 }
437 }
438
439 #[inline]
440 fn mul_by_cofactor(&self) -> Self {
441 self.double().double().double()
442 }
443}
444
445#[inline]
446fn sqrt(a: &FieldElement) -> Option<FieldElement> {
447 let mut candidate = a.pow_sqrt_exponent();
448 if !candidate.square().ct_eq(a) {
449 candidate = candidate.mul(SQRT_M1);
450 }
451 if candidate.square().ct_eq(a) {
452 Some(candidate)
453 } else {
454 None
455 }
456}
457
458fn scalar_mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint {
459 let mut table = [EdwardsPoint::identity(); 16];
460 table[1] = *point;
461 let mut i = 2;
462 while i < 16 {
463 table[i] = table[i - 1].add(point);
464 i += 1;
465 }
466
467 scalar_mul_table(&table, scalar)
468}
469
470#[cfg(feature = "std")]
472fn scalar_mul_base(scalar: &Scalar) -> EdwardsPoint {
473 use std::sync::LazyLock;
474 static TABLE: LazyLock<[EdwardsPoint; 16]> = LazyLock::new(|| {
475 let mut t = [EdwardsPoint::identity(); 16];
476 t[1] = BASEPOINT;
477 let mut i = 2;
478 while i < 16 {
479 t[i] = t[i - 1].add(&BASEPOINT);
480 i += 1;
481 }
482 t
483 });
484
485 scalar_mul_table(&TABLE, scalar)
486}
487
488#[cfg(not(feature = "std"))]
489#[inline]
490fn scalar_mul_base(scalar: &Scalar) -> EdwardsPoint {
491 scalar_mul(&BASEPOINT, scalar)
492}
493
494#[inline]
495fn scalar_mul_table(table: &[EdwardsPoint; 16], scalar: &Scalar) -> EdwardsPoint {
496 let mut result = EdwardsPoint::identity();
497 let mut win = 63;
498 loop {
499 let idx = scalar_window(scalar, win);
500 let selected = ct_select_from_table(&table, idx);
501 result = result.add(&selected);
502 if win == 0 {
503 break;
504 }
505 result = result.double().double().double().double();
506 win -= 1;
507 }
508 result
509}
510
511#[inline]
512fn ct_select_from_table(table: &[EdwardsPoint; 16], index: usize) -> EdwardsPoint {
513 let mut result = table[0];
514 let mut i = 1;
515 while i < 16 {
516 let diff = i ^ index;
517 let choice = ((diff.wrapping_sub(1) >> (usize::BITS - 1)) & 1) != 0;
518 result = EdwardsPoint::select(&table[i], &result, choice);
519 i += 1;
520 }
521 result
522}
523
524#[inline]
525fn scalar_window(scalar: &Scalar, window: usize) -> usize {
526 let bit_pos = window * 4;
527 let limb_idx = bit_pos / 64;
528 let limb = scalar.0.limbs[limb_idx];
529 ((limb >> (bit_pos % 64)) & 0xf) as usize
530}
531
532fn hash_to_scalar(parts: &[&[u8]]) -> Scalar {
533 let mut hasher = Sha512::new();
534 let mut i = 0usize;
535 while i < parts.len() {
536 hasher.update(parts[i]);
537 i += 1;
538 }
539 let digest = hasher.sum();
540 Scalar::reduce_bytes_mod_l(digest.as_ref())
541}
542
543fn expand_secret(private_key: &[u8; SECRET_KEY_SIZE]) -> (Scalar, [u8; 32]) {
544 let mut digest = Sha512::hash(private_key);
545 let digest: &mut [u8; 64] = digest.as_mut().try_into().unwrap();
546
547 digest[0] &= 248;
548 digest[31] &= 63;
549 digest[31] |= 64;
550
551 let scalar = Scalar::reduce_bytes_mod_l(&digest[..32]);
552 let mut prefix = [0u8; 32];
553 prefix.copy_from_slice(&digest[32..]);
554 (scalar, prefix)
555}
556
557fn ed25519_verify(
558 point: &EdwardsPoint,
559 message: &[u8],
560 signature: &[u8; SIGNATURE_SIZE],
561) -> Result<(), EllipticCurveError> {
562 let r_bytes: &[u8; 32] = &signature[..32].try_into().unwrap();
563 let r = EdwardsPoint::from_bytes(r_bytes).ok_or(EllipticCurveError::InvalidSignature)?;
564
565 let s =
566 Scalar::from_canonical_bytes(&signature[32..].try_into().unwrap()).ok_or(EllipticCurveError::Unspecified)?;
567
568 let k = hash_to_scalar(&[
569 r_bytes,
570 &point.to_bytes().ok_or(EllipticCurveError::Unspecified)?,
571 message,
572 ]);
573
574 let lhs = scalar_mul_base(&s).mul_by_cofactor();
575 let rhs = r.add(&scalar_mul(point, &k)).mul_by_cofactor();
576
577 match (lhs.to_bytes(), rhs.to_bytes()) {
579 (Some(lhs), Some(rhs)) if constant_time_eq(&lhs, &rhs) => Ok(()),
580 _ => Err(EllipticCurveError::InvalidSignature),
581 }
582}
583
584#[cfg(test)]
585mod tests {
586 use super::*;
587 use crate::curve25519::x25519;
588
589 const BASEPOINT_COMPRESSED: [u8; 32] = [
590 0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
591 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
592 ];
593
594 fn decode_hex<const N: usize>(hex_bytes: &str) -> [u8; N] {
595 let bytes = hex::decode(hex_bytes).unwrap();
596 assert_eq!(bytes.len(), N);
597 let mut out = [0u8; N];
598 out.copy_from_slice(&bytes);
599 out
600 }
601
602 fn decode_hex_vec(hex_bytes: &str) -> Vec<u8> {
603 hex::decode(hex_bytes).unwrap()
604 }
605
606 #[test]
607 fn sign_verify_roundtrip() {
608 let seed = decode_hex::<32>("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60");
609 let priv_key = SecretKey::from_bytes(&seed);
610 let pub_key = priv_key.public_key();
611
612 let messages: [&[u8]; 4] = [b"", b"hello", b"test message", &[0xffu8; 256]];
613 for msg in &messages {
614 let sig = priv_key.sign(msg);
615 assert!(pub_key.verify(msg, &sig).is_ok());
616 assert!(pub_key.verify(b"wrong", &sig).is_err());
617 }
618 }
619
620 #[test]
621 fn public_key_bytes_roundtrip() {
622 let seed = decode_hex::<32>("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60");
623 let priv_key = SecretKey::from_bytes(&seed);
624 let pub_key = priv_key.public_key();
625 let pub_bytes = pub_key.to_bytes();
626 let restored = PublicKey::from_bytes(&pub_bytes).unwrap();
627 assert_eq!(pub_bytes, restored.to_bytes());
628 }
629
630 #[test]
631 fn private_key_bytes_roundtrip() {
632 let seed = decode_hex::<32>("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60");
633 let priv_key = SecretKey::from_bytes(&seed);
634 assert_eq!(priv_key.to_bytes(), seed);
635 }
636
637 #[test]
638 fn generate_produces_valid_keys() {
639 let priv_key = SecretKey::generate();
640 let pub_key = priv_key.public_key();
641 let sig = priv_key.sign(b"hello");
642 assert!(pub_key.verify(b"hello", &sig).is_ok());
643 }
644
645 #[test]
646 fn rejects_invalid_public_key() {
647 assert!(PublicKey::from_bytes(&[0xffu8; 32]).is_err());
648 let p_enc = decode_hex::<32>("edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f");
649 assert!(PublicKey::from_bytes(&p_enc).is_err());
650 }
651
652 fn check_vector(
653 seed_hex: &'static str,
654 public_key_hex: &'static str,
655 message_hex: &'static str,
656 signature_hex: &'static str,
657 ) {
658 let seed = decode_hex::<32>(seed_hex);
659 let pk_expected = decode_hex::<32>(public_key_hex);
660 let sig_expected = decode_hex::<64>(signature_hex);
661 let msg = decode_hex_vec(message_hex);
662
663 let priv_key = SecretKey::from_bytes(&seed);
664 assert_eq!(priv_key.public_key().to_bytes(), pk_expected);
665 assert_eq!(priv_key.sign(&msg), sig_expected);
666 assert!(priv_key.public_key().verify(&msg, &sig_expected).is_ok());
667 }
668
669 #[test]
670 fn rfc8032_vectors() {
671 check_vector(
672 "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
673 "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
674 "",
675 "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b",
676 );
677 check_vector(
678 "4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb",
679 "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c",
680 "72",
681 "92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00",
682 );
683 check_vector(
684 "c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7",
685 "fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025",
686 "af82",
687 "6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a",
688 );
689 }
690
691 #[test]
692 fn go_golden_vectors() {
693 let data = include_str!("../../testdata/ed25519/sign.input");
694
695 for line in data.lines() {
696 let mut parts = line.split(':');
697 let private_and_public = parts.next().unwrap();
698 let public_key = parts.next().unwrap();
699 let message = parts.next().unwrap();
700 let signature_with_message = parts.next().unwrap();
701 assert!(parts.next().is_some());
702 assert!(parts.next().is_none());
703
704 check_vector(&private_and_public[..64], public_key, message, &signature_with_message[..128]);
705 }
706 }
707
708 #[test]
709 fn verify_rejects_tampering_and_non_canonical_s() {
710 let seed = decode_hex::<32>("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60");
711 let pub_key = SecretKey::from_bytes(&seed).public_key();
712 let signature = SecretKey::from_bytes(&seed).sign(b"message");
713
714 assert!(pub_key.verify(b"message", &signature).is_ok());
715 assert!(pub_key.verify(b"tampered", &signature).is_err());
716
717 let mut bad_signature = signature;
718 bad_signature[0] ^= 0x80;
719 assert!(pub_key.verify(b"message", &bad_signature).is_err());
720
721 let mut non_canonical_s = signature;
722 non_canonical_s[32..].copy_from_slice(&[
723 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00,
724 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
725 ]);
726 assert!(pub_key.verify(b"message", &non_canonical_s).is_err());
727 }
728
729 #[test]
730 fn public_key_validation_rejects_invalid_encodings() {
731 let valid = decode_hex::<32>("d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a");
732 assert!(PublicKey::from_bytes(&valid).is_ok());
733
734 let mut invalid = [0xffu8; 32];
735 assert!(PublicKey::from_bytes(&invalid).is_err());
736
737 invalid = valid;
738 invalid[31] |= 0x80;
739 invalid[..31].fill(0);
740 assert!(PublicKey::from_bytes(&invalid).is_err());
741 }
742
743 #[test]
744 fn cctv_ed25519_vectors() {
745 let data = include_str!("../../testdata/ed25519/cctv_vectors.txt");
746
747 for line in data.lines() {
748 let parts: Vec<&str> = line.split(':').collect();
749 assert_eq!(parts.len(), 5, "malformed line: {line}");
750 let number = parts[0];
751 let key_hex = parts[1];
752 let sig_hex = parts[2];
753 let msg_hex = parts[3];
754 let flags_str = parts[4];
755
756 let flags: Vec<&str> = if flags_str.is_empty() {
757 vec![]
758 } else {
759 flags_str.split(',').collect()
760 };
761
762 let has_non_canonical_a = flags.contains(&"non_canonical_A");
763 let has_non_canonical_r = flags.contains(&"non_canonical_R");
764 let should_reject = has_non_canonical_a || has_non_canonical_r;
765
766 let public_key = decode_hex::<32>(key_hex);
767 let signature = decode_hex::<64>(sig_hex);
768 let message = decode_hex_vec(msg_hex);
769
770 let pub_key = PublicKey::from_bytes(&public_key);
771 let result = pub_key.and_then(|pk| pk.verify(&message, &signature));
772
773 if should_reject {
774 assert!(
775 result.is_err(),
776 "vector #{number} should be rejected (flags: {flags_str}) but was accepted",
777 );
778 } else {
779 assert!(
780 result.is_ok(),
781 "vector #{number} should be accepted (flags: {flags_str}) but was rejected",
782 );
783 }
784 }
785 }
786
787 #[test]
788 fn rfc8032_extended_vectors() {
789 check_vector(
790 "833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42",
791 "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf",
792 "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f",
793 "dc2a4459e7369633a52b1bf277839a00201009a3efbf3ecb69bea2186c26b58909351fc9ac90b3ecfdfbc7c66431e0303dca179c138ac17ad9bef1177331a704",
794 );
795 }
796
797 #[test]
798 fn verify_rejects_all_zero_signature() {
799 let public_key = decode_hex::<32>("d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a");
800 let signature = [0u8; 64];
801 let pk = PublicKey::from_bytes(&public_key).unwrap();
802 let result = pk.verify(b"test", &signature);
803 assert!(result.is_err());
804 }
805
806 #[test]
807 fn verify_rejects_s_equals_l() {
808 let seed = decode_hex::<32>("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60");
809 let pub_key = SecretKey::from_bytes(&seed).public_key();
810 let signature = SecretKey::from_bytes(&seed).sign(b"test");
811
812 let mut bad_sig = signature;
813 bad_sig[32..].copy_from_slice(&[
814 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00,
815 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
816 ]);
817 assert!(pub_key.verify(b"test", &bad_sig).is_err());
818 }
819
820 #[test]
821 fn verify_rejects_non_canonical_point_encodings() {
822 let non_canonical_key = decode_hex::<32>("edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f");
823 assert!(PublicKey::from_bytes(&non_canonical_key).is_err());
824
825 let seed = decode_hex::<32>("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60");
826 let pub_key = SecretKey::from_bytes(&seed).public_key();
827 let mut bad_sig = SecretKey::from_bytes(&seed).sign(b"test");
828 bad_sig[..32].copy_from_slice(&[
829 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
830 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
831 ]);
832 assert!(pub_key.verify(b"test", &bad_sig).is_err());
833 }
834
835 #[test]
836 fn edwards_identity_point_roundtrip() {
837 let id_bytes = [
838 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
839 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
840 ];
841 let point = EdwardsPoint::from_bytes(&id_bytes).unwrap();
842 let roundtripped = point.to_bytes().unwrap();
843 assert_eq!(roundtripped, id_bytes);
844 }
845
846 #[test]
847 fn ed25519_to_montgomery_u_conversion() {
848 let seed = decode_hex::<32>("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60");
849 let priv_key = SecretKey::from_bytes(&seed);
850 let ed_pub = priv_key.public_key();
851
852 let u = ed_pub
853 .to_montgomery_u()
854 .expect("valid ed point must convert to montgomery u");
855 let u_bytes = u.to_bytes();
856
857 let x_pub_from_ed = x25519::PublicKey::try_from(&ed_pub).unwrap();
858 assert_eq!(u_bytes, x_pub_from_ed.to_bytes());
859
860 assert_ne!(u_bytes, [0u8; 32], "montgomery u must be non-zero for non-identity point");
861 }
862
863 #[test]
864 fn rfc8032_test_1024() {
865 check_vector(
866 "f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5",
867 "278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e",
868 "08b8b2b733424243760fe426a4b54908632110a66c2f6591eabd3345e3e4eb98fa6e264bf09efe12ee50f8f54e9f77b1e355f6c50544e23fb1433ddf73be84d879de7c0046dc4996d9e773f4bc9efe5738829adb26c81b37c93a1b270b20329d658675fc6ea534e0810a4432826bf58c941efb65d57a338bbd2e26640f89ffbc1a858efcb8550ee3a5e1998bd177e93a7363c344fe6b199ee5d02e82d522c4feba15452f80288a821a579116ec6dad2b3b310da903401aa62100ab5d1a36553e06203b33890cc9b832f79ef80560ccb9a39ce767967ed628c6ad573cb116dbefefd75499da96bd68a8a97b928a8bbc103b6621fcde2beca1231d206be6cd9ec7aff6f6c94fcd7204ed3455c68c83f4a41da4af2b74ef5c53f1d8ac70bdcb7ed185ce81bd84359d44254d95629e9855a94a7c1958d1f8ada5d0532ed8a5aa3fb2d17ba70eb6248e594e1a2297acbbb39d502f1a8c6eb6f1ce22b3de1a1f40cc24554119a831a9aad6079cad88425de6bde1a9187ebb6092cf67bf2b13fd65f27088d78b7e883c8759d2c4f5c65adb7553878ad575f9fad878e80a0c9ba63bcbcc2732e69485bbc9c90bfbd62481d9089beccf80cfe2df16a2cf65bd92dd597b0707e0917af48bbb75fed413d238f5555a7a569d80c3414a8d0859dc65a46128bab27af87a71314f318c782b23ebfe808b82b0ce26401d2e22f04d83d1255dc51addd3b75a2b1ae0784504df543af8969be3ea7082ff7fc9888c144da2af58429ec96031dbcad3dad9af0dcbaaaf268cb8fcffead94f3c7ca495e056a9b47acdb751fb73e666c6c655ade8297297d07ad1ba5e43f1bca32301651339e22904cc8c42f58c30c04aafdb038dda0847dd988dcda6f3bfd15c4b4c4525004aa06eeff8ca61783aacec57fb3d1f92b0fe2fd1a85f6724517b65e614ad6808d6f6ee34dff7310fdc82aebfd904b01e1dc54b2927094b2db68d6f903b68401adebf5a7e08d78ff4ef5d63653a65040cf9bfd4aca7984a74d37145986780fc0b16ac451649de6188a7dbdf191f64b5fc5e2ab47b57f7f7276cd419c17a3ca8e1b939ae49e488acba6b965610b5480109c8b17b80e1b7b750dfc7598d5d5011fd2dcc5600a32ef5b52a1ecc820e308aa342721aac0943bf6686b64b2579376504ccc493d97e6aed3fb0f9cd71a43dd497f01f17c0e2cb3797aa2a2f256656168e6c496afc5fb93246f6b1116398a346f1a641f3b041e989f7914f90cc2c7fff357876e506b50d334ba77c225bc307ba537152f3f1610e4eafe595f6d9d90d11faa933a15ef1369546868a7f3a45a96768d40fd9d03412c091c6315cf4fde7cb68606937380db2eaaa707b4c4185c32eddcdd306705e4dc1ffc872eeee475a64dfac86aba41c0618983f8741c5ef68d3a101e8a3b8cac60c905c15fc910840b94c00a0b9d0",
869 "0aab4c900501b3e24d7cdf4663326a3a87df5e4843b2cbdb67cbf6e460fec350aa5371b1508f9f4528ecea23c436d94b5e8fcd4f681e30a6ac00a9704a188a03",
870 );
871 }
872
873 #[test]
874 fn wycheproof_ed25519_vectors() {
875 #[derive(serde::Deserialize)]
876 struct TestJson {
877 #[serde(rename = "testGroups")]
878 test_groups: Vec<TestGroup>,
879 }
880
881 #[derive(serde::Deserialize)]
882 struct TestGroup {
883 #[serde(rename = "publicKey")]
884 public_key: PublicKeyJson,
885 tests: Vec<TestCase>,
886 }
887
888 #[derive(serde::Deserialize)]
889 struct PublicKeyJson {
890 pk: String,
891 }
892
893 #[derive(serde::Deserialize)]
894 struct TestCase {
895 #[serde(rename = "tcId")]
896 tc_id: u32,
897 msg: String,
900 sig: String,
901 result: String,
902 }
903
904 let data = include_str!("../../testdata/wycheproof/testvectors_v1/ed25519_test.json");
905 let parsed: TestJson = serde_json::from_str(data).unwrap();
906
907 let mut valid_tested = 0usize;
908 let mut invalid_tested = 0usize;
909 let mut skipped = 0usize;
910
911 for group in &parsed.test_groups {
912 let public_key = decode_hex::<32>(&group.public_key.pk);
913 let pk = PublicKey::from_bytes(&public_key).unwrap();
914
915 for test in &group.tests {
916 let msg = decode_hex_vec(&test.msg);
917 let sig_hex = &test.sig;
918 if sig_hex.len() != 128 {
919 skipped += 1;
920 continue;
921 }
922 let signature = decode_hex::<64>(sig_hex);
923 let should_be_valid = test.result == "valid";
924
925 let result = pk.verify(&msg, &signature);
926 if should_be_valid {
927 assert!(
928 result.is_ok(),
929 "Wycheproof test #{}: expected valid but got {:?}",
930 test.tc_id,
931 result,
932 );
933 valid_tested += 1;
934 } else {
935 assert!(result.is_err(), "Wycheproof test #{}: expected invalid but got ok", test.tc_id,);
936 invalid_tested += 1;
937 }
938 }
939 }
940
941 assert!(valid_tested > 0, "must test at least one valid Wycheproof vector");
942 assert!(invalid_tested > 0, "must test at least one invalid Wycheproof vector");
943 assert!(skipped > 0, "some truncated signatures should be skipped");
944
945 eprintln!("Wycheproof ed25519: {valid_tested} valid, {invalid_tested} invalid, {skipped} skipped");
946 }
947
948 #[test]
949 fn sign_verify_roundtrip_various_lengths() {
950 let seed = decode_hex::<32>("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60");
951 let priv_key = SecretKey::from_bytes(&seed);
952 let pub_key = priv_key.public_key();
953
954 for len in [0, 1, 2, 16, 32, 64, 128, 255, 256, 1024] {
955 let message: Vec<u8> = (0..len).map(|i| (i & 0xff) as u8).collect();
956 let signature = priv_key.sign(&message);
957 assert!(
958 pub_key.verify(&message, &signature).is_ok(),
959 "roundtrip failed for message length {len}"
960 );
961 }
962 }
963
964 #[test]
965 fn fixed_window_matches_scalar_mul() {
966 for _ in 0..100 {
967 let s = Scalar(U256::from_limbs([
968 rand::random::<u64>(),
969 rand::random::<u64>(),
970 rand::random::<u64>(),
971 rand::random::<u64>(),
972 ]));
973 let s = Scalar(s.0.add_mod(&U256::ZERO, &MODULUS_L));
974 let old_result = scalar_mul(&BASEPOINT, &s);
975 let new_result = scalar_mul_base(&s);
976 assert_eq!(old_result.to_bytes(), new_result.to_bytes(), "mismatch for scalar={:x}", s.0,);
977 }
978 }
979
980 #[test]
981 fn mul_mod_barrett_agrees_with_mul_mod() {
982 for _ in 0..100 {
983 let a = U256::from_limbs([
984 rand::random::<u64>(),
985 rand::random::<u64>(),
986 rand::random::<u64>(),
987 rand::random::<u64>(),
988 ]);
989 let b = U256::from_limbs([
990 rand::random::<u64>(),
991 rand::random::<u64>(),
992 rand::random::<u64>(),
993 rand::random::<u64>(),
994 ]);
995 let slow = a.mul_mod(&b, &MODULUS_L);
996 let fast = a.mul_mod_barrett(&b, &MODULUS_L, &BAR_MU);
997 assert_eq!(slow, fast, "mismatch for a={a:x}, b={b:x}");
998 }
999 }
1000
1001 #[test]
1002 fn basepoint() {
1003 assert_eq!(BASEPOINT, EdwardsPoint::from_bytes(&BASEPOINT_COMPRESSED).unwrap())
1004 }
1005}