1use core::fmt;
23
24use crate::hash::Digest;
25use crate::public_key::bigint::BigUint;
26use crate::public_key::ec_edwards::{EdwardsMulTable, EdwardsPoint, TwistedEdwardsCurve};
27use crate::public_key::io::{
28 decode_biguints, encode_biguints, pem_unwrap, pem_wrap, xml_unwrap, xml_wrap,
29};
30use crate::Csprng;
31
32const EDDSA_PUBLIC_LABEL: &str = "CRYPTOGRAPHY EDDSA PUBLIC KEY";
33const EDDSA_PRIVATE_LABEL: &str = "CRYPTOGRAPHY EDDSA PRIVATE KEY";
34
35#[derive(Clone, Debug)]
37pub struct EdDsaPublicKey {
38 curve: TwistedEdwardsCurve,
40 a_point: EdwardsPoint,
42 a_table: EdwardsMulTable,
44}
45
46#[derive(Clone)]
48pub struct EdDsaPrivateKey {
49 curve: TwistedEdwardsCurve,
51 d: BigUint,
53 a_point: EdwardsPoint,
58}
59
60#[derive(Clone, Debug, Eq, PartialEq)]
62pub struct EdDsaSignature {
63 r_point: EdwardsPoint,
65 s: BigUint,
67}
68
69pub struct EdDsa;
71
72impl PartialEq for EdDsaPublicKey {
73 fn eq(&self, other: &Self) -> bool {
74 self.curve.same_curve(&other.curve) && self.a_point == other.a_point
75 }
76}
77
78impl Eq for EdDsaPublicKey {}
79
80impl PartialEq for EdDsaPrivateKey {
81 fn eq(&self, other: &Self) -> bool {
82 self.curve.same_curve(&other.curve) && self.d == other.d
83 }
84}
85
86impl Eq for EdDsaPrivateKey {}
87
88impl EdDsaPublicKey {
89 #[must_use]
91 pub fn curve(&self) -> &TwistedEdwardsCurve {
92 &self.curve
93 }
94
95 #[must_use]
97 pub fn public_point(&self) -> &EdwardsPoint {
98 &self.a_point
99 }
100
101 #[must_use]
103 pub fn to_wire_bytes(&self) -> Vec<u8> {
104 self.curve.encode_point(&self.a_point)
105 }
106
107 #[must_use]
109 pub fn from_wire_bytes(curve: TwistedEdwardsCurve, bytes: &[u8]) -> Option<Self> {
110 let a_point = curve.decode_point(bytes)?;
111 if !validate_public_point(&curve, &a_point) {
112 return None;
113 }
114 let a_table = curve.precompute_mul_table(&a_point);
115 Some(Self {
116 curve,
117 a_point,
118 a_table,
119 })
120 }
121
122 #[must_use]
124 pub fn verify_message<H: Digest>(&self, message: &[u8], signature: &EdDsaSignature) -> bool {
125 if signature.s.is_zero() || signature.s >= self.curve.n {
126 return false;
127 }
128 if signature.r_point.is_neutral()
129 || !self.curve.is_on_curve(&signature.r_point)
130 || !point_in_prime_subgroup(&self.curve, &signature.r_point)
131 {
132 return false;
133 }
134
135 let challenge =
136 challenge_scalar::<H>(&self.curve, &signature.r_point, &self.a_point, message);
137
138 let lhs = self.curve.scalar_mul_base(&signature.s);
139 let rhs = self.curve.add(
140 &signature.r_point,
141 &self.curve.scalar_mul_cached(&self.a_table, &challenge),
142 );
143 lhs == rhs
144 }
145
146 #[must_use]
148 pub fn verify_message_bytes<H: Digest>(&self, message: &[u8], signature: &[u8]) -> bool {
149 let Some(signature) = EdDsaSignature::from_key_blob(signature, &self.curve) else {
150 return false;
151 };
152 self.verify_message::<H>(message, &signature)
153 }
154
155 #[must_use]
159 pub fn to_key_blob(&self) -> Vec<u8> {
160 encode_biguints(&[
161 &self.curve.p,
162 &self.curve.a,
163 &self.curve.d,
164 &self.curve.n,
165 &self.curve.gx,
166 &self.curve.gy,
167 &self.a_point.x,
168 &self.a_point.y,
169 ])
170 }
171
172 #[must_use]
174 pub fn from_key_blob(blob: &[u8]) -> Option<Self> {
175 let mut fields = decode_biguints(blob)?.into_iter();
176 let p = fields.next()?;
177 let a = fields.next()?;
178 let d = fields.next()?;
179 let n = fields.next()?;
180 let gx = fields.next()?;
181 let gy = fields.next()?;
182 let ax = fields.next()?;
183 let ay = fields.next()?;
184 if fields.next().is_some() {
185 return None;
186 }
187 let curve = TwistedEdwardsCurve::new(p, a, d, n, gx, gy)?;
188 if ax >= curve.p || ay >= curve.p {
191 return None;
192 }
193 let a_point = EdwardsPoint::new(ax, ay);
194 if !validate_public_point(&curve, &a_point) {
195 return None;
196 }
197 let a_table = curve.precompute_mul_table(&a_point);
198 Some(Self {
199 curve,
200 a_point,
201 a_table,
202 })
203 }
204
205 #[must_use]
206 pub fn to_pem(&self) -> String {
207 pem_wrap(EDDSA_PUBLIC_LABEL, &self.to_key_blob())
208 }
209
210 #[must_use]
211 pub fn from_pem(pem: &str) -> Option<Self> {
212 let blob = pem_unwrap(EDDSA_PUBLIC_LABEL, pem)?;
213 Self::from_key_blob(&blob)
214 }
215
216 #[must_use]
217 pub fn to_xml(&self) -> String {
218 xml_wrap(
219 "EdDsaPublicKey",
220 &[
221 ("p", &self.curve.p),
222 ("a", &self.curve.a),
223 ("d", &self.curve.d),
224 ("n", &self.curve.n),
225 ("gx", &self.curve.gx),
226 ("gy", &self.curve.gy),
227 ("ax", &self.a_point.x),
228 ("ay", &self.a_point.y),
229 ],
230 )
231 }
232
233 #[must_use]
234 pub fn from_xml(xml: &str) -> Option<Self> {
235 let mut fields = xml_unwrap(
236 "EdDsaPublicKey",
237 &["p", "a", "d", "n", "gx", "gy", "ax", "ay"],
238 xml,
239 )?
240 .into_iter();
241 let p = fields.next()?;
242 let a = fields.next()?;
243 let d = fields.next()?;
244 let n = fields.next()?;
245 let gx = fields.next()?;
246 let gy = fields.next()?;
247 let ax = fields.next()?;
248 let ay = fields.next()?;
249 if fields.next().is_some() {
250 return None;
251 }
252 let curve = TwistedEdwardsCurve::new(p, a, d, n, gx, gy)?;
253 if ax >= curve.p || ay >= curve.p {
254 return None;
255 }
256 let a_point = EdwardsPoint::new(ax, ay);
257 if !validate_public_point(&curve, &a_point) {
258 return None;
259 }
260 let a_table = curve.precompute_mul_table(&a_point);
261 Some(Self {
262 curve,
263 a_point,
264 a_table,
265 })
266 }
267}
268
269impl EdDsaPrivateKey {
270 #[must_use]
272 pub fn curve(&self) -> &TwistedEdwardsCurve {
273 &self.curve
274 }
275
276 #[must_use]
278 pub fn private_scalar(&self) -> &BigUint {
279 &self.d
280 }
281
282 #[must_use]
284 pub fn public_point(&self) -> &EdwardsPoint {
285 &self.a_point
286 }
287
288 #[must_use]
290 pub fn to_public_key(&self) -> EdDsaPublicKey {
291 EdDsaPublicKey {
292 curve: self.curve.clone(),
293 a_point: self.a_point.clone(),
294 a_table: self.curve.precompute_mul_table(&self.a_point),
295 }
296 }
297
298 #[must_use]
304 pub fn sign_message_with_nonce<H: Digest>(
305 &self,
306 message: &[u8],
307 nonce: &BigUint,
308 ) -> Option<EdDsaSignature> {
309 if nonce.is_zero() || nonce >= &self.curve.n {
310 return None;
311 }
312
313 let r_point = self.curve.scalar_mul_base(nonce);
314 if r_point.is_neutral() {
315 return None;
316 }
317 let challenge = challenge_scalar::<H>(&self.curve, &r_point, &self.a_point, message);
318 let ed = BigUint::mod_mul(&challenge, &self.d, &self.curve.n);
319 let s = nonce.add_ref(&ed).modulo(&self.curve.n);
320 Some(EdDsaSignature { r_point, s })
321 }
322
323 #[must_use]
325 pub fn sign_message<H: Digest, R: Csprng>(
326 &self,
327 message: &[u8],
328 rng: &mut R,
329 ) -> Option<EdDsaSignature> {
330 loop {
331 let nonce = self.curve.random_scalar(rng);
332 if let Some(signature) = self.sign_message_with_nonce::<H>(message, &nonce) {
333 return Some(signature);
334 }
335 }
336 }
337
338 #[must_use]
340 pub fn sign_message_bytes<H: Digest, R: Csprng>(
341 &self,
342 message: &[u8],
343 rng: &mut R,
344 ) -> Option<Vec<u8>> {
345 let signature = self.sign_message::<H, R>(message, rng)?;
346 Some(signature.to_key_blob())
347 }
348
349 #[must_use]
353 pub fn to_key_blob(&self) -> Vec<u8> {
354 encode_biguints(&[
355 &self.curve.p,
356 &self.curve.a,
357 &self.curve.d,
358 &self.curve.n,
359 &self.curve.gx,
360 &self.curve.gy,
361 &self.d,
362 ])
363 }
364
365 #[must_use]
367 pub fn from_key_blob(blob: &[u8]) -> Option<Self> {
368 let mut fields = decode_biguints(blob)?.into_iter();
369 let p = fields.next()?;
370 let a = fields.next()?;
371 let d_curve = fields.next()?;
372 let n = fields.next()?;
373 let gx = fields.next()?;
374 let gy = fields.next()?;
375 let d = fields.next()?;
376 if fields.next().is_some() {
377 return None;
378 }
379 let curve = TwistedEdwardsCurve::new(p, a, d_curve, n, gx, gy)?;
380 if d.is_zero() || d >= curve.n {
381 return None;
382 }
383 let a_point = curve.scalar_mul_base(&d);
384 Some(Self { curve, d, a_point })
385 }
386
387 #[must_use]
388 pub fn to_pem(&self) -> String {
389 pem_wrap(EDDSA_PRIVATE_LABEL, &self.to_key_blob())
390 }
391
392 #[must_use]
393 pub fn from_pem(pem: &str) -> Option<Self> {
394 let blob = pem_unwrap(EDDSA_PRIVATE_LABEL, pem)?;
395 Self::from_key_blob(&blob)
396 }
397
398 #[must_use]
399 pub fn to_xml(&self) -> String {
400 xml_wrap(
401 "EdDsaPrivateKey",
402 &[
403 ("p", &self.curve.p),
404 ("a", &self.curve.a),
405 ("d", &self.curve.d),
406 ("n", &self.curve.n),
407 ("gx", &self.curve.gx),
408 ("gy", &self.curve.gy),
409 ("private", &self.d),
410 ],
411 )
412 }
413
414 #[must_use]
415 pub fn from_xml(xml: &str) -> Option<Self> {
416 let mut fields = xml_unwrap(
417 "EdDsaPrivateKey",
418 &["p", "a", "d", "n", "gx", "gy", "private"],
419 xml,
420 )?
421 .into_iter();
422 let p = fields.next()?;
423 let a = fields.next()?;
424 let d_curve = fields.next()?;
425 let n = fields.next()?;
426 let gx = fields.next()?;
427 let gy = fields.next()?;
428 let d = fields.next()?;
429 if fields.next().is_some() {
430 return None;
431 }
432 let curve = TwistedEdwardsCurve::new(p, a, d_curve, n, gx, gy)?;
433 if d.is_zero() || d >= curve.n {
434 return None;
435 }
436 let a_point = curve.scalar_mul_base(&d);
437 Some(Self { curve, d, a_point })
438 }
439}
440
441impl fmt::Debug for EdDsaPrivateKey {
442 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
443 f.write_str("EdDsaPrivateKey(<redacted>)")
444 }
445}
446
447impl EdDsaSignature {
448 #[must_use]
450 pub fn nonce_point(&self) -> &EdwardsPoint {
451 &self.r_point
452 }
453
454 #[must_use]
456 pub fn response(&self) -> &BigUint {
457 &self.s
458 }
459
460 #[must_use]
464 pub fn to_key_blob(&self) -> Vec<u8> {
465 encode_biguints(&[&self.r_point.x, &self.r_point.y, &self.s])
466 }
467
468 #[must_use]
470 pub fn from_key_blob(blob: &[u8], curve: &TwistedEdwardsCurve) -> Option<Self> {
471 let mut fields = decode_biguints(blob)?.into_iter();
472 let rx = fields.next()?;
473 let ry = fields.next()?;
474 let s = fields.next()?;
475 if fields.next().is_some() || s.is_zero() || s >= curve.n {
476 return None;
477 }
478 if rx >= curve.p || ry >= curve.p {
483 return None;
484 }
485 let r_point = EdwardsPoint::new(rx, ry);
486 if !curve.is_on_curve(&r_point) {
487 return None;
488 }
489 Some(Self { r_point, s })
490 }
491}
492
493impl EdDsa {
494 #[must_use]
496 pub fn generate<R: Csprng>(
497 curve: TwistedEdwardsCurve,
498 rng: &mut R,
499 ) -> (EdDsaPublicKey, EdDsaPrivateKey) {
500 let (d, a_point) = curve.generate_keypair(rng);
501 let public = EdDsaPublicKey {
502 curve: curve.clone(),
503 a_point: a_point.clone(),
504 a_table: curve.precompute_mul_table(&a_point),
505 };
506 let private = EdDsaPrivateKey { curve, d, a_point };
507 (public, private)
508 }
509
510 #[must_use]
512 pub fn from_secret_scalar(
513 curve: TwistedEdwardsCurve,
514 secret: &BigUint,
515 ) -> Option<(EdDsaPublicKey, EdDsaPrivateKey)> {
516 if secret.is_zero() || secret >= &curve.n {
517 return None;
518 }
519 let a_point = curve.scalar_mul_base(secret);
520 if !validate_public_point(&curve, &a_point) {
521 return None;
522 }
523 Some((
524 EdDsaPublicKey {
525 curve: curve.clone(),
526 a_point: a_point.clone(),
527 a_table: curve.precompute_mul_table(&a_point),
528 },
529 EdDsaPrivateKey {
530 curve,
531 d: secret.clone(),
532 a_point,
533 },
534 ))
535 }
536}
537
538fn validate_public_point(curve: &TwistedEdwardsCurve, point: &EdwardsPoint) -> bool {
540 !point.is_neutral() && curve.is_on_curve(point) && point_in_prime_subgroup(curve, point)
541}
542
543fn point_in_prime_subgroup(curve: &TwistedEdwardsCurve, point: &EdwardsPoint) -> bool {
545 curve.scalar_mul(point, &curve.n).is_neutral()
546}
547
548fn challenge_scalar<H: Digest>(
550 curve: &TwistedEdwardsCurve,
551 r_point: &EdwardsPoint,
552 a_point: &EdwardsPoint,
553 message: &[u8],
554) -> BigUint {
555 let mut transcript = curve.encode_point(r_point);
556 transcript.extend_from_slice(&curve.encode_point(a_point));
557 transcript.extend_from_slice(message);
558 BigUint::from_be_bytes(&H::digest(&transcript)).modulo(&curve.n)
559}
560
561#[cfg(test)]
562mod tests {
563 use super::{EdDsa, EdDsaPrivateKey, EdDsaPublicKey, EdDsaSignature};
564 use crate::public_key::bigint::BigUint;
565 use crate::public_key::ec_edwards::ed25519;
566 use crate::public_key::io::encode_biguints;
567 use crate::{CtrDrbgAes256, Sha512};
568
569 fn rng() -> CtrDrbgAes256 {
570 CtrDrbgAes256::new(&[0x5a; 48])
571 }
572
573 #[test]
574 fn roundtrip_sign_verify_ed25519() {
575 let curve = ed25519();
576 let (public, private) = EdDsa::generate(curve, &mut rng());
577 let sig = private
578 .sign_message::<Sha512, _>(b"edwards signature", &mut rng())
579 .expect("sign");
580 assert!(public.verify_message::<Sha512>(b"edwards signature", &sig));
581 assert!(!public.verify_message::<Sha512>(b"wrong", &sig));
582 }
583
584 #[test]
585 fn sign_with_explicit_nonce_roundtrip() {
586 let curve = ed25519();
587 let secret = BigUint::from_u64(7);
588 let nonce = BigUint::from_u64(11);
589 let (public, private) = EdDsa::from_secret_scalar(curve, &secret).expect("explicit secret");
590 let sig = private
591 .sign_message_with_nonce::<Sha512>(b"abc", &nonce)
592 .expect("explicit nonce");
593 assert!(public.verify_message::<Sha512>(b"abc", &sig));
594 }
595
596 #[test]
597 fn sign_message_with_nonce_is_repeatable() {
598 let curve = ed25519();
599 let secret = BigUint::from_u64(7);
600 let nonce = BigUint::from_u64(11);
601 let (_public, private) =
602 EdDsa::from_secret_scalar(curve, &secret).expect("explicit secret");
603 let lhs = private
604 .sign_message_with_nonce::<Sha512>(b"abc", &nonce)
605 .expect("first signature");
606 let rhs = private
607 .sign_message_with_nonce::<Sha512>(b"abc", &nonce)
608 .expect("second signature");
609 assert_eq!(lhs, rhs);
610 }
611
612 #[test]
613 fn tampered_signature_is_rejected() {
614 let curve = ed25519();
615 let (public, private) = EdDsa::generate(curve, &mut rng());
616 let mut sig = private
617 .sign_message::<Sha512, _>(b"tamper", &mut rng())
618 .expect("sign");
619 sig.s = sig.s.add_ref(&BigUint::one()).modulo(&public.curve().n);
620 if sig.s.is_zero() {
621 sig.s = BigUint::one();
622 }
623 assert!(!public.verify_message::<Sha512>(b"tamper", &sig));
624 }
625
626 #[test]
627 fn key_serialization_roundtrip() {
628 let curve = ed25519();
629 let (public, private) = EdDsa::generate(curve, &mut rng());
630
631 let public_bin = public.to_key_blob();
632 let public_pem = public.to_pem();
633 let public_xml = public.to_xml();
634 assert_eq!(
635 EdDsaPublicKey::from_key_blob(&public_bin).expect("public binary"),
636 public
637 );
638 assert_eq!(
639 EdDsaPublicKey::from_pem(&public_pem).expect("public pem"),
640 public
641 );
642 assert_eq!(
643 EdDsaPublicKey::from_xml(&public_xml).expect("public xml"),
644 public
645 );
646
647 let private_bin = private.to_key_blob();
648 let private_pem = private.to_pem();
649 let private_xml = private.to_xml();
650 let private_round = EdDsaPrivateKey::from_key_blob(&private_bin).expect("private binary");
651 assert_eq!(private_round, private);
652 assert_eq!(private_round.to_public_key(), public);
653 assert_eq!(
654 EdDsaPrivateKey::from_pem(&private_pem).expect("private pem"),
655 private
656 );
657 assert_eq!(
658 EdDsaPrivateKey::from_xml(&private_xml).expect("private xml"),
659 private
660 );
661 }
662
663 #[test]
664 fn public_bytes_roundtrip() {
665 let curve = ed25519();
666 let (public, _) = EdDsa::generate(curve.clone(), &mut rng());
667 let bytes = public.to_wire_bytes();
668 let round = EdDsaPublicKey::from_wire_bytes(curve, &bytes).expect("public bytes");
669 assert_eq!(round, public);
670 }
671
672 #[test]
673 fn signature_binary_roundtrip() {
674 let curve = ed25519();
675 let (public, private) = EdDsa::generate(curve, &mut rng());
676 let sig = private
677 .sign_message::<Sha512, _>(b"serialize", &mut rng())
678 .expect("sign");
679 let blob = sig.to_key_blob();
680 let decoded = EdDsaSignature::from_key_blob(&blob, public.curve()).expect("decode sig");
681 assert_eq!(decoded, sig);
682 assert!(public.verify_message::<Sha512>(b"serialize", &decoded));
683 }
684
685 #[test]
686 fn signature_binary_rejects_out_of_range_s() {
687 let curve = ed25519();
688 let base = curve.base_point();
689 let blob = encode_biguints(&[&base.x, &base.y, &curve.n]);
690 assert!(EdDsaSignature::from_key_blob(&blob, &curve).is_none());
691 }
692}