1#![allow(clippy::needless_range_loop)]
9
10use super::field::{
40 CURVE448_P, FieldElement, field_add, field_inv, field_mul, field_neg, field_sqrt_p3mod4, field_sub,
41};
42
43mod consts;
44use crate::Xof;
45use crate::hash::sha3::Shake256;
46use consts::{ED448_BX_BE, ED448_BY_BE, ED448_L};
47
48type Fe = FieldElement<7>;
50
51const P: [u64; 7] = CURVE448_P;
52
53#[inline]
56fn fe_zero() -> Fe {
57 Fe::ZERO
58}
59#[inline]
60fn fe_one() -> Fe {
61 Fe::one()
62}
63#[inline]
64fn fe_u64(v: u64) -> Fe {
65 let mut f = Fe::ZERO;
66 f.limbs[0] = v;
67 f
68}
69#[inline]
70fn fe_add(a: &Fe, b: &Fe) -> Fe {
71 field_add(a, b, &P)
72}
73#[inline]
74fn fe_sub(a: &Fe, b: &Fe) -> Fe {
75 field_sub(a, b, &P)
76}
77#[inline]
78fn fe_mul(a: &Fe, b: &Fe) -> Fe {
79 field_mul(a, b, &P)
80}
81#[inline]
82fn fe_sqr(a: &Fe) -> Fe {
83 field_mul(a, a, &P)
84}
85#[inline]
86fn fe_neg(a: &Fe) -> Fe {
87 field_neg(a, &P)
88}
89#[inline]
90fn fe_inv(a: &Fe) -> Fe {
91 field_inv(a, &P)
92}
93
94#[inline]
96fn fe_d() -> Fe {
97 fe_neg(&fe_u64(39081))
98}
99
100#[inline]
103fn fe_ct_select(a: &Fe, b: &Fe, choice: u8) -> Fe {
104 let mask = core::hint::black_box(0u64.wrapping_sub(choice as u64));
105 let inv = !mask;
106 let mut out = Fe::ZERO;
107 for i in 0..7 {
108 out.limbs[i] = (b.limbs[i] & mask) | (a.limbs[i] & inv);
109 }
110 out
111}
112
113#[inline]
115fn fe_is_odd(a: &Fe) -> u8 {
116 (a.limbs[0] & 1) as u8
117}
118
119fn fe_is_canonical(v: &Fe) -> bool {
121 let mut borrow: u64 = 0;
123 for i in 0..7 {
124 let diff = (v.limbs[i] as u128)
125 .wrapping_sub(P[i] as u128)
126 .wrapping_sub(borrow as u128);
127 borrow = ((diff >> 64) as u64) & 1;
128 }
129 borrow == 1
130}
131
132#[derive(Clone)]
141pub struct Ed448Point {
142 x: Fe,
143 y: Fe,
144 z: Fe,
145}
146
147impl Ed448Point {
148 pub fn identity() -> Self {
150 Ed448Point {
151 x: fe_zero(),
152 y: fe_one(),
153 z: fe_one(),
154 }
155 }
156
157 pub fn base() -> Self {
159 Ed448Point {
160 x: Fe::from_bytes_be(&ED448_BX_BE),
161 y: Fe::from_bytes_be(&ED448_BY_BE),
162 z: fe_one(),
163 }
164 }
165
166 pub fn add(&self, q: &Ed448Point) -> Ed448Point {
168 let xcp = fe_mul(&self.x, &q.x);
169 let ycp = fe_mul(&self.y, &q.y);
170 let zcp = fe_mul(&self.z, &q.z);
171 let b = fe_sqr(&zcp);
172 let e = fe_mul(&fe_d(), &fe_mul(&xcp, &ycp));
173 let f = fe_sub(&b, &e);
174 let g = fe_add(&b, &e);
175 let sum = fe_mul(&fe_add(&self.x, &self.y), &fe_add(&q.x, &q.y));
177 let inner = fe_sub(&fe_sub(&sum, &xcp), &ycp);
178 let x3 = fe_mul(&fe_mul(&zcp, &f), &inner);
179 let y3 = fe_mul(&fe_mul(&zcp, &g), &fe_sub(&ycp, &xcp));
180 let z3 = fe_mul(&f, &g);
181 Ed448Point { x: x3, y: y3, z: z3 }
182 }
183
184 pub fn double(&self) -> Ed448Point {
186 let x1s = fe_sqr(&self.x);
187 let y1s = fe_sqr(&self.y);
188 let z1s = fe_sqr(&self.z);
189 let xys = fe_add(&self.x, &self.y);
190 let f = fe_add(&x1s, &y1s);
191 let j = fe_sub(&f, &fe_add(&z1s, &z1s));
192 let x3 = fe_mul(&fe_sub(&fe_sub(&fe_sqr(&xys), &x1s), &y1s), &j);
193 let y3 = fe_mul(&f, &fe_sub(&x1s, &y1s));
194 let z3 = fe_mul(&f, &j);
195 Ed448Point { x: x3, y: y3, z: z3 }
196 }
197
198 pub fn ct_select(a: &Ed448Point, b: &Ed448Point, choice: u8) -> Ed448Point {
200 Ed448Point {
201 x: fe_ct_select(&a.x, &b.x, choice),
202 y: fe_ct_select(&a.y, &b.y, choice),
203 z: fe_ct_select(&a.z, &b.z, choice),
204 }
205 }
206
207 pub fn scalar_mul(&self, scalar_le: &[u8]) -> Ed448Point {
213 let mut r = Ed448Point::identity();
214 let nbits = scalar_le.len() * 8;
215 for i in (0..nbits).rev() {
216 r = r.double();
217 let bit = (scalar_le[i / 8] >> (i % 8)) & 1;
218 let r_plus = r.add(self);
219 r = Ed448Point::ct_select(&r, &r_plus, bit);
220 }
221 r
222 }
223
224 pub fn equals(&self, q: &Ed448Point) -> bool {
226 fe_mul(&self.x, &q.z) == fe_mul(&q.x, &self.z) && fe_mul(&self.y, &q.z) == fe_mul(&q.y, &self.z)
227 }
228
229 pub fn is_identity(&self) -> bool {
231 self.x.is_zero() && self.y == self.z
232 }
233
234 pub fn encode(&self) -> [u8; 57] {
237 let zinv = fe_inv(&self.z);
238 let xp = fe_mul(&self.x, &zinv);
239 let yp = fe_mul(&self.y, &zinv);
240 let yb = yp.to_bytes_le(); let mut out = [0u8; 57];
242 out[..56].copy_from_slice(&yb);
243 out[56] = fe_is_odd(&xp) << 7;
244 out
245 }
246
247 pub fn decode(s: &[u8; 57]) -> Option<Ed448Point> {
252 let sign = (s[56] >> 7) & 1;
253 if s[56] & 0x7f != 0 {
255 return None;
256 }
257 let y = Fe::from_bytes_le(&s[..56]);
258 if !fe_is_canonical(&y) {
259 return None;
260 }
261 let y2 = fe_sqr(&y);
263 let num = fe_sub(&y2, &fe_one());
264 let den = fe_sub(&fe_mul(&fe_d(), &y2), &fe_one());
265 let x2 = fe_mul(&num, &fe_inv(&den));
266 let mut x = field_sqrt_p3mod4(&x2, &P);
268 if fe_sqr(&x) != x2 {
269 return None; }
271 if x.is_zero() && sign == 1 {
273 return None;
274 }
275 if fe_is_odd(&x) != sign {
277 x = fe_neg(&x);
278 }
279 Some(Ed448Point { x, y, z: fe_one() })
280 }
281}
282
283#[cfg(test)]
289fn l_bytes_le() -> [u8; 56] {
290 let mut out = [0u8; 56];
291 for (i, limb) in ED448_L.iter().enumerate() {
292 out[i * 8..i * 8 + 8].copy_from_slice(&limb.to_le_bytes());
293 }
294 out
295}
296
297pub fn reduce_mod_l(input_le: &[u8]) -> [u8; 57] {
302 let l = ED448_L; let mut rem = [0u64; 7];
304
305 let total_bits = input_le.len() * 8;
306 for i in (0..total_bits).rev() {
307 let mut carry = 0u64;
309 for limb in rem.iter_mut() {
310 let new_carry = *limb >> 63;
311 *limb = (*limb << 1) | carry;
312 carry = new_carry;
313 }
314 rem[0] |= ((input_le[i / 8] >> (i % 8)) & 1) as u64;
316
317 let mut borrow: u64 = 0;
319 let mut trial = [0u64; 7];
320 for j in 0..7 {
321 let diff = (rem[j] as u128).wrapping_sub(l[j] as u128).wrapping_sub(borrow as u128);
322 trial[j] = diff as u64;
323 borrow = ((diff >> 64) as u64) & 1;
324 }
325 let need_sub = carry | (1u64.wrapping_sub(borrow));
327 let mask = core::hint::black_box(0u64.wrapping_sub(need_sub));
328 let inv = !mask;
329 for j in 0..7 {
330 rem[j] = (trial[j] & mask) | (rem[j] & inv);
331 }
332 }
333
334 let mut out = [0u8; 57];
335 for (i, limb) in rem.iter().enumerate() {
336 out[i * 8..i * 8 + 8].copy_from_slice(&limb.to_le_bytes());
337 }
338 out
339}
340
341fn shake256_114(data: &[u8]) -> [u8; 114] {
345 let mut x = Shake256::new();
346 x.update(data);
347 let mut out = [0u8; 114];
348 x.squeeze(&mut out);
349 out
350}
351
352fn shake256_64(data: &[u8]) -> [u8; 64] {
354 let mut x = Shake256::new();
355 x.update(data);
356 let mut out = [0u8; 64];
357 x.squeeze(&mut out);
358 out
359}
360
361fn hash_dom4(hflag: u8, ctx: &[u8], parts: &[&[u8]]) -> [u8; 114] {
364 let mut x = Shake256::new();
365 x.update(b"SigEd448");
366 x.update(&[hflag, ctx.len() as u8]);
367 x.update(ctx);
368 for p in parts {
369 x.update(p);
370 }
371 let mut out = [0u8; 114];
372 x.squeeze(&mut out);
373 out
374}
375
376fn clamp(mut a: [u8; 57]) -> [u8; 57] {
379 a[0] &= 0xFC;
380 a[55] |= 0x80;
381 a[56] = 0;
382 a
383}
384
385fn scalar_lt_l(s: &[u8; 57]) -> bool {
387 if s[56] != 0 {
388 return false; }
390 let v = Fe::from_bytes_le(&s[..56]);
391 let mut borrow: u64 = 0;
392 for i in 0..7 {
393 let diff = (v.limbs[i] as u128)
394 .wrapping_sub(ED448_L[i] as u128)
395 .wrapping_sub(borrow as u128);
396 borrow = ((diff >> 64) as u64) & 1;
397 }
398 borrow == 1
399}
400
401#[derive(Clone, Copy, Debug, PartialEq, Eq)]
405pub struct Ed448PublicKey(pub [u8; 57]);
406
407#[derive(Clone, Copy)]
409pub struct Ed448SecretKey(pub [u8; 57]);
410
411#[derive(Clone, Copy, Debug, PartialEq, Eq)]
413pub struct Ed448Signature(pub [u8; 114]);
414
415pub fn ed448_keygen(secret: &[u8; 57]) -> (Ed448PublicKey, Ed448SecretKey) {
417 let khash = shake256_114(secret);
418 let mut a = [0u8; 57];
419 a.copy_from_slice(&khash[..57]);
420 let a = clamp(a);
421 let pk = Ed448Point::base().scalar_mul(&a).encode();
422 (Ed448PublicKey(pk), Ed448SecretKey(*secret))
423}
424
425fn ed448_sign_internal(sk: &Ed448SecretKey, msg: &[u8], ctx: &[u8], hflag: u8) -> [u8; 114] {
432 let khash = shake256_114(&sk.0);
433 let mut a = [0u8; 57];
434 a.copy_from_slice(&khash[..57]);
435 let a = clamp(a);
436 let prefix = &khash[57..]; let pk = Ed448Point::base().scalar_mul(&a).encode();
439
440 let r = reduce_mod_l(&hash_dom4(hflag, ctx, &[prefix, msg]));
442 let rr = Ed448Point::base().scalar_mul(&r).encode();
443
444 let h = reduce_mod_l(&hash_dom4(hflag, ctx, &[&rr, &pk, msg]));
446
447 let a_red = reduce_mod_l(&a);
450 let r_fe = Fe::from_bytes_le(&r[..56]);
451 let h_fe = Fe::from_bytes_le(&h[..56]);
452 let a_fe = Fe::from_bytes_le(&a_red[..56]);
453 let s_fe = field_add(&r_fe, &field_mul(&h_fe, &a_fe, &ED448_L), &ED448_L);
454 let s_bytes = s_fe.to_bytes_le(); let mut sig = [0u8; 114];
457 sig[..57].copy_from_slice(&rr);
458 sig[57..113].copy_from_slice(&s_bytes);
459 sig
460}
461
462fn ed448_verify_internal(pk: &Ed448PublicKey, msg: &[u8], sig: &[u8; 114], ctx: &[u8], hflag: u8) -> bool {
465 let mut rr = [0u8; 57];
466 rr.copy_from_slice(&sig[..57]);
467 let mut s = [0u8; 57];
468 s.copy_from_slice(&sig[57..]);
469
470 if !scalar_lt_l(&s) {
471 return false;
472 }
473 let r_point = match Ed448Point::decode(&rr) {
474 Some(p) => p,
475 None => return false,
476 };
477 let a_point = match Ed448Point::decode(&pk.0) {
478 Some(p) => p,
479 None => return false,
480 };
481
482 let h = reduce_mod_l(&hash_dom4(hflag, ctx, &[&rr, &pk.0, msg]));
483
484 let lhs = Ed448Point::base().scalar_mul(&s).double().double();
485 let rhs = r_point.add(&a_point.scalar_mul(&h)).double().double();
486 lhs.equals(&rhs)
487}
488
489pub fn ed448_sign(sk: &Ed448SecretKey, msg: &[u8]) -> Ed448Signature {
493 Ed448Signature(ed448_sign_internal(sk, msg, b"", 0))
494}
495
496pub fn ed448_verify(pk: &Ed448PublicKey, msg: &[u8], sig: &Ed448Signature) -> bool {
498 ed448_verify_internal(pk, msg, &sig.0, b"", 0)
499}
500
501pub fn ed448ctx_sign(sk: &Ed448SecretKey, msg: &[u8], context: &[u8]) -> Option<Ed448Signature> {
503 if context.len() > 255 {
504 return None;
505 }
506 Some(Ed448Signature(ed448_sign_internal(sk, msg, context, 0)))
507}
508
509pub fn ed448ctx_verify(pk: &Ed448PublicKey, msg: &[u8], context: &[u8], sig: &Ed448Signature) -> bool {
511 if context.len() > 255 {
512 return false;
513 }
514 ed448_verify_internal(pk, msg, &sig.0, context, 0)
515}
516
517pub fn ed448ph_sign(sk: &Ed448SecretKey, msg: &[u8], context: &[u8]) -> Option<Ed448Signature> {
519 if context.len() > 255 {
520 return None;
521 }
522 let ph = shake256_64(msg);
523 Some(Ed448Signature(ed448_sign_internal(sk, &ph, context, 1)))
524}
525
526pub fn ed448ph_verify(pk: &Ed448PublicKey, msg: &[u8], context: &[u8], sig: &Ed448Signature) -> bool {
528 if context.len() > 255 {
529 return false;
530 }
531 let ph = shake256_64(msg);
532 ed448_verify_internal(pk, &ph, &sig.0, context, 1)
533}
534
535pub fn ed448ph_sign_prehashed(sk: &Ed448SecretKey, prehashed: &[u8], context: &[u8]) -> Option<Ed448Signature> {
537 if prehashed.len() != 64 || context.len() > 255 {
538 return None;
539 }
540 Some(Ed448Signature(ed448_sign_internal(sk, prehashed, context, 1)))
541}
542
543pub fn ed448ph_verify_prehashed(pk: &Ed448PublicKey, prehashed: &[u8], context: &[u8], sig: &Ed448Signature) -> bool {
545 if prehashed.len() != 64 || context.len() > 255 {
546 return false;
547 }
548 ed448_verify_internal(pk, prehashed, &sig.0, context, 1)
549}
550
551#[cfg(test)]
552mod tests {
553 use super::*;
554
555 fn hex_to_vec(h: &str) -> Vec<u8> {
556 (0..h.len())
557 .step_by(2)
558 .map(|i| u8::from_str_radix(&h[i..i + 2], 16).unwrap())
559 .collect()
560 }
561 fn hex57(h: &str) -> [u8; 57] {
562 let mut a = [0u8; 57];
563 a.copy_from_slice(&hex_to_vec(h));
564 a
565 }
566 fn hex114(h: &str) -> [u8; 114] {
567 let mut a = [0u8; 114];
568 a.copy_from_slice(&hex_to_vec(h));
569 a
570 }
571
572 #[test]
575 fn rfc8032_blank_vector() {
576 let sk = hex57(
577 "6c82a562cb808d10d632be89c8513ebf6c929f34ddfa8c9f63c9960ef6e348a3\
578 528c8a3fcc2f044e39a3fc5b94492f8f032e7549a20098f95b",
579 );
580 let pk_expected = hex57(
581 "5fd7449b59b461fd2ce787ec616ad46a1da1342485a70e1f8a0ea75d80e96778\
582 edf124769b46c7061bd6783df1e50f6cd1fa1abeafe8256180",
583 );
584 let sig_expected = hex114(
585 "533a37f6bbe457251f023c0d88f976ae2dfb504a843e34d2074fd823d41a591f\
586 2b233f034f628281f2fd7a22ddd47d7828c59bd0a21bfd3980ff0d2028d4b18a\
587 9df63e006c5d1c2d345b925d8dc00b4104852db99ac5c7cdda8530a113a0f4db\
588 b61149f05a7363268c71d95808ff2e652600",
589 );
590 let (pk, sk2) = ed448_keygen(&sk);
591 assert_eq!(pk.0, pk_expected, "keygen public key mismatch");
592 let sig = ed448_sign(&sk2, b"");
593 assert_eq!(sig.0, sig_expected, "signature mismatch (not RFC-conformant)");
594 assert!(ed448_verify(&pk, b"", &sig), "verify rejected a valid signature");
595 let mut bad = sig;
596 bad.0[0] ^= 1;
597 assert!(!ed448_verify(&pk, b"", &bad), "verify accepted a tampered signature");
598 }
599
600 #[test]
602 fn rfc8032_ctx_vector() {
603 let sk = hex57(
604 "c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463a\
605 fbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e",
606 );
607 let pk_exp = hex57(
608 "43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c086\
609 6aea01eb00742802b8438ea4cb82169c235160627b4c3a9480",
610 );
611 let sig_exp = hex114(
612 "d4f8f6131770dd46f40867d6fd5d5055de43541f8c5e35abbcd001b32a89f7d2\
613 151f7647f11d8ca2ae279fb842d607217fce6e042f6815ea000c85741de5c8da\
614 1144a6a1aba7f96de42505d7a7298524fda538fccbbb754f578c1cad10d54d0d\
615 5428407e85dcbc98a49155c13764e66c3c00",
616 );
617 let msg = [0x03u8];
618 let ctx = [0x66u8, 0x6f, 0x6f]; let (pk, sk2) = ed448_keygen(&sk);
620 assert_eq!(pk.0, pk_exp);
621 let sig = ed448ctx_sign(&sk2, &msg, &ctx).expect("ctx <= 255");
622 assert_eq!(sig.0, sig_exp, "ctx signature not RFC-conformant");
623 assert!(ed448ctx_verify(&pk, &msg, &ctx, &sig));
624 assert!(!ed448ctx_verify(&pk, &msg, b"bar", &sig));
626 }
627
628 #[test]
630 fn rfc8032_ph_vector() {
631 let sk = hex57(
632 "833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42\
633 ef7822e0d5104127dc05d6dbefde69e3ab2cec7c867c6e2c49",
634 );
635 let pk_exp = hex57(
636 "259b71c19f83ef77a7abd26524cbdb3161b590a48f7d17de3ee0ba9c52beb743\
637 c09428a131d6b1b57303d90d8132c276d5ed3d5d01c0f53880",
638 );
639 let sig_exp = hex114(
640 "822f6901f7480f3d5f562c592994d9693602875614483256505600bbc281ae38\
641 1f54d6bce2ea911574932f52a4e6cadd78769375ec3ffd1b801a0d9b3f4030cd\
642 433964b6457ea39476511214f97469b57dd32dbc560a9a94d00bff07620464a3\
643 ad203df7dc7ce360c3cd3696d9d9fab90f00",
644 );
645 let msg = [0x61u8, 0x62, 0x63]; let (pk, sk2) = ed448_keygen(&sk);
647 assert_eq!(pk.0, pk_exp);
648 let sig = ed448ph_sign(&sk2, &msg, b"").expect("ctx <= 255");
649 assert_eq!(sig.0, sig_exp, "ph signature not RFC-conformant");
650 assert!(ed448ph_verify(&pk, &msg, b"", &sig));
651 let ph = shake256_64(&msg);
653 let sig2 = ed448ph_sign_prehashed(&sk2, &ph, b"").expect("64-byte prehash");
654 assert_eq!(sig2.0, sig.0, "prehashed path disagrees with ed448ph_sign");
655 }
656
657 #[test]
658 fn base_point_on_curve() {
659 let b = Ed448Point::base();
661 let x2 = fe_sqr(&b.x);
662 let y2 = fe_sqr(&b.y);
663 let lhs = fe_add(&x2, &y2);
664 let rhs = fe_add(&fe_one(), &fe_mul(&fe_d(), &fe_mul(&x2, &y2)));
665 assert_eq!(lhs, rhs, "base point not on edwards448");
666 }
667
668 #[test]
669 fn order_of_base_point() {
670 let lb = Ed448Point::base().scalar_mul(&l_bytes_le());
672 assert!(lb.is_identity(), "[L]·B != identity (wrong base point or order)");
673 }
674
675 #[test]
676 fn encode_decode_roundtrip_base() {
677 let b = Ed448Point::base();
678 let enc = b.encode();
679 let dec = Ed448Point::decode(&enc).expect("base point must decode");
680 assert!(b.equals(&dec), "decode(encode(B)) != B");
681 assert_eq!(dec.encode(), enc);
683 }
684
685 #[test]
686 fn scalar_mul_distributive_small() {
687 let b = Ed448Point::base();
689 let three = {
690 let mut s = [0u8; 56];
691 s[0] = 3;
692 s
693 };
694 let lhs = b.scalar_mul(&three);
695 let rhs = b.add(&b).add(&b);
696 assert!(lhs.equals(&rhs), "[3]B != B+B+B");
697 }
698
699 #[test]
700 fn reduce_mod_l_known() {
701 let l = l_bytes_le();
703 let red_l = reduce_mod_l(&l);
704 assert!(red_l.iter().all(|&b| b == 0), "L mod L != 0");
705
706 let mut lp1 = l;
707 lp1[0] = lp1[0].wrapping_add(1);
709 let red = reduce_mod_l(&lp1);
710 assert_eq!(red[0], 1);
711 assert!(red[1..].iter().all(|&b| b == 0), "(L+1) mod L != 1");
712 }
713}