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, PartialEq, Eq)]
111#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop))]
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")]
126 pub fn generate() -> Result<SecretKey, EllipticCurveError> {
127 let seed: [u8; SECRET_KEY_SIZE] = crate::random::bytes()?;
128 Ok(SecretKey::from_bytes(&seed))
129 }
130
131 pub fn from_bytes(seed: &[u8; SECRET_KEY_SIZE]) -> SecretKey {
132 let (scalar, prefix) = expand_secret(seed);
133 let public_point = scalar_mul_base(&scalar);
134 let public_bytes = public_point
135 .to_bytes()
136 .expect("basepoint multiplication must produce a valid point");
137 SecretKey {
138 seed: *seed,
139 scalar,
140 prefix,
141 public_point,
142 public_bytes,
143 }
144 }
145
146 pub fn sign(&self, message: &[u8]) -> [u8; SIGNATURE_SIZE] {
151 let r = hash_to_scalar(&[&self.prefix, message]);
152 let r_point = scalar_mul_base(&r)
153 .to_bytes()
154 .expect("basepoint multiplication must produce a valid point");
155
156 let k = hash_to_scalar(&[&r_point, &self.public_bytes, message]);
157 let s = r.add(k.mul(self.scalar));
158
159 let mut signature = [0u8; SIGNATURE_SIZE];
160 signature[..32].copy_from_slice(&r_point);
161 signature[32..].copy_from_slice(&s.to_bytes());
162 signature
163 }
164
165 #[inline]
166 pub fn to_bytes(&self) -> [u8; SECRET_KEY_SIZE] {
167 self.seed
168 }
169
170 #[inline]
171 pub fn public_key(&self) -> PublicKey {
172 PublicKey {
173 point: self.public_point,
174 bytes: self.public_bytes,
175 }
176 }
177}
178
179impl From<&[u8; SECRET_KEY_SIZE]> for SecretKey {
180 fn from(bytes: &[u8; SECRET_KEY_SIZE]) -> Self {
181 Self::from_bytes(bytes)
182 }
183}
184
185impl TryFrom<&[u8]> for SecretKey {
186 type Error = EllipticCurveError;
187
188 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
189 Ok(Self::from_bytes(bytes.try_into().map_err(|_| EllipticCurveError::InvalidKey)?))
190 }
191}
192
193#[derive(Clone, Debug, PartialEq, Eq)]
221pub struct PublicKey {
222 point: EdwardsPoint,
223 bytes: [u8; PUBLIC_KEY_SIZE],
224}
225
226impl PublicKey {
227 pub fn from_bytes(key: &[u8; PUBLIC_KEY_SIZE]) -> Result<PublicKey, EllipticCurveError> {
234 let point = EdwardsPoint::from_bytes(key.try_into().unwrap()).ok_or(EllipticCurveError::InvalidKey)?;
235 if point.is_small_order() {
236 return Err(EllipticCurveError::InvalidKey);
237 }
238 Ok(PublicKey {
239 point,
240 bytes: *key,
241 })
242 }
243
244 pub fn verify(&self, message: &[u8], signature: &[u8; SIGNATURE_SIZE]) -> Result<(), EllipticCurveError> {
245 ed25519_verify(&self.point, message, signature)
246 }
247
248 #[inline]
249 pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_SIZE] {
250 self.bytes
251 }
252
253 pub fn to_montgomery_u(&self) -> Option<FieldElement> {
254 let inv_z = self.point.z.invert()?;
255 let y = self.point.y.mul(inv_z);
256 let one = FieldElement::ONE;
257 let u = (one.add(y)).mul((one.sub(y)).invert()?);
258 Some(u)
259 }
260}
261
262impl TryFrom<&[u8]> for PublicKey {
263 type Error = EllipticCurveError;
264
265 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
266 Self::from_bytes(bytes.try_into().map_err(|_| EllipticCurveError::InvalidKey)?)
267 }
268}
269
270#[derive(Clone, Copy, Debug, PartialEq, Eq)]
271#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize))]
272struct Scalar(U256);
273
274impl Scalar {
275 fn from_canonical_bytes(bytes: &[u8; 32]) -> Option<Self> {
276 let value = U256::from_le_slice(bytes);
277 if value.ct_ge(&MODULUS_L) {
278 None
279 } else {
280 Some(Self(value))
281 }
282 }
283
284 fn reduce_bytes_mod_l(bytes: &[u8]) -> Self {
293 let len = bytes.len();
294 if len <= 32 {
295 let mut padded = [0u8; 32];
296 padded[..len].copy_from_slice(bytes);
297 let val = U256::from_le_slice(&padded);
298 let mut result = val;
299 let mut i = 16;
300 while i > 0 {
301 let (diff, borrow) = result.sub_raw(&MODULUS_L);
302 result = U256::ct_select(&diff, &result, borrow == 0);
303 i -= 1;
304 }
305 Self(result)
306 } else {
307 let chunk_count = len.div_ceil(32);
308 let mut acc = U256::ZERO;
309 let mut chunk_idx = chunk_count;
310 while chunk_idx > 0 {
311 chunk_idx -= 1;
312 let chunk_start = chunk_idx * 32;
313 let chunk_end = usize::min(chunk_start + 32, len);
314 let chunk_len = chunk_end - chunk_start;
315
316 if !acc.is_zero() {
317 acc = acc.mul_mod_barrett_w::<10>(&R, &MODULUS_L, &BAR_MU);
318 }
319
320 let mut padded = [0u8; 32];
321 padded[..chunk_len].copy_from_slice(&bytes[chunk_start..chunk_end]);
322 let mut chunk_reduced = U256::from_le_slice(&padded);
323 let mut j = 16;
324 while j > 0 {
325 let (diff, borrow) = chunk_reduced.sub_raw(&MODULUS_L);
326 chunk_reduced = U256::ct_select(&diff, &chunk_reduced, borrow == 0);
327 j -= 1;
328 }
329
330 acc = acc.add_mod(&chunk_reduced, &MODULUS_L);
331 }
332 Self(acc)
333 }
334 }
335
336 #[inline]
337 fn to_bytes(self) -> [u8; 32] {
338 self.0.to_le_bytes_fixed::<32>()
339 }
340
341 #[inline]
342 fn add(self, rhs: Self) -> Self {
343 Self(self.0.add_mod(&rhs.0, &MODULUS_L))
344 }
345
346 #[inline]
347 fn mul(self, rhs: Self) -> Self {
348 Self(self.0.mul_mod_barrett_w::<10>(&rhs.0, &MODULUS_L, &BAR_MU))
349 }
350}
351
352#[derive(Clone, Copy, Debug, PartialEq, Eq)]
353#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize))]
354struct EdwardsPoint {
355 x: FieldElement,
356 y: FieldElement,
357 z: FieldElement,
358 t: FieldElement,
359}
360
361impl EdwardsPoint {
362 #[inline]
363 fn identity() -> Self {
364 Self {
365 x: FieldElement::ZERO,
366 y: FieldElement::ONE,
367 z: FieldElement::ONE,
368 t: FieldElement::ZERO,
369 }
370 }
371
372 #[inline]
373 fn from_affine(x: FieldElement, y: FieldElement) -> Self {
374 Self {
375 x,
376 y,
377 z: FieldElement::ONE,
378 t: x.mul(y),
379 }
380 }
381
382 fn from_bytes(bytes: &[u8; 32]) -> Option<Self> {
383 let sign = (bytes[31] >> 7) == 1;
384 let mut y_bytes = *bytes;
385 y_bytes[31] &= 0x7f;
386 let y = FieldElement::from_canonical_bytes(&y_bytes)?;
387 let y2 = y.square();
388 let u = y2.sub(FieldElement::ONE);
389 let v = EDWARDS_D.mul(y2).add(FieldElement::ONE);
390 let x2 = u.mul(v.invert()?);
391 let mut x = sqrt(&x2)?;
392 if x.is_zero() && sign {
393 return None;
394 }
395 if x.is_odd() != sign {
396 x = x.negate();
397 }
398 Some(Self::from_affine(x, y))
399 }
400
401 fn to_bytes(self) -> Option<[u8; 32]> {
402 let inv_z = self.z.invert()?;
403 let x = self.x.mul(inv_z);
404 let y = self.y.mul(inv_z);
405 let mut out = y.to_bytes();
406 if x.is_odd() {
407 out[31] |= 0x80;
408 }
409 Some(out)
410 }
411
412 #[inline]
413 fn add(&self, rhs: &Self) -> Self {
414 let a = self.y.sub(self.x).mul(rhs.y.sub(rhs.x));
415 let b = self.y.add(self.x).mul(rhs.y.add(rhs.x));
416 let c = self.t.mul(rhs.t).mul(EDWARDS_2D);
417 let z1z2 = self.z.mul(rhs.z);
418 let d = z1z2.add(z1z2);
419 let e = b.sub(a);
420 let f = d.sub(c);
421 let g = d.add(c);
422 let h = b.add(a);
423 Self {
424 x: e.mul(f),
425 y: g.mul(h),
426 t: e.mul(h),
427 z: f.mul(g),
428 }
429 }
430
431 #[inline]
432 fn double(&self) -> Self {
433 let a = self.x.square();
434 let b = self.y.square();
435 let z2 = self.z.square();
436 let c = z2.add(z2);
437 let d = a.negate();
438 let e = self.x.add(self.y).square().sub(a).sub(b);
439 let g = d.add(b);
440 let f = g.sub(c);
441 let h = d.sub(b);
442 Self {
443 x: e.mul(f),
444 y: g.mul(h),
445 t: e.mul(h),
446 z: f.mul(g),
447 }
448 }
449
450 #[inline]
451 fn select(a: &Self, b: &Self, choice: bool) -> Self {
452 Self {
453 x: FieldElement::select(&a.x, &b.x, choice),
454 y: FieldElement::select(&a.y, &b.y, choice),
455 z: FieldElement::select(&a.z, &b.z, choice),
456 t: FieldElement::select(&a.t, &b.t, choice),
457 }
458 }
459
460 #[inline]
461 fn mul_by_cofactor(&self) -> Self {
462 self.double().double().double()
463 }
464
465 #[inline]
472 fn is_small_order(&self) -> bool {
473 let p = self.mul_by_cofactor();
474 p.x.is_zero() && p.y.ct_eq(&p.z)
475 }
476}
477
478#[inline]
479fn sqrt(a: &FieldElement) -> Option<FieldElement> {
480 let mut candidate = a.pow_sqrt_exponent();
481 if !candidate.square().ct_eq(a) {
482 candidate = candidate.mul(SQRT_M1);
483 }
484 if candidate.square().ct_eq(a) {
485 Some(candidate)
486 } else {
487 None
488 }
489}
490
491fn scalar_mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint {
492 let mut table = [EdwardsPoint::identity(); 16];
493 table[1] = *point;
494 let mut i = 2;
495 while i < 16 {
496 table[i] = table[i - 1].add(point);
497 i += 1;
498 }
499
500 scalar_mul_table(&table, scalar)
501}
502
503#[cfg(feature = "std")]
505fn scalar_mul_base(scalar: &Scalar) -> EdwardsPoint {
506 use std::sync::LazyLock;
507 static TABLE: LazyLock<[EdwardsPoint; 16]> = LazyLock::new(|| {
508 let mut t = [EdwardsPoint::identity(); 16];
509 t[1] = BASEPOINT;
510 let mut i = 2;
511 while i < 16 {
512 t[i] = t[i - 1].add(&BASEPOINT);
513 i += 1;
514 }
515 t
516 });
517
518 scalar_mul_table(&TABLE, scalar)
519}
520
521#[cfg(not(feature = "std"))]
522#[inline]
523fn scalar_mul_base(scalar: &Scalar) -> EdwardsPoint {
524 scalar_mul(&BASEPOINT, scalar)
525}
526
527#[inline]
528fn scalar_mul_table(table: &[EdwardsPoint; 16], scalar: &Scalar) -> EdwardsPoint {
529 let mut result = EdwardsPoint::identity();
530 let mut win = 63;
531 loop {
532 let idx = scalar_window(scalar, win);
533 let selected = ct_select_from_table(table, idx);
534 result = result.add(&selected);
535 if win == 0 {
536 break;
537 }
538 result = result.double().double().double().double();
539 win -= 1;
540 }
541 result
542}
543
544#[inline]
545fn ct_select_from_table(table: &[EdwardsPoint; 16], index: usize) -> EdwardsPoint {
546 let mut result = table[0];
547 let mut i = 1;
548 while i < 16 {
549 let diff = i ^ index;
550 let choice = ((diff.wrapping_sub(1) >> (usize::BITS - 1)) & 1) != 0;
551 result = EdwardsPoint::select(&table[i], &result, choice);
552 i += 1;
553 }
554 result
555}
556
557#[inline]
558fn scalar_window(scalar: &Scalar, window: usize) -> usize {
559 let bit_pos = window * 4;
560 let limb_idx = bit_pos / 64;
561 let limb = scalar.0.limbs[limb_idx];
562 ((limb >> (bit_pos % 64)) & 0xf) as usize
563}
564
565fn hash_to_scalar(parts: &[&[u8]]) -> Scalar {
566 let mut hasher = Sha512::new();
567 let mut i = 0usize;
568 while i < parts.len() {
569 hasher.update(parts[i]);
570 i += 1;
571 }
572 let digest = hasher.sum();
573 Scalar::reduce_bytes_mod_l(digest.as_ref())
574}
575
576fn expand_secret(private_key: &[u8; SECRET_KEY_SIZE]) -> (Scalar, [u8; 32]) {
577 let mut digest = Sha512::hash(private_key);
578 let digest: &mut [u8; 64] = digest.as_mut().try_into().unwrap();
579
580 digest[0] &= 248;
581 digest[31] &= 63;
582 digest[31] |= 64;
583
584 let scalar = Scalar::reduce_bytes_mod_l(&digest[..32]);
585 let mut prefix = [0u8; 32];
586 prefix.copy_from_slice(&digest[32..]);
587 (scalar, prefix)
588}
589
590fn ed25519_verify(
591 point: &EdwardsPoint,
592 message: &[u8],
593 signature: &[u8; SIGNATURE_SIZE],
594) -> Result<(), EllipticCurveError> {
595 let r_bytes: &[u8; 32] = &signature[..32].try_into().unwrap();
596 let r = EdwardsPoint::from_bytes(r_bytes).ok_or(EllipticCurveError::InvalidSignature)?;
597
598 let s =
599 Scalar::from_canonical_bytes(&signature[32..].try_into().unwrap()).ok_or(EllipticCurveError::Unspecified)?;
600
601 let k = hash_to_scalar(&[
602 r_bytes,
603 &point.to_bytes().ok_or(EllipticCurveError::Unspecified)?,
604 message,
605 ]);
606
607 let lhs = scalar_mul_base(&s);
613 let rhs = r.add(&scalar_mul(point, &k));
614
615 match (lhs.to_bytes(), rhs.to_bytes()) {
618 (Some(lhs), Some(rhs)) if constant_time_eq(&lhs, &rhs) => Ok(()),
619 _ => Err(EllipticCurveError::InvalidSignature),
620 }
621}
622
623#[cfg(test)]
624mod tests {
625 use super::*;
626 use crate::curve25519::x25519;
627
628 #[cfg(feature = "zeroize")]
629 #[test]
630 fn secret_key_zeroize_clears_seed() {
631 use zeroize::Zeroize;
632
633 let mut key = SecretKey::from_bytes(&[1u8; SECRET_KEY_SIZE]);
634 assert_ne!(key.to_bytes(), [0u8; SECRET_KEY_SIZE]);
635 key.zeroize();
636 assert_eq!(key.to_bytes(), [0u8; SECRET_KEY_SIZE]);
637 }
638
639 const BASEPOINT_COMPRESSED: [u8; 32] = [
640 0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
641 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
642 ];
643
644 fn decode_hex<const N: usize>(hex_bytes: &str) -> [u8; N] {
645 let bytes = hex::decode(hex_bytes).unwrap();
646 assert_eq!(bytes.len(), N);
647 let mut out = [0u8; N];
648 out.copy_from_slice(&bytes);
649 out
650 }
651
652 fn decode_hex_vec(hex_bytes: &str) -> Vec<u8> {
653 hex::decode(hex_bytes).unwrap()
654 }
655
656 #[test]
657 fn sign_verify_roundtrip() {
658 let seed = decode_hex::<32>("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60");
659 let priv_key = SecretKey::from_bytes(&seed);
660 let pub_key = priv_key.public_key();
661
662 let messages: [&[u8]; 4] = [b"", b"hello", b"test message", &[0xffu8; 256]];
663 for msg in &messages {
664 let sig = priv_key.sign(msg);
665 assert!(pub_key.verify(msg, &sig).is_ok());
666 assert!(pub_key.verify(b"wrong", &sig).is_err());
667 }
668 }
669
670 #[test]
671 fn public_key_bytes_roundtrip() {
672 let seed = decode_hex::<32>("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60");
673 let priv_key = SecretKey::from_bytes(&seed);
674 let pub_key = priv_key.public_key();
675 let pub_bytes = pub_key.to_bytes();
676 let restored = PublicKey::from_bytes(&pub_bytes).unwrap();
677 assert_eq!(pub_bytes, restored.to_bytes());
678 }
679
680 #[test]
681 fn private_key_bytes_roundtrip() {
682 let seed = decode_hex::<32>("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60");
683 let priv_key = SecretKey::from_bytes(&seed);
684 assert_eq!(priv_key.to_bytes(), seed);
685 }
686
687 #[test]
688 fn generate_produces_valid_keys() {
689 let priv_key = SecretKey::generate().unwrap();
690 let pub_key = priv_key.public_key();
691 let sig = priv_key.sign(b"hello");
692 assert!(pub_key.verify(b"hello", &sig).is_ok());
693 }
694
695 #[test]
696 fn rejects_invalid_public_key() {
697 assert!(PublicKey::from_bytes(&[0xffu8; 32]).is_err());
698 let p_enc = decode_hex::<32>("edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f");
699 assert!(PublicKey::from_bytes(&p_enc).is_err());
700 }
701
702 fn check_vector(
703 seed_hex: &'static str,
704 public_key_hex: &'static str,
705 message_hex: &'static str,
706 signature_hex: &'static str,
707 ) {
708 let seed = decode_hex::<32>(seed_hex);
709 let pk_expected = decode_hex::<32>(public_key_hex);
710 let sig_expected = decode_hex::<64>(signature_hex);
711 let msg = decode_hex_vec(message_hex);
712
713 let priv_key = SecretKey::from_bytes(&seed);
714 assert_eq!(priv_key.public_key().to_bytes(), pk_expected);
715 assert_eq!(priv_key.sign(&msg), sig_expected);
716 assert!(priv_key.public_key().verify(&msg, &sig_expected).is_ok());
717 }
718
719 #[test]
720 fn rfc8032_vectors() {
721 check_vector(
722 "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
723 "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
724 "",
725 "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b",
726 );
727 check_vector(
728 "4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb",
729 "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c",
730 "72",
731 "92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00",
732 );
733 check_vector(
734 "c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7",
735 "fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025",
736 "af82",
737 "6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a",
738 );
739 }
740
741 #[test]
742 fn go_golden_vectors() {
743 let data = include_str!("../../testdata/ed25519/sign.input");
744
745 for line in data.lines() {
746 let mut parts = line.split(':');
747 let private_and_public = parts.next().unwrap();
748 let public_key = parts.next().unwrap();
749 let message = parts.next().unwrap();
750 let signature_with_message = parts.next().unwrap();
751 assert!(parts.next().is_some());
752 assert!(parts.next().is_none());
753
754 check_vector(&private_and_public[..64], public_key, message, &signature_with_message[..128]);
755 }
756 }
757
758 #[test]
759 fn verify_rejects_tampering_and_non_canonical_s() {
760 let seed = decode_hex::<32>("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60");
761 let pub_key = SecretKey::from_bytes(&seed).public_key();
762 let signature = SecretKey::from_bytes(&seed).sign(b"message");
763
764 assert!(pub_key.verify(b"message", &signature).is_ok());
765 assert!(pub_key.verify(b"tampered", &signature).is_err());
766
767 let mut bad_signature = signature;
768 bad_signature[0] ^= 0x80;
769 assert!(pub_key.verify(b"message", &bad_signature).is_err());
770
771 let mut non_canonical_s = signature;
772 non_canonical_s[32..].copy_from_slice(&[
773 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00,
774 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
775 ]);
776 assert!(pub_key.verify(b"message", &non_canonical_s).is_err());
777 }
778
779 #[test]
780 fn public_key_validation_rejects_invalid_encodings() {
781 let valid = decode_hex::<32>("d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a");
782 assert!(PublicKey::from_bytes(&valid).is_ok());
783
784 let mut invalid = [0xffu8; 32];
785 assert!(PublicKey::from_bytes(&invalid).is_err());
786
787 invalid = valid;
788 invalid[31] |= 0x80;
789 invalid[..31].fill(0);
790 assert!(PublicKey::from_bytes(&invalid).is_err());
791 }
792
793 #[test]
794 fn rejects_low_order_public_keys() {
795 let low_order_points: [&str; 8] = [
799 "0000000000000000000000000000000000000000000000000000000000000000",
800 "0000000000000000000000000000000000000000000000000000000000000080",
801 "0100000000000000000000000000000000000000000000000000000000000000",
802 "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05",
803 "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85",
804 "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a",
805 "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
806 "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
807 ];
808
809 for hex_key in low_order_points {
810 let bytes = decode_hex::<32>(hex_key);
811 assert!(
812 PublicKey::from_bytes(&bytes).is_err(),
813 "low-order public key {hex_key} must be rejected",
814 );
815 }
816 }
817
818 #[test]
819 fn is_small_order_detects_torsion_points() {
820 assert!(!BASEPOINT.is_small_order());
821 assert!(EdwardsPoint::identity().is_small_order());
822
823 let order_two = EdwardsPoint::from_bytes(&decode_hex::<32>(
825 "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
826 ))
827 .unwrap();
828 assert!(order_two.is_small_order());
829 }
830
831 #[test]
832 fn rejects_low_order_key_forgery() {
833 let identity = decode_hex::<32>("0100000000000000000000000000000000000000000000000000000000000000");
837 let order_two = decode_hex::<32>("ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f");
838
839 for key in [identity, order_two] {
840 assert!(PublicKey::from_bytes(&key).is_err());
841 }
842
843 let s = Scalar::from_canonical_bytes(&[7u8; 32]).expect("7...7 is a valid scalar");
846 let mut forged_signature = [0u8; SIGNATURE_SIZE];
847 forged_signature[..32].copy_from_slice(&scalar_mul_base(&s).to_bytes().unwrap());
848 forged_signature[32..].copy_from_slice(&s.to_bytes());
849
850 let genuine_seed = decode_hex::<32>("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60");
851 let genuine = SecretKey::from_bytes(&genuine_seed).public_key();
852 assert!(genuine.verify(b"forged message", &forged_signature).is_err());
853 }
854
855 #[test]
856 fn cctv_ed25519_vectors() {
857 let data = include_str!("../../testdata/ed25519/cctv_vectors.txt");
858
859 for line in data.lines() {
860 let parts: Vec<&str> = line.split(':').collect();
861 assert_eq!(parts.len(), 5, "malformed line: {line}");
862 let number = parts[0];
863 let key_hex = parts[1];
864 let sig_hex = parts[2];
865 let msg_hex = parts[3];
866 let flags_str = parts[4];
867
868 let flags: Vec<&str> = if flags_str.is_empty() {
869 vec![]
870 } else {
871 flags_str.split(',').collect()
872 };
873
874 let has_non_canonical_a = flags.contains(&"non_canonical_A");
875 let has_non_canonical_r = flags.contains(&"non_canonical_R");
876 let has_low_order_a = flags.contains(&"low_order_A");
881 let has_low_order_residue = flags.contains(&"low_order_residue");
882 let should_reject = has_non_canonical_a || has_non_canonical_r || has_low_order_a || has_low_order_residue;
883
884 let public_key = decode_hex::<32>(key_hex);
885 let signature = decode_hex::<64>(sig_hex);
886 let message = decode_hex_vec(msg_hex);
887
888 let pub_key = PublicKey::from_bytes(&public_key);
889 let result = pub_key.and_then(|pk| pk.verify(&message, &signature));
890
891 if should_reject {
892 assert!(
893 result.is_err(),
894 "vector #{number} should be rejected (flags: {flags_str}) but was accepted",
895 );
896 } else {
897 assert!(
898 result.is_ok(),
899 "vector #{number} should be accepted (flags: {flags_str}) but was rejected",
900 );
901 }
902 }
903 }
904
905 #[test]
906 fn rfc8032_extended_vectors() {
907 check_vector(
908 "833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42",
909 "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf",
910 "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f",
911 "dc2a4459e7369633a52b1bf277839a00201009a3efbf3ecb69bea2186c26b58909351fc9ac90b3ecfdfbc7c66431e0303dca179c138ac17ad9bef1177331a704",
912 );
913 }
914
915 #[test]
916 fn verify_rejects_all_zero_signature() {
917 let public_key = decode_hex::<32>("d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a");
918 let signature = [0u8; 64];
919 let pk = PublicKey::from_bytes(&public_key).unwrap();
920 let result = pk.verify(b"test", &signature);
921 assert!(result.is_err());
922 }
923
924 #[test]
925 fn verify_rejects_s_equals_l() {
926 let seed = decode_hex::<32>("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60");
927 let pub_key = SecretKey::from_bytes(&seed).public_key();
928 let signature = SecretKey::from_bytes(&seed).sign(b"test");
929
930 let mut bad_sig = signature;
931 bad_sig[32..].copy_from_slice(&[
932 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00,
933 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
934 ]);
935 assert!(pub_key.verify(b"test", &bad_sig).is_err());
936 }
937
938 #[test]
939 fn verify_rejects_non_canonical_point_encodings() {
940 let non_canonical_key = decode_hex::<32>("edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f");
941 assert!(PublicKey::from_bytes(&non_canonical_key).is_err());
942
943 let seed = decode_hex::<32>("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60");
944 let pub_key = SecretKey::from_bytes(&seed).public_key();
945 let mut bad_sig = SecretKey::from_bytes(&seed).sign(b"test");
946 bad_sig[..32].copy_from_slice(&[
947 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
948 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
949 ]);
950 assert!(pub_key.verify(b"test", &bad_sig).is_err());
951 }
952
953 #[test]
958 fn edwards_identity_point_roundtrip() {
959 let id_bytes = [
960 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
961 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
962 ];
963 let point = EdwardsPoint::from_bytes(&id_bytes).unwrap();
964 let roundtripped = point.to_bytes().unwrap();
965 assert_eq!(roundtripped, id_bytes);
966 }
967
968 #[test]
969 fn ed25519_to_montgomery_u_conversion() {
970 let seed = decode_hex::<32>("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60");
971 let priv_key = SecretKey::from_bytes(&seed);
972 let ed_pub = priv_key.public_key();
973
974 let u = ed_pub
975 .to_montgomery_u()
976 .expect("valid ed point must convert to montgomery u");
977 let u_bytes = u.to_bytes();
978
979 let x_pub_from_ed = x25519::PublicKey::try_from(&ed_pub).unwrap();
980 assert_eq!(u_bytes, x_pub_from_ed.to_bytes());
981
982 assert_ne!(u_bytes, [0u8; 32], "montgomery u must be non-zero for non-identity point");
983 }
984
985 #[test]
986 fn rfc8032_test_1024() {
987 check_vector(
988 "f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5",
989 "278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e",
990 "08b8b2b733424243760fe426a4b54908632110a66c2f6591eabd3345e3e4eb98fa6e264bf09efe12ee50f8f54e9f77b1e355f6c50544e23fb1433ddf73be84d879de7c0046dc4996d9e773f4bc9efe5738829adb26c81b37c93a1b270b20329d658675fc6ea534e0810a4432826bf58c941efb65d57a338bbd2e26640f89ffbc1a858efcb8550ee3a5e1998bd177e93a7363c344fe6b199ee5d02e82d522c4feba15452f80288a821a579116ec6dad2b3b310da903401aa62100ab5d1a36553e06203b33890cc9b832f79ef80560ccb9a39ce767967ed628c6ad573cb116dbefefd75499da96bd68a8a97b928a8bbc103b6621fcde2beca1231d206be6cd9ec7aff6f6c94fcd7204ed3455c68c83f4a41da4af2b74ef5c53f1d8ac70bdcb7ed185ce81bd84359d44254d95629e9855a94a7c1958d1f8ada5d0532ed8a5aa3fb2d17ba70eb6248e594e1a2297acbbb39d502f1a8c6eb6f1ce22b3de1a1f40cc24554119a831a9aad6079cad88425de6bde1a9187ebb6092cf67bf2b13fd65f27088d78b7e883c8759d2c4f5c65adb7553878ad575f9fad878e80a0c9ba63bcbcc2732e69485bbc9c90bfbd62481d9089beccf80cfe2df16a2cf65bd92dd597b0707e0917af48bbb75fed413d238f5555a7a569d80c3414a8d0859dc65a46128bab27af87a71314f318c782b23ebfe808b82b0ce26401d2e22f04d83d1255dc51addd3b75a2b1ae0784504df543af8969be3ea7082ff7fc9888c144da2af58429ec96031dbcad3dad9af0dcbaaaf268cb8fcffead94f3c7ca495e056a9b47acdb751fb73e666c6c655ade8297297d07ad1ba5e43f1bca32301651339e22904cc8c42f58c30c04aafdb038dda0847dd988dcda6f3bfd15c4b4c4525004aa06eeff8ca61783aacec57fb3d1f92b0fe2fd1a85f6724517b65e614ad6808d6f6ee34dff7310fdc82aebfd904b01e1dc54b2927094b2db68d6f903b68401adebf5a7e08d78ff4ef5d63653a65040cf9bfd4aca7984a74d37145986780fc0b16ac451649de6188a7dbdf191f64b5fc5e2ab47b57f7f7276cd419c17a3ca8e1b939ae49e488acba6b965610b5480109c8b17b80e1b7b750dfc7598d5d5011fd2dcc5600a32ef5b52a1ecc820e308aa342721aac0943bf6686b64b2579376504ccc493d97e6aed3fb0f9cd71a43dd497f01f17c0e2cb3797aa2a2f256656168e6c496afc5fb93246f6b1116398a346f1a641f3b041e989f7914f90cc2c7fff357876e506b50d334ba77c225bc307ba537152f3f1610e4eafe595f6d9d90d11faa933a15ef1369546868a7f3a45a96768d40fd9d03412c091c6315cf4fde7cb68606937380db2eaaa707b4c4185c32eddcdd306705e4dc1ffc872eeee475a64dfac86aba41c0618983f8741c5ef68d3a101e8a3b8cac60c905c15fc910840b94c00a0b9d0",
991 "0aab4c900501b3e24d7cdf4663326a3a87df5e4843b2cbdb67cbf6e460fec350aa5371b1508f9f4528ecea23c436d94b5e8fcd4f681e30a6ac00a9704a188a03",
992 );
993 }
994
995 #[test]
996 fn wycheproof_ed25519_vectors() {
997 #[derive(serde::Deserialize)]
998 struct TestJson {
999 #[serde(rename = "testGroups")]
1000 test_groups: Vec<TestGroup>,
1001 }
1002
1003 #[derive(serde::Deserialize)]
1004 struct TestGroup {
1005 #[serde(rename = "publicKey")]
1006 public_key: PublicKeyJson,
1007 tests: Vec<TestCase>,
1008 }
1009
1010 #[derive(serde::Deserialize)]
1011 struct PublicKeyJson {
1012 pk: String,
1013 }
1014
1015 #[derive(serde::Deserialize)]
1016 struct TestCase {
1017 #[serde(rename = "tcId")]
1018 tc_id: u32,
1019 msg: String,
1022 sig: String,
1023 result: String,
1024 }
1025
1026 let data = include_str!("../../testdata/wycheproof/testvectors_v1/ed25519_test.json");
1027 let parsed: TestJson = serde_json::from_str(data).unwrap();
1028
1029 let mut valid_tested = 0usize;
1030 let mut invalid_tested = 0usize;
1031 let mut skipped = 0usize;
1032
1033 for group in &parsed.test_groups {
1034 let public_key = decode_hex::<32>(&group.public_key.pk);
1035 let pk = PublicKey::from_bytes(&public_key).unwrap();
1036
1037 for test in &group.tests {
1038 let msg = decode_hex_vec(&test.msg);
1039 let sig_hex = &test.sig;
1040 if sig_hex.len() != 128 {
1041 skipped += 1;
1042 continue;
1043 }
1044 let signature = decode_hex::<64>(sig_hex);
1045 let should_be_valid = test.result == "valid";
1046
1047 let result = pk.verify(&msg, &signature);
1048 if should_be_valid {
1049 assert!(
1050 result.is_ok(),
1051 "Wycheproof test #{}: expected valid but got {:?}",
1052 test.tc_id,
1053 result,
1054 );
1055 valid_tested += 1;
1056 } else {
1057 assert!(result.is_err(), "Wycheproof test #{}: expected invalid but got ok", test.tc_id,);
1058 invalid_tested += 1;
1059 }
1060 }
1061 }
1062
1063 assert!(valid_tested > 0, "must test at least one valid Wycheproof vector");
1064 assert!(invalid_tested > 0, "must test at least one invalid Wycheproof vector");
1065 assert!(skipped > 0, "some truncated signatures should be skipped");
1066
1067 eprintln!("Wycheproof ed25519: {valid_tested} valid, {invalid_tested} invalid, {skipped} skipped");
1068 }
1069
1070 #[test]
1071 fn sign_verify_roundtrip_various_lengths() {
1072 let seed = decode_hex::<32>("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60");
1073 let priv_key = SecretKey::from_bytes(&seed);
1074 let pub_key = priv_key.public_key();
1075
1076 for len in [0, 1, 2, 16, 32, 64, 128, 255, 256, 1024] {
1077 let message: Vec<u8> = (0..len).map(|i| (i & 0xff) as u8).collect();
1078 let signature = priv_key.sign(&message);
1079 assert!(
1080 pub_key.verify(&message, &signature).is_ok(),
1081 "roundtrip failed for message length {len}"
1082 );
1083 }
1084 }
1085
1086 #[test]
1087 fn fixed_window_matches_scalar_mul() {
1088 for _ in 0..100 {
1089 let s = Scalar(U256::from_limbs([
1090 rand::random::<u64>(),
1091 rand::random::<u64>(),
1092 rand::random::<u64>(),
1093 rand::random::<u64>(),
1094 ]));
1095 let s = Scalar(s.0.add_mod(&U256::ZERO, &MODULUS_L));
1096 let old_result = scalar_mul(&BASEPOINT, &s);
1097 let new_result = scalar_mul_base(&s);
1098 assert_eq!(old_result.to_bytes(), new_result.to_bytes(), "mismatch for scalar={:x}", s.0,);
1099 }
1100 }
1101
1102 #[test]
1103 fn mul_mod_barrett_agrees_with_mul_mod() {
1104 for _ in 0..100 {
1105 let a = U256::from_limbs([
1106 rand::random::<u64>(),
1107 rand::random::<u64>(),
1108 rand::random::<u64>(),
1109 rand::random::<u64>(),
1110 ]);
1111 let b = U256::from_limbs([
1112 rand::random::<u64>(),
1113 rand::random::<u64>(),
1114 rand::random::<u64>(),
1115 rand::random::<u64>(),
1116 ]);
1117 let slow = a.mul_mod(&b, &MODULUS_L);
1118 let fast = a.mul_mod_barrett(&b, &MODULUS_L, &BAR_MU);
1119 assert_eq!(slow, fast, "mismatch for a={a:x}, b={b:x}");
1120 }
1121 }
1122
1123 #[test]
1124 fn basepoint() {
1125 assert_eq!(BASEPOINT, EdwardsPoint::from_bytes(&BASEPOINT_COMPRESSED).unwrap())
1126 }
1127}