From 27ee13b6efa6f339058582aed67acde8a6b0f4a0 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 1 Dec 2016 00:40:48 +0000 Subject: [PATCH 001/351] Initial commit --- .gitignore | 13 ++ Cargo.toml | 17 +++ REAME.md | 4 + src/ed25519.rs | 383 +++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 75 ++++++++++ 5 files changed, 492 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 REAME.md create mode 100644 src/ed25519.rs create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8188387 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +target +Cargo.lock + +.cargo + +*~ +\#* +.\#* +*.swp +*.orig +*.bak + +*.s diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..cc62481 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "ed25519" +version = "0.0.0" +authors = ["Isis Lovecruft "] +readme = "README.md" +license-file = "LICENSE" +repository = "https://code.ciph.re/isis/ed25519rs" +keywords = ["cryptography", "ed25519", "signature", "elliptic", "curve", "ECC"] +description = "A fast and efficient implementation of ed25519 signing and verification." +exclude = [ ".gitignore" ] + + +[dependencies] +arrayref = "0.3.2" +rust-crypto = "^0.2" +rand = "^0.3" +curve25519-dalek = { version = "0.0.0", git = "ssh://gogs@code.ciph.re:22/isis/curve25519-dalek.git" } diff --git a/REAME.md b/REAME.md new file mode 100644 index 0000000..c252ef6 --- /dev/null +++ b/REAME.md @@ -0,0 +1,4 @@ +# ed25519: Rust implementation + +This is a Rust implementation of ed25519 signing and verification. + diff --git a/src/ed25519.rs b/src/ed25519.rs new file mode 100644 index 0000000..59300a8 --- /dev/null +++ b/src/ed25519.rs @@ -0,0 +1,383 @@ +// -*- mode: rust; -*- +// +// To the extent possible under law, the authors have waived all copyright and +// related or neighboring rights to curve25519-dalek, using the Creative +// Commons "CC0" public domain dedication. See +// for full details. +// +// Authors: +// - Isis Agora Lovecruft + +//! A Rust implementation of ed25519 key generation, signing, and verification. + +use std::fmt::Debug; + +use crypto::digest::Digest; +use crypto::sha2::Sha512; + +use rand::Rng; + +use curve25519_dalek::curve; +use curve25519_dalek::curve::CompressedPoint; +use curve25519_dalek::curve::ExtendedPoint; +use curve25519_dalek::curve::ProjectivePoint; +use curve25519_dalek::field::FieldElement; +use curve25519_dalek::curve25519::{Curve25519Public, Curve25519Secret}; +use curve25519_dalek::scalar::Scalar; +use curve25519_dalek::util::arrays_equal_ct; + + +/// An ed25519 signature. +#[derive(Copy)] +pub struct Signature(pub [u8; 64]); + +impl Clone for Signature { + fn clone(&self) -> Self { *self } +} + +impl Debug for Signature { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "Signature: {:?}", &self.0[..]) + } +} + +impl Signature { + /// View this signature as an array of 32 bytes. + #[inline] + pub fn to_bytes(&self) -> [u8; 64] { + self.0 + } +} + +/// An ed25519 private key. +pub struct SecretKey(pub [u8; 64]); + +impl Debug for SecretKey { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "SecretKey: {:?}", &self.0[..]) + } +} + +impl SecretKey { + /// View this secret key as an array of 32 bytes. + #[inline] + pub fn to_bytes(&self) -> [u8; 64] { + self.0 + } + + /// Convert an ed25519 private key into a corresponding curve25519 private key. + /// + /// # Return + /// + /// A curve25519 public key, as would result from `PublicKey.to_curve25519()`. + pub fn to_curve25519(&self) -> Curve25519Secret { // PrivateKeyToCurve25519 + let mut h: Sha512 = Sha512::new(); + let mut hash: [u8; 64] = [0u8; 64]; + + h.input(&self.to_bytes()); + h.result(&mut hash); + + let digest: &mut [u8; 32] = array_mut_ref!(hash, 0, 32); + + digest[0] &= 248; + digest[31] &= 127; + digest[31] |= 64; + + Curve25519Secret(*digest) + } + + /// Sign a message with this keypair's secret key. + pub fn sign(&self, message: &[u8]) -> Signature { + let mut h: Sha512 = Sha512::new(); + let mut hash: [u8; 64] = [0u8; 64]; + let signature_bytes: Vec; + let mut expanded_key_secret: Scalar; + let mesg_digest: Scalar; + let hram_digest: Scalar; + let r: ExtendedPoint; + let s: Scalar; + let t: CompressedPoint; + + let secret_key: &[u8; 32] = array_ref!(&self.0, 0, 32); + let public_key: &[u8; 32] = array_ref!(&self.0, 32, 32); + + h.input(secret_key); + h.result(&mut hash); + + expanded_key_secret = Scalar(*array_ref!(&hash, 0, 32)); + expanded_key_secret[0] &= 248; + expanded_key_secret[31] &= 63; + expanded_key_secret[31] |= 64; + + h.reset(); + h.input(public_key); + h.input(&message); + h.result(&mut hash); + + mesg_digest = Scalar::reduce(&hash); + + r = ExtendedPoint::basepoint_mult(&mesg_digest); + + h.reset(); + h.input(&r.compress().to_bytes()[..]); + h.input(public_key); + h.input(&message); + h.result(&mut hash); + + hram_digest = Scalar::reduce(&hash); + + s = Scalar::multiply_add(&hram_digest, &expanded_key_secret, &mesg_digest); + t = r.compress(); + + signature_bytes = [t.0, s.0].concat(); + Signature(*array_ref!(&signature_bytes, 0, 64)) + } +} + +/// An ed25519 public key. +#[derive(Copy, Clone)] +pub struct PublicKey(pub CompressedPoint); + +impl Debug for PublicKey { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "PublicKey( CompressedPoint( {:?} ))", self.0) + } +} + +impl PublicKey { + /// View this public key as an array of 32 bytes. + #[inline] + pub fn to_bytes(&self) -> [u8; 32] { + self.0.to_bytes() + } + + /// Convert this public key to its underlying extended twisted Edwards coordinate. + #[inline] + fn decompress(&self) -> Option { + self.0.decompress() + } + + /// Convert this ed25519 public key to a curve25519 public key. + pub fn to_curve25519(&self) -> Option { // PublicKeyToCurve25519 + let a: ExtendedPoint; + let x: FieldElement; + + match self.decompress() { + Some(element) => a = element, + None => return None, + } + // a.Z == 1 as a postcondition of from_bytes() + x = a.edwards_to_montgomery_x(); + + Some(Curve25519Public(x.to_bytes())) + } + + /// Verify a signature on a message with this keypair's public key. + /// + /// # Return + /// + /// Returns true if the signature was successfully verified, and + /// false otherwise. + pub fn verify(&self, message: &[u8], signature: &Signature) -> bool { + let mut h: Sha512 = Sha512::new(); + let mut a: ExtendedPoint; + let ao: Option; + let r: ProjectivePoint; + let mut digest: [u8; 64]; + let digest_reduced: Scalar; + + if signature.0[63] & 224 != 0 { + return false; + } + ao = self.decompress(); + + if ao.is_some() { + a = ao.unwrap(); + } else { + return false; + } + a = -(&a); + + digest = [0u8; 64]; + + let top_half: &[u8; 32] = array_ref!(&signature.0, 32, 32); + let bottom_half: &[u8; 32] = array_ref!(&signature.0, 0, 32); + + h.input(&bottom_half[..]); + h.input(&self.to_bytes()); + h.input(&message); + h.result(&mut digest); + + digest_reduced = Scalar::reduce(&digest); + r = curve::double_scalar_mult_vartime(&digest_reduced, &a, &Scalar(*top_half)); + + if arrays_equal_ct(bottom_half, &r.compress().to_bytes()) == 1 { + return true + } else { + return false + } + } +} + +/// An ed25519 keypair. +#[derive(Debug)] +pub struct Keypair { + /// The public half of this keypair. + pub public: PublicKey, + /// The secret half of this keypair. + pub secret: SecretKey, +} + +impl Keypair { + /// Generate an ed25519 keypair. + /// + /// # Input + /// + /// A CSPRING with a `fill_bytes()` method, e.g. the one returned + /// from `rand::OsRng::new()` (in the `rand` crate). + // we reassign 0 bytes to the temp variable t to overwrite it + #[allow(unused_assignments)] + pub fn generate(cspring: &mut T) -> Keypair { + let mut h: Sha512 = Sha512::new(); + let mut hash: [u8; 64] = [0u8; 64]; + let mut t: [u8; 32] = [0u8; 32]; + let mut sk: [u8; 64] = [0u8; 64]; + let pk: [u8; 32]; + let mut digest: &mut [u8; 32]; + + cspring.fill_bytes(&mut t); + + h.input(&t); + h.result(&mut hash); + + digest = array_mut_ref!(&mut hash, 0, 32); + digest[0] &= 248; + digest[31] &= 127; + digest[31] |= 64; + + pk = ExtendedPoint::basepoint_mult(&Scalar(*digest)).compress().to_bytes(); + + for i in 0..32 { + sk[i] = t[i]; + sk[i+32] = pk[i]; + t[i] = 0; + } + + Keypair{ + public: PublicKey(CompressedPoint(pk)), + secret: SecretKey(sk), + } + } + + /// Sign a message with this keypair's secret key. + pub fn sign(&self, message: &[u8]) -> Signature { + self.secret.sign(message) + } + + /// Verify a signature on a message with this keypair's public key. + pub fn verify(&self, message: &[u8], signature: &Signature) -> bool { + self.public.verify(message, signature) + } +} + +#[cfg(test)] +mod test { + use test::Bencher; + use curve25519_dalek::curve::ExtendedPoint; + use rand::OsRng; + use rand::Rng; + use super::*; + + /// A fake RNG which simply returns zeroes. + struct ZeroRng; + + impl ZeroRng { + fn new() -> ZeroRng { + ZeroRng + } + } + + impl Rng for ZeroRng { + fn next_u32(&mut self) -> u32 { 0u32 } + + fn fill_bytes(&mut self, bytes: &mut [u8]) { + for i in 0 .. bytes.len() { + bytes[i] = 0; + } + } + } + + #[test] + fn test_unmarshal_marshal() { // TestUnmarshalMarshal + let mut cspring: OsRng; + let mut keypair: Keypair; + let mut x: Option; + let a: ExtendedPoint; + let public: PublicKey; + + cspring = OsRng::new().unwrap(); + + // from_bytes() fails if vx²-u=0 and vx²+u=0 + loop { + keypair = Keypair::generate(&mut cspring); + x = keypair.public.decompress(); + + if x.is_some() { + a = x.unwrap(); + break; + } + } + public = PublicKey(a.compress()); + + assert!(keypair.public.0 == public.0); + } + + #[test] + fn test_sign_verify() { // TestSignVerify + let mut cspring: OsRng; + let keypair: Keypair; + let good_sig: Signature; + let bad_sig: Signature; + + let good: &[u8] = "test message".as_bytes(); + let bad: &[u8] = "wrong message".as_bytes(); + + cspring = OsRng::new().unwrap(); + keypair = Keypair::generate(&mut cspring); + good_sig = keypair.sign(&good); + bad_sig = keypair.sign(&bad); + + assert!(keypair.verify(&good, &good_sig) == true, + "Verification of a valid signature failed!"); + assert!(keypair.verify(&good, &bad_sig) == false, + "Verification of a signature on a different message passed!"); + assert!(keypair.verify(&bad, &good_sig) == false, + "Verification of a signature on a different message passed!"); + } + + #[bench] + fn bench_sign(b: &mut Bencher) { + let mut cspring: OsRng = OsRng::new().unwrap(); + let keypair: Keypair = Keypair::generate(&mut cspring); + let msg: &[u8] = "test message".as_bytes(); + + b.iter(| | keypair.sign(msg)); + } + + #[bench] + fn bench_verify(b: &mut Bencher) { + let mut cspring: OsRng = OsRng::new().unwrap(); + let keypair: Keypair = Keypair::generate(&mut cspring); + let msg: &[u8] = "test message".as_bytes(); + let sig: Signature = keypair.sign(msg); + + b.iter(| | keypair.verify(msg, &sig)); + } + + #[bench] + fn bench_key_generation(b: &mut Bencher) { + let mut rng: ZeroRng = ZeroRng::new(); + + b.iter(| | Keypair::generate(&mut rng)); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..780ba15 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,75 @@ +// -*- mode: rust; -*- +// +// To the extent possible under law, the authors have waived all copyright and +// related or neighboring rights to curve25519-dalek, using the Creative +// Commons "CC0" public domain dedication. See +// for full details. +// +// Authors: +// - Isis Agora Lovecruft + +//! ed25519 signatures and verification +//! +//! # Example +//! +//! Creating an ed25519 signature on a message is simple. +//! +//! First, we need to generate a `Keypair`, which includes both public +//! and secret halves of an asymmetric key. To do so, we need a +//! cryptographically secure random number generator (CSPRING). For +//! this example, we'll use the operating system's builtin PRNG to +//! generate a keypair: +//! +//! ```ignore +//! extern crate rand; +//! extern crate ed25519; +//! +//! use rand::Rng; +//! use rand::OsRng; +//! use ed25519::Keypair; +//! +//! let mut cspring: OsRng = OsRng::new().unwrap(); +//! let keypair: Keypair = Keypair::generate(&mut cspring); +//! ``` +//! +//! We can now use this `keypair` to sign a message: +//! +//! ```ignore +//! let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); +//! let signature: Signature = keypair.sign(message); +//! ``` +//! +//! As well as to verify that this is, indeed, a valid signature on +//! that `message`: +//! +//! ```ignore +//! let verified: bool = keypair.verify(message, &signature); +//! +//! assert!(verified); +//! ``` +//! +//! Anyone else, given the `public` half of the `keypair` can also easily +//! verify this signature: +//! +//! ```ignore +//! let public_key: PublicKey = keypair.public; +//! let verified: bool = public_key.verify(message, &signature); +//! +//! assert!(verified); +//! ``` + +#![feature(rand)] +#![allow(unused_features)] +#![feature(test)] + +#[macro_use] +extern crate arrayref; +extern crate crypto; +extern crate curve25519_dalek; +extern crate rand; +extern crate test; + +mod ed25519; + +// Export everything public in ed25519. +pub use ed25519::*; From 9186fb9a6d23d50088785f6f42eabc256d256947 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 8 Dec 2016 10:18:43 +0000 Subject: [PATCH 002/351] Bump to version 0.1.0 in Cargo.toml. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index cc62481..e3cb200 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519" -version = "0.0.0" +version = "0.1.0" authors = ["Isis Lovecruft "] readme = "README.md" license-file = "LICENSE" From 1dc3865bf001d219c32ee1cfa0d1e8c1c4d7af10 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 8 Dec 2016 10:19:01 +0000 Subject: [PATCH 003/351] Fix up the keywords and description. --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e3cb200..fe8dbc5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,8 +5,8 @@ authors = ["Isis Lovecruft "] readme = "README.md" license-file = "LICENSE" repository = "https://code.ciph.re/isis/ed25519rs" -keywords = ["cryptography", "ed25519", "signature", "elliptic", "curve", "ECC"] -description = "A fast and efficient implementation of ed25519 signing and verification." +keywords = ["cryptography", "ed25519", "signature", "ECC"] +description = "Fast and efficient ed25519 signing and verification." exclude = [ ".gitignore" ] From d9ec8386da7eb7a12f4625be2e344e0d5fed1ae9 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 8 Dec 2016 10:19:34 +0000 Subject: [PATCH 004/351] Remove conversion to/from X25519 keys. --- src/ed25519.rs | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 59300a8..330d7a7 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -22,7 +22,6 @@ use curve25519_dalek::curve::CompressedPoint; use curve25519_dalek::curve::ExtendedPoint; use curve25519_dalek::curve::ProjectivePoint; use curve25519_dalek::field::FieldElement; -use curve25519_dalek::curve25519::{Curve25519Public, Curve25519Secret}; use curve25519_dalek::scalar::Scalar; use curve25519_dalek::util::arrays_equal_ct; @@ -65,27 +64,6 @@ impl SecretKey { self.0 } - /// Convert an ed25519 private key into a corresponding curve25519 private key. - /// - /// # Return - /// - /// A curve25519 public key, as would result from `PublicKey.to_curve25519()`. - pub fn to_curve25519(&self) -> Curve25519Secret { // PrivateKeyToCurve25519 - let mut h: Sha512 = Sha512::new(); - let mut hash: [u8; 64] = [0u8; 64]; - - h.input(&self.to_bytes()); - h.result(&mut hash); - - let digest: &mut [u8; 32] = array_mut_ref!(hash, 0, 32); - - digest[0] &= 248; - digest[31] &= 127; - digest[31] |= 64; - - Curve25519Secret(*digest) - } - /// Sign a message with this keypair's secret key. pub fn sign(&self, message: &[u8]) -> Signature { let mut h: Sha512 = Sha512::new(); @@ -157,21 +135,6 @@ impl PublicKey { self.0.decompress() } - /// Convert this ed25519 public key to a curve25519 public key. - pub fn to_curve25519(&self) -> Option { // PublicKeyToCurve25519 - let a: ExtendedPoint; - let x: FieldElement; - - match self.decompress() { - Some(element) => a = element, - None => return None, - } - // a.Z == 1 as a postcondition of from_bytes() - x = a.edwards_to_montgomery_x(); - - Some(Curve25519Public(x.to_bytes())) - } - /// Verify a signature on a message with this keypair's public key. /// /// # Return From b4bcc5e3b5a7ba9c675d948a7256258ea1da5b88 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 8 Dec 2016 10:20:52 +0000 Subject: [PATCH 005/351] Remove an unused import. --- src/ed25519.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 330d7a7..425ae78 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -21,7 +21,6 @@ use curve25519_dalek::curve; use curve25519_dalek::curve::CompressedPoint; use curve25519_dalek::curve::ExtendedPoint; use curve25519_dalek::curve::ProjectivePoint; -use curve25519_dalek::field::FieldElement; use curve25519_dalek::scalar::Scalar; use curve25519_dalek::util::arrays_equal_ct; From a64bbfc09ffbc10ca23b1fd623187d587cd2a30a Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 8 Dec 2016 10:39:07 +0000 Subject: [PATCH 006/351] Change curve25519-dalek requirement to 0.1.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index fe8dbc5..ec82490 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,4 +14,4 @@ exclude = [ ".gitignore" ] arrayref = "0.3.2" rust-crypto = "^0.2" rand = "^0.3" -curve25519-dalek = { version = "0.0.0", git = "ssh://gogs@code.ciph.re:22/isis/curve25519-dalek.git" } +curve25519-dalek = "0.1.0" From f7d9c3718e9161f42450f4573ebb97748cc88d60 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 8 Dec 2016 11:34:56 +0000 Subject: [PATCH 007/351] Rewrite the README. --- REAME.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/REAME.md b/REAME.md index c252ef6..c825da1 100644 --- a/REAME.md +++ b/REAME.md @@ -1,4 +1,19 @@ -# ed25519: Rust implementation +# ed25519: a Rust implementation -This is a Rust implementation of ed25519 signing and verification. +Fast and efficient Rust implementation of ed25519 key generation, signing, and +verification. +# Installation + +To install, add the following to the dependencies section of your project's +`Cargo.toml`: + + ed25519 = "0.1.0" + +Then, in your library or executable source, add: + + extern crate ed25519 + +# TODO + + * Maybe add methods to make exporting keys for backup easier. From 1edff9723112d6fcc85aeddba7d285ca408794aa Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 8 Dec 2016 11:46:08 +0000 Subject: [PATCH 008/351] Add benchmarks to the README. --- REAME.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/REAME.md b/REAME.md index c825da1..b33873a 100644 --- a/REAME.md +++ b/REAME.md @@ -3,6 +3,39 @@ Fast and efficient Rust implementation of ed25519 key generation, signing, and verification. +# Benchmarks + +On an Intel i5 Sandy Bridge running at 2.6 GHz, with TurboBoost enabled (and +also running in QubesOS with *lots* of other VMs executing), this code +achieves the following performance benchmarks: + + ∃!isisⒶwintermute:(release/0.1.0 *$)~/code/rust/ed25519 ∴ cargo bench + Finished release [optimized] target(s) in 0.0 secs + Running target/release/deps/ed25519-0135748522c518d8 + + running 5 tests + test ed25519::test::test_sign_verify ... ignored + test ed25519::test::test_unmarshal_marshal ... ignored + test ed25519::test::bench_key_generation ... bench: 54,837 ns/iter (+/- 11,613) + test ed25519::test::bench_sign ... bench: 69,735 ns/iter (+/- 21,902) + test ed25519::test::bench_verify ... bench: 183,891 ns/iter (+/- 75,304) + + test result: ok. 0 passed; 0 failed; 2 ignored; 3 measured + +In comparision, the equivalent package in Golang performs as follows: + + ∃!isisⒶwintermute:(master *=)~/code/go/src/github.com/agl/ed25519 ∴ go test -bench . + PASS + BenchmarkKeyGeneration 20000 85880 ns/op + BenchmarkSigning 20000 89115 ns/op + BenchmarkVerification 10000 212585 ns/op + ok github.com/agl/ed25519 7.500s + +Making key generation, signing, and verification a rough average of one third +faster, one fifth faster, and one eighth faster respectively. Of course, this +is just my machine, and these results—nowhere near rigorous—should be taken +with a fistful of salt. + # Installation To install, add the following to the dependencies section of your project's @@ -17,3 +50,4 @@ Then, in your library or executable source, add: # TODO * Maybe add methods to make exporting keys for backup easier. + * Benchmark in comparison to the ed25519_ref10 code. From 0282179c8a9a1e5bb201ef94cd09902b0033c4ce Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 8 Dec 2016 21:40:01 +0000 Subject: [PATCH 009/351] Use 'license' directive rather than 'license-file'. When using the 'licence-file' directive, crates.io states that the license is "non-standard", when really it should say that the code is CC0 (public domain). --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ec82490..078f0b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "ed25519" version = "0.1.0" authors = ["Isis Lovecruft "] readme = "README.md" -license-file = "LICENSE" +license = "CC0-1.0" repository = "https://code.ciph.re/isis/ed25519rs" keywords = ["cryptography", "ed25519", "signature", "ECC"] description = "Fast and efficient ed25519 signing and verification." From d943e2b323041459e1bfdb133b25610bc4d15a29 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 8 Dec 2016 22:02:37 +0000 Subject: [PATCH 010/351] Rename the repo. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 078f0b0..c33c7d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Isis Lovecruft "] readme = "README.md" license = "CC0-1.0" -repository = "https://code.ciph.re/isis/ed25519rs" +repository = "https://code.ciph.re/isis/ed25519-dalek" keywords = ["cryptography", "ed25519", "signature", "ECC"] description = "Fast and efficient ed25519 signing and verification." exclude = [ ".gitignore" ] From 67137255da0a57d39e276ce9ef3c812b50d68bdc Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 8 Dec 2016 22:21:57 +0000 Subject: [PATCH 011/351] Change the package name to ed25519-dalek. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c33c7d0..ce065e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ed25519" +name = "ed25519-dalek" version = "0.1.0" authors = ["Isis Lovecruft "] readme = "README.md" From 2a74e2858780c2925b90af2f2e634122ae8944e6 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 8 Dec 2016 22:22:22 +0000 Subject: [PATCH 012/351] Use any curve25519-dalek version greater than 0.1. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ce065e0..bb58d4f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,4 +14,4 @@ exclude = [ ".gitignore" ] arrayref = "0.3.2" rust-crypto = "^0.2" rand = "^0.3" -curve25519-dalek = "0.1.0" +curve25519-dalek = "^0.1" From d5f27c471cce64c41cc04af3403e971760622b2d Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 8 Dec 2016 22:25:31 +0000 Subject: [PATCH 013/351] Add warning and documentation sections to the README. --- REAME.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/REAME.md b/REAME.md index b33873a..e270bde 100644 --- a/REAME.md +++ b/REAME.md @@ -36,6 +36,19 @@ faster, one fifth faster, and one eighth faster respectively. Of course, this is just my machine, and these results—nowhere near rigorous—should be taken with a fistful of salt. +## Warning + +[Our elliptic curve library](https://github.com/isislovecruft/curve25519-dalek) +(which this code uses) has **not** yet received sufficient peer review by +other qualified cryptographers to be considered in any way, shape, or form, +safe. + +**USE AT YOUR OWN RISK** + +# Documentation + +Documentation is available [here](https://docs.rs/ed25519-dalek). + # Installation To install, add the following to the dependencies section of your project's From 40330e4005c0498a0195c711997588fc75f6f239 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 8 Dec 2016 22:28:34 +0000 Subject: [PATCH 014/351] Add badges. --- REAME.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/REAME.md b/REAME.md index e270bde..a315d33 100644 --- a/REAME.md +++ b/REAME.md @@ -1,7 +1,7 @@ -# ed25519: a Rust implementation +# ed25519-dalek ![](https://img.shields.io/crates/v/ed25519-dalek.svg) ![](https://docs.rs/ed25519-dalek/badge.svg) Fast and efficient Rust implementation of ed25519 key generation, signing, and -verification. +verification in Rust. # Benchmarks From b36b4ccc1c020f9ed863b52f62e865d39996a509 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 8 Dec 2016 22:32:51 +0000 Subject: [PATCH 015/351] Add a TODO for how we could speed this up even further. --- REAME.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/REAME.md b/REAME.md index a315d33..4e6dbb3 100644 --- a/REAME.md +++ b/REAME.md @@ -64,3 +64,7 @@ Then, in your library or executable source, add: * Maybe add methods to make exporting keys for backup easier. * Benchmark in comparison to the ed25519_ref10 code. + * We can probably make this go even faster if we implement SHA512, + rather than using the rust-crypto implementation whose API requires + that we allocate memory and memzero it before mutating to store the + digest. From 50f53841bf0ec6db41f0aef4ddc5684ab0172fef Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 9 Dec 2016 00:52:38 +0000 Subject: [PATCH 016/351] Ensure signatures match bytewise with reference and Go implementations. --- Cargo.toml | 5 +- TESTVECTORS | 128 ++++++++++++++++++++++++++++++++++++++++ src/ed25519.rs | 155 ++++++++++++++++++++++++++++++++++++++++++++++++- src/lib.rs | 4 ++ 4 files changed, 290 insertions(+), 2 deletions(-) create mode 100644 TESTVECTORS diff --git a/Cargo.toml b/Cargo.toml index bb58d4f..5afaa97 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ license = "CC0-1.0" repository = "https://code.ciph.re/isis/ed25519-dalek" keywords = ["cryptography", "ed25519", "signature", "ECC"] description = "Fast and efficient ed25519 signing and verification." -exclude = [ ".gitignore" ] +exclude = [ ".gitignore", "TESTVECTORS" ] [dependencies] @@ -15,3 +15,6 @@ arrayref = "0.3.2" rust-crypto = "^0.2" rand = "^0.3" curve25519-dalek = "^0.1" + +[dev-dependencies] +rustc-serialize = "0.3" diff --git a/TESTVECTORS b/TESTVECTORS new file mode 100644 index 0000000..4234759 --- /dev/null +++ b/TESTVECTORS @@ -0,0 +1,128 @@ +9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a:d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a::e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b: +4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c:3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c:72:92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c0072: +c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025:fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025:af82:6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40aaf82: +0d4a05b07352a5436e180356da0ae6efa0345ff7fb1572575772e8005ed978e9e61a185bcef2613a6c7cb79763ce945d3b245d76114dd440bcf5f2dc1aa57057:e61a185bcef2613a6c7cb79763ce945d3b245d76114dd440bcf5f2dc1aa57057:cbc77b:d9868d52c2bebce5f3fa5a79891970f309cb6591e3e1702a70276fa97c24b3a8e58606c38c9758529da50ee31b8219cba45271c689afa60b0ea26c99db19b00ccbc77b: +6df9340c138cc188b5fe4464ebaa3f7fc206a2d55c3434707e74c9fc04e20ebbc0dac102c4533186e25dc43128472353eaabdb878b152aeb8e001f92d90233a7:c0dac102c4533186e25dc43128472353eaabdb878b152aeb8e001f92d90233a7:5f4c8989:124f6fc6b0d100842769e71bd530664d888df8507df6c56dedfdb509aeb93416e26b918d38aa06305df3095697c18b2aa832eaa52edc0ae49fbae5a85e150c075f4c8989: +b780381a65edf8b78f6945e8dbec7941ac049fd4c61040cf0c324357975a293ce253af0766804b869bb1595be9765b534886bbaab8305bf50dbc7f899bfb5f01:e253af0766804b869bb1595be9765b534886bbaab8305bf50dbc7f899bfb5f01:18b6bec097:b2fc46ad47af464478c199e1f8be169f1be6327c7f9a0a6689371ca94caf04064a01b22aff1520abd58951341603faed768cf78ce97ae7b038abfe456aa17c0918b6bec097: +78ae9effe6f245e924a7be63041146ebc670dbd3060cba67fbc6216febc44546fbcfbfa40505d7f2be444a33d185cc54e16d615260e1640b2b5087b83ee3643d:fbcfbfa40505d7f2be444a33d185cc54e16d615260e1640b2b5087b83ee3643d:89010d855972:6ed629fc1d9ce9e1468755ff636d5a3f40a5d9c91afd93b79d241830f7e5fa29854b8f20cc6eecbb248dbd8d16d14e99752194e4904d09c74d639518839d230089010d855972: +691865bfc82a1e4b574eecde4c7519093faf0cf867380234e3664645c61c5f7998a5e3a36e67aaba89888bf093de1ad963e774013b3902bfab356d8b90178a63:98a5e3a36e67aaba89888bf093de1ad963e774013b3902bfab356d8b90178a63:b4a8f381e70e7a:6e0af2fe55ae377a6b7a7278edfb419bd321e06d0df5e27037db8812e7e3529810fa5552f6c0020985ca17a0e02e036d7b222a24f99b77b75fdd16cb05568107b4a8f381e70e7a: +3b26516fb3dc88eb181b9ed73f0bcd52bcd6b4c788e4bcaf46057fd078bee073f81fb54a825fced95eb033afcd64314075abfb0abd20a970892503436f34b863:f81fb54a825fced95eb033afcd64314075abfb0abd20a970892503436f34b863:4284abc51bb67235:d6addec5afb0528ac17bb178d3e7f2887f9adbb1ad16e110545ef3bc57f9de2314a5c8388f723b8907be0f3ac90c6259bbe885ecc17645df3db7d488f805fa084284abc51bb67235: +edc6f5fbdd1cee4d101c063530a30490b221be68c036f5b07d0f953b745df192c1a49c66e617f9ef5ec66bc4c6564ca33de2a5fb5e1464062e6d6c6219155efd:c1a49c66e617f9ef5ec66bc4c6564ca33de2a5fb5e1464062e6d6c6219155efd:672bf8965d04bc5146:2c76a04af2391c147082e33faacdbe56642a1e134bd388620b852b901a6bc16ff6c9cc9404c41dea12ed281da067a1513866f9d964f8bdd24953856c50042901672bf8965d04bc5146: +4e7d21fb3b1897571a445833be0f9fd41cd62be3aa04040f8934e1fcbdcacd4531b2524b8348f7ab1dfafa675cc538e9a84e3fe5819e27c12ad8bbc1a36e4dff:31b2524b8348f7ab1dfafa675cc538e9a84e3fe5819e27c12ad8bbc1a36e4dff:33d7a786aded8c1bf691:28e4598c415ae9de01f03f9f3fab4e919e8bf537dd2b0cdf6e79b9e6559c9409d9151a4c40f083193937627c369488259e99da5a9f0a87497fa6696a5dd6ce0833d7a786aded8c1bf691: +a980f892db13c99a3e8971e965b2ff3d41eafd54093bc9f34d1fd22d84115bb644b57ee30cdb55829d0a5d4f046baef078f1e97a7f21b62d75f8e96ea139c35f:44b57ee30cdb55829d0a5d4f046baef078f1e97a7f21b62d75f8e96ea139c35f:3486f68848a65a0eb5507d:77d389e599630d934076329583cd4105a649a9292abc44cd28c40000c8e2f5ac7660a81c85b72af8452d7d25c070861dae91601c7803d656531650dd4e5c41003486f68848a65a0eb5507d: +5b5a619f8ce1c66d7ce26e5a2ae7b0c04febcd346d286c929e19d0d5973bfef96fe83693d011d111131c4f3fbaaa40a9d3d76b30012ff73bb0e39ec27ab18257:6fe83693d011d111131c4f3fbaaa40a9d3d76b30012ff73bb0e39ec27ab18257:5a8d9d0a22357e6655f9c785:0f9ad9793033a2fa06614b277d37381e6d94f65ac2a5a94558d09ed6ce922258c1a567952e863ac94297aec3c0d0c8ddf71084e504860bb6ba27449b55adc40e5a8d9d0a22357e6655f9c785: +940c89fe40a81dafbdb2416d14ae469119869744410c3303bfaa0241dac57800a2eb8c0501e30bae0cf842d2bde8dec7386f6b7fc3981b8c57c9792bb94cf2dd:a2eb8c0501e30bae0cf842d2bde8dec7386f6b7fc3981b8c57c9792bb94cf2dd:b87d3813e03f58cf19fd0b6395:d8bb64aad8c9955a115a793addd24f7f2b077648714f49c4694ec995b330d09d640df310f447fd7b6cb5c14f9fe9f490bcf8cfadbfd2169c8ac20d3b8af49a0cb87d3813e03f58cf19fd0b6395: +9acad959d216212d789a119252ebfe0c96512a23c73bd9f3b202292d6916a738cf3af898467a5b7a52d33d53bc037e2642a8da996903fc252217e9c033e2f291:cf3af898467a5b7a52d33d53bc037e2642a8da996903fc252217e9c033e2f291:55c7fa434f5ed8cdec2b7aeac173:6ee3fe81e23c60eb2312b2006b3b25e6838e02106623f844c44edb8dafd66ab0671087fd195df5b8f58a1d6e52af42908053d55c7321010092748795ef94cf0655c7fa434f5ed8cdec2b7aeac173: +d5aeee41eeb0e9d1bf8337f939587ebe296161e6bf5209f591ec939e1440c300fd2a565723163e29f53c9de3d5e8fbe36a7ab66e1439ec4eae9c0a604af291a5:fd2a565723163e29f53c9de3d5e8fbe36a7ab66e1439ec4eae9c0a604af291a5:0a688e79be24f866286d4646b5d81c:f68d04847e5b249737899c014d31c805c5007a62c0a10d50bb1538c5f35503951fbc1e08682f2cc0c92efe8f4985dec61dcbd54d4b94a22547d24451271c8b000a688e79be24f866286d4646b5d81c: +0a47d10452ae2febec518a1c7c362890c3fc1a49d34b03b6467d35c904a8362d34e5a8508c4743746962c066e4badea2201b8ab484de5c4f94476ccd2143955b:34e5a8508c4743746962c066e4badea2201b8ab484de5c4f94476ccd2143955b:c942fa7ac6b23ab7ff612fdc8e68ef39:2a3d27dc40d0a8127949a3b7f908b3688f63b7f14f651aacd715940bdbe27a0809aac142f47ab0e1e44fa490ba87ce5392f33a891539caf1ef4c367cae54500cc942fa7ac6b23ab7ff612fdc8e68ef39: +f8148f7506b775ef46fdc8e8c756516812d47d6cfbfa318c27c9a22641e56f170445e456dacc7d5b0bbed23c8200cdb74bdcb03e4c7b73f0a2b9b46eac5d4372:0445e456dacc7d5b0bbed23c8200cdb74bdcb03e4c7b73f0a2b9b46eac5d4372:7368724a5b0efb57d28d97622dbde725af:3653ccb21219202b8436fb41a32ba2618c4a133431e6e63463ceb3b6106c4d56e1d2ba165ba76eaad3dc39bffb130f1de3d8e6427db5b71938db4e272bc3e20b7368724a5b0efb57d28d97622dbde725af: +77f88691c4eff23ebb7364947092951a5ff3f10785b417e918823a552dab7c7574d29127f199d86a8676aec33b4ce3f225ccb191f52c191ccd1e8cca65213a6b:74d29127f199d86a8676aec33b4ce3f225ccb191f52c191ccd1e8cca65213a6b:bd8e05033f3a8bcdcbf4beceb70901c82e31:fbe929d743a03c17910575492f3092ee2a2bf14a60a3fcacec74a58c7334510fc262db582791322d6c8c41f1700adb80027ecabc14270b703444ae3ee7623e0abd8e05033f3a8bcdcbf4beceb70901c82e31: +ab6f7aee6a0837b334ba5eb1b2ad7fcecfab7e323cab187fe2e0a95d80eff1325b96dca497875bf9664c5e75facf3f9bc54bae913d66ca15ee85f1491ca24d2c:5b96dca497875bf9664c5e75facf3f9bc54bae913d66ca15ee85f1491ca24d2c:8171456f8b907189b1d779e26bc5afbb08c67a:73bca64e9dd0db88138eedfafcea8f5436cfb74bfb0e7733cf349baa0c49775c56d5934e1d38e36f39b7c5beb0a836510c45126f8ec4b6810519905b0ca07c098171456f8b907189b1d779e26bc5afbb08c67a: +8d135de7c8411bbdbd1b31e5dc678f2ac7109e792b60f38cd24936e8a898c32d1ca281938529896535a7714e3584085b86ef9fec723f42819fc8dd5d8c00817f:1ca281938529896535a7714e3584085b86ef9fec723f42819fc8dd5d8c00817f:8ba6a4c9a15a244a9c26bb2a59b1026f21348b49:a1adc2bc6a2d980662677e7fdff6424de7dba50f5795ca90fdf3e96e256f3285cac71d3360482e993d0294ba4ec7440c61affdf35fe83e6e04263937db93f1058ba6a4c9a15a244a9c26bb2a59b1026f21348b49: +0e765d720e705f9366c1ab8c3fa84c9a44370c06969f803296884b2846a652a47fae45dd0a05971026d410bc497af5be7d0827a82a145c203f625dfcb8b03ba8:7fae45dd0a05971026d410bc497af5be7d0827a82a145c203f625dfcb8b03ba8:1d566a6232bbaab3e6d8804bb518a498ed0f904986:bb61cf84de61862207c6a455258bc4db4e15eea0317ff88718b882a06b5cf6ec6fd20c5a269e5d5c805bafbcc579e2590af414c7c227273c102a10070cdfe80f1d566a6232bbaab3e6d8804bb518a498ed0f904986: +db36e326d676c2d19cc8fe0c14b709202ecfc761d27089eb6ea4b1bb021ecfa748359b850d23f0715d94bb8bb75e7e14322eaf14f06f28a805403fbda002fc85:48359b850d23f0715d94bb8bb75e7e14322eaf14f06f28a805403fbda002fc85:1b0afb0ac4ba9ab7b7172cddc9eb42bba1a64bce47d4:b6dcd09989dfbac54322a3ce87876e1d62134da998c79d24b50bd7a6a797d86a0e14dc9d7491d6c14a673c652cfbec9f962a38c945da3b2f0879d0b68a9213001b0afb0ac4ba9ab7b7172cddc9eb42bba1a64bce47d4: +c89955e0f7741d905df0730b3dc2b0ce1a13134e44fef3d40d60c020ef19df77fdb30673402faf1c8033714f3517e47cc0f91fe70cf3836d6c23636e3fd2287c:fdb30673402faf1c8033714f3517e47cc0f91fe70cf3836d6c23636e3fd2287c:507c94c8820d2a5793cbf3442b3d71936f35fe3afef316:7ef66e5e86f2360848e0014e94880ae2920ad8a3185a46b35d1e07dea8fa8ae4f6b843ba174d99fa7986654a0891c12a794455669375bf92af4cc2770b579e0c507c94c8820d2a5793cbf3442b3d71936f35fe3afef316: +4e62627fc221142478aee7f00781f817f662e3b75db29bb14ab47cf8e84104d6b1d39801892027d58a8c64335163195893bfc1b61dbeca3260497e1f30371107:b1d39801892027d58a8c64335163195893bfc1b61dbeca3260497e1f30371107:d3d615a8472d9962bb70c5b5466a3d983a4811046e2a0ef5:836afa764d9c48aa4770a4388b654e97b3c16f082967febca27f2fc47ddfd9244b03cfc729698acf5109704346b60b230f255430089ddc56912399d1122de70ad3d615a8472d9962bb70c5b5466a3d983a4811046e2a0ef5: +6b83d7da8908c3e7205b39864b56e5f3e17196a3fc9c2f5805aad0f5554c142dd0c846f97fe28585c0ee159015d64c56311c886eddcc185d296dbb165d2625d6:d0c846f97fe28585c0ee159015d64c56311c886eddcc185d296dbb165d2625d6:6ada80b6fa84f7034920789e8536b82d5e4678059aed27f71c:16e462a29a6dd498685a3718b3eed00cc1598601ee47820486032d6b9acc9bf89f57684e08d8c0f05589cda2882a05dc4c63f9d0431d6552710812433003bc086ada80b6fa84f7034920789e8536b82d5e4678059aed27f71c: +19a91fe23a4e9e33ecc474878f57c64cf154b394203487a7035e1ad9cd697b0d2bf32ba142ba4622d8f3e29ecd85eea07b9c47be9d64412c9b510b27dd218b23:2bf32ba142ba4622d8f3e29ecd85eea07b9c47be9d64412c9b510b27dd218b23:82cb53c4d5a013bae5070759ec06c3c6955ab7a4050958ec328c:881f5b8c5a030df0f75b6634b070dd27bd1ee3c08738ae349338b3ee6469bbf9760b13578a237d5182535ede121283027a90b5f865d63a6537dca07b44049a0f82cb53c4d5a013bae5070759ec06c3c6955ab7a4050958ec328c: +1d5b8cb6215c18141666baeefcf5d69dad5bea9a3493dddaa357a4397a13d4de94d23d977c33e49e5e4992c68f25ec99a27c41ce6b91f2bfa0cd8292fe962835:94d23d977c33e49e5e4992c68f25ec99a27c41ce6b91f2bfa0cd8292fe962835:a9a8cbb0ad585124e522abbfb40533bdd6f49347b55b18e8558cb0:3acd39bec8c3cd2b44299722b5850a0400c1443590fd4861d59aae7496acb3df73fc3fdf7969ae5f50ba47dddc435246e5fd376f6b891cd4c2caf5d614b6170ca9a8cbb0ad585124e522abbfb40533bdd6f49347b55b18e8558cb0: +6a91b3227c472299089bdce9356e726a40efd840f11002708b7ee55b64105ac29d084aa8b97a6b9bafa496dbc6f76f3306a116c9d917e681520a0f914369427e:9d084aa8b97a6b9bafa496dbc6f76f3306a116c9d917e681520a0f914369427e:5cb6f9aa59b80eca14f6a68fb40cf07b794e75171fba96262c1c6adc:f5875423781b66216cb5e8998de5d9ffc29d1d67107054ace3374503a9c3ef811577f269de81296744bd706f1ac478caf09b54cdf871b3f802bd57f9a6cb91015cb6f9aa59b80eca14f6a68fb40cf07b794e75171fba96262c1c6adc: +93eaa854d791f05372ce72b94fc6503b2ff8ae6819e6a21afe825e27ada9e4fb16cee8a3f2631834c88b670897ff0b08ce90cc147b4593b3f1f403727f7e7ad5:16cee8a3f2631834c88b670897ff0b08ce90cc147b4593b3f1f403727f7e7ad5:32fe27994124202153b5c70d3813fdee9c2aa6e7dc743d4d535f1840a5:d834197c1a3080614e0a5fa0aaaa808824f21c38d692e6ffbd200f7dfb3c8f44402a7382180b98ad0afc8eec1a02acecf3cb7fde627b9f18111f260ab1db9a0732fe27994124202153b5c70d3813fdee9c2aa6e7dc743d4d535f1840a5: +941cac69fb7b1815c57bb987c4d6c2ad2c35d5f9a3182a79d4ba13eab253a8ad23be323c562dfd71ce65f5bba56a74a3a6dfc36b573d2f94f635c7f9b4fd5a5b:23be323c562dfd71ce65f5bba56a74a3a6dfc36b573d2f94f635c7f9b4fd5a5b:bb3172795710fe00054d3b5dfef8a11623582da68bf8e46d72d27cece2aa:0f8fad1e6bde771b4f5420eac75c378bae6db5ac6650cd2bc210c1823b432b48e016b10595458ffab92f7a8989b293ceb8dfed6c243a2038fc06652aaaf16f02bb3172795710fe00054d3b5dfef8a11623582da68bf8e46d72d27cece2aa: +1acdbb793b0384934627470d795c3d1dd4d79cea59ef983f295b9b59179cbb283f60c7541afa76c019cf5aa82dcdb088ed9e4ed9780514aefb379dabc844f31a:3f60c7541afa76c019cf5aa82dcdb088ed9e4ed9780514aefb379dabc844f31a:7cf34f75c3dac9a804d0fcd09eba9b29c9484e8a018fa9e073042df88e3c56:be71ef4806cb041d885effd9e6b0fbb73d65d7cdec47a89c8a994892f4e55a568c4cc78d61f901e80dbb628b86a23ccd594e712b57fa94c2d67ec266348785077cf34f75c3dac9a804d0fcd09eba9b29c9484e8a018fa9e073042df88e3c56: +8ed7a797b9cea8a8370d419136bcdf683b759d2e3c6947f17e13e2485aa9d420b49f3a78b1c6a7fca8f3466f33bc0e929f01fba04306c2a7465f46c3759316d9:b49f3a78b1c6a7fca8f3466f33bc0e929f01fba04306c2a7465f46c3759316d9:a750c232933dc14b1184d86d8b4ce72e16d69744ba69818b6ac33b1d823bb2c3:04266c033b91c1322ceb3446c901ffcf3cc40c4034e887c9597ca1893ba7330becbbd8b48142ef35c012c6ba51a66df9308cb6268ad6b1e4b03e70102495790ba750c232933dc14b1184d86d8b4ce72e16d69744ba69818b6ac33b1d823bb2c3: +f2ab396fe8906e3e5633e99cabcd5b09df0859b516230b1e0450b580b65f616c8ea074245159a116aa7122a25ec16b891d625a68f33660423908f6bdc44f8c1b:8ea074245159a116aa7122a25ec16b891d625a68f33660423908f6bdc44f8c1b:5a44e34b746c5fd1898d552ab354d28fb4713856d7697dd63eb9bd6b99c280e187:a06a23d982d81ab883aae230adbc368a6a9977f003cebb00d4c2e4018490191a84d3a282fdbfb2fc88046e62de43e15fb575336b3c8b77d19ce6a009ce51f50c5a44e34b746c5fd1898d552ab354d28fb4713856d7697dd63eb9bd6b99c280e187: +550a41c013f79bab8f06e43ad1836d51312736a9713806fafe6645219eaa1f9daf6b7145474dc9954b9af93a9cdb34449d5b7c651c824d24e230b90033ce59c0:af6b7145474dc9954b9af93a9cdb34449d5b7c651c824d24e230b90033ce59c0:8bc4185e50e57d5f87f47515fe2b1837d585f0aae9e1ca383b3ec908884bb900ff27:16dc1e2b9fa909eefdc277ba16ebe207b8da5e91143cde78c5047a89f681c33c4e4e3428d5c928095903a811ec002d52a39ed7f8b3fe1927200c6dd0b9ab3e048bc4185e50e57d5f87f47515fe2b1837d585f0aae9e1ca383b3ec908884bb900ff27: +19ac3e272438c72ddf7b881964867cb3b31ff4c793bb7ea154613c1db068cb7ef85b80e050a1b9620db138bfc9e100327e25c257c59217b601f1f6ac9a413d3f:f85b80e050a1b9620db138bfc9e100327e25c257c59217b601f1f6ac9a413d3f:95872d5f789f95484e30cbb0e114028953b16f5c6a8d9f65c003a83543beaa46b38645:ea855d781cbea4682e350173cb89e8619ccfddb97cdce16f9a2f6f6892f46dbe68e04b12b8d88689a7a31670cdff409af98a93b49a34537b6aa009d2eb8b470195872d5f789f95484e30cbb0e114028953b16f5c6a8d9f65c003a83543beaa46b38645: +ca267de96c93c238fafb1279812059ab93ac03059657fd994f8fa5a09239c821017370c879090a81c7f272c2fc80e3aac2bc603fcb379afc98691160ab745b26:017370c879090a81c7f272c2fc80e3aac2bc603fcb379afc98691160ab745b26:e05f71e4e49a72ec550c44a3b85aca8f20ff26c3ee94a80f1b431c7d154ec9603ee02531:ac957f82335aa7141e96b59d63e3ccee95c3a2c47d026540c2af42dc9533d5fd81827d1679ad187aeaf37834915e75b147a9286806c8017516ba43dd051a5e0ce05f71e4e49a72ec550c44a3b85aca8f20ff26c3ee94a80f1b431c7d154ec9603ee02531: +3dff5e899475e7e91dd261322fab09980c52970de1da6e2e201660cc4fce7032f30162bac98447c4042fac05da448034629be2c6a58d30dfd578ba9fb5e3930b:f30162bac98447c4042fac05da448034629be2c6a58d30dfd578ba9fb5e3930b:938f0e77621bf3ea52c7c4911c5157c2d8a2a858093ef16aa9b107e69d98037ba139a3c382:5efe7a92ff9623089b3e3b78f352115366e26ba3fb1a416209bc029e9cadccd9f4affa333555a8f3a35a9d0f7c34b292cae77ec96fa3adfcaadee2d9ced8f805938f0e77621bf3ea52c7c4911c5157c2d8a2a858093ef16aa9b107e69d98037ba139a3c382: +9a6b847864e70cfe8ba6ab22fa0ca308c0cc8bec7141fbcaa3b81f5d1e1cfcfc34ad0fbdb2566507a81c2b1f8aa8f53dccaa64cc87ada91b903e900d07eee930:34ad0fbdb2566507a81c2b1f8aa8f53dccaa64cc87ada91b903e900d07eee930:838367471183c71f7e717724f89d401c3ad9863fd9cc7aa3cf33d3c529860cb581f3093d87da:2ab255169c489c54c732232e37c87349d486b1eba20509dbabe7fed329ef08fd75ba1cd145e67b2ea26cb5cc51cab343eeb085fe1fd7b0ec4c6afcd9b979f905838367471183c71f7e717724f89d401c3ad9863fd9cc7aa3cf33d3c529860cb581f3093d87da: +575be07afca5d063c238cd9b8028772cc49cda34471432a2e166e096e2219efc94e5eb4d5024f49d7ebf79817c8de11497dc2b55622a51ae123ffc749dbb16e0:94e5eb4d5024f49d7ebf79817c8de11497dc2b55622a51ae123ffc749dbb16e0:33e5918b66d33d55fe717ca34383eae78f0af82889caf6696e1ac9d95d1ffb32cba755f9e3503e:58271d44236f3b98c58fd7ae0d2f49ef2b6e3affdb225aa3ba555f0e11cc53c23ad19baf24346590d05d7d5390582082cf94d39cad6530ab93d13efb3927950633e5918b66d33d55fe717ca34383eae78f0af82889caf6696e1ac9d95d1ffb32cba755f9e3503e: +15ffb45514d43444d61fcb105e30e135fd268523dda20b82758b1794231104411772c5abc2d23fd2f9d1c3257be7bc3c1cd79cee40844b749b3a7743d2f964b8:1772c5abc2d23fd2f9d1c3257be7bc3c1cd79cee40844b749b3a7743d2f964b8:da9c5559d0ea51d255b6bd9d7638b876472f942b330fc0e2b30aea68d77368fce4948272991d257e:6828cd7624e793b8a4ceb96d3c2a975bf773e5ff6645f353614058621e58835289e7f31f42dfe6af6d736f2644511e320c0fa698582a79778d18730ed3e8cb08da9c5559d0ea51d255b6bd9d7638b876472f942b330fc0e2b30aea68d77368fce4948272991d257e: +fe0568642943b2e1afbfd1f10fe8df87a4236bea40dce742072cb21886eec1fa299ebd1f13177dbdb66a912bbf712038fdf73b06c3ac020c7b19126755d47f61:299ebd1f13177dbdb66a912bbf712038fdf73b06c3ac020c7b19126755d47f61:c59d0862ec1c9746abcc3cf83c9eeba2c7082a036a8cb57ce487e763492796d47e6e063a0c1feccc2d:d59e6dfcc6d7e3e2c58dec81e985d245e681acf6594a23c59214f7bed8015d813c7682b60b3583440311e72a8665ba2c96dec23ce826e160127e18132b030404c59d0862ec1c9746abcc3cf83c9eeba2c7082a036a8cb57ce487e763492796d47e6e063a0c1feccc2d: +5ecb16c2df27c8cf58e436a9d3affbd58e9538a92659a0f97c4c4f994635a8cada768b20c437dd3aa5f84bb6a077ffa34ab68501c5352b5cc3fdce7fe6c2398d:da768b20c437dd3aa5f84bb6a077ffa34ab68501c5352b5cc3fdce7fe6c2398d:56f1329d9a6be25a6159c72f12688dc8314e85dd9e7e4dc05bbecb7729e023c86f8e0937353f27c7ede9:1c723a20c6772426a670e4d5c4a97c6ebe9147f71bb0a415631e44406e290322e4ca977d348fe7856a8edc235d0fe95f7ed91aefddf28a77e2c7dbfd8f552f0a56f1329d9a6be25a6159c72f12688dc8314e85dd9e7e4dc05bbecb7729e023c86f8e0937353f27c7ede9: +d599d637b3c30a82a9984e2f758497d144de6f06b9fba04dd40fd949039d7c846791d8ce50a44689fc178727c5c3a1c959fbeed74ef7d8e7bd3c1ab4da31c51f:6791d8ce50a44689fc178727c5c3a1c959fbeed74ef7d8e7bd3c1ab4da31c51f:a7c04e8ba75d0a03d8b166ad7a1d77e1b91c7aaf7befdd99311fc3c54a684ddd971d5b3211c3eeaff1e54e:ebf10d9ac7c96108140e7def6fe9533d727646ff5b3af273c1df95762a66f32b65a09634d013f54b5dd6011f91bc336ca8b355ce33f8cfbec2535a4c427f8205a7c04e8ba75d0a03d8b166ad7a1d77e1b91c7aaf7befdd99311fc3c54a684ddd971d5b3211c3eeaff1e54e: +30ab8232fa7018f0ce6c39bd8f782fe2e159758bb0f2f4386c7f28cfd2c85898ecfb6a2bd42f31b61250ba5de7e46b4719afdfbc660db71a7bd1df7b0a3abe37:ecfb6a2bd42f31b61250ba5de7e46b4719afdfbc660db71a7bd1df7b0a3abe37:63b80b7956acbecf0c35e9ab06b914b0c7014fe1a4bbc0217240c1a33095d707953ed77b15d211adaf9b97dc:9af885344cc7239498f712df80bc01b80638291ed4a1d28baa5545017a72e2f65649ccf9603da6eb5bfab9f5543a6ca4a7af3866153c76bf66bf95def615b00c63b80b7956acbecf0c35e9ab06b914b0c7014fe1a4bbc0217240c1a33095d707953ed77b15d211adaf9b97dc: +0ddcdc872c7b748d40efe96c2881ae189d87f56148ed8af3ebbbc80324e38bdd588ddadcbcedf40df0e9697d8bb277c7bb1498fa1d26ce0a835a760b92ca7c85:588ddadcbcedf40df0e9697d8bb277c7bb1498fa1d26ce0a835a760b92ca7c85:65641cd402add8bf3d1d67dbeb6d41debfbef67e4317c35b0a6d5bbbae0e034de7d670ba1413d056f2d6f1de12:c179c09456e235fe24105afa6e8ec04637f8f943817cd098ba95387f9653b2add181a31447d92d1a1ddf1ceb0db62118de9dffb7dcd2424057cbdff5d41d040365641cd402add8bf3d1d67dbeb6d41debfbef67e4317c35b0a6d5bbbae0e034de7d670ba1413d056f2d6f1de12: +89f0d68299ba0a5a83f248ae0c169f8e3849a9b47bd4549884305c9912b46603aba3e795aab2012acceadd7b3bd9daeeed6ff5258bdcd7c93699c2a3836e3832:aba3e795aab2012acceadd7b3bd9daeeed6ff5258bdcd7c93699c2a3836e3832:4f1846dd7ad50e545d4cfbffbb1dc2ff145dc123754d08af4e44ecc0bc8c91411388bc7653e2d893d1eac2107d05:2c691fa8d487ce20d5d2fa41559116e0bbf4397cf5240e152556183541d66cf753582401a4388d390339dbef4d384743caa346f55f8daba68ba7b9131a8a6e0b4f1846dd7ad50e545d4cfbffbb1dc2ff145dc123754d08af4e44ecc0bc8c91411388bc7653e2d893d1eac2107d05: +0a3c1844e2db070fb24e3c95cb1cc6714ef84e2ccd2b9dd2f1460ebf7ecf13b172e409937e0610eb5c20b326dc6ea1bbbc0406701c5cd67d1fbde09192b07c01:72e409937e0610eb5c20b326dc6ea1bbbc0406701c5cd67d1fbde09192b07c01:4c8274d0ed1f74e2c86c08d955bde55b2d54327e82062a1f71f70d536fdc8722cdead7d22aaead2bfaa1ad00b82957:87f7fdf46095201e877a588fe3e5aaf476bd63138d8a878b89d6ac60631b3458b9d41a3c61a588e1db8d29a5968981b018776c588780922f5aa732ba6379dd054c8274d0ed1f74e2c86c08d955bde55b2d54327e82062a1f71f70d536fdc8722cdead7d22aaead2bfaa1ad00b82957: +c8d7a8818b98dfdb20839c871cb5c48e9e9470ca3ad35ba2613a5d3199c8ab2390d2efbba4d43e6b2b992ca16083dbcfa2b322383907b0ee75f3e95845d3c47f:90d2efbba4d43e6b2b992ca16083dbcfa2b322383907b0ee75f3e95845d3c47f:783e33c3acbdbb36e819f544a7781d83fc283d3309f5d3d12c8dcd6b0b3d0e89e38cfd3b4d0885661ca547fb9764abff:fa2e994421aef1d5856674813d05cbd2cf84ef5eb424af6ecd0dc6fdbdc2fe605fe985883312ecf34f59bfb2f1c9149e5b9cc9ecda05b2731130f3ed28ddae0b783e33c3acbdbb36e819f544a7781d83fc283d3309f5d3d12c8dcd6b0b3d0e89e38cfd3b4d0885661ca547fb9764abff: +b482703612d0c586f76cfcb21cfd2103c957251504a8c0ac4c86c9c6f3e429fffd711dc7dd3b1dfb9df9704be3e6b26f587fe7dd7ba456a91ba43fe51aec09ad:fd711dc7dd3b1dfb9df9704be3e6b26f587fe7dd7ba456a91ba43fe51aec09ad:29d77acfd99c7a0070a88feb6247a2bce9984fe3e6fbf19d4045042a21ab26cbd771e184a9a75f316b648c6920db92b87b:58832bdeb26feafc31b46277cf3fb5d7a17dfb7ccd9b1f58ecbe6feb979666828f239ba4d75219260ecac0acf40f0e5e2590f4caa16bbbcd8a155d347967a60729d77acfd99c7a0070a88feb6247a2bce9984fe3e6fbf19d4045042a21ab26cbd771e184a9a75f316b648c6920db92b87b: +84e50dd9a0f197e3893c38dbd91fafc344c1776d3a400e2f0f0ee7aa829eb8a22c50f870ee48b36b0ac2f8a5f336fb090b113050dbcc25e078200a6e16153eea:2c50f870ee48b36b0ac2f8a5f336fb090b113050dbcc25e078200a6e16153eea:f3992cde6493e671f1e129ddca8038b0abdb77bb9035f9f8be54bd5d68c1aeff724ff47d29344391dc536166b8671cbbf123:69e6a4491a63837316e86a5f4ba7cd0d731ecc58f1d0a264c67c89befdd8d3829d8de13b33cc0bf513931715c7809657e2bfb960e5c764c971d733746093e500f3992cde6493e671f1e129ddca8038b0abdb77bb9035f9f8be54bd5d68c1aeff724ff47d29344391dc536166b8671cbbf123: +b322d46577a2a991a4d1698287832a39c487ef776b4bff037a05c7f1812bdeeceb2bcadfd3eec2986baff32b98e7c4dbf03ff95d8ad5ff9aa9506e5472ff845f:eb2bcadfd3eec2986baff32b98e7c4dbf03ff95d8ad5ff9aa9506e5472ff845f:19f1bf5dcf1750c611f1c4a2865200504d82298edd72671f62a7b1471ac3d4a30f7de9e5da4108c52a4ce70a3e114a52a3b3c5:c7b55137317ca21e33489ff6a9bfab97c855dc6f85684a70a9125a261b56d5e6f149c5774d734f2d8debfc77b721896a8267c23768e9badb910eef83ec25880219f1bf5dcf1750c611f1c4a2865200504d82298edd72671f62a7b1471ac3d4a30f7de9e5da4108c52a4ce70a3e114a52a3b3c5: +960cab5034b9838d098d2dcbf4364bec16d388f6376d73a6273b70f82bbc98c05e3c19f2415acf729f829a4ebd5c40e1a6bc9fbca95703a9376087ed0937e51a:5e3c19f2415acf729f829a4ebd5c40e1a6bc9fbca95703a9376087ed0937e51a:f8b21962447b0a8f2e4279de411bea128e0be44b6915e6cda88341a68a0d818357db938eac73e0af6d31206b3948f8c48a447308:27d4c3a1811ef9d4360b3bdd133c2ccc30d02c2f248215776cb07ee4177f9b13fc42dd70a6c2fed8f225c7663c7f182e7ee8eccff20dc7b0e1d5834ec5b1ea01f8b21962447b0a8f2e4279de411bea128e0be44b6915e6cda88341a68a0d818357db938eac73e0af6d31206b3948f8c48a447308: +eb77b2638f23eebc82efe45ee9e5a0326637401e663ed029699b21e6443fb48e9ef27608961ac711de71a6e2d4d4663ea3ecd42fb7e4e8627c39622df4af0bbc:9ef27608961ac711de71a6e2d4d4663ea3ecd42fb7e4e8627c39622df4af0bbc:99e3d00934003ebafc3e9fdb687b0f5ff9d5782a4b1f56b9700046c077915602c3134e22fc90ed7e690fddd4433e2034dcb2dc99ab:18dc56d7bd9acd4f4daa78540b4ac8ff7aa9815f45a0bba370731a14eaabe96df8b5f37dbf8eae4cb15a64b244651e59d6a3d6761d9e3c50f2d0cbb09c05ec0699e3d00934003ebafc3e9fdb687b0f5ff9d5782a4b1f56b9700046c077915602c3134e22fc90ed7e690fddd4433e2034dcb2dc99ab: +b625aa89d3f7308715427b6c39bbac58effd3a0fb7316f7a22b99ee5922f2dc965a99c3e16fea894ec33c6b20d9105e2a04e2764a4769d9bbd4d8bacfeab4a2e:65a99c3e16fea894ec33c6b20d9105e2a04e2764a4769d9bbd4d8bacfeab4a2e:e07241dbd3adbe610bbe4d005dd46732a4c25086ecb8ec29cd7bca116e1bf9f53bfbf3e11fa49018d39ff1154a06668ef7df5c678e6a:01bb901d83b8b682d3614af46a807ba2691358feb775325d3423f549ff0aa5757e4e1a74e9c70f9721d8f354b319d4f4a1d91445c870fd0ffb94fed64664730de07241dbd3adbe610bbe4d005dd46732a4c25086ecb8ec29cd7bca116e1bf9f53bfbf3e11fa49018d39ff1154a06668ef7df5c678e6a: +b1c9f8bd03fe82e78f5c0fb06450f27dacdf716434db268275df3e1dc177af427fc88b1f7b3f11c629be671c21621f5c10672fafc8492da885742059ee6774cf:7fc88b1f7b3f11c629be671c21621f5c10672fafc8492da885742059ee6774cf:331da7a9c1f87b2ac91ee3b86d06c29163c05ed6f8d8a9725b471b7db0d6acec7f0f702487163f5eda020ca5b493f399e1c8d308c3c0c2:4b229951ef262f16978f7914bc672e7226c5f8379d2778c5a2dc0a2650869f7acfbd0bcd30fdb0619bb44fc1ae5939b87cc318133009c20395b6c7eb98107701331da7a9c1f87b2ac91ee3b86d06c29163c05ed6f8d8a9725b471b7db0d6acec7f0f702487163f5eda020ca5b493f399e1c8d308c3c0c2: +6d8cdb2e075f3a2f86137214cb236ceb89a6728bb4a200806bf3557fb78fac6957a04c7a5113cddfe49a4c124691d46c1f9cdc8f343f9dcb72a1330aeca71fda:57a04c7a5113cddfe49a4c124691d46c1f9cdc8f343f9dcb72a1330aeca71fda:7f318dbd121c08bfddfeff4f6aff4e45793251f8abf658403358238984360054f2a862c5bb83ed89025d2014a7a0cee50da3cb0e76bbb6bf:a6cbc947f9c87d1455cf1a708528c090f11ecee4855d1dbaadf47454a4de55fa4ce84b36d73a5b5f8f59298ccf21992df492ef34163d87753b7e9d32f2c3660b7f318dbd121c08bfddfeff4f6aff4e45793251f8abf658403358238984360054f2a862c5bb83ed89025d2014a7a0cee50da3cb0e76bbb6bf: +47adc6d6bf571ee9570ca0f75b604ac43e303e4ab339ca9b53cacc5be45b2ccba3f527a1c1f17dfeed92277347c9f98ab475de1755b0ab546b8a15d01b9bd0be:a3f527a1c1f17dfeed92277347c9f98ab475de1755b0ab546b8a15d01b9bd0be:ce497c5ff5a77990b7d8f8699eb1f5d8c0582f70cb7ac5c54d9d924913278bc654d37ea227590e15202217fc98dac4c0f3be2183d133315739:4e8c318343c306adbba60c92b75cb0569b9219d8a86e5d57752ed235fc109a43c2cf4e942cacf297279fbb28675347e08027722a4eb7395e00a17495d32edf0bce497c5ff5a77990b7d8f8699eb1f5d8c0582f70cb7ac5c54d9d924913278bc654d37ea227590e15202217fc98dac4c0f3be2183d133315739: +3c19b50b0fe47961719c381d0d8da9b9869d312f13e3298b97fb22f0af29cbbe0f7eda091499625e2bae8536ea35cda5483bd16a9c7e416b341d6f2c83343612:0f7eda091499625e2bae8536ea35cda5483bd16a9c7e416b341d6f2c83343612:8ddcd63043f55ec3bfc83dceae69d8f8b32f4cdb6e2aebd94b4314f8fe7287dcb62732c9052e7557fe63534338efb5b6254c5d41d2690cf5144f:efbd41f26a5d62685516f882b6ec74e0d5a71830d203c231248f26e99a9c6578ec900d68cdb8fa7216ad0d24f9ecbc9ffa655351666582f626645395a31fa7048ddcd63043f55ec3bfc83dceae69d8f8b32f4cdb6e2aebd94b4314f8fe7287dcb62732c9052e7557fe63534338efb5b6254c5d41d2690cf5144f: +34e1e9d539107eb86b393a5ccea1496d35bc7d5e9a8c5159d957e4e5852b3eb00ecb2601d5f7047428e9f909883a12420085f04ee2a88b6d95d3d7f2c932bd76:0ecb2601d5f7047428e9f909883a12420085f04ee2a88b6d95d3d7f2c932bd76:a6d4d0542cfe0d240a90507debacabce7cbbd48732353f4fad82c7bb7dbd9df8e7d9a16980a45186d8786c5ef65445bcc5b2ad5f660ffc7c8eaac0:32d22904d3e7012d6f5a441b0b4228064a5cf95b723a66b048a087ecd55920c31c204c3f2006891a85dd1932e3f1d614cfd633b5e63291c6d8166f3011431e09a6d4d0542cfe0d240a90507debacabce7cbbd48732353f4fad82c7bb7dbd9df8e7d9a16980a45186d8786c5ef65445bcc5b2ad5f660ffc7c8eaac0: +49dd473ede6aa3c866824a40ada4996c239a20d84c9365e4f0a4554f8031b9cf788de540544d3feb0c919240b390729be487e94b64ad973eb65b4669ecf23501:788de540544d3feb0c919240b390729be487e94b64ad973eb65b4669ecf23501:3a53594f3fba03029318f512b084a071ebd60baec7f55b028dc73bfc9c74e0ca496bf819dd92ab61cd8b74be3c0d6dcd128efc5ed3342cba124f726c:d2fde02791e720852507faa7c3789040d9ef86646321f313ac557f4002491542dd67d05c6990cdb0d495501fbc5d5188bfbb84dc1bf6098bee0603a47fc2690f3a53594f3fba03029318f512b084a071ebd60baec7f55b028dc73bfc9c74e0ca496bf819dd92ab61cd8b74be3c0d6dcd128efc5ed3342cba124f726c: +331c64da482b6b551373c36481a02d8136ecadbb01ab114b4470bf41607ac57152a00d96a3148b4726692d9eff89160ea9f99a5cc4389f361fed0bb16a42d521:52a00d96a3148b4726692d9eff89160ea9f99a5cc4389f361fed0bb16a42d521:20e1d05a0d5b32cc8150b8116cef39659dd5fb443ab15600f78e5b49c45326d9323f2850a63c3808859495ae273f58a51e9de9a145d774b40ba9d753d3:22c99aa946ead39ac7997562810c01c20b46bd610645bd2d56dcdcbaacc5452c74fbf4b8b1813b0e94c30d808ce5498e61d4f7ccbb4cc5f04dfc6140825a960020e1d05a0d5b32cc8150b8116cef39659dd5fb443ab15600f78e5b49c45326d9323f2850a63c3808859495ae273f58a51e9de9a145d774b40ba9d753d3: +5c0b96f2af8712122cf743c8f8dc77b6cd5570a7de13297bb3dde1886213cce20510eaf57d7301b0e1d527039bf4c6e292300a3a61b4765434f3203c100351b1:0510eaf57d7301b0e1d527039bf4c6e292300a3a61b4765434f3203c100351b1:54e0caa8e63919ca614b2bfd308ccfe50c9ea888e1ee4446d682cb5034627f97b05392c04e835556c31c52816a48e4fb196693206b8afb4408662b3cb575:06e5d8436ac7705b3a90f1631cdd38ec1a3fa49778a9b9f2fa5ebea4e7d560ada7dd26ff42fafa8ba420323742761aca6904940dc21bbef63ff72daab45d430b54e0caa8e63919ca614b2bfd308ccfe50c9ea888e1ee4446d682cb5034627f97b05392c04e835556c31c52816a48e4fb196693206b8afb4408662b3cb575: +bf5ba5d6a49dd5ef7b4d5d7d3e4ecc505c01f6ccee4c54b5ef7b40af6a4541401be034f813017b900d8990af45fad5b5214b573bd303ef7a75ef4b8c5c5b9842:1be034f813017b900d8990af45fad5b5214b573bd303ef7a75ef4b8c5c5b9842:16152c2e037b1c0d3219ced8e0674aee6b57834b55106c5344625322da638ecea2fc9a424a05ee9512d48fcf75dd8bd4691b3c10c28ec98ee1afa5b863d1c36795ed18105db3a9aabd9d2b4c1747adbaf1a56ffcc0c533c1c0faef331cdb79d961fa39f880a1b8b1164741822efb15a7259a465bef212855751fab66a897bfa211abe0ea2f2e1cd8a11d80e142cde1263eec267a3138ae1fcf4099db0ab53d64f336f4bcd7a363f6db112c0a2453051a0006f813aaf4ae948a2090619374fa58052409c28ef76225687df3cb2d1b0bfb43b09f47f1232f790e6d8dea759e57942099f4c4bd3390f28afc2098244961465c643fc8b29766af2bcbc5440b86e83608cfc937be98bb4827fd5e6b689adc2e26513db531076a6564396255a09975b7034dac06461b255642e3a7ed75fa9fc265011f5f6250382a84ac268d63ba64:279cace6fdaf3945e3837df474b28646143747632bede93e7a66f5ca291d2c24978512ca0cb8827c8c322685bd605503a5ec94dbae61bbdcae1e49650602bc0716152c2e037b1c0d3219ced8e0674aee6b57834b55106c5344625322da638ecea2fc9a424a05ee9512d48fcf75dd8bd4691b3c10c28ec98ee1afa5b863d1c36795ed18105db3a9aabd9d2b4c1747adbaf1a56ffcc0c533c1c0faef331cdb79d961fa39f880a1b8b1164741822efb15a7259a465bef212855751fab66a897bfa211abe0ea2f2e1cd8a11d80e142cde1263eec267a3138ae1fcf4099db0ab53d64f336f4bcd7a363f6db112c0a2453051a0006f813aaf4ae948a2090619374fa58052409c28ef76225687df3cb2d1b0bfb43b09f47f1232f790e6d8dea759e57942099f4c4bd3390f28afc2098244961465c643fc8b29766af2bcbc5440b86e83608cfc937be98bb4827fd5e6b689adc2e26513db531076a6564396255a09975b7034dac06461b255642e3a7ed75fa9fc265011f5f6250382a84ac268d63ba64: +65de297b70cbe80980500af0561a24db50001000125f4490366d8300d3128592ba8e2ad929bdcea538741042b57f2067d3153707a453770db9f3c4ca75504d24:ba8e2ad929bdcea538741042b57f2067d3153707a453770db9f3c4ca75504d24:131d8f4c2c94b153565b86592e770c987a443461b39aa2408b29e213ab057affc598b583739d6603a83fef0afc514721db0e76f9bd1b72b98c565cc8881af5747c0ba6f58c53dd2377da6c0d3aa805620cc4e75d52aabcba1f9b2849e08bd1b6b92e6f06615b814519606a02dc65a8609f5b29e9c2af5a894f7116ef28cfd1e7b76b64061732f7a5a3f8aa4c2e569e627a3f9749aa597be49d6b94436c352dd5fa7b83c92d2610faa32095ca302152d91a3c9776750e758ee8e9e402c6f5385eaa5df23850e54beb1be437a416c7115ed6aa6de13b55482532787e0bee34b83f3084406765635497c931b62a0518f1fbc2b891dc7262c7c6b67eda594fa530d74c9329bad5be94c287fbcde53aa80272b83322613d9368e5904076fdbcc88b2c0e59c10b02c448e00d1b3e7a9c9640feffb9523a8a60e1d83f04a4b8df69153b:7a9b736b01cc92a3349f1a3c32dbd91959825394ff443c567405e899c8185ce8fad9500e1fce89d95a6253c00477435acf04bff993de1b00495def0834ee1f07131d8f4c2c94b153565b86592e770c987a443461b39aa2408b29e213ab057affc598b583739d6603a83fef0afc514721db0e76f9bd1b72b98c565cc8881af5747c0ba6f58c53dd2377da6c0d3aa805620cc4e75d52aabcba1f9b2849e08bd1b6b92e6f06615b814519606a02dc65a8609f5b29e9c2af5a894f7116ef28cfd1e7b76b64061732f7a5a3f8aa4c2e569e627a3f9749aa597be49d6b94436c352dd5fa7b83c92d2610faa32095ca302152d91a3c9776750e758ee8e9e402c6f5385eaa5df23850e54beb1be437a416c7115ed6aa6de13b55482532787e0bee34b83f3084406765635497c931b62a0518f1fbc2b891dc7262c7c6b67eda594fa530d74c9329bad5be94c287fbcde53aa80272b83322613d9368e5904076fdbcc88b2c0e59c10b02c448e00d1b3e7a9c9640feffb9523a8a60e1d83f04a4b8df69153b: +0826e7333324e7ec8c764292f6015d4670e9b8d7c4a89e8d909e8ef435d18d15ffb2348ca8a018058be71d1512f376f91e8b0d552581254e107602217395e662:ffb2348ca8a018058be71d1512f376f91e8b0d552581254e107602217395e662:7f9e3e2f03c9df3d21b990f5a4af8295734afe783accc34fb1e9b8e95a0fd837af7e05c13cda0de8fadac9205265a0792b52563bdc2fee766348befcc56b88bbb95f154414fb186ec436aa62ea6fcabb11c017a9d2d15f67e595980e04c9313bc94fbc8c1134c2f40332bc7e311ac1ce11b505f8572ada7fbe196fba822d9a914492fa7185e9f3bea4687200a524c673a1cdf87eb3a140dcdb6a8875613488a2b00adf7175341c1c257635fa1a53a3e21d60c228399eea0991f112c60f653d7148e2c5ceb98f940831f070db1084d79156cc82c46bc9b8e884f3fa81be2da4cdda46bcaa24cc461f76ee647bb0f0f8c15ac5daa795b945e6f85bb310362e48d8095c782c61c52b481b4b002ad06ea74b8d306eff71abf21db710a8913cbe48332be0a0b3f31e0c7a6eba85ce33f357c7aeccd30bfb1a6574408b66fe404d31c3c5:4bac7fabec8724d81ab09ae130874d70b5213492104372f601ae5abb10532799373c4dad215876441f474e2c006be37c3c8f5f6f017d0870414fd276a8f428087f9e3e2f03c9df3d21b990f5a4af8295734afe783accc34fb1e9b8e95a0fd837af7e05c13cda0de8fadac9205265a0792b52563bdc2fee766348befcc56b88bbb95f154414fb186ec436aa62ea6fcabb11c017a9d2d15f67e595980e04c9313bc94fbc8c1134c2f40332bc7e311ac1ce11b505f8572ada7fbe196fba822d9a914492fa7185e9f3bea4687200a524c673a1cdf87eb3a140dcdb6a8875613488a2b00adf7175341c1c257635fa1a53a3e21d60c228399eea0991f112c60f653d7148e2c5ceb98f940831f070db1084d79156cc82c46bc9b8e884f3fa81be2da4cdda46bcaa24cc461f76ee647bb0f0f8c15ac5daa795b945e6f85bb310362e48d8095c782c61c52b481b4b002ad06ea74b8d306eff71abf21db710a8913cbe48332be0a0b3f31e0c7a6eba85ce33f357c7aeccd30bfb1a6574408b66fe404d31c3c5: +00ad6227977b5f38ccda994d928bba9086d2daeb013f8690db986648b90c1d4591a4ea005752b92cbebf99a8a5cbecd240ae3f016c44ad141b2e57ddc773dc8e:91a4ea005752b92cbebf99a8a5cbecd240ae3f016c44ad141b2e57ddc773dc8e:cb5bc5b98b2efce43543e91df041e0dbb53ed8f67bf0f197c52b2211e7a45e2e1ec818c1a80e10abf6a43535f5b79d974d8ae28a2295c0a6521763b607d5103c6aef3b2786bd5afd7563695660684337bc3090739fb1cd53a9d644139b6d4caec75bda7f2521fbfe676ab45b98cb317aa7ca79fc54a3d7c578466a6aa64e434e923465a7f211aa0c61681bb8486e90206a25250d3fdae6fb03299721e99e2a914910d91760089b5d281e131e6c836bc2de08f7e02c48d323c647e9536c00ec1039201c0362618c7d47aa8e7b9715ffc439987ae1d31154a6198c5aa11c128f4082f556c99baf103ecadc3b2f3b2ec5b469623bc03a53caf3814b16300aedbda538d676d1f607102639db2a62c446707ce6469bd873a0468225be88b0aef5d4020459b94b32fe2b0133e92e7ba54dd2a5397ed85f966ab39ed0730cca8e7dacb8a336:dc501db79fd782bc88cae792557d5d273f9ba560c7d90037fe84ac879d684f612a77452c4443e95c07b8be192c35769b17bbdfca42280de796d92119d833670dcb5bc5b98b2efce43543e91df041e0dbb53ed8f67bf0f197c52b2211e7a45e2e1ec818c1a80e10abf6a43535f5b79d974d8ae28a2295c0a6521763b607d5103c6aef3b2786bd5afd7563695660684337bc3090739fb1cd53a9d644139b6d4caec75bda7f2521fbfe676ab45b98cb317aa7ca79fc54a3d7c578466a6aa64e434e923465a7f211aa0c61681bb8486e90206a25250d3fdae6fb03299721e99e2a914910d91760089b5d281e131e6c836bc2de08f7e02c48d323c647e9536c00ec1039201c0362618c7d47aa8e7b9715ffc439987ae1d31154a6198c5aa11c128f4082f556c99baf103ecadc3b2f3b2ec5b469623bc03a53caf3814b16300aedbda538d676d1f607102639db2a62c446707ce6469bd873a0468225be88b0aef5d4020459b94b32fe2b0133e92e7ba54dd2a5397ed85f966ab39ed0730cca8e7dacb8a336: +1521c6dbd6f724de73eaf7b56264f01035c04e01c1f3eb3cbe83efd26c439ada2f61a26ffb68ba4f6e141529dc2617e8531c7151404808093b4fa7fedaea255d:2f61a26ffb68ba4f6e141529dc2617e8531c7151404808093b4fa7fedaea255d:3e3c7c490788e4b1d42f5cbcae3a9930bf617ebdff447f7be2ac2ba7cd5bcfc015760963e6fe5b956fb7cdb35bd5a17f5429ca664f437f08753a741c2bc8692b71a9115c582a25b2f74d329854d60b7817c079b3523aaff8793c2f72fff8cd10592c54e738df1d6452fb72da131c6731ea5c953c62ea177ac1f4735e5154477387109afae15f3ed6eeb08606e28c81d4386f03b9376924b6ef8d221ee29547f82a7ede48e1dc17723e3d42171eeaf96ac84bedc2a01dd86f4d085734fd69f91b5263e439083ff0318536adff4147308e3aafd1b58bb74f6fb0214a46fdcd3524f18df5a719ce57319e791b4ea606b499bfa57a60e707f94e18f1fed22f91bc79e6364a843f9cbf93825c465e9cae9072bc9d3ec4471f21ab2f7e99a633f587aac3db78ae9666a89a18008dd61d60218554411a65740ffd1ae3adc06595e3b7876407b6:a817ed23ec398a128601c1832dc6af7643bf3a5f517bcc579450fdb4759028f4966164125f6ebd0d6bf86ff298a39c766d0c21fdb0cbfdf81cd0eb1f03cd8a083e3c7c490788e4b1d42f5cbcae3a9930bf617ebdff447f7be2ac2ba7cd5bcfc015760963e6fe5b956fb7cdb35bd5a17f5429ca664f437f08753a741c2bc8692b71a9115c582a25b2f74d329854d60b7817c079b3523aaff8793c2f72fff8cd10592c54e738df1d6452fb72da131c6731ea5c953c62ea177ac1f4735e5154477387109afae15f3ed6eeb08606e28c81d4386f03b9376924b6ef8d221ee29547f82a7ede48e1dc17723e3d42171eeaf96ac84bedc2a01dd86f4d085734fd69f91b5263e439083ff0318536adff4147308e3aafd1b58bb74f6fb0214a46fdcd3524f18df5a719ce57319e791b4ea606b499bfa57a60e707f94e18f1fed22f91bc79e6364a843f9cbf93825c465e9cae9072bc9d3ec4471f21ab2f7e99a633f587aac3db78ae9666a89a18008dd61d60218554411a65740ffd1ae3adc06595e3b7876407b6: +17e5f0a8f34751babc5c723ecf339306992f39ea065ac140fcbc397d2dd32c4b4f1e23cc0f2f69c88ef9162ab5f8c59fb3b8ab2096b77e782c63c07c8c4f2b60:4f1e23cc0f2f69c88ef9162ab5f8c59fb3b8ab2096b77e782c63c07c8c4f2b60:c0fad790024019bd6fc08a7a92f5f2ac35cf6432e2eaa53d482f6e1204935336cb3ae65a63c24d0ec6539a10ee18760f2f520537774cdec6e96b55536011daa8f8bcb9cdaf6df5b34648448ac7d7cb7c6bd80d67fbf330f8765297766046a925ab52411d1604c3ed6a85173040125658a32cf4c854ef2813df2be6f3830e5eee5a6163a83ca8849f612991a31e9f88028e50bf8535e11755fad029d94cf25959f6695d09c1ba4315d40f7cf51b3f8166d02faba7511ecd8b1dded5f10cd6843455cff707ed225396c61d0820d20ada70d0c3619ff679422061c9f7c76e97d5a37af61fd62212d2dafc647ebbb979e61d9070ec03609a07f5fc57d119ae64b7a6ef92a5afae660a30ed48d702cc3128c633b4f19060a0578101729ee979f790f45bdbb5fe1a8a62f01a61a31d61af07030450fa0417323e9407bc76e73130e7c69d62e6a7:efe2cb63fe7b4fc98946dc82fb6998e741ed9ce6b9c1a93bb45bc0a7d8396d7405282b43fe363ba5b23589f8e1fae130e157ce888cd72d053d0cc19d257a4300c0fad790024019bd6fc08a7a92f5f2ac35cf6432e2eaa53d482f6e1204935336cb3ae65a63c24d0ec6539a10ee18760f2f520537774cdec6e96b55536011daa8f8bcb9cdaf6df5b34648448ac7d7cb7c6bd80d67fbf330f8765297766046a925ab52411d1604c3ed6a85173040125658a32cf4c854ef2813df2be6f3830e5eee5a6163a83ca8849f612991a31e9f88028e50bf8535e11755fad029d94cf25959f6695d09c1ba4315d40f7cf51b3f8166d02faba7511ecd8b1dded5f10cd6843455cff707ed225396c61d0820d20ada70d0c3619ff679422061c9f7c76e97d5a37af61fd62212d2dafc647ebbb979e61d9070ec03609a07f5fc57d119ae64b7a6ef92a5afae660a30ed48d702cc3128c633b4f19060a0578101729ee979f790f45bdbb5fe1a8a62f01a61a31d61af07030450fa0417323e9407bc76e73130e7c69d62e6a7: +0cd7aa7d605e44d5ffb97966b2cb93c189e4c5a85db87fad7ab8d62463c59b594889855fe4116b4913927f47f2273bf559c3b394a983631a25ae597033185e46:4889855fe4116b4913927f47f2273bf559c3b394a983631a25ae597033185e46:28a55dda6cd0844b6577c9d6da073a4dc35cbc98ac158ab54cf88fd20cc87e83c4bba2d74d82ce0f4854ec4db513de400465aaa5eee790bc84f16337072d3a91cde40d6e0df1ba0cc0645f5d5cbbb642381d7b9e211d25267a8acf77d1edb69c3a630f5b133d24f046a81bf22ff03b31d8447e12c3f7b77114a70cbd20bbd08b0b3827a6bbcf90409e344447a7fbc59bdd97d729071f8d71dcc33e6ef2cbab1d411edf13734db1dd9703276f5eb2d6aa2cb8952dd6712bfae809ce08c3aa502b8135713fac0a9c25b1d45b6a5831e02421bba65b81a596efa24b0576bd1dc7fdfb49be762875e81bd540722bc06140b9aa2ef7b84a801e41ded68d4546ac4873d9e7ced649b64fadaf0b5c4b6eb8d036315233f4326ca01e03393050cd027c24f67303fb846bd2c6b3dba06bed0d59a36289d24bd648f7db0b3a81346612593e3ddd18c557:bf9115fd3d02706e398d4bf3b02a82674ff3041508fd39d29f867e501634b9261f516a794f98738d7c7013a3f2f858ffdd08047fb6bf3dddfb4b4f4cbeef300328a55dda6cd0844b6577c9d6da073a4dc35cbc98ac158ab54cf88fd20cc87e83c4bba2d74d82ce0f4854ec4db513de400465aaa5eee790bc84f16337072d3a91cde40d6e0df1ba0cc0645f5d5cbbb642381d7b9e211d25267a8acf77d1edb69c3a630f5b133d24f046a81bf22ff03b31d8447e12c3f7b77114a70cbd20bbd08b0b3827a6bbcf90409e344447a7fbc59bdd97d729071f8d71dcc33e6ef2cbab1d411edf13734db1dd9703276f5eb2d6aa2cb8952dd6712bfae809ce08c3aa502b8135713fac0a9c25b1d45b6a5831e02421bba65b81a596efa24b0576bd1dc7fdfb49be762875e81bd540722bc06140b9aa2ef7b84a801e41ded68d4546ac4873d9e7ced649b64fadaf0b5c4b6eb8d036315233f4326ca01e03393050cd027c24f67303fb846bd2c6b3dba06bed0d59a36289d24bd648f7db0b3a81346612593e3ddd18c557: +33371d9e892f9875052ac8e325ba505e7477c1ace24ba7822643d43d0acef3de35929bded27c249c87d8b8d82f59260a575327b546c3a167c69f5992d5b8e006:35929bded27c249c87d8b8d82f59260a575327b546c3a167c69f5992d5b8e006:27a32efba28204be59b7ff5fe488ca158a91d5986091ecc4458b49e090dd37cbfede7c0f46186fabcbdff78d2844155808efffd873ed9c9261526e04e4f7050b8d7bd267a0fe3d5a449378d54a4febbd2f26824338e2aaaf35a32ff0f62504bda5c2e44abc63159f336cf25e6bb40ddb7d8825dff18fd51fc01951eaedcd33707007e1203ca58b4f7d242f8166a907e099932c001bfb1ec9a61e0ef2da4e8446af208201315d69681710d425d2400c387d7b9df321a4aec602b9c656c3e2310bff8756d18b802134b15604f4edc111149a9879e31241dd34f702f4c349617b13529769a772f5e52a89c098e0dca5920667893a250061b17991626eb9319298685be46b6a8b68422444fa5a36bcf3a687e2eccb9322c87dc80165da898930850b98fc863cada1aa99c6d61c451b9ccf4874c7f0e75b0a0c602f044812c71765adaf02025395b0:985ca446ddc007827cc8f2852cbd8115ef8c5975e9d7ce96d74dfed859aa14a4c15254006bea5e08359efe2625d715e0897ee5a16f151203be5010418637de0527a32efba28204be59b7ff5fe488ca158a91d5986091ecc4458b49e090dd37cbfede7c0f46186fabcbdff78d2844155808efffd873ed9c9261526e04e4f7050b8d7bd267a0fe3d5a449378d54a4febbd2f26824338e2aaaf35a32ff0f62504bda5c2e44abc63159f336cf25e6bb40ddb7d8825dff18fd51fc01951eaedcd33707007e1203ca58b4f7d242f8166a907e099932c001bfb1ec9a61e0ef2da4e8446af208201315d69681710d425d2400c387d7b9df321a4aec602b9c656c3e2310bff8756d18b802134b15604f4edc111149a9879e31241dd34f702f4c349617b13529769a772f5e52a89c098e0dca5920667893a250061b17991626eb9319298685be46b6a8b68422444fa5a36bcf3a687e2eccb9322c87dc80165da898930850b98fc863cada1aa99c6d61c451b9ccf4874c7f0e75b0a0c602f044812c71765adaf02025395b0: +beedb8073df58f8c1bffbdbd77ec7decb2c82a9babecefc0331507bdc2c2a7e7b27e908b805e296fc30d2e474b060cd50c0f6f520b3671712183bd89d4e733e9:b27e908b805e296fc30d2e474b060cd50c0f6f520b3671712183bd89d4e733e9:35ca57f0f915e5209d54ea4b871ffb585354df1b4a4a1796fbe4d6227d3e1aba5171ed0391a79e83e24d82fdafd15c17b28bf6c94d618c74d65264e58faaacd2902872fdd0efa22e8d2d7ce8e3b8197f0c3615b0a385235fa9fd8e4564ee6e6b1650b4cfb94d872c805c32d4f3a18f966461d3adbb605fa525884f8eb197627396ba4d995d78ac02948a0eaabb58519b9a8e2e7985cd1de2c71d8918d96a0168660ce17cddf364e3ec0d4bd90f2104751a1927ee1d23f3e7a69840ed040b00e5f6e4866ec58813149cc382aebf6162608c79574d553f47230e924a0ef1ebf55d8e1a52abb62a2d7ac86027c7c03cc83fa1949da29e2f3037ab986fd2fffe650e3149babae5a50b1ee9696f3babec72e29697c82422814d272085500fd837fe3c7a973ef4c169af12dd7f02700620bb045bdbf84623f326350570b3cadbc9aea4200b28287e17ab:8c890cccadc7760e1e82e43c44b3dc0b685a48b479ae13cc0a6b0557d0fb1cbabba63d2a96843412ea8d36c50acbf52b92cfb2dce49dc48af6ddcf8ee47a860835ca57f0f915e5209d54ea4b871ffb585354df1b4a4a1796fbe4d6227d3e1aba5171ed0391a79e83e24d82fdafd15c17b28bf6c94d618c74d65264e58faaacd2902872fdd0efa22e8d2d7ce8e3b8197f0c3615b0a385235fa9fd8e4564ee6e6b1650b4cfb94d872c805c32d4f3a18f966461d3adbb605fa525884f8eb197627396ba4d995d78ac02948a0eaabb58519b9a8e2e7985cd1de2c71d8918d96a0168660ce17cddf364e3ec0d4bd90f2104751a1927ee1d23f3e7a69840ed040b00e5f6e4866ec58813149cc382aebf6162608c79574d553f47230e924a0ef1ebf55d8e1a52abb62a2d7ac86027c7c03cc83fa1949da29e2f3037ab986fd2fffe650e3149babae5a50b1ee9696f3babec72e29697c82422814d272085500fd837fe3c7a973ef4c169af12dd7f02700620bb045bdbf84623f326350570b3cadbc9aea4200b28287e17ab: +9184ef618816832592bc8eb35f4ffd4ff98dfbf7776c90f2aad212ce7e03351e687b7726010d9bde2c90e573cd2a2a702ff28c4a2af70afc7315c94d575601e5:687b7726010d9bde2c90e573cd2a2a702ff28c4a2af70afc7315c94d575601e5:729eb7e54a9d00c58617af18c345b8dc6e5b4e0f57de2f3c02e54a2ec8f1425ec2e240775b5ab0c10f84ac8bafda4584f7e21c655faecd8030a98906bd68398f26b5d58d92b6cf045e9bd9743c74c9a342ec61ce57f37b981eac4d8bf034608866e985bb68686a68b4a2af88b992a2a6d2dc8ce88bfb0a36cf28bbab7024abfa2bea53313b66c906f4f7cf66970f540095bd0104aa4924dd82e15413c22679f847e48cd0c7ec1f677e005fec0177fbd5c559fc39add613991fbaeae4d24d39d309ef74647f8192cc4c62d0642028c76a1b951f6bc9639deb91ecc08be6043f2109705a42c7eae712649d91d96ccbbfb63d8d0dd6dd112160f61361ecdc6793929ca9aef9ab56944a6fa4a7df1e279eaf58ce8323a9cf62c94279fff7440fbc936baa61489c999330badcb9fc0e184bc5093f330cbb242f71fb378738fea10511dd438364d7f76bcc:b3c24e75132c563475422d5ea412b5c1e8e6e5ea1c08ead1393c412da134c9a1638284ea7e2ca032fe3d3e32a9066a8c8839903f6ef46e966bb5e492d8c2aa00729eb7e54a9d00c58617af18c345b8dc6e5b4e0f57de2f3c02e54a2ec8f1425ec2e240775b5ab0c10f84ac8bafda4584f7e21c655faecd8030a98906bd68398f26b5d58d92b6cf045e9bd9743c74c9a342ec61ce57f37b981eac4d8bf034608866e985bb68686a68b4a2af88b992a2a6d2dc8ce88bfb0a36cf28bbab7024abfa2bea53313b66c906f4f7cf66970f540095bd0104aa4924dd82e15413c22679f847e48cd0c7ec1f677e005fec0177fbd5c559fc39add613991fbaeae4d24d39d309ef74647f8192cc4c62d0642028c76a1b951f6bc9639deb91ecc08be6043f2109705a42c7eae712649d91d96ccbbfb63d8d0dd6dd112160f61361ecdc6793929ca9aef9ab56944a6fa4a7df1e279eaf58ce8323a9cf62c94279fff7440fbc936baa61489c999330badcb9fc0e184bc5093f330cbb242f71fb378738fea10511dd438364d7f76bcc: +354e13152ee1fe748a1252204c6527bdc1b1eb2eb53678150e6359924708d812d45ff6c5fb83e7bb9669aa8960deb7dbc665c988439b6c9ef672c6811dc8bcf6:d45ff6c5fb83e7bb9669aa8960deb7dbc665c988439b6c9ef672c6811dc8bcf6:8e5fccf66b1ba6169cb685733d9d0e0190361c90bcab95c163285a97fe356d2bdcde3c9380268805a384d063da09ccd9969cc3ff7431e60a8e9f869cd62faa0e356151b280bc526e577c2c538c9a724dc48bf88b70321d7e1eeedb3c4af706748c942e67bdabdb41bec2977b1523069e31e29b76300288f88a51b384b80cc2526f1679340ddec3881f5cd28b0378d9cd0a812b68dd3f68f7a23e1b54bee7466ac765cf38df04d67441dfa498c4bffc52045fa6d2dbcdbfa33dfaa77644ffccef0decdb6790c70a0d734ec287cc338cb5a909c0055189301169c4f7702c05c0911a27b16ef9ed934fa6a0ca7b13e413523422535647968030edc40cd73e7d6b345b7581f438316d68e3cd292b846d3f4f7c4862bc7e6b3fb89a27f6f60cd7db2e34ec9aae1013fe37acff8ad888cb9a593ef5e621eae5186c58b31dcfde22870e336d33f440f6b8d49a:de2b46e65f3decef34332e500f2e11306fbdcf1be85a1c1ee68ba3045dcec2c7be608d22927da1f44c0e2083ae622cf3c29d893887994efcfa2ca594f5051f038e5fccf66b1ba6169cb685733d9d0e0190361c90bcab95c163285a97fe356d2bdcde3c9380268805a384d063da09ccd9969cc3ff7431e60a8e9f869cd62faa0e356151b280bc526e577c2c538c9a724dc48bf88b70321d7e1eeedb3c4af706748c942e67bdabdb41bec2977b1523069e31e29b76300288f88a51b384b80cc2526f1679340ddec3881f5cd28b0378d9cd0a812b68dd3f68f7a23e1b54bee7466ac765cf38df04d67441dfa498c4bffc52045fa6d2dbcdbfa33dfaa77644ffccef0decdb6790c70a0d734ec287cc338cb5a909c0055189301169c4f7702c05c0911a27b16ef9ed934fa6a0ca7b13e413523422535647968030edc40cd73e7d6b345b7581f438316d68e3cd292b846d3f4f7c4862bc7e6b3fb89a27f6f60cd7db2e34ec9aae1013fe37acff8ad888cb9a593ef5e621eae5186c58b31dcfde22870e336d33f440f6b8d49a: +7ff62d4b3c4d99d342d4bb401d726b21e99f4ef592149fc311b68761f5567ff67fdfdb9eca29d3f01d9486d7e112ce03aa37b91326a4283b9c03999c5eda099a:7fdfdb9eca29d3f01d9486d7e112ce03aa37b91326a4283b9c03999c5eda099a:99c44c796572a4823fc6c3807730839173774c05dbfc1492ed0d00509a95a1de37274b3135ed0456a1718e576597dc13f2a2ab37a45c06cbb4a2d22afad4d5f3d90ab3d8da4dcdaa06d44f2219088401c5dceee26055c4782f78d7d63a380608e1bef89eeef338c2f0897da106fafce2fb2ebc5db669c7c172c9cfe77d3109d239fe5d005c8ee751511b5a88317c729b0d8b70b52f6bd3cda2fe865c77f36e4f1b635f336e036bd718bec90ee78a802811510c4058c1ba364017253aa842922e1dd7d7a0f0fc9c69e43fc4eaeffaaf1ae5fa5d2d73b43079617baba030923fe5b13d2c1c4fe6fac3f2db74e2020a734b6121a0302fce820ba0580ce6135348fdf0632e0008df03ee112168f5cfa0037a26a1f69b1f1317edf2a3ab367455a77e00691215d7aa3133c2159d3da2b134cf04f0defbf07a6064011e64dd14d4f8f064356655428804c2771a:058f79927fbf6178724815c7b11c63baaa90bcc15d7272be082f8a9141861c816433055f6cf6491424853f9ec78bb91ace913a93411b4e5ed58bc4ba5715c60a99c44c796572a4823fc6c3807730839173774c05dbfc1492ed0d00509a95a1de37274b3135ed0456a1718e576597dc13f2a2ab37a45c06cbb4a2d22afad4d5f3d90ab3d8da4dcdaa06d44f2219088401c5dceee26055c4782f78d7d63a380608e1bef89eeef338c2f0897da106fafce2fb2ebc5db669c7c172c9cfe77d3109d239fe5d005c8ee751511b5a88317c729b0d8b70b52f6bd3cda2fe865c77f36e4f1b635f336e036bd718bec90ee78a802811510c4058c1ba364017253aa842922e1dd7d7a0f0fc9c69e43fc4eaeffaaf1ae5fa5d2d73b43079617baba030923fe5b13d2c1c4fe6fac3f2db74e2020a734b6121a0302fce820ba0580ce6135348fdf0632e0008df03ee112168f5cfa0037a26a1f69b1f1317edf2a3ab367455a77e00691215d7aa3133c2159d3da2b134cf04f0defbf07a6064011e64dd14d4f8f064356655428804c2771a: +6cabadd03f8a2e6ebab96a74f80e18164e4d1b6baa678f5a82e25604af989aaf2a4a3179564194e00100c18bc35351d8b135bbae5b32b28fce1d7b6766ca4b32:2a4a3179564194e00100c18bc35351d8b135bbae5b32b28fce1d7b6766ca4b32:279f78cf3b9ccfc6e1b01e1a82f50ed172e9a8e1e702bb15661dd7dc3a456ff7a7a7fdfb081db3867079630c7f70fd753292ec60ecbf50632e9aa45b996505c66e6dc3c6ae892e21b6a8705e4bbae8f16a3378554b31fdb0139dcd15c96a8a7e4b88756a86d18db5dc74fd7691197dd88e2c7d5df52b049344cdc477c9cd7e89eda99ccfb1d00814d0152b9654df3279372ca5f18b1c946f2894a76b079ddb1c3cd61fbb969aeec9193a6b88fb7d136c07f9821e5c1074b4e93bcaf6fa14d0d1d7e1707589d77ec1337206e53a1f06cc26672ff95c13d5ff444766931ba30a0afdcdadd2098e9c41fd87a3f23cd16dbb0efbf8092ce33e327f42610990e1cee6cb8e54951aa081e69765ae4009aeed758e768de50c23d9a22b4a06dc4d19fc8cbd0cdef4c983461755d0a3b5d6a9c12253e09568339ff7e5f78c5fdf7ec89f9186a621a8c0eed11b67022e:4e65c6c1d493045e8a9250e397c1d1d30ffed24db66a8961aa458f8f0fcb760c39fe8657d7ab8f84000b96d519717cff71f926522c1efec7f8b2624eae55f60c279f78cf3b9ccfc6e1b01e1a82f50ed172e9a8e1e702bb15661dd7dc3a456ff7a7a7fdfb081db3867079630c7f70fd753292ec60ecbf50632e9aa45b996505c66e6dc3c6ae892e21b6a8705e4bbae8f16a3378554b31fdb0139dcd15c96a8a7e4b88756a86d18db5dc74fd7691197dd88e2c7d5df52b049344cdc477c9cd7e89eda99ccfb1d00814d0152b9654df3279372ca5f18b1c946f2894a76b079ddb1c3cd61fbb969aeec9193a6b88fb7d136c07f9821e5c1074b4e93bcaf6fa14d0d1d7e1707589d77ec1337206e53a1f06cc26672ff95c13d5ff444766931ba30a0afdcdadd2098e9c41fd87a3f23cd16dbb0efbf8092ce33e327f42610990e1cee6cb8e54951aa081e69765ae4009aeed758e768de50c23d9a22b4a06dc4d19fc8cbd0cdef4c983461755d0a3b5d6a9c12253e09568339ff7e5f78c5fdf7ec89f9186a621a8c0eed11b67022e: +0fa0c32c3ae34be51b92f91945405981a8e202488558a8e220c288c7d6a5532dd6aee62bd91fc9453635ffcc02b2f38dcab13285140380580ccdff0865df0492:d6aee62bd91fc9453635ffcc02b2f38dcab13285140380580ccdff0865df0492:53f44be0e5997ff07264cb64ba1359e2801def8755e64a2362bddaf597e672d021d34fface6d97e0f2b1f6ae625fd33d3c4f6e9ff7d0c73f1da8defb23f324975e921bb2473258177a16612567edf7d5760f3f3e3a6d26aaabc5fde4e2043f73fa70f128020933b1ba3b6bd69498e9503ea670f1ed880d3651f2e4c59e79cabc86e9b703394294112d5d8e213c317423b525a6df70106a9d658a262028b5f45100cb77d1150d8fe461eed434f241015f3276ad7b09a291b4a7f35e3c30051cbf13b1d4a7fa0c81a50f939e7c49673afdc87883c9e3e61f5a1df03755470fda74bf23ea88676b258a97a280d5f90b52b714b596035bae08c8d0fe6d94f8949559b1f27d7116cf59dd3cfbf18202a09c13f5c4fbc8d97225492887d32870c2297e34debd9876d6d01ac27a16b088b079079f2b20feb02537cda314c43cb2dca371b9df37ed11ec97e1a7a6993a:7e9ab85ee94fe4b35dcb545329a0ef25923de5c9dc23e7df1a7e77ab0dcfb89e03f4e785ca6429cb2b0df50da6230f733f00f33a45c4e576cd40bdb84f1ae00153f44be0e5997ff07264cb64ba1359e2801def8755e64a2362bddaf597e672d021d34fface6d97e0f2b1f6ae625fd33d3c4f6e9ff7d0c73f1da8defb23f324975e921bb2473258177a16612567edf7d5760f3f3e3a6d26aaabc5fde4e2043f73fa70f128020933b1ba3b6bd69498e9503ea670f1ed880d3651f2e4c59e79cabc86e9b703394294112d5d8e213c317423b525a6df70106a9d658a262028b5f45100cb77d1150d8fe461eed434f241015f3276ad7b09a291b4a7f35e3c30051cbf13b1d4a7fa0c81a50f939e7c49673afdc87883c9e3e61f5a1df03755470fda74bf23ea88676b258a97a280d5f90b52b714b596035bae08c8d0fe6d94f8949559b1f27d7116cf59dd3cfbf18202a09c13f5c4fbc8d97225492887d32870c2297e34debd9876d6d01ac27a16b088b079079f2b20feb02537cda314c43cb2dca371b9df37ed11ec97e1a7a6993a: +7b06f88026fa86f39fce2426f67cc5996bedd0cfc4b5ebb1b5e3edbb47e080aa3f1469ee6a2e7867e2e9012d402cf5a4861497c01df879a1deb1c539830b58de:3f1469ee6a2e7867e2e9012d402cf5a4861497c01df879a1deb1c539830b58de:71175d4e21721297d9176d817f4e785d9600d923f987fe0b26fd79d33a5ea5d1e818b71f0f92b8c73afddabdcc27f6d16e26aafa874cfd77a00e06c36b041487582bb933760f88b419127345776ea418f83522254fed33819bc5c95f8f8404cc144ebf1486c88515409d3433aaf519d9920f5256e629419e9a95580a35b069b8d25533dfcbc98ad36404a951808e01378c03266326d120046975fde07daef3266caacd821c1403499d7fdf17c033c8d8c3f28f162b5f09dfdaca06285f00c6cb986dfdf5151aa6639608b5b13e78d65a4368585b16138754fbd113835a686cd066c2b89bb0953c24d50e77bf0fc457c1e0fcf5d44da8db9a88f062be3b688d5cdcff1d1c00e81ec9d413882295b341fee8fa427dc109adeb5f284eec202f1bef115bf96b1782d3ccdeb682b69bf92d170c007d5df80e1ed962f677dc24a145a1e4e829e8dec0104e5f78365944:42f133e34e3eb7032a133ed781537ec62e44a5ce8381e5e0bf9e13a914a4b2c757811d6d3b1e86672424ea4230d10f7c610abb7069e61e319b4066a2bd7bc90071175d4e21721297d9176d817f4e785d9600d923f987fe0b26fd79d33a5ea5d1e818b71f0f92b8c73afddabdcc27f6d16e26aafa874cfd77a00e06c36b041487582bb933760f88b419127345776ea418f83522254fed33819bc5c95f8f8404cc144ebf1486c88515409d3433aaf519d9920f5256e629419e9a95580a35b069b8d25533dfcbc98ad36404a951808e01378c03266326d120046975fde07daef3266caacd821c1403499d7fdf17c033c8d8c3f28f162b5f09dfdaca06285f00c6cb986dfdf5151aa6639608b5b13e78d65a4368585b16138754fbd113835a686cd066c2b89bb0953c24d50e77bf0fc457c1e0fcf5d44da8db9a88f062be3b688d5cdcff1d1c00e81ec9d413882295b341fee8fa427dc109adeb5f284eec202f1bef115bf96b1782d3ccdeb682b69bf92d170c007d5df80e1ed962f677dc24a145a1e4e829e8dec0104e5f78365944: +c3f5e149968a24f4de9119531975f443015ccca305d7119ed4749e8bf6d94fc739aaccdb948a4038538a4588322f806bb129b5876c4bec51271afe4f49690045:39aaccdb948a4038538a4588322f806bb129b5876c4bec51271afe4f49690045:c46370e37f2e0cadcf93402f1f0cb048f52881ba750b7a43f56ab11ce348732fb57e7f9aaf8dfcbe455e14e983c248d026a27e7f148d5db5a53f94635702b895127771047a876d14107386c5e0ff8933345bbd7a936d990d33efa28c2ec4e4864ffd2ff576f7c88f954cfc1c459e883bb712dae3cdf6632066f1f4d13a509615b3360cadc5a307f23e52a51b40a6feebe0b18d0e9ee4e348f33cd81a8def222f6a59b12861d335bd9af85cc004be46f1d3a424f4870ae9dc587e5a4ade136b9370649348c33ac3bf1febeebffea37085ed59cac9d9e696470b234609e9a10a9d431ff91e69cb5135fd117ff58a36539744ebe70cea6973c00c7a4d57b62f4a7136d731b8e46ff18ec0ed69070031905075d8541d568cfce6eeb76242b7819a7b6a93552111bb88f165527cfa6966d39fcbe0a7dea008e39c7a3e577ab307cd1d0ea326833d52654e172955f3fcd4:5fa2b531677b00b85b0a313cbd479f55f4ab3ec5cfce5e454d2b74176ccc3399c899f9d6b51ed4c1e76185ac9fe730c4b4014044f7041185bc3c85722eb2ea02c46370e37f2e0cadcf93402f1f0cb048f52881ba750b7a43f56ab11ce348732fb57e7f9aaf8dfcbe455e14e983c248d026a27e7f148d5db5a53f94635702b895127771047a876d14107386c5e0ff8933345bbd7a936d990d33efa28c2ec4e4864ffd2ff576f7c88f954cfc1c459e883bb712dae3cdf6632066f1f4d13a509615b3360cadc5a307f23e52a51b40a6feebe0b18d0e9ee4e348f33cd81a8def222f6a59b12861d335bd9af85cc004be46f1d3a424f4870ae9dc587e5a4ade136b9370649348c33ac3bf1febeebffea37085ed59cac9d9e696470b234609e9a10a9d431ff91e69cb5135fd117ff58a36539744ebe70cea6973c00c7a4d57b62f4a7136d731b8e46ff18ec0ed69070031905075d8541d568cfce6eeb76242b7819a7b6a93552111bb88f165527cfa6966d39fcbe0a7dea008e39c7a3e577ab307cd1d0ea326833d52654e172955f3fcd4: +42305c9302f45ea6f87e26e2208fd94b3c4ad037b1b6c83cf6677aa1096a013c3b97b1f11ce45ba46ffbb25b76bfc5ad7b77f90cc69ed76115dea4029469d587:3b97b1f11ce45ba46ffbb25b76bfc5ad7b77f90cc69ed76115dea4029469d587:d110828d449198d675e74e8e39439fd15e75bf2cc1f430abfb245836885bafc420f754b89d2fbbf6dd3490792e7a4f766073cfe3b302d089831ace869e2730fde45c2121ec3ef217aa9c43fa7cc7e9ed0a01ad9f1d2fc3613638ca9fc193c98b37455bf5dbf8f38b64708dfdca6c21f0975f1017c5da5f6434bda9f033cec2a631ab50318e017b170b240bf01eb8b36c7e1cb59e7736ac34444208132a8f59e4f313d65d849c6a4fdf13e20ecaee3823e589a171b39b2489497b06e6ff58c2c9f1dc5d3aa3bd10e6443e22d42d07b783f79fd43a46e1cde314b663a95f7246dea131fcd46d1dc333c5454f86b2c4e2e424dea405cc2230d4dcd39a2eab2f92845cf6a7994192063f1202749ef52dcb96f2b79ed6a98118ca0b99ba2285490860eb4c61ab78b9ddc6acc7ad883fa5e96f9d029171223abf7573e36230e0a81f6c1311151473ee264f4b842e923dcb3b:18d05e5d01668e83f40fa3bbee28b388acf318d1b0b5ad668c672f345c8eda14c2f884cd2a9039459ce0810bc5b580fe70d3964a43edb49e73a6ff914bbf040cd110828d449198d675e74e8e39439fd15e75bf2cc1f430abfb245836885bafc420f754b89d2fbbf6dd3490792e7a4f766073cfe3b302d089831ace869e2730fde45c2121ec3ef217aa9c43fa7cc7e9ed0a01ad9f1d2fc3613638ca9fc193c98b37455bf5dbf8f38b64708dfdca6c21f0975f1017c5da5f6434bda9f033cec2a631ab50318e017b170b240bf01eb8b36c7e1cb59e7736ac34444208132a8f59e4f313d65d849c6a4fdf13e20ecaee3823e589a171b39b2489497b06e6ff58c2c9f1dc5d3aa3bd10e6443e22d42d07b783f79fd43a46e1cde314b663a95f7246dea131fcd46d1dc333c5454f86b2c4e2e424dea405cc2230d4dcd39a2eab2f92845cf6a7994192063f1202749ef52dcb96f2b79ed6a98118ca0b99ba2285490860eb4c61ab78b9ddc6acc7ad883fa5e96f9d029171223abf7573e36230e0a81f6c1311151473ee264f4b842e923dcb3b: +c57a43dcd7bab8516009546918d71ad459b7345efdca8d4f19929875c839d7222083b444236b9ab31d4e00c89d55c6260fee71ac1a47c4b5ba227404d382b82d:2083b444236b9ab31d4e00c89d55c6260fee71ac1a47c4b5ba227404d382b82d:a4f6d9c281cf81a28a0b9e77499aa24bde96cc1264374491c008294ee0af6f6e4bbb686396f59068d358e30fe9992db0c6f16680a1c71e27a4a907ac607d39bdc3258c7956482fb37996f4beb3e5051b8148019a1c256e2ee999ebc8ce64c54e07fedb4fbd8953ebd93b7d69ce5a0082edd6209d12d3619b4fd2eae916461f72a4ce727157251a19209bbff9fbdbd289436f3fcacc6b4e1318521a47839cba4b14f7d7a21e7b5d6b6a753d5804afcd2b1eb7779b92abab8afa8aa4fa51caec0b85dcd0fc2a0676036d3f56630a831ffeb502861dd89161c708a9c006c73c930ce5b94756426ff18aa112fb4eb9a68500b48d4eedbd4167b6ffd0a11d49443a173ce9d949436748fc0634f06bb08b8f3423f4463dba7b4d199b64df578117f0a2645f0b2a1e2ada27d286f76733f25b82ed1d48a5c3898d4ad621e50ed9060daad40a39532e4d1bf162ce36804d5d4e2d:1edef9bc036971f1fa88edf45393c802e6c1a1631c8a06871a09a320821dce40beca97e53a0361a955a4c6d60b8ca8e400c81340911ccb4f56284041cdbb1804a4f6d9c281cf81a28a0b9e77499aa24bde96cc1264374491c008294ee0af6f6e4bbb686396f59068d358e30fe9992db0c6f16680a1c71e27a4a907ac607d39bdc3258c7956482fb37996f4beb3e5051b8148019a1c256e2ee999ebc8ce64c54e07fedb4fbd8953ebd93b7d69ce5a0082edd6209d12d3619b4fd2eae916461f72a4ce727157251a19209bbff9fbdbd289436f3fcacc6b4e1318521a47839cba4b14f7d7a21e7b5d6b6a753d5804afcd2b1eb7779b92abab8afa8aa4fa51caec0b85dcd0fc2a0676036d3f56630a831ffeb502861dd89161c708a9c006c73c930ce5b94756426ff18aa112fb4eb9a68500b48d4eedbd4167b6ffd0a11d49443a173ce9d949436748fc0634f06bb08b8f3423f4463dba7b4d199b64df578117f0a2645f0b2a1e2ada27d286f76733f25b82ed1d48a5c3898d4ad621e50ed9060daad40a39532e4d1bf162ce36804d5d4e2d: +2dddb6b8fd04fa90ece1a709f8418f2e5d0c9c43afe7cfce19e6ad15a73476f78059de6a7c4776489ecc2e7d707ffce30285bf30a23f78d72db49cfd6ed0d492:8059de6a7c4776489ecc2e7d707ffce30285bf30a23f78d72db49cfd6ed0d492:474baa590a4cd72d5424e51d8257b3d44325bc4c5063a0033c86ebbe99ed7212184c19944d082a115379dd4cece973faa0bca6485bd25f3744a719e70aa0291e1b5a96e637c140616a98263357c76b6eb0083fe51414e386870d0fdc7dd9abe4ff6fb5bbf1e7b15dac3e08e2615f655c3104ceb32a4cc2c9e9c43cf282d346ac253ccc46b635ae040973b49735720ffb890469a567c5824e0c00d7ccd5509a718092a906461c4d6163eaf422418f5fc6e009fc3f529ac61a2f89bb8e0ed45d940c4c2331ff8d8e1d6d58d417d8fc2656a02e8701aee75aed918724eebe4a2cf4744c5c401e217023df68a6f6a0228bd05a679a697d8de7036b9ed269090d3c65486afb91e27954eb15b964665ede7ad008f12fb3a9d0e69c13b4254f43819e0818a4195f68b8a38ae81f3fcb1879c95ab4cd0ffc38e381089260cca967ace5a085b457ab5eb363852101377570f9ac9e38:c634ea7bf72e895a2e796e2834201415b8b45e05e045559284eb9052c0e84f62a5a9f0c9764f7576788c7228b19ef517c195497325a48a9344b147c12fd75509474baa590a4cd72d5424e51d8257b3d44325bc4c5063a0033c86ebbe99ed7212184c19944d082a115379dd4cece973faa0bca6485bd25f3744a719e70aa0291e1b5a96e637c140616a98263357c76b6eb0083fe51414e386870d0fdc7dd9abe4ff6fb5bbf1e7b15dac3e08e2615f655c3104ceb32a4cc2c9e9c43cf282d346ac253ccc46b635ae040973b49735720ffb890469a567c5824e0c00d7ccd5509a718092a906461c4d6163eaf422418f5fc6e009fc3f529ac61a2f89bb8e0ed45d940c4c2331ff8d8e1d6d58d417d8fc2656a02e8701aee75aed918724eebe4a2cf4744c5c401e217023df68a6f6a0228bd05a679a697d8de7036b9ed269090d3c65486afb91e27954eb15b964665ede7ad008f12fb3a9d0e69c13b4254f43819e0818a4195f68b8a38ae81f3fcb1879c95ab4cd0ffc38e381089260cca967ace5a085b457ab5eb363852101377570f9ac9e38: +5547f1004baedfce5cfc0850b05302374aad24f6163994ecd751df3af3c106207ce620787385ee1951ac49a77352ee0d6f8c5cd47df74e9e3216a6324fc7cf7f:7ce620787385ee1951ac49a77352ee0d6f8c5cd47df74e9e3216a6324fc7cf7f:a6c17eeb5b8066c2cd9a89667317a945a0c7c96996e77ae854c509c6cd0631e922ad04503af87a3c4628adafed7600d071c078a22e7f64bda08a362b38b26ca15006d38acf532d0dedea4177a2d33f06956d80e963848ec791b2762fa99449b4f1a1ed9b3f2580be3ac7d7f52fb14421d6222ba76f807750c6cbb0b16f0895fc73d9dfc587e1a9e5d1e58375fbab705b8f0c1fd7df8b3ad446f2f08459e7ed1af59556fbc966dc249c1cf604f3e677c8a09d4363608774bf3811bef0642748c55c516c7a580fa3499050acb30eed870d0d91174cb623e98c3ad121cf81f04e57d49b008424a98a31eeaaf5f38e000f903d48d215ed52f862d636a5a73607de85760167267efe30f8a26ebc5aa0c09f5b258d3361ca69d1d7ee07b59648179ab2170ec50c07f6616f216872529421a6334a4a1ed3d2671ef47bc9a92afb58314e832db8a9003408a0487503fe4f67770dd4b6:29df3ad589009c667baa5e72dabb4e53cb7876de4e7efe5cc21ead7fa878db57f97c1103ddb39a861eb88653c1d4ec3b4306e4584b47b8bc90423119e7e4af00a6c17eeb5b8066c2cd9a89667317a945a0c7c96996e77ae854c509c6cd0631e922ad04503af87a3c4628adafed7600d071c078a22e7f64bda08a362b38b26ca15006d38acf532d0dedea4177a2d33f06956d80e963848ec791b2762fa99449b4f1a1ed9b3f2580be3ac7d7f52fb14421d6222ba76f807750c6cbb0b16f0895fc73d9dfc587e1a9e5d1e58375fbab705b8f0c1fd7df8b3ad446f2f08459e7ed1af59556fbc966dc249c1cf604f3e677c8a09d4363608774bf3811bef0642748c55c516c7a580fa3499050acb30eed870d0d91174cb623e98c3ad121cf81f04e57d49b008424a98a31eeaaf5f38e000f903d48d215ed52f862d636a5a73607de85760167267efe30f8a26ebc5aa0c09f5b258d3361ca69d1d7ee07b59648179ab2170ec50c07f6616f216872529421a6334a4a1ed3d2671ef47bc9a92afb58314e832db8a9003408a0487503fe4f67770dd4b6: +3dd7203c237aefe9e38a201ff341490179905f9f100828da18fcbe58768b5760f067d7b2ff3a957e8373a7d42ef0832bcda84ebf287249a184a212a94c99ea5b:f067d7b2ff3a957e8373a7d42ef0832bcda84ebf287249a184a212a94c99ea5b:db28ed31ac04b0c2decee7a6b24fc9a082cc262ca7ccf2a247d6372ec3e9120ecedb4542ea593fea30335c5ab9dd318a3b4fd5834299cf3f53d9ef46137b273c390ec3c26a0b4470d0d94b77d82cae4b24587837b167bb7f8166710baeb3ee70af797316cb7d05fa57e468ae3f0bd449404d8528808b41fcca62f5e0a2aa5d8f3acab008cc5f6e5ab02777bdcde87f0a10ef06a4bb37fe02c94815cf76bfb8f5cdd865cc26dcb5cf492edfd547b535e2e6a6d8540956dcba62cfea19a9474406e934337e454270e01036ac45793b6b8aceda187a08d56a2ce4e98f42ea375b101a6b9fcb4231d171aa463eeb43586a4b82a387bcddaf71a80fd5c1f7292efc2bd8e70c11eaa817106061b6c461c4883d613cc06c7e2a03f73d90fc55cdc07265eefd36be72270383d6c676cae37c93691f1ae3d927b3a1cd963e4229757ae5231eea73a9f71515628305410ac2593b325cc631:4c036935a96abc0d050d907bedbe9946fb97439f039c742e051ccf09add7df44d17da98c2ca01bdc2424da1e4debf347f8fff48ac8030d2cc07f9575c044be04db28ed31ac04b0c2decee7a6b24fc9a082cc262ca7ccf2a247d6372ec3e9120ecedb4542ea593fea30335c5ab9dd318a3b4fd5834299cf3f53d9ef46137b273c390ec3c26a0b4470d0d94b77d82cae4b24587837b167bb7f8166710baeb3ee70af797316cb7d05fa57e468ae3f0bd449404d8528808b41fcca62f5e0a2aa5d8f3acab008cc5f6e5ab02777bdcde87f0a10ef06a4bb37fe02c94815cf76bfb8f5cdd865cc26dcb5cf492edfd547b535e2e6a6d8540956dcba62cfea19a9474406e934337e454270e01036ac45793b6b8aceda187a08d56a2ce4e98f42ea375b101a6b9fcb4231d171aa463eeb43586a4b82a387bcddaf71a80fd5c1f7292efc2bd8e70c11eaa817106061b6c461c4883d613cc06c7e2a03f73d90fc55cdc07265eefd36be72270383d6c676cae37c93691f1ae3d927b3a1cd963e4229757ae5231eea73a9f71515628305410ac2593b325cc631: +282775df9ebbd7c5a65f3a2b096e36ee64a8f8ea719da77758739e4e7476111da2b49646033a13937cad6b0e914e3cec54989c252ca5643d076555d8c55e56e0:a2b49646033a13937cad6b0e914e3cec54989c252ca5643d076555d8c55e56e0:14cc50c2973ea9d0187a73f71cb9f1ce07e739e049ec2b27e6613c10c26b73a2a966e01ac3be8b505aeaad1485c1c2a3c6c2b00f81b9e5f927b73bfd498601a7622e8544837aad02e72bf72196dc246902e58af253ad7e025e3666d3bfc46b5b02f0eb4a37c9554992abc8651de12fd813177379bb0ce172cd8aaf937f979642bc2ed7c7a430cb14c3cd3101b9f6b91ee3f542acdf017f8c2116297f4564768f4db95dad8a9bcdc8da4d8fb13ef6e2da0b1316d3c8c2f3ed836b35fe2fd33effb409e3bc1b0f85225d2a1de3bfc2d20563946475c4d7ca9fddbaf59ad8f8961d287ae7dd803e7af1fa612329b1bdc04e225600ae731bc01ae0925aed62ac50d46086f3646cf47b072f0d3b044b36f85cec729a8bb2b92883ca4dfb34a8ee8a0273b31af50982bb6131bfa11d55504b1f6f1a0a00438ca26d8ab4f48bcddc9d5a38851abede4151d5b70d720732a00abea2c8b979:15763973859402907d8dcb86adc24a2a168ba3abf2246173d6348afed51ef60b0c0edeff4e10bcef4c6e5778c8bc1f5e9ee0237373445b455155d23de127a20214cc50c2973ea9d0187a73f71cb9f1ce07e739e049ec2b27e6613c10c26b73a2a966e01ac3be8b505aeaad1485c1c2a3c6c2b00f81b9e5f927b73bfd498601a7622e8544837aad02e72bf72196dc246902e58af253ad7e025e3666d3bfc46b5b02f0eb4a37c9554992abc8651de12fd813177379bb0ce172cd8aaf937f979642bc2ed7c7a430cb14c3cd3101b9f6b91ee3f542acdf017f8c2116297f4564768f4db95dad8a9bcdc8da4d8fb13ef6e2da0b1316d3c8c2f3ed836b35fe2fd33effb409e3bc1b0f85225d2a1de3bfc2d20563946475c4d7ca9fddbaf59ad8f8961d287ae7dd803e7af1fa612329b1bdc04e225600ae731bc01ae0925aed62ac50d46086f3646cf47b072f0d3b044b36f85cec729a8bb2b92883ca4dfb34a8ee8a0273b31af50982bb6131bfa11d55504b1f6f1a0a00438ca26d8ab4f48bcddc9d5a38851abede4151d5b70d720732a00abea2c8b979: +4730a5cf9772d7d6665ba787bea4c95252e6ecd63ec62390547bf100c0a46375f9f094f7cc1d40f1926b5b22dce465784468b20ab349bc6d4fdf78d0042bbc5b:f9f094f7cc1d40f1926b5b22dce465784468b20ab349bc6d4fdf78d0042bbc5b:e7476d2e668420e1b0fadfbaa54286fa7fa890a87b8280e26078152295e1e6e55d1241435cc430a8693bb10cde4643f59cbfcc256f45f5090c909a14c7fc49d37bfc25af11e8f4c83f4c32d4aabf43b20fa382bb6622a1848f8ffc4dff3408bb4ec7c67a35b4cdaee5e279c0fc0a66093a9f36a60fdd65e6334a804e845c8530b6fda363b5640337d027243ccfb3c177f43e717896e46ead7f72ca06aa0ff1e77247121baf48be9a445f729ca1390fc46151cbd33fcbd7373f27a6ba55c92cbf6945b09b44b9a4e5800d403070ae66048997b2197f02181a097e563f9b9acc841139258a258bc610d3bd891637356b2edc8c184c35c65af91aaf7b1c16d74a5f5f862548139254ecf550631d5f8849afdb5b64cf366ff2633a93f3a18c39b5150245fb5f33c9e4e2d94af6963a70b88f9e7e519f8fa2a0f2e3749de883d0e6f052a949d0fc7153a8693f6d801d7352eb2f7a465c0e:552c7347bdfe131646ce0932d82a36d2c1b76d7c30ee890e0592e19f9d18b9a56f48d7a9b68c017da6b550c943af4a907baf317e419fbbc96f6cf4bfad42de00e7476d2e668420e1b0fadfbaa54286fa7fa890a87b8280e26078152295e1e6e55d1241435cc430a8693bb10cde4643f59cbfcc256f45f5090c909a14c7fc49d37bfc25af11e8f4c83f4c32d4aabf43b20fa382bb6622a1848f8ffc4dff3408bb4ec7c67a35b4cdaee5e279c0fc0a66093a9f36a60fdd65e6334a804e845c8530b6fda363b5640337d027243ccfb3c177f43e717896e46ead7f72ca06aa0ff1e77247121baf48be9a445f729ca1390fc46151cbd33fcbd7373f27a6ba55c92cbf6945b09b44b9a4e5800d403070ae66048997b2197f02181a097e563f9b9acc841139258a258bc610d3bd891637356b2edc8c184c35c65af91aaf7b1c16d74a5f5f862548139254ecf550631d5f8849afdb5b64cf366ff2633a93f3a18c39b5150245fb5f33c9e4e2d94af6963a70b88f9e7e519f8fa2a0f2e3749de883d0e6f052a949d0fc7153a8693f6d801d7352eb2f7a465c0e: +2770aadd1d123e9547832dfb2a837eba089179ef4f23abc4a53f2a714e423ee23c5fbb07530dd3a20ff35a500e3708926310fed8a899690232b42c15bd86e5dc:3c5fbb07530dd3a20ff35a500e3708926310fed8a899690232b42c15bd86e5dc:a5cc2055eba3cf6f0c6332c1f2ab5854870913b03ff7093bc94f335add44332231d9869f027d82efd5f1227144ab56e3222dc3ddccf062d9c1b0c1024d9b416dfa3ee8a7027923003465e0ffaefb75b9f29dc6bcf213adc5e318fd8ba93a7aa5bfb495de9d7c5e1a196cd3a2d7721f8ba785aa9052a1811c7fcc8f93932765059cab9c9b718945895ef26f3ac048d4cabf91a9e6aa83ac14d43156827837914eb763a23cba53f60f150f4b70203ec1833ff105849457a8da7327661fb23a554164e05fcf0146b10674964be6f6aa0acc94c41ad57180e5180d199bd9102f55d740e81789b15671bbd0670e6de5d97e1ae626d8a0ebc32c8fd9d24737274e47d2dd5941a272e72a598928ad109cde937bf248d57f5d2942983c51e2a89f8f054d5c48dfad8fcf1ffa97f7de6a3a43ca15fc6720efaec69f0836d84223f9776d111ec2bbc69b2dfd58be8ca12c072164b718cd7c246d64:f267715e9a84c7314f2d5869ef4ab8d2149a13f7e8e1c728c423906293b49ce6283454dd1c7b04741df2eabedc4d6ab1397dc95a679df04d2c17d66c79bb7601a5cc2055eba3cf6f0c6332c1f2ab5854870913b03ff7093bc94f335add44332231d9869f027d82efd5f1227144ab56e3222dc3ddccf062d9c1b0c1024d9b416dfa3ee8a7027923003465e0ffaefb75b9f29dc6bcf213adc5e318fd8ba93a7aa5bfb495de9d7c5e1a196cd3a2d7721f8ba785aa9052a1811c7fcc8f93932765059cab9c9b718945895ef26f3ac048d4cabf91a9e6aa83ac14d43156827837914eb763a23cba53f60f150f4b70203ec1833ff105849457a8da7327661fb23a554164e05fcf0146b10674964be6f6aa0acc94c41ad57180e5180d199bd9102f55d740e81789b15671bbd0670e6de5d97e1ae626d8a0ebc32c8fd9d24737274e47d2dd5941a272e72a598928ad109cde937bf248d57f5d2942983c51e2a89f8f054d5c48dfad8fcf1ffa97f7de6a3a43ca15fc6720efaec69f0836d84223f9776d111ec2bbc69b2dfd58be8ca12c072164b718cd7c246d64: +4fdab7c1600e70114b11f533242376af7614b4d5da046ac4bedea21d8a361598a25c9a94d6e4ecd95a4bd6805f762eb1c457a8d45d243238b1839cbba8f441cc:a25c9a94d6e4ecd95a4bd6805f762eb1c457a8d45d243238b1839cbba8f441cc:da405890d11a872c119dab5efcbff61e931f38eccca457edc626d3ea29ed4fe3154fafec1444da74343c06ad90ac9d17b511bcb73bb49d90bafb7c7ea800bd58411df1275c3cae71b700a5dab491a4261678587956aa4a219e1ac6dd3fb2cb8c46197218e726dc7ed234526a6b01c0d72cb93ab3f4f38a08e5940b3f61a72ad2789a0532000fac1d2d2e3ad632ac8b62bb3ff5b99d53597bf4d44b19674924df9b3db3d0253f74627ccab30031c85e291c58b5fa9167522a46746fc307036745d4f9817786e5d300e6c5d503125fea01dec3e3fedbf3861ca2627a0518fb2b24e5a7a014178719e9b345f7b249ce3a413280c8deb674f59a25be92a8ab6400c7c52b0728ae34e22b2ec200c1cbaba2ccd8af29249d17af60c36007a722fc80258a7bebab1cdaad7462a8b7588c2f7e27c6d07afcf60117fed11bd6859e75e3b4fcee3981881e95dd116827dd4b369af069d3c8f2676f8a:5075c090cfbeb6b01802af7f4da5aa4f434d5ee2f3530eebb75c85e08621f83edc08aa96693894a4277633ba81e19e9e55af5c495daa5e1a6f8cbb79c01c7207da405890d11a872c119dab5efcbff61e931f38eccca457edc626d3ea29ed4fe3154fafec1444da74343c06ad90ac9d17b511bcb73bb49d90bafb7c7ea800bd58411df1275c3cae71b700a5dab491a4261678587956aa4a219e1ac6dd3fb2cb8c46197218e726dc7ed234526a6b01c0d72cb93ab3f4f38a08e5940b3f61a72ad2789a0532000fac1d2d2e3ad632ac8b62bb3ff5b99d53597bf4d44b19674924df9b3db3d0253f74627ccab30031c85e291c58b5fa9167522a46746fc307036745d4f9817786e5d300e6c5d503125fea01dec3e3fedbf3861ca2627a0518fb2b24e5a7a014178719e9b345f7b249ce3a413280c8deb674f59a25be92a8ab6400c7c52b0728ae34e22b2ec200c1cbaba2ccd8af29249d17af60c36007a722fc80258a7bebab1cdaad7462a8b7588c2f7e27c6d07afcf60117fed11bd6859e75e3b4fcee3981881e95dd116827dd4b369af069d3c8f2676f8a: +264504604e70d72dc4474dbb34913e9c0f806dfe18c7879a41762a9e4390ec61eb2b518ce7dc71c91f3665581651fd03af84c46bf1fed2433222353bc7ec511d:eb2b518ce7dc71c91f3665581651fd03af84c46bf1fed2433222353bc7ec511d:901d70e67ed242f2ec1dda813d4c052cfb31fd00cfe5446bf3b93fdb950f952d94ef9c99d1c264a6b13c3554a264beb97ed20e6b5d66ad84db5d8f1de35c496f947a23270954051f8e4dbe0d3ef9ab3003dd47b859356cecb81c50affa68c15dadb5f864d5e1bb4d3bada6f3aba1c83c438d79a94bfb50b43879e9cef08a2bfb22fad943dbf7683779746e31c486f01fd644905048b112ee258042153f46d1c7772a0624bcd6941e9062cfda75dc8712533f4057335c298038cbca29ebdb560a295a88339692808eb3481fd9735ea414f620c143b2133f57bb64e44778a8ca70918202d157426102e1dfc0a8f7b1ae487b74f02792633154dfe74caa1b7088fda22fa8b9bc354c585f1567706e2955493870f54169e0d7691159df43897961d24a852ea970c514948f3b48f71ee586e72ec78db820f253e08db84f6f312c4333bd0b732fe75883507783e9a1fd4fbab8e5870f9bf7ad58aa:eea439a00f7e459b402b835150a779eed171ab971bd1b58dcc7f9386dadd583de8dc69e267121dde41f0f9493d450b16219cdf3c22f09482ce402fe17ca49e08901d70e67ed242f2ec1dda813d4c052cfb31fd00cfe5446bf3b93fdb950f952d94ef9c99d1c264a6b13c3554a264beb97ed20e6b5d66ad84db5d8f1de35c496f947a23270954051f8e4dbe0d3ef9ab3003dd47b859356cecb81c50affa68c15dadb5f864d5e1bb4d3bada6f3aba1c83c438d79a94bfb50b43879e9cef08a2bfb22fad943dbf7683779746e31c486f01fd644905048b112ee258042153f46d1c7772a0624bcd6941e9062cfda75dc8712533f4057335c298038cbca29ebdb560a295a88339692808eb3481fd9735ea414f620c143b2133f57bb64e44778a8ca70918202d157426102e1dfc0a8f7b1ae487b74f02792633154dfe74caa1b7088fda22fa8b9bc354c585f1567706e2955493870f54169e0d7691159df43897961d24a852ea970c514948f3b48f71ee586e72ec78db820f253e08db84f6f312c4333bd0b732fe75883507783e9a1fd4fbab8e5870f9bf7ad58aa: +2ca7447a3668b748b1fd3d52d2080d30e34d397bb2846caf8f659ac168788ca5ab331cd40a31d0173c0c8c1c17002532807bf89e3edb6d34c2dd8294632b9fbc:ab331cd40a31d0173c0c8c1c17002532807bf89e3edb6d34c2dd8294632b9fbc:a82bcd9424bffda0f2f5e9eae17835dbe468f61b785aab82934737a91c5f602cb7c617cdffe87cad726a4972e15a7b8ee147f062d2a5a4d89706b571fa8aa2b95981c78abeaaae86203fa2c0e07297406ea8c27111a86dbe1d5a7c3b7ae930904d9890f6d4abebd1412a73ad5feea64acf065d3e63b5cbe20cf20bbd2d8b94f9053ed5f66633482530124446605918de66455e8cf4b101a127233c4e27d5d55bf95bd3195d0340d43531fc75faf8dded5275bf89750de838fd10c31745be4ca41fa871cb0f9b016706a1a7e3c44bb90ac7a8ad51e272389292fd6c98ad7a069e76e3f5f3e0cc770b9e9b35a765d0d93712d7cdabd17e5d01dd8183af4ad9365db0a0fa41381fce60a081df1c5ab0f8c18f95a7a8b582dfff7f149ea579df0623b33b7508f0c663f01e3a2dcd9dfbee51cc615220fdaffdab51bdae42cb9f7fa9e3b7c69cc8ada5ccd642529ba514fdc54fcf2720b8f5d08b95:f93ada15ae9cd2b54f26f86f0c28392aed5eb6b6b44d01a4e33a54e7da37c38e8d53366f73fd85be642e4ec81236d163f0d025e76c8bbdd65d43df49f09c1f01a82bcd9424bffda0f2f5e9eae17835dbe468f61b785aab82934737a91c5f602cb7c617cdffe87cad726a4972e15a7b8ee147f062d2a5a4d89706b571fa8aa2b95981c78abeaaae86203fa2c0e07297406ea8c27111a86dbe1d5a7c3b7ae930904d9890f6d4abebd1412a73ad5feea64acf065d3e63b5cbe20cf20bbd2d8b94f9053ed5f66633482530124446605918de66455e8cf4b101a127233c4e27d5d55bf95bd3195d0340d43531fc75faf8dded5275bf89750de838fd10c31745be4ca41fa871cb0f9b016706a1a7e3c44bb90ac7a8ad51e272389292fd6c98ad7a069e76e3f5f3e0cc770b9e9b35a765d0d93712d7cdabd17e5d01dd8183af4ad9365db0a0fa41381fce60a081df1c5ab0f8c18f95a7a8b582dfff7f149ea579df0623b33b7508f0c663f01e3a2dcd9dfbee51cc615220fdaffdab51bdae42cb9f7fa9e3b7c69cc8ada5ccd642529ba514fdc54fcf2720b8f5d08b95: +494ea9bcce26885b7d17d1fc114448f239f0ce46e5f247b4c999fa86296924726901e5efae57536ba5fdd96b59657359065f25d391a1aa8cdc0d38bb5d53c139:6901e5efae57536ba5fdd96b59657359065f25d391a1aa8cdc0d38bb5d53c139:3badbfa5f5a8aa2cce0a60e686cdce654d24452f98fd54872e7395b39464380a0e185557ea134d095730864f4254d3dd946970c10c804fcc0899dfa024205be0f80b1c75449523324fe6a0751e47b4ff4822b8c33e9eaf1d1d96e0de3d4acd89696b7fcc03d49f92f82b9725700b350db1a87615369545561b8599f5ea920a310a8bafc0e8d7468cbf6f3820e943594afdd5166e4e3309dddd7694ef67e694f34fc62724ff96ac3364176f34e8a02b4cf569db5b8f77d58512aedabf0bcd1c2df12db3a9473f948c5c3243309aae46c49efd088b60f31a8a72ad7e5a35acc5d89fa66807eb5d3ba9cdf08d4753cb85089ee36f5c96b432b6928352afad58012225d6157f9e3611426df921b6d1d8374628a63031e9ffb90e42ffbba021f174f68503155430152c9155dc98ffa26c4fab065e1f8e4622c2f28a8cb043110b617441140f8e20adc16f799d1d5096b1f50532be5042d21b81ea46c7:548a093a680361b7dc56f14503b55eeec3b3f4fd4ca99d6aedce0830f7f4ae2f7328539b34c48fc9760922333dae9c7c017e7db73b8faa6c06be05e347992b063badbfa5f5a8aa2cce0a60e686cdce654d24452f98fd54872e7395b39464380a0e185557ea134d095730864f4254d3dd946970c10c804fcc0899dfa024205be0f80b1c75449523324fe6a0751e47b4ff4822b8c33e9eaf1d1d96e0de3d4acd89696b7fcc03d49f92f82b9725700b350db1a87615369545561b8599f5ea920a310a8bafc0e8d7468cbf6f3820e943594afdd5166e4e3309dddd7694ef67e694f34fc62724ff96ac3364176f34e8a02b4cf569db5b8f77d58512aedabf0bcd1c2df12db3a9473f948c5c3243309aae46c49efd088b60f31a8a72ad7e5a35acc5d89fa66807eb5d3ba9cdf08d4753cb85089ee36f5c96b432b6928352afad58012225d6157f9e3611426df921b6d1d8374628a63031e9ffb90e42ffbba021f174f68503155430152c9155dc98ffa26c4fab065e1f8e4622c2f28a8cb043110b617441140f8e20adc16f799d1d5096b1f50532be5042d21b81ea46c7: +00d735ebaee75dd579a40dfd82508274d01a1572df99b811d5b01190d82192e4ba02517c0fdd3e2614b3f7bf99ed9b492b80edf0495d230f881730ea45bc17c4:ba02517c0fdd3e2614b3f7bf99ed9b492b80edf0495d230f881730ea45bc17c4:59c0b69af95d074c88fdc8f063bfdc31b5f4a9bc9cecdffa8128e01e7c1937dde5eb0570b51b7b5d0a67a3555b4cdce2bca7a31a4fe8e1d03ab32b4035e6dadbf1532059ee01d3d9a7633a0e706a1154cab22a07cd74c06a3cb601244cf3cf35a35c3100ba47f31372a2da65dcff0d7a80a1055d8aa99212e899aad7f02e949e6fee4d3c9cefa85069eaff1f6ad06fc300c871ab82b2bedb934d20875c2a263242cdb7f9be192a8710b24c7ea98d43daec8baa5553c678a38f0e0adf7d3ff2dcc799a1dbad6eab1c3d9458a9db922f02e75cfab9d65c7336dae71895d5bb15cac203f2b38b9996c410f8655ad22d3c091c20b7f926d45e780128f19747462abc5c58932fbb9e0bc62d53868802f1b083f183b8a1f9434986d5cf97c04e2f3e145730cba98779c7fed0cab1c05d5e4653c6c3f6736260bc78ee4372862ffe9e90371d762c7432781f35ced884a4baca05653ef25f25a6f3d5628308:dcdc54611937d2bd06cacd9818b3be15ce7425427a75f50d197a337a3b8ba6714ef48866f243bd5ac7415e914517a2c1c5a953f432b99db0e620d64f74eb850559c0b69af95d074c88fdc8f063bfdc31b5f4a9bc9cecdffa8128e01e7c1937dde5eb0570b51b7b5d0a67a3555b4cdce2bca7a31a4fe8e1d03ab32b4035e6dadbf1532059ee01d3d9a7633a0e706a1154cab22a07cd74c06a3cb601244cf3cf35a35c3100ba47f31372a2da65dcff0d7a80a1055d8aa99212e899aad7f02e949e6fee4d3c9cefa85069eaff1f6ad06fc300c871ab82b2bedb934d20875c2a263242cdb7f9be192a8710b24c7ea98d43daec8baa5553c678a38f0e0adf7d3ff2dcc799a1dbad6eab1c3d9458a9db922f02e75cfab9d65c7336dae71895d5bb15cac203f2b38b9996c410f8655ad22d3c091c20b7f926d45e780128f19747462abc5c58932fbb9e0bc62d53868802f1b083f183b8a1f9434986d5cf97c04e2f3e145730cba98779c7fed0cab1c05d5e4653c6c3f6736260bc78ee4372862ffe9e90371d762c7432781f35ced884a4baca05653ef25f25a6f3d5628308: +8c34b905440b61911d1d8137c53d46a1a76d4609af973e18eb4c5709295627bbb69a8b2fdf5c20e734c2ffb294bc8ae1011d664f11afe7fbc471925cf72fa99d:b69a8b2fdf5c20e734c2ffb294bc8ae1011d664f11afe7fbc471925cf72fa99d:30b57a389b48a0beb1a48432bff6b314bded79c4a1763a5acb57cea1bfb4c6d016cf090f5bd05bbd114e33ae7c17782dfa264f46c45f8c599c603016fe9ff05b6b5a99e92fe713a4cd5c41b292ed2bb2e9cf33a440542e821ec82cbf665c3f02e3dc337d7fdb58e31b27cb2954541468814698510df18c85c81fad12db11ec6b966f4930da5646b991db97445097da30dab61cda53a41083cb96add19de6c5eec323bca9d3530e38c00b35af7360077601be6ac97f3030f930a27b90fe8b6911bae389065adc15e1882300e2a003274d23182d5efd5ba4b9130c07bd5c65fecb8b5cb7eb38836b318befdfd77de4d6ca0181f77ae5740891683225f549dd8426145c97c5818c319f7ab2d868e1a41ceab64c085116069897bf2ca3667652406155ed0646431b6de1ccc03b4279ae4d326679265dce82048e7298e1f87fcec0768ac0f5d8ff84f7210be54d411af8edea7217f4e59413121e148c60da:3e0b72073dc9375eedcca6c4fc1cd315938a050c92716bd2284f4629a962beec0b7d7cf16ab923d58f5b90d3901a8e5c75c8f17dab9998e007d8c49511973d0e30b57a389b48a0beb1a48432bff6b314bded79c4a1763a5acb57cea1bfb4c6d016cf090f5bd05bbd114e33ae7c17782dfa264f46c45f8c599c603016fe9ff05b6b5a99e92fe713a4cd5c41b292ed2bb2e9cf33a440542e821ec82cbf665c3f02e3dc337d7fdb58e31b27cb2954541468814698510df18c85c81fad12db11ec6b966f4930da5646b991db97445097da30dab61cda53a41083cb96add19de6c5eec323bca9d3530e38c00b35af7360077601be6ac97f3030f930a27b90fe8b6911bae389065adc15e1882300e2a003274d23182d5efd5ba4b9130c07bd5c65fecb8b5cb7eb38836b318befdfd77de4d6ca0181f77ae5740891683225f549dd8426145c97c5818c319f7ab2d868e1a41ceab64c085116069897bf2ca3667652406155ed0646431b6de1ccc03b4279ae4d326679265dce82048e7298e1f87fcec0768ac0f5d8ff84f7210be54d411af8edea7217f4e59413121e148c60da: +77a83e18c9f000eeff7deeac959ecba2206c0aa39d2f0e2aed5729482a7a022962b1b316135596bfbca6037ed847c61fb7f09fa36ce90abb7789b86f768b59dd:62b1b316135596bfbca6037ed847c61fb7f09fa36ce90abb7789b86f768b59dd:f3d5fa2acaefd858f1df26e03059cdcbc2468ad74afc993d0db9c4cde4113f8d55c7da71d38ba06520531c61fddb5f33d5f0353be2376e580711be45c0a30b1fa01b55e228c6fa35e3f95b67909fc7df3fd464d93d661a926f9d11f7550c17fbcc3496526e8f10e0c8916677b2be5b319b688f21e81aaa9482e5c93e64ce8c437b9c1e14fefed70a3fee568811dc31cadab3d5b220254465336dc4d97a3bd096b5e065e0cfbe82849e2c1905aca486533f0da7a61f1e9a55b8e2a83262deeb59f2b13d3a8aef5700845b83b25ae2183c0ddac0ce42f8d25674cb0d0d220a6de7c1858bb07d59a3372344d944602aa451d2b937db0fe6feca0beba81721fc361ea7509e2b6d397e1c191b56f54ab436d0d27ab4c061bd661ad1a4452387e8735754d07fa7ef4d4548b172582425b299046e6301b5ba6b914418f149cf722e10bde2e0d41700f12c8429fc897b7819da92292240cd45565458c9a7b29c12:1eaad8420ac12c99ac1ff4476678e3cbbe94da6a797f174664d5ee0f641433fb1e7cb2f5613e10805df8654cd8e0d45d96230932bc7f20b04eae836435134309f3d5fa2acaefd858f1df26e03059cdcbc2468ad74afc993d0db9c4cde4113f8d55c7da71d38ba06520531c61fddb5f33d5f0353be2376e580711be45c0a30b1fa01b55e228c6fa35e3f95b67909fc7df3fd464d93d661a926f9d11f7550c17fbcc3496526e8f10e0c8916677b2be5b319b688f21e81aaa9482e5c93e64ce8c437b9c1e14fefed70a3fee568811dc31cadab3d5b220254465336dc4d97a3bd096b5e065e0cfbe82849e2c1905aca486533f0da7a61f1e9a55b8e2a83262deeb59f2b13d3a8aef5700845b83b25ae2183c0ddac0ce42f8d25674cb0d0d220a6de7c1858bb07d59a3372344d944602aa451d2b937db0fe6feca0beba81721fc361ea7509e2b6d397e1c191b56f54ab436d0d27ab4c061bd661ad1a4452387e8735754d07fa7ef4d4548b172582425b299046e6301b5ba6b914418f149cf722e10bde2e0d41700f12c8429fc897b7819da92292240cd45565458c9a7b29c12: +73b03373ef1fd849005ecd6270dd9906f19f4439e40376cdbc520902bc976812663719e08ba3ba1666f6069a3f54991866b18cc6be41991b02eb3026ff9e155f:663719e08ba3ba1666f6069a3f54991866b18cc6be41991b02eb3026ff9e155f:d5c2deaba795c30aba321bc7de6996f0d90e4d05c747fb4dae8f3451895def6e16e72f38eace756f36635f8fb0b72a3a0c1f54663817a94d4fd346f835ab0e657f001a6f2cecb86d0825bd02639254f7f7f38ca99dbb86c64a633f73baf933aae3563281f4005e2d0e7cec9fbde8e588a957e211068be65b3d3d35bf4e8d5bb3478333df9ced9b2abaf48697994a145e9321499fc5ee560f4fbb6849e1ae8eb3d1de0083a21a03f6a6b28176f0130d3895e50e75e3d7d0947a7bc2c5b9ff69895d27791442ba8d0f2180712b567f712ea912f3b0d92c19342e0106ff1d87b46ad33af300b90855ba9769d366e79425d98e4de19905a04577707cbe625b84691781cd26bf62260b4a8bd605f77af6f970e1b3a112e8918344bd0d8d2e41dfd2ce9895b0246e50887aa3a577ff73be4b6ae60feb0ca36f6a5f8171ed209e5c566529c0940d9b4bd744ccee56e54a9a0c6e4da520dd315c2872b02db563703e:a40abe98fc69da8a1ff9ff5c2cca93632e975980ee8b82c3c376022d6524ab736d01b072f2b681b5f1cd3ea067012ed6d074e949c42327a366caa9e4750a3c08d5c2deaba795c30aba321bc7de6996f0d90e4d05c747fb4dae8f3451895def6e16e72f38eace756f36635f8fb0b72a3a0c1f54663817a94d4fd346f835ab0e657f001a6f2cecb86d0825bd02639254f7f7f38ca99dbb86c64a633f73baf933aae3563281f4005e2d0e7cec9fbde8e588a957e211068be65b3d3d35bf4e8d5bb3478333df9ced9b2abaf48697994a145e9321499fc5ee560f4fbb6849e1ae8eb3d1de0083a21a03f6a6b28176f0130d3895e50e75e3d7d0947a7bc2c5b9ff69895d27791442ba8d0f2180712b567f712ea912f3b0d92c19342e0106ff1d87b46ad33af300b90855ba9769d366e79425d98e4de19905a04577707cbe625b84691781cd26bf62260b4a8bd605f77af6f970e1b3a112e8918344bd0d8d2e41dfd2ce9895b0246e50887aa3a577ff73be4b6ae60feb0ca36f6a5f8171ed209e5c566529c0940d9b4bd744ccee56e54a9a0c6e4da520dd315c2872b02db563703e: +eab179e41ed5c889ffe6aabdc054faf1307c395e46e313e17a14fe01023ffa3086f34746d3f7a01ddbe322f1aca56d22856d38733a3a6900bb08e776450ec803:86f34746d3f7a01ddbe322f1aca56d22856d38733a3a6900bb08e776450ec803:971095cebe5031530224387c5c31966e389b8566390054cf45264b44e18964b7be52c33c4ffb259af16283438fa15dd66bc7791b7533ef10cb0beab524a6437626f4cc74512851adcc2fb129055a482c61107383fb7c5241831d5551634eef0dc0b8f9053a00971aa8fa1ae0898e4b481b6707e97c0f942040b339d92fc17bbade74675af243d8b2dafb15b1db55d12415b85f3037291930ab61600ba3431f8eb425be4491614728af101e81c091f348bc5ffd1bde6ae6cad5c15b3aa7358078cc4effb54a86e7f0e0c55e4cfe0a54605ed443fdf2aaba016585da617e77341d52889d75dd540d39fe8b7993ed705cfddea0cb0d5a731d6bfcdb816afaff47e963eedebdf241af5593353d6d401a34f029a8cdeb1904cc2caa4f9635cc2ba6b7b1a29da625ffc383be2f5a8f1fa4f39b2d4b4f4c2d8838ce258a04d4a120493fdf07f68c0ffd1c16b768a35c55fea2cac696b5c20efc10865cde8a64627dcd:143cb28027c2f82e375e5f340e7fe6e60ce7bd51000b49c74168af85e26ed2ed630ed2672090164cc54b052da694ebdd21a21b3053f4dcfd7895ea5f6c8aa80d971095cebe5031530224387c5c31966e389b8566390054cf45264b44e18964b7be52c33c4ffb259af16283438fa15dd66bc7791b7533ef10cb0beab524a6437626f4cc74512851adcc2fb129055a482c61107383fb7c5241831d5551634eef0dc0b8f9053a00971aa8fa1ae0898e4b481b6707e97c0f942040b339d92fc17bbade74675af243d8b2dafb15b1db55d12415b85f3037291930ab61600ba3431f8eb425be4491614728af101e81c091f348bc5ffd1bde6ae6cad5c15b3aa7358078cc4effb54a86e7f0e0c55e4cfe0a54605ed443fdf2aaba016585da617e77341d52889d75dd540d39fe8b7993ed705cfddea0cb0d5a731d6bfcdb816afaff47e963eedebdf241af5593353d6d401a34f029a8cdeb1904cc2caa4f9635cc2ba6b7b1a29da625ffc383be2f5a8f1fa4f39b2d4b4f4c2d8838ce258a04d4a120493fdf07f68c0ffd1c16b768a35c55fea2cac696b5c20efc10865cde8a64627dcd: +fbf146ebd51075570ec51ac410ae9f391db75b610ada6362b4dbd949656cfb66be7c2f5b21d746c8ea3245ce6f268e9da74e00fa85c9c475260c68fa1af6361f:be7c2f5b21d746c8ea3245ce6f268e9da74e00fa85c9c475260c68fa1af6361f:cd7ad4f17fcff73acc402dc102d09079b29aaf2a0f4b27cf6beeb1e2b23d19ab47deb3ae1becd68861ea279c46691738f4fff47c43047c4f8b56b6bbcc3fde0723d44120dcd307a6310dc4f366b8f3cd52db19b8266a487f7872391c45fe0d3248a7abf2c20022d3769547f683067dcc363cd22fd7cda3cadc15804056f0e2aa2b795008c598be7a961805e6df291ba3041c47ff5640275f46e6ae82092d21abcbcfba11e730216008822de3ce462400596da79f7ae5d1df8389112ad98868fa94fb0546bfe6a67aa8d28c4d32072d2eadd6256255f18c2382e662dfa922a680e06a43622c4871d27d1807f7b2703070c83db8dd929c06038b2183cb8e2b9ec4c778d7ecf9e9ffac77fa7737b055feac2e7982aeeec0b72f1bbca2424e1a844bbac79cb2e7400f81dc449d0560b521a7c16bb4167e6696586058a9b8ed2e5116690b77f2a17e5c0b16a83dcbd2e24552293e258b32ba7f844944379342698627:6768006fe0f201b217dd10eb05d4b82adcfeb2ecfc8373c3308f4150394811eb60491881a2e53d1289d96478e18a64c34b2a19832cdccfd96a2e4a0c469fdc0bcd7ad4f17fcff73acc402dc102d09079b29aaf2a0f4b27cf6beeb1e2b23d19ab47deb3ae1becd68861ea279c46691738f4fff47c43047c4f8b56b6bbcc3fde0723d44120dcd307a6310dc4f366b8f3cd52db19b8266a487f7872391c45fe0d3248a7abf2c20022d3769547f683067dcc363cd22fd7cda3cadc15804056f0e2aa2b795008c598be7a961805e6df291ba3041c47ff5640275f46e6ae82092d21abcbcfba11e730216008822de3ce462400596da79f7ae5d1df8389112ad98868fa94fb0546bfe6a67aa8d28c4d32072d2eadd6256255f18c2382e662dfa922a680e06a43622c4871d27d1807f7b2703070c83db8dd929c06038b2183cb8e2b9ec4c778d7ecf9e9ffac77fa7737b055feac2e7982aeeec0b72f1bbca2424e1a844bbac79cb2e7400f81dc449d0560b521a7c16bb4167e6696586058a9b8ed2e5116690b77f2a17e5c0b16a83dcbd2e24552293e258b32ba7f844944379342698627: +dff0eb6b426dea2fd33c1d3fc24df9b31b486facb7edb8502954a3e8da99d9fdc245085ece69fb9aa560d0c27fdb634f7a840d41d8463660fbe82483b0f3cc3a:c245085ece69fb9aa560d0c27fdb634f7a840d41d8463660fbe82483b0f3cc3a:e7c9e313d86160f4c74aa0ae07369ee22b27f81b3f69097affae28dae48483fb52a5c062306b59610f5cdbff6332b1960cd6f2b8f7b41578c20f0bc9637a0fdfc739d61f699a573f1c1a0b49294506cf4487965e5bb07bbf81803cb3d5cb3829c66c4bee7fc800ede216150934d277dea50edb097b992f11bb669fdf140bf6ae9fec46c3ea32f888fde9d154ea84f01c51265a7d3fef6eefc1ccdbffd1e2c897f05546a3b1ca11d9517cd667c660ec3960f7a8e5e80202a78d3a388b92f5c1dee14ae6acf8e17c841c9557c35a2eeced6e6af6372148e483ccd06c8fe344924e1019fb91cbf7941b9a176a073415867210670410c5dbd0ac4a50e6c0a509ddfdc555f60d696d41c77db8e6c84d5181f872755e64a721b061fcd68c463db4d32c9e01ea501267de22879d7fc12c8ca0379edb45abaa6e64dda2af6d40ccf24fbebad7b5a8d3e52007945ecd3ddc1e3efeb522581ac80e98c863ba0c590a3ed95cd1:6b48b10f545ddb7a89cd5829f4e5b20146cf6bc96e550d06f65de8bdae7ccdded26cd630f86c9266bccf88e924033e04f83a54f8290d7f734cf8673cca8f9703e7c9e313d86160f4c74aa0ae07369ee22b27f81b3f69097affae28dae48483fb52a5c062306b59610f5cdbff6332b1960cd6f2b8f7b41578c20f0bc9637a0fdfc739d61f699a573f1c1a0b49294506cf4487965e5bb07bbf81803cb3d5cb3829c66c4bee7fc800ede216150934d277dea50edb097b992f11bb669fdf140bf6ae9fec46c3ea32f888fde9d154ea84f01c51265a7d3fef6eefc1ccdbffd1e2c897f05546a3b1ca11d9517cd667c660ec3960f7a8e5e80202a78d3a388b92f5c1dee14ae6acf8e17c841c9557c35a2eeced6e6af6372148e483ccd06c8fe344924e1019fb91cbf7941b9a176a073415867210670410c5dbd0ac4a50e6c0a509ddfdc555f60d696d41c77db8e6c84d5181f872755e64a721b061fcd68c463db4d32c9e01ea501267de22879d7fc12c8ca0379edb45abaa6e64dda2af6d40ccf24fbebad7b5a8d3e52007945ecd3ddc1e3efeb522581ac80e98c863ba0c590a3ed95cd1: +9f32958c7679b90fd5036056a75ec2eb2f56ec1effc7c012461dc89a3a1674201d7269dcb6d1f584e662d4ce251de0aba290ef78b97d448afb1e5333f1976d26:1d7269dcb6d1f584e662d4ce251de0aba290ef78b97d448afb1e5333f1976d26:a56ba86c71360504087e745c41627092ad6b49a71e9daa5640e1044bf04d4f071ad728779e95d1e2460584e6f0773545da82d4814c9189a120f12f3e3819813e5b240d0f26436f70ee353b4d20cea54a1460b5b8f1008d6f95f3aa2d8f1e908fced50d624e3a096938b9353854b96da463a2798a5a312ec790842c10c446e3350c764bf5c972593b9987bf23256daa8894d47f22e85b97607e66fc08a12c789c4746080368d321bb9015a1155b65523ad8e99bb989b44eac756b0734acd7c6357c70b59743246d1652d91b0f9896965141345b9945cf34980452f3502974edb76b9c785fb0f4395266b055f3b5db8aab68e9d7102a1cd9ee3d142504f0e88b282e603a738e051d98de05d1fcc65b5f7e99c4111cc0aec489abd0ecad311bfc13e7d1653b9c31e81c998037f959d5cd980835aa0e0b09bcbed634391151da02bc01a36c9a5800afb984163a7bb815edbc0226eda0595c724ca9b3f8a71178f0d20a5a:9881a5763bdb259a3fefbba3d957162d6c70b804fa94ab613406a6ec42505b8789465ca1a9a33e1895988842270c55e5bdd5483f6b17b31781b593507a6c1808a56ba86c71360504087e745c41627092ad6b49a71e9daa5640e1044bf04d4f071ad728779e95d1e2460584e6f0773545da82d4814c9189a120f12f3e3819813e5b240d0f26436f70ee353b4d20cea54a1460b5b8f1008d6f95f3aa2d8f1e908fced50d624e3a096938b9353854b96da463a2798a5a312ec790842c10c446e3350c764bf5c972593b9987bf23256daa8894d47f22e85b97607e66fc08a12c789c4746080368d321bb9015a1155b65523ad8e99bb989b44eac756b0734acd7c6357c70b59743246d1652d91b0f9896965141345b9945cf34980452f3502974edb76b9c785fb0f4395266b055f3b5db8aab68e9d7102a1cd9ee3d142504f0e88b282e603a738e051d98de05d1fcc65b5f7e99c4111cc0aec489abd0ecad311bfc13e7d1653b9c31e81c998037f959d5cd980835aa0e0b09bcbed634391151da02bc01a36c9a5800afb984163a7bb815edbc0226eda0595c724ca9b3f8a71178f0d20a5a: +f86d6f766f88b00717b7d6327eb26cf3ceeba5385184426f9cfd8295e2421ff2cb1d250504754183704dbe21c323d66f9f9011758f6d8dab6f597b199662145b:cb1d250504754183704dbe21c323d66f9f9011758f6d8dab6f597b199662145b:da8423a6b7a18f20aa1f90ed2331b17b24067c40175bc25d8109e21d87ac00528eb3b2f66a2b52dc7ef2f8cecb75c76099cfa23db8da897043ba1cce31e2dfea46075f5e073203eaeb3d62c84c107b6dab33a14eaf149aa61850c15f5a58d88a15aba9196f9e495e8dbecbcf7e8444f5dd72a08a099d7f6209990b562974ea829ef11d29a920e3a799d0d92cb50d50f817631ab09de97c31e9a05f4d78d649fcd93a83752078ab3bb0e16c564d4fb07ca923c0374ba5bf1eea7e73668e135031feafcbb47cbc2ae30ec16a39b9c337e0a62eecdd80c0b7a04924ac3972da4fa9299c14b5a53d37b08bf02268b3bac9ea9355090eeb04ad87bee0593ba4e4443dda38a97afbf2db9952df63f178f3b4c52bcc132be8d9e26881213abdeb7e1c44c4061548909f0520f0dd7520fc408ea28c2cebc0f53063a2d30570e05350e52b390dd9b67662984847be9ad9b4cd50b069ffd29dd9c62ef14701f8d012a4a70c8431cc:ec61c0b292203a8f1d87235ede92b74723c8d23408423773ae50b1e9bc4464e03e446da9dce4c39f6dd159bea26c009ed00120bc36d4a247dc0d24bcefcc110cda8423a6b7a18f20aa1f90ed2331b17b24067c40175bc25d8109e21d87ac00528eb3b2f66a2b52dc7ef2f8cecb75c76099cfa23db8da897043ba1cce31e2dfea46075f5e073203eaeb3d62c84c107b6dab33a14eaf149aa61850c15f5a58d88a15aba9196f9e495e8dbecbcf7e8444f5dd72a08a099d7f6209990b562974ea829ef11d29a920e3a799d0d92cb50d50f817631ab09de97c31e9a05f4d78d649fcd93a83752078ab3bb0e16c564d4fb07ca923c0374ba5bf1eea7e73668e135031feafcbb47cbc2ae30ec16a39b9c337e0a62eecdd80c0b7a04924ac3972da4fa9299c14b5a53d37b08bf02268b3bac9ea9355090eeb04ad87bee0593ba4e4443dda38a97afbf2db9952df63f178f3b4c52bcc132be8d9e26881213abdeb7e1c44c4061548909f0520f0dd7520fc408ea28c2cebc0f53063a2d30570e05350e52b390dd9b67662984847be9ad9b4cd50b069ffd29dd9c62ef14701f8d012a4a70c8431cc: +a5b34cefab9479df8389d7e6f6c146aa8affb0bec837f78af64624a145cc344e7b0f4f24d9972bc6fe83826c52716ad1e0d7d19f123858cb3e99fa636ac9631a:7b0f4f24d9972bc6fe83826c52716ad1e0d7d19f123858cb3e99fa636ac9631a:e21e98af6c2bac70557eb0e864da2c2b4d6c0a39a059d3477251f6178a39676f4749e7fbea623f148a43a8b0fe0610506fa658abd2f5fa39198f2636b724db22d1aebc2ab07b2b6dbffdee8cece81e1af1493ec1964e16bf86ab258ca0feb77e3c8717e44038abe152c14be15660bf93b2d48d92c4ed7074d2494210621bcf204fba88c654d5ffe01e1a53d08f70bb237089dc807216ff6a85dbec3102237d42590778acf6c1dc566d5a2bb9a63bc21c329c272e5965baeeb0fe891de3cc8cbfa8e541a8881df68942e7ff8dc656bd08575f6aaf924a176d663b1a1f43574d11768c701b269561e55438dbebfd443d2115cb933d1cde4a915b54c325c27f499ef02bd012ff1f9a36390922887600fe712bcdc23eb5974a305372ad52951f83f0e58cc49e289841621917f1fcb0235147240dae4cf3b99b6ac6d8de94efe7c4436714508bcd0114c56068ff1b7c16d51bd906437874d6549ab5d8087896872ec8a09d7412:2fbd899d72b6d39e4f45b8b62cbbd5f3c0acb1ad8540913fa585877e91ccfef7bee50a4b0f9fedf5cc1e0d1953ad399c8389a93391e1b7c929af6d6f3b796c08e21e98af6c2bac70557eb0e864da2c2b4d6c0a39a059d3477251f6178a39676f4749e7fbea623f148a43a8b0fe0610506fa658abd2f5fa39198f2636b724db22d1aebc2ab07b2b6dbffdee8cece81e1af1493ec1964e16bf86ab258ca0feb77e3c8717e44038abe152c14be15660bf93b2d48d92c4ed7074d2494210621bcf204fba88c654d5ffe01e1a53d08f70bb237089dc807216ff6a85dbec3102237d42590778acf6c1dc566d5a2bb9a63bc21c329c272e5965baeeb0fe891de3cc8cbfa8e541a8881df68942e7ff8dc656bd08575f6aaf924a176d663b1a1f43574d11768c701b269561e55438dbebfd443d2115cb933d1cde4a915b54c325c27f499ef02bd012ff1f9a36390922887600fe712bcdc23eb5974a305372ad52951f83f0e58cc49e289841621917f1fcb0235147240dae4cf3b99b6ac6d8de94efe7c4436714508bcd0114c56068ff1b7c16d51bd906437874d6549ab5d8087896872ec8a09d7412: +ad75c9ce299c4d59393367d77a4c9f8df8dcec765c6dbd25b527fb7669913604b9910548fe6312a119c9993eebcfb9dc90030ffb0e4de2b7ccd23cbeb4fef71b:b9910548fe6312a119c9993eebcfb9dc90030ffb0e4de2b7ccd23cbeb4fef71b:62fc5ab67deb1fee9ab6cca3b88a1df1e589f0fd4a88f4aa7738948761fe84372c5b18e4655220c1d84d52acad32e229a5c756c20fc62fe4b4b4e5fd7077ae4ed5397aa796f2307ceedb6505b39297856f4aeb5e70938e36ee24a0ac7d9868306f6b53910623b7dc89a6672ad738576ed5d88831dd338321c8902bc2061f65e94d452fdfa0dc665cefb92308e52301bd4627006b363d06b775a395914d8c863e95a00d6893f3376134c429f56478145e4456f7a12d65bb2b8965d728cb2ddbb708f7125c237095a92195d92fa727a372f3545ae701f3808fee802c8967a76e8a940e55fb2d810bfb47ada156f0eda1829b159cf05c7f36cf3847d7b21de84c3dc0fe658347f79396a01139a508b60022db1c0e5aeef47e445e66f783e62c96597bdb16f209c08a9132c7573136170ee3ebf24261265a89fb4f10333375e20b33ab7403464f5249461c6853c5fddb9f58af816892910393a7077b799fdc3489720998feea86:6b7ef27bcfbf2b714985033764fccff555e3f5bc44610d6c8c62117cb3831a07f4a8bddb0eaed1d46b0289b15de1aa4dcc17d71be96a09e66ba4dc4627c7870562fc5ab67deb1fee9ab6cca3b88a1df1e589f0fd4a88f4aa7738948761fe84372c5b18e4655220c1d84d52acad32e229a5c756c20fc62fe4b4b4e5fd7077ae4ed5397aa796f2307ceedb6505b39297856f4aeb5e70938e36ee24a0ac7d9868306f6b53910623b7dc89a6672ad738576ed5d88831dd338321c8902bc2061f65e94d452fdfa0dc665cefb92308e52301bd4627006b363d06b775a395914d8c863e95a00d6893f3376134c429f56478145e4456f7a12d65bb2b8965d728cb2ddbb708f7125c237095a92195d92fa727a372f3545ae701f3808fee802c8967a76e8a940e55fb2d810bfb47ada156f0eda1829b159cf05c7f36cf3847d7b21de84c3dc0fe658347f79396a01139a508b60022db1c0e5aeef47e445e66f783e62c96597bdb16f209c08a9132c7573136170ee3ebf24261265a89fb4f10333375e20b33ab7403464f5249461c6853c5fddb9f58af816892910393a7077b799fdc3489720998feea86: +1ced574529b9b416977e92eb39448a8717cac2934a243a5c44fb44b73ccc16da85e167d5f062fee82014f3c8b1beaed8eefb2c22d8649c424b86b21b11eb8bda:85e167d5f062fee82014f3c8b1beaed8eefb2c22d8649c424b86b21b11eb8bda:1b3b953cce6d15303c61ca707609f70e7250f6c0deba56a8ce522b5986689651cdb848b842b2229661b8eeabfb8570749ed6c2b10a8fbf515053b5ea7d7a9228349e4646f9505e198029fec9ce0f38e4e0ca73625842d64caf8ced070a6e29c743586aa3db6d82993ac71fd38b783162d8fe04ffd0fa5cbc381d0e219c91937df6c973912fc02fda5377312468274c4bee6dca7f79c8b544861ed5babcf5c50e1473491be01708ac7c9ff58f1e40f855497ce9d7cc47b9410f2edd00f6496740243b8d03b2f5fa742b9c630867f77ac42f2b62c14e5ebddc7b647a05fff43670745f2851eff4909f5d27d57ae87f61e965ee60fdf97724c59267f2610b7ad5de919856d64d7c212659ce8656149b6a6d29d8f92b312be50b6e2a431d36ae022b00a6fe360e3af65432899c43be0427e36d21cfec81f21aa53b33db5ed2c37da8f96ac3e7dc67a1de37546cf7de1008c7e1adbe0f34fa7eb2434d94e6a13f4cf86a98d497622f:e0303aefe08a77738dcc657afbb9b835ed279613a53c73fdc5ddbfb350e5cff4d6c9bb43dc07c95bf4e23b64c40f8804c7169952e3c8d59a7197241bfed0740f1b3b953cce6d15303c61ca707609f70e7250f6c0deba56a8ce522b5986689651cdb848b842b2229661b8eeabfb8570749ed6c2b10a8fbf515053b5ea7d7a9228349e4646f9505e198029fec9ce0f38e4e0ca73625842d64caf8ced070a6e29c743586aa3db6d82993ac71fd38b783162d8fe04ffd0fa5cbc381d0e219c91937df6c973912fc02fda5377312468274c4bee6dca7f79c8b544861ed5babcf5c50e1473491be01708ac7c9ff58f1e40f855497ce9d7cc47b9410f2edd00f6496740243b8d03b2f5fa742b9c630867f77ac42f2b62c14e5ebddc7b647a05fff43670745f2851eff4909f5d27d57ae87f61e965ee60fdf97724c59267f2610b7ad5de919856d64d7c212659ce8656149b6a6d29d8f92b312be50b6e2a431d36ae022b00a6fe360e3af65432899c43be0427e36d21cfec81f21aa53b33db5ed2c37da8f96ac3e7dc67a1de37546cf7de1008c7e1adbe0f34fa7eb2434d94e6a13f4cf86a98d497622f: +f0790d93e2d3b84f61ef4c807147aba410e415e72b71b0d61d01026fed99da3defdf649fb033cf328e0b287796f8a25e9c6e2e871b33c2c21a4028a8a25a4b28:efdf649fb033cf328e0b287796f8a25e9c6e2e871b33c2c21a4028a8a25a4b28:7973e9f32d74805992eb65da0d637335e50eff0ce68ea2d1f3a02de704492b9cfbe7e7ba96fdb42bb821a513d73fc60402e92c855deaed73ffeaf70952029062c833e14ec1b14f144e2207f6a0e727e5a7e3cbab27d5972970f69518a15b093e740cc0ce11bf5248f0826b8a98bde8bf2c7082c97aff158d08371118c89021cc3974ae8f76d86673c3f824b62c79c4b41f40eaa8943738f03300f68cbe175468eb235a9ff0e6537f8714e97e8f08ca444e41191063b5fabd156e85dcf66606b81dad4a95065584b3e0658c20a706eaf4a0777da4d2e0cd2a0fca60109c2b4403db3f03cd4781c1fbb0272202bcb11687808c50cb98f64b7f3fd3d43333bb5a061b9e377090abb1e0a885cb26b73c163e63ff6451ff2f4ec8249c7e152bd03973a1e964e2b5b235281a938399a112a24529e383a560dc50bb1b622ad74ef35658dcb10ffe022568ac3ffae5b465a8ed7643e8561b352ee9944a35d882c712b187788a0abae5a22f:08773a6a78762cbb1e25fcbb29139941bdf16f4e09a1fa08fc701f32f933edd74c0ae983c12a0a5b020b6bcf44bb719dde8ed0781a8298265640e1608c98b3017973e9f32d74805992eb65da0d637335e50eff0ce68ea2d1f3a02de704492b9cfbe7e7ba96fdb42bb821a513d73fc60402e92c855deaed73ffeaf70952029062c833e14ec1b14f144e2207f6a0e727e5a7e3cbab27d5972970f69518a15b093e740cc0ce11bf5248f0826b8a98bde8bf2c7082c97aff158d08371118c89021cc3974ae8f76d86673c3f824b62c79c4b41f40eaa8943738f03300f68cbe175468eb235a9ff0e6537f8714e97e8f08ca444e41191063b5fabd156e85dcf66606b81dad4a95065584b3e0658c20a706eaf4a0777da4d2e0cd2a0fca60109c2b4403db3f03cd4781c1fbb0272202bcb11687808c50cb98f64b7f3fd3d43333bb5a061b9e377090abb1e0a885cb26b73c163e63ff6451ff2f4ec8249c7e152bd03973a1e964e2b5b235281a938399a112a24529e383a560dc50bb1b622ad74ef35658dcb10ffe022568ac3ffae5b465a8ed7643e8561b352ee9944a35d882c712b187788a0abae5a22f: +4cb9df7ce6fae9d62ba09e8eb70e4c969bdeafcb5ec7d7024326e6603b0621bf018069dd0eb44055a35cd8c77c37ca9fb1ad2417271385e134b2f4e81f52033c:018069dd0eb44055a35cd8c77c37ca9fb1ad2417271385e134b2f4e81f52033c:14627d6ea0e7895460759476dc74c42800ceef994327518151490d9df23067914e44788a12768ccb25471b9c3ba9d14fb436dcba38429b3a0456877763c49175d0e082683e07a9058f3685c6279307b2303d1221b9c29793d8a4877f6df51587384dadf751c5f7bfbd207d519622c37b51ceeee2c20d8269f8cb88d3fe43d6d434d5bbd0e203c1532d97ba552147227496c87f67b50bb76193add0144df1c176657585408362ca2ed04ad62acf1c25e341dfd1498d85b4b1349a8b0b9b02c43523c55853419bfed37d5a2cdf17dfbf1a3bd7759d6ae180f9d27dcd9a8933e29a7c0a30771eea7c2e0fa242925d2336dce585629057d844323964f6d3d11ff0b3f829a3be8c9f0468a6823d8e70ab5a2da21e15fa8b041a29812222e9c30b2bd9a12d1fdee6f87876e8ce81009637a8bb2236129a47ca74289ee4aad429ffe29f47430241ca8cc3848b7200fd6e1470651a9a0a6f72c9033e831df051408a6260f65cbaf6e012b18e:e33c07836c537d6bfbd0f4592d6e35b163499ba78dc7ffcec565d04f9a7db781943e29e6ce76763e9baddf57437fd9c6b03239a6e6850e4502a356c2e12c370514627d6ea0e7895460759476dc74c42800ceef994327518151490d9df23067914e44788a12768ccb25471b9c3ba9d14fb436dcba38429b3a0456877763c49175d0e082683e07a9058f3685c6279307b2303d1221b9c29793d8a4877f6df51587384dadf751c5f7bfbd207d519622c37b51ceeee2c20d8269f8cb88d3fe43d6d434d5bbd0e203c1532d97ba552147227496c87f67b50bb76193add0144df1c176657585408362ca2ed04ad62acf1c25e341dfd1498d85b4b1349a8b0b9b02c43523c55853419bfed37d5a2cdf17dfbf1a3bd7759d6ae180f9d27dcd9a8933e29a7c0a30771eea7c2e0fa242925d2336dce585629057d844323964f6d3d11ff0b3f829a3be8c9f0468a6823d8e70ab5a2da21e15fa8b041a29812222e9c30b2bd9a12d1fdee6f87876e8ce81009637a8bb2236129a47ca74289ee4aad429ffe29f47430241ca8cc3848b7200fd6e1470651a9a0a6f72c9033e831df051408a6260f65cbaf6e012b18e: +a136e009d53e5ef59d0946bc175663a86bc0fcd29eadd95cfc9d266037b1e4fb9c1806ec0454f58314eb8397d64287dee386640d8491aba364607688841715a0:9c1806ec0454f58314eb8397d64287dee386640d8491aba364607688841715a0:a49d1c3d49e13c2eda56868a8824aa9f8d2bf72f21955ebafd07b3bdc8e924de20936cee513d8a64a47173a3bd659eff1accff8244b26aae1a0c27fa891bf4d85e8fb1b76a6cab1e7f74c89ee07bb40d714326f09b3fd40632fad208ea816f9072028c14b5b54ecc1c5b7fc809e7e0786e2f11495e76017eb62aa4563f3d00ee84348d9838cd17649f6929a6d206f60e6fc82e0c3464b27e0e6abd22f4469bdfd4cb54f77e329b80f71bf42129ec13c9dfe192adfaa42ee3ddeeda385816fbad5f411938c63b560f4ecd94534be7d98725cd94c99ce492f0f069ba0ec08f877a7812ef27ae19d7a77be63f66bcf8d6cf3a1a61fc9cfef104c7462a21ca7f03afb5bb1ac8c75124b554e8d044b810d95ff8c9dd09a34484d8c4b6c95f95c3c22823f52ce844293724d5259191f1ba0929e2acdbb8b9a7a8adf0c52e78acdfdf057b0985881afbed4dbebdebbdae0a2b63bd4e90f96afdcbbd78f506309f9bdb650013cb73faed73904e:bc094ba91c115dee15d753361a75f3f03d6af45c92157e95dbe8d32194b6c5ce72b9dc66f73df12dca0b639f3e791d478616a1f8d7359a42c8eae0dda16b1606a49d1c3d49e13c2eda56868a8824aa9f8d2bf72f21955ebafd07b3bdc8e924de20936cee513d8a64a47173a3bd659eff1accff8244b26aae1a0c27fa891bf4d85e8fb1b76a6cab1e7f74c89ee07bb40d714326f09b3fd40632fad208ea816f9072028c14b5b54ecc1c5b7fc809e7e0786e2f11495e76017eb62aa4563f3d00ee84348d9838cd17649f6929a6d206f60e6fc82e0c3464b27e0e6abd22f4469bdfd4cb54f77e329b80f71bf42129ec13c9dfe192adfaa42ee3ddeeda385816fbad5f411938c63b560f4ecd94534be7d98725cd94c99ce492f0f069ba0ec08f877a7812ef27ae19d7a77be63f66bcf8d6cf3a1a61fc9cfef104c7462a21ca7f03afb5bb1ac8c75124b554e8d044b810d95ff8c9dd09a34484d8c4b6c95f95c3c22823f52ce844293724d5259191f1ba0929e2acdbb8b9a7a8adf0c52e78acdfdf057b0985881afbed4dbebdebbdae0a2b63bd4e90f96afdcbbd78f506309f9bdb650013cb73faed73904e: +ff0f1c57dd884fbeea6e2917282b79ba67f8a6851267b9f4636dafda33bd2b5bfef6378ad12a7c252fa6eb742b05064b41530ff019dc680ab544c027ea2836e7:fef6378ad12a7c252fa6eb742b05064b41530ff019dc680ab544c027ea2836e7:522a5e5eff5b5e98fad6878a9d72df6eb318622610a1e1a48183f5590ecef5a6df671b28be91c88cdf7ae2881147fe6c37c28b43f64cf981c455c59e765ce94e1b6491631deaeef6d1da9ebca88643c77f83eae2cfdd2d97f604fe45081d1be5c4ae2d875996b8b6fecd707d3fa219a93ba0488e55247b405e330cfb97d31a1361c9b2084bdb13fb0c058925db8c3c649c9a3e937b533cc6310fa3b16126fb3cc9bb2b35c5c8300015488a30fadca3c8871fa70dfdc7055bf8e631f20c9b2528311e324a7c4edd5462079f3441c9ecf55fa999e731372344fdc0d413e417aaa001a1b2d3d9bc000fec1b02bd7a88a812d9d8a66f9464764c070c93041eefb17ce74eff6d4aff75f0cbf6a789a9ecde74abe33130fca0da853aa7c3313ada3f0ae2f595c6796a93685e729dd18a669d6381825ab3f36a391e7525b2a807a52fa5ec2a030a8cf3b77337ac41fceb580e845eed655a48b547238c2e8137c92f8c27e585caad3106eee3814a:d5008486726cce330a29dd7e4d7474d735798201afd1206feb869a112e5b43523c06976761be3cf9b2716378273c94f93572a7d2b8982634e0755c632b449008522a5e5eff5b5e98fad6878a9d72df6eb318622610a1e1a48183f5590ecef5a6df671b28be91c88cdf7ae2881147fe6c37c28b43f64cf981c455c59e765ce94e1b6491631deaeef6d1da9ebca88643c77f83eae2cfdd2d97f604fe45081d1be5c4ae2d875996b8b6fecd707d3fa219a93ba0488e55247b405e330cfb97d31a1361c9b2084bdb13fb0c058925db8c3c649c9a3e937b533cc6310fa3b16126fb3cc9bb2b35c5c8300015488a30fadca3c8871fa70dfdc7055bf8e631f20c9b2528311e324a7c4edd5462079f3441c9ecf55fa999e731372344fdc0d413e417aaa001a1b2d3d9bc000fec1b02bd7a88a812d9d8a66f9464764c070c93041eefb17ce74eff6d4aff75f0cbf6a789a9ecde74abe33130fca0da853aa7c3313ada3f0ae2f595c6796a93685e729dd18a669d6381825ab3f36a391e7525b2a807a52fa5ec2a030a8cf3b77337ac41fceb580e845eed655a48b547238c2e8137c92f8c27e585caad3106eee3814a: +0bc6af64de5709d3dbc28f7ef6d3fe28b6de529f08f5857ccb910695de454f56fb491fc900237bdc7e9a119f27150cd911935cd3628749ff40ef41f3955bc8ac:fb491fc900237bdc7e9a119f27150cd911935cd3628749ff40ef41f3955bc8ac:ac7886e4f4172a22c95e8eea37437b375d72accedcee6cc6e816763301a2d8ef4d6f31a2c1d635818b7026a395ce0dafd71c5180893af76b7ea056c972d680eca01dcbdbae6b26f1c5f33fc988b824fbbe00cacc316469a3bae07aa7c8885af7f65f42e75cef94dbb9aab4825143c85070e7716b7612f64ef0b0166011d23eb5654aa098b02d8d71e57c8fa17bff2fe97dc8193177eadc09fb192d80aa92afa98720d4614817ff3c39d3acce18906fa3de09618931d0d7a60c4429cbfa20cf165c947929ac293ae6c06e7e8f25f1264291e3e1c98f5d93e6ecc2389bc60dbbf4a621b132c552a99c95d26d8d1af61138b570a0de4b497ebe8051c7273a98e6e7876d0b327503af3cb2cc4091ce1925cb2f2957f4ec56ee90f8a09dd57d6e83067a356a4cfe65b1b7a4465da2ab133b0efb5e7d4dbb811bcbbde712afbf0f7dd3f326222284b8c74eac7ad6257fa8c632b7da2559a6266e91e0ef90dbb0aa968f75376b693fcaa5da342221:dbc7134d1cd6b0813b53352714b6df939498e91cf37c324337d9c088a1b998347d26185b430900412929e4f63e910379fc42e355a4e98f6fee27dafad1957206ac7886e4f4172a22c95e8eea37437b375d72accedcee6cc6e816763301a2d8ef4d6f31a2c1d635818b7026a395ce0dafd71c5180893af76b7ea056c972d680eca01dcbdbae6b26f1c5f33fc988b824fbbe00cacc316469a3bae07aa7c8885af7f65f42e75cef94dbb9aab4825143c85070e7716b7612f64ef0b0166011d23eb5654aa098b02d8d71e57c8fa17bff2fe97dc8193177eadc09fb192d80aa92afa98720d4614817ff3c39d3acce18906fa3de09618931d0d7a60c4429cbfa20cf165c947929ac293ae6c06e7e8f25f1264291e3e1c98f5d93e6ecc2389bc60dbbf4a621b132c552a99c95d26d8d1af61138b570a0de4b497ebe8051c7273a98e6e7876d0b327503af3cb2cc4091ce1925cb2f2957f4ec56ee90f8a09dd57d6e83067a356a4cfe65b1b7a4465da2ab133b0efb5e7d4dbb811bcbbde712afbf0f7dd3f326222284b8c74eac7ad6257fa8c632b7da2559a6266e91e0ef90dbb0aa968f75376b693fcaa5da342221: +2f5e83bd5b412e71ae3e9084cd369efcc79bf6037c4b174dfd6a11fb0f5da218a22a6da29a5ef6240c49d8896e3a0f1a4281a266c77d383ee6f9d25ffacbb872:a22a6da29a5ef6240c49d8896e3a0f1a4281a266c77d383ee6f9d25ffacbb872:b766273f060ef3b2ae3340454a391b426bc2e97264f8674553eb00dd6ecfdd59b611d8d662929fec710d0e462020e12cdbf9c1ec8858e85671acf8b7b14424ce92079d7d801e2ad9acac036bc8d2dfaa72aa839bff30c0aa7e414a882c00b645ff9d31bcf5a54382def4d0142efa4f06e823257ff132ee968cdc6738c53f53b84c8df76e9f78dd5056cf3d4d5a80a8f84e3edec48520f2cb4583e708539355ef7aa86fb5a0e87a94dcf14f30a2cca568f139d9ce59eaf459a5c5916cc8f20b26aaf6c7c029379aedb05a07fe585ccac60307c1f58ca9f859157d06d06baa394aace79d51b8cb38cfa2598141e245624e5ab9b9d68731173348905315bf1a5ad61d1e8adaeb810e4e8a86d7c13537b0be860ab2ed35b73399b8808aa91d750f77943f8a8b7e89fdb50728aa3dbbd8a41a6e00756f438c9b9e9d55872df5a9068add8a972b7e43edad9ced2237ca1367be4b7cdb66a54ea12eef129471158610eaf28f99f7f686557dcdf644ea:9f80922bc8db32d0cc43f9936affebe7b2bc35a5d82277cd187b5d50dc7fc4c4832fffa34e9543806b485c04548e7c75429425e14d55d91fc1052efd8667430bb766273f060ef3b2ae3340454a391b426bc2e97264f8674553eb00dd6ecfdd59b611d8d662929fec710d0e462020e12cdbf9c1ec8858e85671acf8b7b14424ce92079d7d801e2ad9acac036bc8d2dfaa72aa839bff30c0aa7e414a882c00b645ff9d31bcf5a54382def4d0142efa4f06e823257ff132ee968cdc6738c53f53b84c8df76e9f78dd5056cf3d4d5a80a8f84e3edec48520f2cb4583e708539355ef7aa86fb5a0e87a94dcf14f30a2cca568f139d9ce59eaf459a5c5916cc8f20b26aaf6c7c029379aedb05a07fe585ccac60307c1f58ca9f859157d06d06baa394aace79d51b8cb38cfa2598141e245624e5ab9b9d68731173348905315bf1a5ad61d1e8adaeb810e4e8a86d7c13537b0be860ab2ed35b73399b8808aa91d750f77943f8a8b7e89fdb50728aa3dbbd8a41a6e00756f438c9b9e9d55872df5a9068add8a972b7e43edad9ced2237ca1367be4b7cdb66a54ea12eef129471158610eaf28f99f7f686557dcdf644ea: +722a2da50e42c11a61c9afac7be1a2fed2267d650f8f7d8e5bc706b807c1b91dfd0b964562f823721e649c3fedb432a76f91e0aead7c61d35f95ed7726d78589:fd0b964562f823721e649c3fedb432a76f91e0aead7c61d35f95ed7726d78589:173e8bb885e1f9081404acac999041d2ecfcb73f945e0db36e631d7cd1ab999eb717f34bf07874bf3d34e2530eb6085f4a9f88ae1b0f7d80f221456a8e9a8890b91a50192deaaacc0a1a615a87841e2c5a9e057957af6e48e78cc86198e32e7aa24dcf6cffa329bc72606d65b11682c8ba736cce22a05785df1146331e41609cf9ca711cf464958297138b58a9073f3bbf06ad8a85d135de66652104d88b49d27ad41e59bcc44c7fab68f53f0502e293ffcabaaf755927dfdffbfde3b35c080b5de4c8b785f4da64ef357bc0d1466a6a96560c3c4f3e3c0b563a003f5f95f237171bce1a001771a04ede7cdd9b8ca770fd36ef90e9fe0000a8d7685fd153cc7282de95920a8f8f0898d00bf0c6c933fe5bb9653ff146c4e2acd1a2e0c23c1244844dacf8652716302c2032f9c114679ed26b3ee3ab4a7b18bc4e3071f0977db57cd0ac68c0727a09b4f125fb64af2850b26c8a484263334e2da902d744737044e79ab1cf5b2f93a022b63d40cd:c2695a57172aaa31bd0890f231ca8eeec0287a87172669a899ad0891cea4c47579b50420e791cdec8c182c8a0e8dde21b2480b0cfd8111e28e5603347a352d04173e8bb885e1f9081404acac999041d2ecfcb73f945e0db36e631d7cd1ab999eb717f34bf07874bf3d34e2530eb6085f4a9f88ae1b0f7d80f221456a8e9a8890b91a50192deaaacc0a1a615a87841e2c5a9e057957af6e48e78cc86198e32e7aa24dcf6cffa329bc72606d65b11682c8ba736cce22a05785df1146331e41609cf9ca711cf464958297138b58a9073f3bbf06ad8a85d135de66652104d88b49d27ad41e59bcc44c7fab68f53f0502e293ffcabaaf755927dfdffbfde3b35c080b5de4c8b785f4da64ef357bc0d1466a6a96560c3c4f3e3c0b563a003f5f95f237171bce1a001771a04ede7cdd9b8ca770fd36ef90e9fe0000a8d7685fd153cc7282de95920a8f8f0898d00bf0c6c933fe5bb9653ff146c4e2acd1a2e0c23c1244844dacf8652716302c2032f9c114679ed26b3ee3ab4a7b18bc4e3071f0977db57cd0ac68c0727a09b4f125fb64af2850b26c8a484263334e2da902d744737044e79ab1cf5b2f93a022b63d40cd: +5fe9c3960ed5bd374cc94d42357e6a24dc7e3060788f726365defacf13cd12da0ce7b155c8b20ebdaacdc2aa23627e34b1f9ace980650a2530c7607d04814eb4:0ce7b155c8b20ebdaacdc2aa23627e34b1f9ace980650a2530c7607d04814eb4:c9490d83d9c3a9370f06c91af001685a02fe49b5ca667733fff189eee853ec1667a6c1b6c787e9244812d2d532866ab74dfc870d6f14033b6bcd39852a3900f8f08cd95a74cb8cbe02b8b8b51e993a06adfebd7fc9854ae5d29f4df9642871d0c5e470d903cfbcbd5adb3275628f28a80bf8c0f0376687dae673bf7a8547e80d4a9855ae2572fc2b205dc8a198016ddc9b50995f5b39f368f540504a551803d6dd5f874828e5541ded052894d9e2dc5e6aa351087e790c0dd5d9c4decb217e4db81c98a184b264e6daeac0f11e074cae2bfc899f54b419c65dcc22664a915fbfffac35cee0f286eb7b144933db933e16c4bcb650d537722489de236373fd8d65fc86118b6def37ca4608bc6ce927b65436ffda7f02bfbf88b045ae7d2c2b45a0b30c8f2a04df953221088c555fe9a5df260982a3d64df194ee952fa9a98c31b96493db6180d13d67c36716f95f8c0bd7a039ad990667ca34a83ac1a18c37dd7c7736aa6b9b6fc2b1ac0ce119ef77:379f9c54c413af0d192e9bc736b29da9d521e7ba7841d309f9bcc1e742ec4308fe9f7ba51e0b22aed487cb4aa3913b9bebfb3aacd38f4039f9bbbebe1ad80002c9490d83d9c3a9370f06c91af001685a02fe49b5ca667733fff189eee853ec1667a6c1b6c787e9244812d2d532866ab74dfc870d6f14033b6bcd39852a3900f8f08cd95a74cb8cbe02b8b8b51e993a06adfebd7fc9854ae5d29f4df9642871d0c5e470d903cfbcbd5adb3275628f28a80bf8c0f0376687dae673bf7a8547e80d4a9855ae2572fc2b205dc8a198016ddc9b50995f5b39f368f540504a551803d6dd5f874828e5541ded052894d9e2dc5e6aa351087e790c0dd5d9c4decb217e4db81c98a184b264e6daeac0f11e074cae2bfc899f54b419c65dcc22664a915fbfffac35cee0f286eb7b144933db933e16c4bcb650d537722489de236373fd8d65fc86118b6def37ca4608bc6ce927b65436ffda7f02bfbf88b045ae7d2c2b45a0b30c8f2a04df953221088c555fe9a5df260982a3d64df194ee952fa9a98c31b96493db6180d13d67c36716f95f8c0bd7a039ad990667ca34a83ac1a18c37dd7c7736aa6b9b6fc2b1ac0ce119ef77: +ec2fa541ac14b414149c3825eaa7001b795aa1957d4040dda92573904afa7ee471b363b2408404d7beecdef1e1f511bb6084658b532f7ea63d4e3f5f01c61d31:71b363b2408404d7beecdef1e1f511bb6084658b532f7ea63d4e3f5f01c61d31:2749fc7c4a729e0e0ad71b5b74eb9f9c534ebd02ffc9df4374d813bdd1ae4eb87f1350d5fdc563934515771763e6c33b50e64e0cd114573031d2186b6eca4fc802cddc7cc51d92a61345a17f6ac38cc74d84707a5156be9202dee3444652e79bae7f0d31bd17567961f65dd01a8e4bee38331938ce4b2b550691b99a4bc3c072d186df4b3344a5c8fbfbb9fd2f355f6107e410c3d0c798b68d3fb9c6f7ab5fe27e70871e86767698fe35b77ead4e435a9402cc9ed6a2657b059be0a21003c048bbf5e0ebd93cbb2e71e923cf5c728d1758cd817ad74b454a887126d653b95a7f25e5293b768c9fc5a9c35a2372e3741bc90fd66301427b10824bb4b1e9110bfba84c21a40eb8fed4497e91dc3ffd0438c514c0a8cb4cac6ad0256bf11d5aa7a9c7c00b669b015b0bf81425a21413e2ffb6edc0bd78e385c44fd74558e511c2c25fee1fec18d3990b8690300fa711e93d9854668f0187065e76e7113ae763c30ddd86720b5546a6c3c6f1c43bc67b14:84d18d56f964e3776759bba92c510c2b6d574555c3cddade212da90374554991e7d77e278d63e34693e1958078cc3685f8c41c1f5342e351899638ef612114012749fc7c4a729e0e0ad71b5b74eb9f9c534ebd02ffc9df4374d813bdd1ae4eb87f1350d5fdc563934515771763e6c33b50e64e0cd114573031d2186b6eca4fc802cddc7cc51d92a61345a17f6ac38cc74d84707a5156be9202dee3444652e79bae7f0d31bd17567961f65dd01a8e4bee38331938ce4b2b550691b99a4bc3c072d186df4b3344a5c8fbfbb9fd2f355f6107e410c3d0c798b68d3fb9c6f7ab5fe27e70871e86767698fe35b77ead4e435a9402cc9ed6a2657b059be0a21003c048bbf5e0ebd93cbb2e71e923cf5c728d1758cd817ad74b454a887126d653b95a7f25e5293b768c9fc5a9c35a2372e3741bc90fd66301427b10824bb4b1e9110bfba84c21a40eb8fed4497e91dc3ffd0438c514c0a8cb4cac6ad0256bf11d5aa7a9c7c00b669b015b0bf81425a21413e2ffb6edc0bd78e385c44fd74558e511c2c25fee1fec18d3990b8690300fa711e93d9854668f0187065e76e7113ae763c30ddd86720b5546a6c3c6f1c43bc67b14: +6132692a5ef27bf476b1e991e6c431a8c764f1aebd470282db3321bb7cb09c207a2d166184f9e5f73bea454486b041ceb5fc2314a7bd59cb718e79f0ec989d84:7a2d166184f9e5f73bea454486b041ceb5fc2314a7bd59cb718e79f0ec989d84:a9c0861665d8c2de06f9301da70afb27b3024b744c6b38b24259294c97b1d1cb4f0dcf7575a8ed454e2f0980f50313a77363415183fe9677a9eb1e06cb6d34a467cb7b0758d6f55c564b5ba15603e202b18856d89e72a23ab07d8853ff77da7aff1caebd7959f2c710ef31f5078a9f2cdae92641a1cc5f74d0c143ec42afbaa5f378a9e10d5bf74587fa5f49c156233247dafd3929acde888dc684337e40cdc5932e7eb73ffcc90b85c0ad460416691aefbd7efd07b657c350946a0e366b37a6c8089aba5c5fe3bbca064afbe9d47fbc83914af1cb43c2b2efa98e0a43be32ba823202001def36817251b65f9b0506cef6683642a46ed612f8ca81ee97bb04d317b517343ade2b77126d1f02a87b7604c8653b6748cf5488fa6d43df809faa19e69292d38c5d397dd8e20c7af7c5334ec977f5010a0f7cb5b89479ca06db4d12627f067d6c42186a6b1f8742f36ae709ba720e3cd898116666d81b190b9b9d2a72202cb690a03f3310429a71dc048cde:eb677f3347e1a1ea929efdf62bf9105a6c8f4993033b4f6d03cb0dbf9c742b270704e383ab7c0676bdb1ad0ce9b16673083c9602ec10ae1dd98e8748b336440ba9c0861665d8c2de06f9301da70afb27b3024b744c6b38b24259294c97b1d1cb4f0dcf7575a8ed454e2f0980f50313a77363415183fe9677a9eb1e06cb6d34a467cb7b0758d6f55c564b5ba15603e202b18856d89e72a23ab07d8853ff77da7aff1caebd7959f2c710ef31f5078a9f2cdae92641a1cc5f74d0c143ec42afbaa5f378a9e10d5bf74587fa5f49c156233247dafd3929acde888dc684337e40cdc5932e7eb73ffcc90b85c0ad460416691aefbd7efd07b657c350946a0e366b37a6c8089aba5c5fe3bbca064afbe9d47fbc83914af1cb43c2b2efa98e0a43be32ba823202001def36817251b65f9b0506cef6683642a46ed612f8ca81ee97bb04d317b517343ade2b77126d1f02a87b7604c8653b6748cf5488fa6d43df809faa19e69292d38c5d397dd8e20c7af7c5334ec977f5010a0f7cb5b89479ca06db4d12627f067d6c42186a6b1f8742f36ae709ba720e3cd898116666d81b190b9b9d2a72202cb690a03f3310429a71dc048cde: +f219b2101164aa9723bde3a7346f68a35061c01f9782072580ba32df903ba891f66b920d5aa1a6085495a1480539beba01ffe60e6a6388d1b2e8eda23355810e:f66b920d5aa1a6085495a1480539beba01ffe60e6a6388d1b2e8eda23355810e:015577d3e4a0ec1ab25930106343ff35ab4f1e0a8a2d844aadbb70e5fc5348ccb679c2295c51d702aaae7f6273ce70297b26cb7a253a3db94332e86a15b4a64491232791f7a8b082ee2834af30400e804647a532e9c454d2a0a7320130ab6d4d860073a34667ac25b7e5e2747ba9f5c94594fb68377ae260369c40713b4e32f23195bf91d3d7f1a2719bf408aad8d8a347b112e84b118817cb06513344021763035272a7db728a0ccdaa949c61715d0764140b3e8c01d20ff1593c7f2d55c4e82a1c0cb1ea58442bf80a741bca91f58ab0581b498ee9fe3c92ca654148ef75313543d1aff382befe1a93b02190ce0102175158e2071d02bacad8dbe9fb940fcb610c105ad52c80feb1ec4e524f4c0ec7983e9ce696fa4fcf4bf0514b8f0432b17d5448fc426fea2b01ac7b26c2aed769927534da22576fc1bba726e9d65be01b59f60a648ace2fc3e5e275789fa637cbbd84be3d6ac24457a6292cd656c7b569a52ffea7916b8d04b4f4a75be7ac95142f:17f0127ca3bafa5f4ee959cd60f772be87a0034961517e39a0a1d0f4b9e26db1336e60c82b352c4cbacdbbd11771c3774f8cc5a1a795d6e4f4ebd51def36770b015577d3e4a0ec1ab25930106343ff35ab4f1e0a8a2d844aadbb70e5fc5348ccb679c2295c51d702aaae7f6273ce70297b26cb7a253a3db94332e86a15b4a64491232791f7a8b082ee2834af30400e804647a532e9c454d2a0a7320130ab6d4d860073a34667ac25b7e5e2747ba9f5c94594fb68377ae260369c40713b4e32f23195bf91d3d7f1a2719bf408aad8d8a347b112e84b118817cb06513344021763035272a7db728a0ccdaa949c61715d0764140b3e8c01d20ff1593c7f2d55c4e82a1c0cb1ea58442bf80a741bca91f58ab0581b498ee9fe3c92ca654148ef75313543d1aff382befe1a93b02190ce0102175158e2071d02bacad8dbe9fb940fcb610c105ad52c80feb1ec4e524f4c0ec7983e9ce696fa4fcf4bf0514b8f0432b17d5448fc426fea2b01ac7b26c2aed769927534da22576fc1bba726e9d65be01b59f60a648ace2fc3e5e275789fa637cbbd84be3d6ac24457a6292cd656c7b569a52ffea7916b8d04b4f4a75be7ac95142f: +fc180035aec0f5ede7bda93bf77ade7a81ed06de07ee2e3aa8576be81608610a4f215e948cae243ee3143b80282ad792c780d2a6b75060ca1d290ca1a8e3151f:4f215e948cae243ee3143b80282ad792c780d2a6b75060ca1d290ca1a8e3151f:b5e8b01625664b222339e0f05f93a990ba48b56ae65439a17520932df011721e284dbe36f98631c066510098a68d7b692a3863e99d58db76ca5667c8043cb10bd7abbaf506529fbb23a5166be038affdb9a234c4f4fcf43bddd6b8d2ce772dd653ed115c095e232b269dd4888d2368cb1c66be29dd383fca67f66765b296564e37555f0c0e484504c591f006ea8533a12583ad2e48318ff6f324ecaf804b1bae04aa896743e67ef61ca383d58e42acfc6410de30776e3ba262373b9e1441943955101a4e768231ad9c6529eff6118dde5df02f94b8d6df2d99f27863b517243a579e7aaff311ea3a0282e47ca876fabc2280fce7adc984dd0b30885b1650f1471dfcb0522d49fec7d042f32a93bc368f076006ea01ec1c7412bf66f62dc88de2c0b74701a5614e855e9fa728fb1f1171385f96afbde70dea02e9aa94dc21848c26302b50ae91f9693a1864e4e095ae03cdc22ad28a0eb7db596779246712fab5f5da327efec3e79612de0a6ccaa536759b8e:a43a71c3a19c35660dae6f31a254b8c0ea3593fc8fca74d13640012b9e9473d4afe070db01e7fb399bf4ca6070e062180011285a67dd6858b761e46c6bd32004b5e8b01625664b222339e0f05f93a990ba48b56ae65439a17520932df011721e284dbe36f98631c066510098a68d7b692a3863e99d58db76ca5667c8043cb10bd7abbaf506529fbb23a5166be038affdb9a234c4f4fcf43bddd6b8d2ce772dd653ed115c095e232b269dd4888d2368cb1c66be29dd383fca67f66765b296564e37555f0c0e484504c591f006ea8533a12583ad2e48318ff6f324ecaf804b1bae04aa896743e67ef61ca383d58e42acfc6410de30776e3ba262373b9e1441943955101a4e768231ad9c6529eff6118dde5df02f94b8d6df2d99f27863b517243a579e7aaff311ea3a0282e47ca876fabc2280fce7adc984dd0b30885b1650f1471dfcb0522d49fec7d042f32a93bc368f076006ea01ec1c7412bf66f62dc88de2c0b74701a5614e855e9fa728fb1f1171385f96afbde70dea02e9aa94dc21848c26302b50ae91f9693a1864e4e095ae03cdc22ad28a0eb7db596779246712fab5f5da327efec3e79612de0a6ccaa536759b8e: +a2836a65427912122d25dcdfc99d7046fe9b53d5c1bb23617f11890e94ca93ed8c12bda214c8abb2286acffbf8112425040aab9f4d8bb7870b98da0159e882f1:8c12bda214c8abb2286acffbf8112425040aab9f4d8bb7870b98da0159e882f1:813d6061c56eae0ff53041c0244aa5e29e13ec0f3fb428d4beb8a99e04bca8c41bddb0db945f487efe38f2fc14a628fafa2462f860e4e34250eb4e93f139ab1b74a2614519e41ee2403be427930ab8bc82ec89ceafb60905bd4ddbbd13bdb19654314fc92373140b962e2258e038d71b9ec66b84ef8319e03551cb707e747f6c40ad476fbefdce71f3a7b67a1af1869bc6440686e7e0855e4f369d1d88b8099fba54714678627bba1aff41e7707bc97eddf890b0c08dce3e9800d24c6f61092ce28d481b5dea5c096c55d72f8946009131fb968e2bc8a054d825adab76740dcf0d758c8bf54ff38659e71b32bfe2e615aaabb0f5293085649cf60b9847bc62011ce3878af628984a5840a4ad5dae3702db367da0f8a165fed0517eb5c442b0145330241b97eeca733ba6688b9c129a61cd1236aff0e27bcf98c28b0fbeea55a3d7c7193d644b2749f986bd46af8938e8faaeafbd9cec3612ab005bd7c3eeafe9a31279ca6102560666ba16136ff1452f850adb:e6a9a6b436559a4320c45c0c2c4a2aedecb90d416d52c82680ac7330d062aebef3e9ac9f2c5ffa455c9be113013a2b282e5600fd306435ada83b1e48ba2a3605813d6061c56eae0ff53041c0244aa5e29e13ec0f3fb428d4beb8a99e04bca8c41bddb0db945f487efe38f2fc14a628fafa2462f860e4e34250eb4e93f139ab1b74a2614519e41ee2403be427930ab8bc82ec89ceafb60905bd4ddbbd13bdb19654314fc92373140b962e2258e038d71b9ec66b84ef8319e03551cb707e747f6c40ad476fbefdce71f3a7b67a1af1869bc6440686e7e0855e4f369d1d88b8099fba54714678627bba1aff41e7707bc97eddf890b0c08dce3e9800d24c6f61092ce28d481b5dea5c096c55d72f8946009131fb968e2bc8a054d825adab76740dcf0d758c8bf54ff38659e71b32bfe2e615aaabb0f5293085649cf60b9847bc62011ce3878af628984a5840a4ad5dae3702db367da0f8a165fed0517eb5c442b0145330241b97eeca733ba6688b9c129a61cd1236aff0e27bcf98c28b0fbeea55a3d7c7193d644b2749f986bd46af8938e8faaeafbd9cec3612ab005bd7c3eeafe9a31279ca6102560666ba16136ff1452f850adb: +f051af426d0c3282fafc8bf912ade1c24211a95ad200e1eef549320e1cb1a252fa87955e0ea13dde49d83dc22e63a2bdf1076725c2cc7f93c76511f28e7944f2:fa87955e0ea13dde49d83dc22e63a2bdf1076725c2cc7f93c76511f28e7944f2:b48d9f84762b3bcc66e96d76a616fa8fe8e01695251f47cfc1b7b17d60dc9f90d576ef64ee7d388504e2c9079638165a889696471c989a876f8f13b63b58d531fea4dd1229fc631668a047bfae2da281feae1b6de3ebe280abe0a82ee00fbfdc22ce2d10e06a0492ff1404dfc094c40b203bf55721dd787ed4e91d5517aaf58d3bdd35d44a65ae6ba75619b339b650518cefcc17493de27a3b5d41788f87edbde72610f181bf06e208e0eb7cdfe881d91a2d6cc77aa19c0fcf330fedb44675d800eb8cff9505d8887544a503cbe373c4847b19e8f3995726efd6649858595c57ccaf0cbc9eb25de83ba046bc9f1838ac7b8953dd81b81ac0f68d0e9338cb55402552afb6bc16949351b926d151a82efc695e8d7da0dd55099366789718ccbf36030bd2c3c109399be26cdb8b9e2a155f3b2cb1bfa71ab69a23625a4ac118fe91cb2c19788cf52a71d730d576b421d96982a51a2991daec440cda7e6cc3282b8312714278b819bfe2387eb96aa91d40173034f428:b8f713578a64466719aceb432fce302a87cf066bf3e102a350616921a840964bfc7e685d8fd17455ac3eb4861edcb8979d35e3a4bd82a078cd707721d733400eb48d9f84762b3bcc66e96d76a616fa8fe8e01695251f47cfc1b7b17d60dc9f90d576ef64ee7d388504e2c9079638165a889696471c989a876f8f13b63b58d531fea4dd1229fc631668a047bfae2da281feae1b6de3ebe280abe0a82ee00fbfdc22ce2d10e06a0492ff1404dfc094c40b203bf55721dd787ed4e91d5517aaf58d3bdd35d44a65ae6ba75619b339b650518cefcc17493de27a3b5d41788f87edbde72610f181bf06e208e0eb7cdfe881d91a2d6cc77aa19c0fcf330fedb44675d800eb8cff9505d8887544a503cbe373c4847b19e8f3995726efd6649858595c57ccaf0cbc9eb25de83ba046bc9f1838ac7b8953dd81b81ac0f68d0e9338cb55402552afb6bc16949351b926d151a82efc695e8d7da0dd55099366789718ccbf36030bd2c3c109399be26cdb8b9e2a155f3b2cb1bfa71ab69a23625a4ac118fe91cb2c19788cf52a71d730d576b421d96982a51a2991daec440cda7e6cc3282b8312714278b819bfe2387eb96aa91d40173034f428: +a103e92672c65f81ea5da1fff1a4038788479e941d503a756f4a755201a57c1dee63a5b69641217acbaf3339da829ec071b9931e5987153514d30140837a7af4:ee63a5b69641217acbaf3339da829ec071b9931e5987153514d30140837a7af4:b1984e9eec085d524c1eb3b95c89c84ae085be5dc65c326e19025e1210a1d50edbbba5d1370cf15d68d687eb113233e0fba50f9433c7d358773950c67931db8296bbcbecec888e87e71a2f7579fad2fa162b85fb97473c456b9a5ce2956676969c7bf4c45679085b62f2c224fc7f458794273f6d12c5f3e0d06951824d1cca3e2f904559ed28e2868b366d79d94dc98667b9b5924268f3e39b1291e5abe4a758f77019dacbb22bd8196e0a83a5677658836e96ca5635055a1e63d65d036a68d87ac2fd283fdda390319909c5cc7680368848873d597f298e0c6172308030ffd452bb1363617b316ed7cd949a165dc8abb53f991aef3f3e9502c5dfe4756b7c6bfdfe89f5e00febdd6afb0402818f11cf8d1d5864fe9da1b86e39aa935831506cf2400ea7ed75bd9533b23e202fe875d7d9638c89d11cb2d6e6021ae6bd27c7754810d35cd3a61494f27b16fc794e2cd2f0d3453ada933865db78c579571f8fc5c5c6be8eaffce6a852e5b3b1c524c49313d427abcb:2aa2035c2ce5b5e6ae161e168f3ad0d6592bcf2c4a049d3ed342fceb56be9c7cb372027573ae0178e8878ebefca7b030327b8aad41857de58cb78e1a00cbac05b1984e9eec085d524c1eb3b95c89c84ae085be5dc65c326e19025e1210a1d50edbbba5d1370cf15d68d687eb113233e0fba50f9433c7d358773950c67931db8296bbcbecec888e87e71a2f7579fad2fa162b85fb97473c456b9a5ce2956676969c7bf4c45679085b62f2c224fc7f458794273f6d12c5f3e0d06951824d1cca3e2f904559ed28e2868b366d79d94dc98667b9b5924268f3e39b1291e5abe4a758f77019dacbb22bd8196e0a83a5677658836e96ca5635055a1e63d65d036a68d87ac2fd283fdda390319909c5cc7680368848873d597f298e0c6172308030ffd452bb1363617b316ed7cd949a165dc8abb53f991aef3f3e9502c5dfe4756b7c6bfdfe89f5e00febdd6afb0402818f11cf8d1d5864fe9da1b86e39aa935831506cf2400ea7ed75bd9533b23e202fe875d7d9638c89d11cb2d6e6021ae6bd27c7754810d35cd3a61494f27b16fc794e2cd2f0d3453ada933865db78c579571f8fc5c5c6be8eaffce6a852e5b3b1c524c49313d427abcb: +d47c1b4b9e50cbb71fd07d096d91d87213d44b024373044761c4822f9d9df880f4e1cb86c8ca2cfee43e58594a8778436d3ea519704e00c1bbe48bbb1c9454f8:f4e1cb86c8ca2cfee43e58594a8778436d3ea519704e00c1bbe48bbb1c9454f8:88d7009d51de3d337eef0f215ea66ab830ec5a9e6823761c3b92ad93ea341db92ece67f4ef4ceb84194ae6926c3d014b2d59781f02e0b32f9a611222cb9a5850c6957cb8079ae64e0832a1f05e5d1a3c572f9d08f1437f76bb3b83b52967c3d48c3576848891c9658d4959eb80656d26cdba0810037c8a18318ff122f8aa8985c773cb317efa2f557f1c3896bcb162df5d87681bb787e7813aa2dea3b0c564d646a92861f444ca1407efbac3d12432cbb70a1d0eaffb11741d3718fedee2b83036189a6fc45a52f74fa487c18fd264a7945f6c9e44b011f5d86613f1939b19f4f4fdf53234057be3f005ad64eebf3c8ffb58cb40956c4336df01d4424b706a0e561d601708d12485e21bcb6d799d8d1d044b400064ec0944501406e70253947006cabbdb2dd6bd8cee4497653d9113a44d4de9b68d4c526fca0b9b0c18fe50fb917fdd9a914fb816108a73a6b3fff9e654e69c9cfe02b05c6c1b9d15c4e65cf31018b8100d784633ee1888eee3572aafa6f189ea22d0:627e7ca7e34ed6331d62b9541c1ea9a9292be7b0a65d805e266b5122272a82db7d765acc7e2a290d685804922f91ed04a3c382c03ff21a1768f584413c4e5f0088d7009d51de3d337eef0f215ea66ab830ec5a9e6823761c3b92ad93ea341db92ece67f4ef4ceb84194ae6926c3d014b2d59781f02e0b32f9a611222cb9a5850c6957cb8079ae64e0832a1f05e5d1a3c572f9d08f1437f76bb3b83b52967c3d48c3576848891c9658d4959eb80656d26cdba0810037c8a18318ff122f8aa8985c773cb317efa2f557f1c3896bcb162df5d87681bb787e7813aa2dea3b0c564d646a92861f444ca1407efbac3d12432cbb70a1d0eaffb11741d3718fedee2b83036189a6fc45a52f74fa487c18fd264a7945f6c9e44b011f5d86613f1939b19f4f4fdf53234057be3f005ad64eebf3c8ffb58cb40956c4336df01d4424b706a0e561d601708d12485e21bcb6d799d8d1d044b400064ec0944501406e70253947006cabbdb2dd6bd8cee4497653d9113a44d4de9b68d4c526fca0b9b0c18fe50fb917fdd9a914fb816108a73a6b3fff9e654e69c9cfe02b05c6c1b9d15c4e65cf31018b8100d784633ee1888eee3572aafa6f189ea22d0: +fc0c32c5eb6c71ea08dc2b300cbcef18fdde3ea20f68f21733237b4ddaab900e47c37d8a080857eb8777a6c0a9a5c927303faf5c320953b5de48e462e12d0062:47c37d8a080857eb8777a6c0a9a5c927303faf5c320953b5de48e462e12d0062:a7b1e2db6bdd96b3d51475603537a76b42b04d7ebd24fe515a887658e4a352e22109335639a59e2534811f4753b70209d0e4698e9d926088826c14689681ea00fa3a2fcaa0047ced3ef287e6172502b215e56497614d86b4cb26bcd77a2e172509360ee58893d01c0d0fb4d4abfe4dbd8d2a2f54190fa2f731c1ceac6829c3ddc9bfb2ffd70c57ba0c2b22d2326fbfe7390db8809f73547ff47b86c36f2bf7454e678c4f1c0fa870bd0e30bbf3278ec8d0c5e9b64aff0af64babc19b70f4cf9a41cb8f95d3cde24f456ba3571c8f021d38e591dec05cb5d1ca7b48f9da4bd734b069a9fd106500c1f408ab7fe8e4a6e6f3ed64da0ed24b01e33df8475f95fa9ed71d04dd30b3cd823755a3401bf5afae10ee7e18ec6fe637c3793fd434b48d7145130447e00299101052558b506554ec9c399f62941c3f414cbc352caa345b930adecfaddac91ee53d1451a65e06201026325de07c931f69bba868a7c87ee23c604ec6794332917dfe2c5b69669b659706917f71eddf96:6887c6e2b98a82af5ee3dfa7ca2cb25d9c10745620a82956acba85cb57c8ec24279fa42f092359a1b6bbeafba050f14b6288209e6ef7bc1e0a2b872c1138f305a7b1e2db6bdd96b3d51475603537a76b42b04d7ebd24fe515a887658e4a352e22109335639a59e2534811f4753b70209d0e4698e9d926088826c14689681ea00fa3a2fcaa0047ced3ef287e6172502b215e56497614d86b4cb26bcd77a2e172509360ee58893d01c0d0fb4d4abfe4dbd8d2a2f54190fa2f731c1ceac6829c3ddc9bfb2ffd70c57ba0c2b22d2326fbfe7390db8809f73547ff47b86c36f2bf7454e678c4f1c0fa870bd0e30bbf3278ec8d0c5e9b64aff0af64babc19b70f4cf9a41cb8f95d3cde24f456ba3571c8f021d38e591dec05cb5d1ca7b48f9da4bd734b069a9fd106500c1f408ab7fe8e4a6e6f3ed64da0ed24b01e33df8475f95fa9ed71d04dd30b3cd823755a3401bf5afae10ee7e18ec6fe637c3793fd434b48d7145130447e00299101052558b506554ec9c399f62941c3f414cbc352caa345b930adecfaddac91ee53d1451a65e06201026325de07c931f69bba868a7c87ee23c604ec6794332917dfe2c5b69669b659706917f71eddf96: +a8d73d639a23cc6a967ef31bcabb5d063e53e1eab8fcc7cab9bc3a17fde9c2f88daa9f4c8b1a44691bf44521f2f7ca45dc7fc61f6a4ce6f98faa41c2a74977d1:8daa9f4c8b1a44691bf44521f2f7ca45dc7fc61f6a4ce6f98faa41c2a74977d1:fd1fac3d53313b11acd29f5a83ac11896dab2530fa47865b2295c0d99dd67c36ed8e5fa549150c794c5549efb5c1d69114d5d607b23285b7212afaab57846a54ae67b9e880e07b6586607cecf6d4eed516a3a75511fe367d88eb871e6d71b7d6aa1367a01421b1088fc2d75e44954b73625c52da8a3a183c60be9da6050f59a453caa53520593671728d431877bfaac913a765fb6a56b75290b2a8aaac34afb9217ba1b0d5850ba0fdabf80969def0feee794ceb60614e3368e63ef20e4c32d341ec9b0328ea9fe139207ed7a626ff08943b415233db7cfcc845c9b63121d4ed52ec3748ab6a1f36b2103c7dc7e9303acea4ba8af7a3e07184fb491e891ede84f0dc41cadc3973028e879acd2031afc29a16092868e2c7f539fc1b792edab195a25ab9830661346b39ef53915de4af52c421eaf172e9da76a08c283a52df907f705d7e8599c5baae0c2af380c1bb46f93484a03f28374324b278992b50b7afa02552cafa503f034f8d866e9b720271dd68ccb685a85fffd1:c4dcef1a2453939b364b340250c3129431431d5ba3f47670ab07ce680c69bf28b678627c76a6360fc40dc109aa7dea371b825e46134f624572182acf3957e70ffd1fac3d53313b11acd29f5a83ac11896dab2530fa47865b2295c0d99dd67c36ed8e5fa549150c794c5549efb5c1d69114d5d607b23285b7212afaab57846a54ae67b9e880e07b6586607cecf6d4eed516a3a75511fe367d88eb871e6d71b7d6aa1367a01421b1088fc2d75e44954b73625c52da8a3a183c60be9da6050f59a453caa53520593671728d431877bfaac913a765fb6a56b75290b2a8aaac34afb9217ba1b0d5850ba0fdabf80969def0feee794ceb60614e3368e63ef20e4c32d341ec9b0328ea9fe139207ed7a626ff08943b415233db7cfcc845c9b63121d4ed52ec3748ab6a1f36b2103c7dc7e9303acea4ba8af7a3e07184fb491e891ede84f0dc41cadc3973028e879acd2031afc29a16092868e2c7f539fc1b792edab195a25ab9830661346b39ef53915de4af52c421eaf172e9da76a08c283a52df907f705d7e8599c5baae0c2af380c1bb46f93484a03f28374324b278992b50b7afa02552cafa503f034f8d866e9b720271dd68ccb685a85fffd1: +79c7dcb7d59a8df6b2b2ba0413059d89680995c20e916da01b8f067dc60cdeb4298743c73918bd556b28f8d4824a09b814752a7aeae7ee04875c53f4d6b108d9:298743c73918bd556b28f8d4824a09b814752a7aeae7ee04875c53f4d6b108d9:5fe202f5b33b7788810d2508a13b3114d69b8596e6eacda05a04a2eb597fa3279c208b5a5b65daacb699f144e1d660e78e139b578331abec5c3c35334454f03e832c8d6e2984df5d450ecb5d33582a78808a9c78f26ebcd1244ef52e3fa6dca115c1f0cb56e38eae0e5b39f5fd863dffd0b2fb5b958f2d739db312fc667a17b031c4c9f8c5a2ad577984cc4146c437580efd2152173fe0d5782cc2ae9831a8d9a04177256018ff7631e0b0d8a99cb28f008b320421e27a74c31359188663456d85e098c1ebd281701097b6ae5a871e5ccc02058a501416cb91c12cef5be6f1914370e563f1a1b2aa41f4b8ee84cd32a1d509e529787d14a445438d807ecd620e2fa26de0da6426864784d4a28f54103e609283b99ee9b2b699c980bbb7882c3ea68ddc90802ac232f2c8e84291987bf3c5240921b59cfa214969317673d0be7f34b1ca0e15ea73c7175401ce550be106b49e62f8db68695e740e0f3a3556a19f3c8e6b91ac1cc23e863fcd0f0d9eb7047aa631e0d2eb9bcc6b:7b7cbe44c771e4371bae13b0722babcc1064155732962f407cba2acd35381d42210bece822f4681121fd4dab745a1f3077922fba1a78045b712902baccac660e5fe202f5b33b7788810d2508a13b3114d69b8596e6eacda05a04a2eb597fa3279c208b5a5b65daacb699f144e1d660e78e139b578331abec5c3c35334454f03e832c8d6e2984df5d450ecb5d33582a78808a9c78f26ebcd1244ef52e3fa6dca115c1f0cb56e38eae0e5b39f5fd863dffd0b2fb5b958f2d739db312fc667a17b031c4c9f8c5a2ad577984cc4146c437580efd2152173fe0d5782cc2ae9831a8d9a04177256018ff7631e0b0d8a99cb28f008b320421e27a74c31359188663456d85e098c1ebd281701097b6ae5a871e5ccc02058a501416cb91c12cef5be6f1914370e563f1a1b2aa41f4b8ee84cd32a1d509e529787d14a445438d807ecd620e2fa26de0da6426864784d4a28f54103e609283b99ee9b2b699c980bbb7882c3ea68ddc90802ac232f2c8e84291987bf3c5240921b59cfa214969317673d0be7f34b1ca0e15ea73c7175401ce550be106b49e62f8db68695e740e0f3a3556a19f3c8e6b91ac1cc23e863fcd0f0d9eb7047aa631e0d2eb9bcc6b: +b9ced0412593fefed95e94ac965e5b23ff9d4b0e797db02bf497994d3b793e60c1629a723189959337f5535201e5d395ba0a03ea8c17660d0f8b6f6e6404bb12:c1629a723189959337f5535201e5d395ba0a03ea8c17660d0f8b6f6e6404bb12:555bb39c1899d57cabe428064c2d925f5fc4cf7059b95fb89a8e9e3a7e426c6c922d9e4d76984ea2383cabb4f2befd89c1f20eaa8a00dbe787cfa70ae2ae6aa90331cbbe580fa5a02184ed05e6c8e89d576af28aeeaf7c4e2500f358a00971a0a75920e854849bf332142975404f598c32e96982043d992bcd1a4fe819bb5634ad03467afc4ce05073f88ba1ba4ae8653a04665cf3f71690fe13343885bc5ebc0e5e62d882f43b7c68900ac9438bf4a81ce90169ec129ee63e2c675a1a5a67e27cc798c48cc23f51078f463b3b7cc14e3bcfd2e9b82c75240934cbdc50c4308f282f193122995606f40135100a291c55afdf8934eb8b61d81421674124dec3b88f9a73110a9e616f5b826b9d343f3ac0e9d7bdf4fd8b648b40f0098b3897a3a1cd65a64570059b8bc5c6743883074c88623c1f5a88c58969e21c692aca236833d3470b3eb09815e1138e9d0650c390eee977422193b00918be8a97cc6199b451b05b5730d1d13358cf74610678f7ac7f7895cc2efc456e03873b:f1b797ded8a6942b12626848340fb719fcddafd98f33e2992d357bfdd35933c7ac561e5b2f939464338c5666854ca885c4d046eb2c54e48a1b5ed266ad34de05555bb39c1899d57cabe428064c2d925f5fc4cf7059b95fb89a8e9e3a7e426c6c922d9e4d76984ea2383cabb4f2befd89c1f20eaa8a00dbe787cfa70ae2ae6aa90331cbbe580fa5a02184ed05e6c8e89d576af28aeeaf7c4e2500f358a00971a0a75920e854849bf332142975404f598c32e96982043d992bcd1a4fe819bb5634ad03467afc4ce05073f88ba1ba4ae8653a04665cf3f71690fe13343885bc5ebc0e5e62d882f43b7c68900ac9438bf4a81ce90169ec129ee63e2c675a1a5a67e27cc798c48cc23f51078f463b3b7cc14e3bcfd2e9b82c75240934cbdc50c4308f282f193122995606f40135100a291c55afdf8934eb8b61d81421674124dec3b88f9a73110a9e616f5b826b9d343f3ac0e9d7bdf4fd8b648b40f0098b3897a3a1cd65a64570059b8bc5c6743883074c88623c1f5a88c58969e21c692aca236833d3470b3eb09815e1138e9d0650c390eee977422193b00918be8a97cc6199b451b05b5730d1d13358cf74610678f7ac7f7895cc2efc456e03873b: +81da168f02d46bb87cda845da43f8a6cba2c016878d6f49c6f061a60f155a04aaff86e98093ca4c71b1b804c5fe451cfdf868250dea30345fa4b89bb09b6a53b:aff86e98093ca4c71b1b804c5fe451cfdf868250dea30345fa4b89bb09b6a53b:6bc6726a34a64aae76ab08c92b179e54ff5d2e65eb2c6c659ae8703cc245cbc2cf45a12b22c468ae61fd9a6627ad0626c9b1e5af412cb483eaee1db11b29f0a510c13e38020e09ae0eee762537a3e9d1a0c7b033d097fdc1f4f82629a9de9ef38da1cf96a940357d5f2e0e7e8dbc29db728a1e6aad876e5e053113d06420272b87cf0c40dfe03a544de96c7aea13ba0029b57b48d99dcc6a650492d78c4cdd1b28e1a115a7e3e7a7cb21333d4ff80858dfb67782c16354b8716596560d7d8e389eb15a052a0bf5d16eb54fb3e4973ad4984e72a187f5347d5b262c32b1647e42b6a53837096cc78c2a05ce1c6e12493a03f1a667584cb97f4fcd57ee944c65b7eed25f7ae0f3f6cede173fdfacf5af1db143730d18096664914ba4cfc6966f392022781c66a9417ca2680b51f63e4fba424ecfdbc6a2f01787d0e7484f8a8ab390aeaa6d1f7ed325d82feaa1692a4984fae43da87329b045da8f0a4f56b695aa935de152ce0385153720979a2b7006d405fcb0fba09e23b85fd19b:4aaca947e3f22cc8b8588ee030ace8f6b5f5711c2974f20cc18c3b655b07a5bc1366b59a1708032d12cae01ab794f8cbcc1a330874a75035db1d69422d2fc00c6bc6726a34a64aae76ab08c92b179e54ff5d2e65eb2c6c659ae8703cc245cbc2cf45a12b22c468ae61fd9a6627ad0626c9b1e5af412cb483eaee1db11b29f0a510c13e38020e09ae0eee762537a3e9d1a0c7b033d097fdc1f4f82629a9de9ef38da1cf96a940357d5f2e0e7e8dbc29db728a1e6aad876e5e053113d06420272b87cf0c40dfe03a544de96c7aea13ba0029b57b48d99dcc6a650492d78c4cdd1b28e1a115a7e3e7a7cb21333d4ff80858dfb67782c16354b8716596560d7d8e389eb15a052a0bf5d16eb54fb3e4973ad4984e72a187f5347d5b262c32b1647e42b6a53837096cc78c2a05ce1c6e12493a03f1a667584cb97f4fcd57ee944c65b7eed25f7ae0f3f6cede173fdfacf5af1db143730d18096664914ba4cfc6966f392022781c66a9417ca2680b51f63e4fba424ecfdbc6a2f01787d0e7484f8a8ab390aeaa6d1f7ed325d82feaa1692a4984fae43da87329b045da8f0a4f56b695aa935de152ce0385153720979a2b7006d405fcb0fba09e23b85fd19b: +af2e60da0f29bb1614fc3f193cc353331986b73f3f9a0aec9421b9473d6a4b6ac8bfe2835822199c6127b806fabeef0cb9ff59f3c81ff0cb89c556f55106af6a:c8bfe2835822199c6127b806fabeef0cb9ff59f3c81ff0cb89c556f55106af6a:7dbb77b88bda94f344416a06b096566c6e8b393931a8243a6cab75c361fde7dc536aec40cded83296a89e8c3bef7d787cfc49401a7b9183f138d5000619ff073c05e2f841d6008358f10a2da7dcfac3d4d70c20d2ec34c7b6d5cd1a734d6bbb11c5fd8d2bce32ac810ef82b4188aa8ea3cfc3032233dc0e2600e9db6e18bc22b10044a31c15baceaf5554de89d2a3466807f244414d080ff2963956c6e83c8e144ed0066088b476ddcb564403447d9159f9089aba2b4d5575c4d8ae66fc8690e7349ed40832e6369c024563ec493bfcc0fc9ac787ac841397fe133167283d80c42f006a99d39e82979da3fa9334bd9ede0d14b41b7466bcebbe8171bc804a645d3723274a1b92bf82fd993358744de92441903d436fd47f23d40052a3829367f202f0553b5e49b76c5e03fa6ce7c3cf5eeb21de967bec4dd355925384ebf96697e823762bac4d43a767c241a4cef724a970d00ff3a8ab3b83eed840075c74e90f306e330013260962161e9d0910de183622ce9a6b8d5144280550fc7:50f9f941a8da9f6240f76d2fa3b06dd6b2292ed32d1c05218097d34d8a19dfe553f76ae3c6b4a2ed20852128461540decf418f52d38e64037eec7771bd1afe007dbb77b88bda94f344416a06b096566c6e8b393931a8243a6cab75c361fde7dc536aec40cded83296a89e8c3bef7d787cfc49401a7b9183f138d5000619ff073c05e2f841d6008358f10a2da7dcfac3d4d70c20d2ec34c7b6d5cd1a734d6bbb11c5fd8d2bce32ac810ef82b4188aa8ea3cfc3032233dc0e2600e9db6e18bc22b10044a31c15baceaf5554de89d2a3466807f244414d080ff2963956c6e83c8e144ed0066088b476ddcb564403447d9159f9089aba2b4d5575c4d8ae66fc8690e7349ed40832e6369c024563ec493bfcc0fc9ac787ac841397fe133167283d80c42f006a99d39e82979da3fa9334bd9ede0d14b41b7466bcebbe8171bc804a645d3723274a1b92bf82fd993358744de92441903d436fd47f23d40052a3829367f202f0553b5e49b76c5e03fa6ce7c3cf5eeb21de967bec4dd355925384ebf96697e823762bac4d43a767c241a4cef724a970d00ff3a8ab3b83eed840075c74e90f306e330013260962161e9d0910de183622ce9a6b8d5144280550fc7: +605f90b53d8e4a3b48b97d745439f2a0807d83b8502e8e2979f03e8d376ac9feaa3fae4cfa6f6bfd14ba0afa36dcb1a2656f36541ad6b3e67f1794b06360a62f:aa3fae4cfa6f6bfd14ba0afa36dcb1a2656f36541ad6b3e67f1794b06360a62f:3bcdcac292ac9519024aaecee2b3e999ff5d3445e9f1eb60940f06b91275b6c5db2722ed4d82fe89605226530f3e6b0737b308cde8956184944f388a80042f6cba274c0f7d1192a0a96b0da6e2d6a61b76518fbee555773a414590a928b4cd545fccf58172f35857120eb96e75c5c8ac9ae3add367d51d34ac403446360ec10f553ea9f14fb2b8b78cba18c3e506b2f04097063a43b2d36431cce02caf11c5a4db8c821752e52985d5af1bfbf4c61572e3fadae3ad424acd81662ea5837a1143b9669391d7b9cfe230cffb3a7bb03f6591c25a4f01c0d2d4aca3e74db1997d3739c851f0327db919ff6e77f6c8a20fdd3e1594e92d01901ab9aef194fc893e70d78c8ae0f480001a515d4f9923ae6278e8927237d05db23e984c92a683882f57b1f1882a74a193ab6912ff241b9ffa662a0d47f29205f084dbde845baaeb5dd36ae6439a437642fa763b57e8dbe84e55813f0151e97e5b9de768b234b8db15c496d4bfcfa1388788972bb50ce030bc6e0ccf4fa7d00d343782f6ba8de0:dd0212e63288cbe14a4569b4d891da3c7f92727c5e7f9a801cf9d6827085e7095b669d7d45f882ca5f0745dccd24d87a57181320191e5b7a47c3f7f2dccbd7073bcdcac292ac9519024aaecee2b3e999ff5d3445e9f1eb60940f06b91275b6c5db2722ed4d82fe89605226530f3e6b0737b308cde8956184944f388a80042f6cba274c0f7d1192a0a96b0da6e2d6a61b76518fbee555773a414590a928b4cd545fccf58172f35857120eb96e75c5c8ac9ae3add367d51d34ac403446360ec10f553ea9f14fb2b8b78cba18c3e506b2f04097063a43b2d36431cce02caf11c5a4db8c821752e52985d5af1bfbf4c61572e3fadae3ad424acd81662ea5837a1143b9669391d7b9cfe230cffb3a7bb03f6591c25a4f01c0d2d4aca3e74db1997d3739c851f0327db919ff6e77f6c8a20fdd3e1594e92d01901ab9aef194fc893e70d78c8ae0f480001a515d4f9923ae6278e8927237d05db23e984c92a683882f57b1f1882a74a193ab6912ff241b9ffa662a0d47f29205f084dbde845baaeb5dd36ae6439a437642fa763b57e8dbe84e55813f0151e97e5b9de768b234b8db15c496d4bfcfa1388788972bb50ce030bc6e0ccf4fa7d00d343782f6ba8de0: +9e2c3d189838f4dd52ef0832886874c5ca493983ddadc07cbc570af2ee9d6209f68d3b81e73557ee1f08bd2d3f46a4718256a0f3cd8d2e03eb8fe882aab65c69:f68d3b81e73557ee1f08bd2d3f46a4718256a0f3cd8d2e03eb8fe882aab65c69:19485f5238ba82eadf5eff14ca75cd42e5d56fea69d5718cfb5b1d40d760899b450e66884558f3f25b7c3de9afc4738d7ac09da5dd4689bbfac07836f5e0be432b1ddcf1b1a075bc9815d0debc865d90bd5a0c5f5604d9b46ace816c57694ecc3d40d8f84df0ede2bc4d577775a027f725de0816f563fa88f88e077720ebb6ac02574604819824db7474d4d0b22cd1bc05768e0fb867ca1c1a7b90b34ab7a41afc66957266ac0c915934aaf31c0cf6927a4f03f23285e6f24afd5813849bb08c203ac2d0336dcbf80d77f6cf7120edfbcdf181db107ec8e00f32449c1d3f5c049a92694b4ea2c6ebe5e2b0f64b5ae50ad3374d246b3270057e724a27cf263b633ab65ecb7f5c266b8007618b10ac9ac83db0febc04fd863d9661ab6e58494766f71b9a867c5a7a4555f667c1af2e54588f162a41ce756407cc4161d607b6e0682980934caa1bef036f7330d9eef01ecc553583fee5994e533a46ca916f60f8b961ae01d20f7abf0df6141b604de733c636b42018cd5f1d1ef4f84cee40fc:38a31b6b465084738262a26c065fe5d9e2886bf9dd35cde05df9bad0cc7db401c750aa19e66090bce25a3c721201e60502c8c10454346648af065eab0ee7d80f19485f5238ba82eadf5eff14ca75cd42e5d56fea69d5718cfb5b1d40d760899b450e66884558f3f25b7c3de9afc4738d7ac09da5dd4689bbfac07836f5e0be432b1ddcf1b1a075bc9815d0debc865d90bd5a0c5f5604d9b46ace816c57694ecc3d40d8f84df0ede2bc4d577775a027f725de0816f563fa88f88e077720ebb6ac02574604819824db7474d4d0b22cd1bc05768e0fb867ca1c1a7b90b34ab7a41afc66957266ac0c915934aaf31c0cf6927a4f03f23285e6f24afd5813849bb08c203ac2d0336dcbf80d77f6cf7120edfbcdf181db107ec8e00f32449c1d3f5c049a92694b4ea2c6ebe5e2b0f64b5ae50ad3374d246b3270057e724a27cf263b633ab65ecb7f5c266b8007618b10ac9ac83db0febc04fd863d9661ab6e58494766f71b9a867c5a7a4555f667c1af2e54588f162a41ce756407cc4161d607b6e0682980934caa1bef036f7330d9eef01ecc553583fee5994e533a46ca916f60f8b961ae01d20f7abf0df6141b604de733c636b42018cd5f1d1ef4f84cee40fc: +575f8fb6c7465e92c250caeec1786224bc3eed729e463953a394c9849cba908f71bfa98f5bea790ff183d924e6655cea08d0aafb617f46d23a17a657f0a9b8b2:71bfa98f5bea790ff183d924e6655cea08d0aafb617f46d23a17a657f0a9b8b2:2cc372e25e53a138793064610e7ef25d9d7422e18e249675a72e79167f43baf452cbacb50182faf80798cc38597a44b307a536360b0bc1030f8397b94cbf147353dd2d671cb8cab219a2d7b9eb828e9635d2eab6eb08182cb03557783fd282aaf7b471747c84acf72debe4514524f8447bafccccec0a840feca9755ff9adb60301c2f25d4e3ba621df5ad72100c45d7a4b91559c725ab56bb29830e35f5a6faf87db23001f11ffba9c0c15440302065827a7d7aaaeab7b446abce333c0d30c3eae9c9da63eb1c0391d4269b12c45b660290611ac29c91dbd80dc6ed302a4d191f2923922f032ab1ac10ca7323b5241c5751c3c004ac39eb1267aa10017ed2dac6c934a250dda8cb06d5be9f563b827bf3c8d95fd7d2a7e7cc3acbee92538bd7ddfba3ab2dc9f791fac76cdf9cd6a6923534cf3e067108f6aa03e320d954085c218038a70cc768b972e49952b9fe171ee1be2a52cd469b8d36b84ee902cd9410db2777192e90070d2e7c56cb6a45f0a839c78c219203b6f1b33cb4504c6a7996427741e6874cf45c5fa5a38765a1ebf1796ce16e63ee509612c40f088cbceffa3affbc13b75a1b9c02c61a180a7e83b17884fe0ec0f2fe57c47e73a22f753eaf50fca655ebb19896b827a3474911c67853c58b4a78fd085a23239b9737ef8a7baff11ddce5f2cae0543f8b45d144ae6918b9a75293ec78ea618cd2cd08c971301cdfa0a9275c1bf441d4c1f878a2e733ce0a33b6ecdacbbf0bdb5c3643fa45a013979cd01396962897421129a88757c0d88b5ac7e44fdbd938ba4bc37de4929d53751fbb43d4e09a80e735244acada8e6749f77787f33763c7472df52934591591fb226c503c8be61a920a7d37eb1686b62216957844c43c484e58745775553:903b484cb24bc503cdced844614073256c6d5aa45f1f9f62c7f22e5649212bc1d6ef9eaa617b6b835a6de2beff2faac83d37a4a5fc5cc3b556f56edde2651f022cc372e25e53a138793064610e7ef25d9d7422e18e249675a72e79167f43baf452cbacb50182faf80798cc38597a44b307a536360b0bc1030f8397b94cbf147353dd2d671cb8cab219a2d7b9eb828e9635d2eab6eb08182cb03557783fd282aaf7b471747c84acf72debe4514524f8447bafccccec0a840feca9755ff9adb60301c2f25d4e3ba621df5ad72100c45d7a4b91559c725ab56bb29830e35f5a6faf87db23001f11ffba9c0c15440302065827a7d7aaaeab7b446abce333c0d30c3eae9c9da63eb1c0391d4269b12c45b660290611ac29c91dbd80dc6ed302a4d191f2923922f032ab1ac10ca7323b5241c5751c3c004ac39eb1267aa10017ed2dac6c934a250dda8cb06d5be9f563b827bf3c8d95fd7d2a7e7cc3acbee92538bd7ddfba3ab2dc9f791fac76cdf9cd6a6923534cf3e067108f6aa03e320d954085c218038a70cc768b972e49952b9fe171ee1be2a52cd469b8d36b84ee902cd9410db2777192e90070d2e7c56cb6a45f0a839c78c219203b6f1b33cb4504c6a7996427741e6874cf45c5fa5a38765a1ebf1796ce16e63ee509612c40f088cbceffa3affbc13b75a1b9c02c61a180a7e83b17884fe0ec0f2fe57c47e73a22f753eaf50fca655ebb19896b827a3474911c67853c58b4a78fd085a23239b9737ef8a7baff11ddce5f2cae0543f8b45d144ae6918b9a75293ec78ea618cd2cd08c971301cdfa0a9275c1bf441d4c1f878a2e733ce0a33b6ecdacbbf0bdb5c3643fa45a013979cd01396962897421129a88757c0d88b5ac7e44fdbd938ba4bc37de4929d53751fbb43d4e09a80e735244acada8e6749f77787f33763c7472df52934591591fb226c503c8be61a920a7d37eb1686b62216957844c43c484e58745775553: diff --git a/src/ed25519.rs b/src/ed25519.rs index 425ae78..91e2427 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -26,6 +26,12 @@ use curve25519_dalek::util::arrays_equal_ct; /// An ed25519 signature. +/// +/// # Note +/// +/// These signatures, unlike the ed25519 reference implementation, are +/// "detached"—that is, they do **not** include a copy of the message which +/// has been signed. #[derive(Copy)] pub struct Signature(pub [u8; 64]); @@ -39,12 +45,40 @@ impl Debug for Signature { } } +impl Eq for Signature {} + +impl PartialEq for Signature { + /// # Note + /// + /// This function happens to be constant time, even though that is not + /// really necessary. + fn eq(&self, other: &Signature) -> bool { + let mut equal: u8 = 0; + + for i in 0..64 { + equal |= self.0[i] ^ other.0[i]; + } + + if equal == 0 { + return true; + } else { + return false; + } + } +} + impl Signature { /// View this signature as an array of 32 bytes. #[inline] pub fn to_bytes(&self) -> [u8; 64] { self.0 } + + /// Construct a `Signature` from a slice of bytes. + #[inline] + pub fn from_bytes(bytes: &[u8]) -> Signature { + Signature(*array_ref!(bytes, 0, 64)) + } } /// An ed25519 private key. @@ -63,6 +97,41 @@ impl SecretKey { self.0 } + /// Construct a `SecretKey` from a slice of bytes. + /// + /// # Warning + /// + /// **The caller is responsible for ensuring that the bytes represent a + /// *masked* secret key. If you do not understand what this means, DO NOT + /// USE THIS CONSTRUCTOR.** + /// + /// # Example + /// + /// ```ignore + /// use ed25519_dalek::SecretKey; + /// + /// let secret_key_bytes: [u8; 64] = [ + /// 157, 97, 177, 157, 239, 253, 90, 96, 186, 132, 74, 244, 146, 236, 44, 196, + /// 68, 73, 197, 105, 123, 50, 105, 25, 112, 59, 172, 3, 28, 174, 127, 96, + /// 215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, + /// 14, 225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26]; + /// let public_key_bytes: [u8; 32] = [ + /// 215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, + /// 14, 225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26]; + /// + /// let secret_key: SecretKey = SecretKey::from_bytes(&[&secret_key_bytes[..32], + /// &public_key_bytes[..32]].concat()[..]); + /// ``` + /// + /// # Returns + /// + /// A `SecretKey`. + #[inline] + #[allow(dead_code)] + fn from_bytes(bytes: &[u8]) -> SecretKey { + SecretKey(*array_ref!(bytes, 0, 64)) + } + /// Sign a message with this keypair's secret key. pub fn sign(&self, message: &[u8]) -> Signature { let mut h: Sha512 = Sha512::new(); @@ -87,7 +156,7 @@ impl SecretKey { expanded_key_secret[31] |= 64; h.reset(); - h.input(public_key); + h.input(&hash[32..]); h.input(&message); h.result(&mut hash); @@ -128,6 +197,36 @@ impl PublicKey { self.0.to_bytes() } + /// Construct a `PublicKey` from a slice of bytes. + /// + /// # Warning + /// + /// The caller is responsible for ensuring that the bytes passed into this + /// method actually represent a `curve25519_dalek::curve::CompressedPoint` + /// and that said compressed point is actually a point on the curve. + /// + /// # Example + /// + /// ```ignore + /// use ed25519_dalek::PublicKey; + /// + /// let public_key_bytes: [u8; 32] = [ + /// 215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, + /// 14, 225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26]; + /// + /// let public_key: PublicKey = PublicKey::from_bytes(&public_key_bytes); + /// + /// ``` + /// + /// # Returns + /// + /// A `PublicKey`. + #[inline] + #[allow(dead_code)] + fn from_bytes(bytes: &[u8]) -> PublicKey { + PublicKey(CompressedPoint(*array_ref!(bytes, 0, 32))) + } + /// Convert this public key to its underlying extended twisted Edwards coordinate. #[inline] fn decompress(&self) -> Option { @@ -244,10 +343,14 @@ impl Keypair { #[cfg(test)] mod test { + use std::io::BufReader; + use std::io::BufRead; + use std::fs::File; use test::Bencher; use curve25519_dalek::curve::ExtendedPoint; use rand::OsRng; use rand::Rng; + use rustc_serialize::hex::FromHex; use super::*; /// A fake RNG which simply returns zeroes. @@ -317,6 +420,56 @@ mod test { "Verification of a signature on a different message passed!"); } + // TESTVECTORS is taken from sign.input.gz in agl's ed25519 Golang + // package. It is a selection of test cases from + // http://ed25519.cr.yp.to/python/sign.input + #[cfg(test)] + #[cfg(not(release))] + #[test] + fn test_golden() { // TestGolden + let mut line: String; + let mut lineno: usize = 0; + + let f = File::open("TESTVECTORS"); + if f.is_err() { + println!("This test is only available when the code has been cloned \ + from the git repository, since the TESTVECTORS file is large \ + and is therefore not included within the distributed crate."); + panic!(); + } + let file = BufReader::new(f.unwrap()); + + for l in file.lines() { + lineno += 1; + line = l.unwrap(); + + let parts: Vec<&str> = line.split(':').collect(); + assert_eq!(parts.len(), 5, "wrong number of fields in line {}", lineno); + + let sec_bytes: &[u8] = &parts[0].from_hex().unwrap(); + let pub_bytes: &[u8] = &parts[1].from_hex().unwrap(); + let message: &[u8] = &parts[2].from_hex().unwrap(); + let sig_bytes: &[u8] = &parts[3].from_hex().unwrap(); + + // The signatures in the test vectors also include the message + // at the end, but we just want R and S. + let sig1: Signature = Signature::from_bytes(sig_bytes); + + assert_eq!(pub_bytes.len(), 32); + + let secret_key: SecretKey = SecretKey::from_bytes(&sec_bytes); + let public_key: PublicKey = PublicKey::from_bytes(&pub_bytes); + let sig2: Signature = secret_key.sign(&message); + + println!("{:?}", sec_bytes); + println!("{:?}", pub_bytes); + + assert!(sig1 == sig2, "Signature bytes not equal on line {}", lineno); + assert!(public_key.verify(&message, &sig2), "Signature verification failed on line {}", lineno); + + } + } + #[bench] fn bench_sign(b: &mut Bencher) { let mut cspring: OsRng = OsRng::new().unwrap(); diff --git a/src/lib.rs b/src/lib.rs index 780ba15..9f2f1e5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -67,7 +67,11 @@ extern crate arrayref; extern crate crypto; extern crate curve25519_dalek; extern crate rand; + +#[cfg(test)] extern crate test; +#[cfg(test)] +extern crate rustc_serialize; mod ed25519; From e971afb7b5374c46af5d4ecfd58fc38e6de7fdb8 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 9 Dec 2016 01:23:34 +0000 Subject: [PATCH 017/351] Rename README file. --- REAME.md => README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename REAME.md => README.md (100%) diff --git a/REAME.md b/README.md similarity index 100% rename from REAME.md rename to README.md From c9b5cb910c83c92056b5803040763c7dbc1570c7 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 9 Dec 2016 01:30:04 +0000 Subject: [PATCH 018/351] Fix installation instructions to use new package name. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4e6dbb3..55b5c31 100644 --- a/README.md +++ b/README.md @@ -54,11 +54,11 @@ Documentation is available [here](https://docs.rs/ed25519-dalek). To install, add the following to the dependencies section of your project's `Cargo.toml`: - ed25519 = "0.1.0" + ed25519-dalek = "0.1.0" Then, in your library or executable source, add: - extern crate ed25519 + extern crate ed25519_dalek # TODO From 6269edb2666a06d227aec3545d2f1d335b03ceab Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Mon, 6 Feb 2017 13:43:37 -0800 Subject: [PATCH 019/351] Update to curve25519-dalek v0.3.0 Changes `curve25519_dalek::curve::CompressedPoint` -> `CompressedEdwardsY` --- Cargo.toml | 2 +- src/ed25519.rs | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5afaa97..d97887d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ exclude = [ ".gitignore", "TESTVECTORS" ] arrayref = "0.3.2" rust-crypto = "^0.2" rand = "^0.3" -curve25519-dalek = "^0.1" +curve25519-dalek = "^0.3" [dev-dependencies] rustc-serialize = "0.3" diff --git a/src/ed25519.rs b/src/ed25519.rs index 91e2427..d0e3b4e 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -18,7 +18,7 @@ use crypto::sha2::Sha512; use rand::Rng; use curve25519_dalek::curve; -use curve25519_dalek::curve::CompressedPoint; +use curve25519_dalek::curve::CompressedEdwardsY; use curve25519_dalek::curve::ExtendedPoint; use curve25519_dalek::curve::ProjectivePoint; use curve25519_dalek::scalar::Scalar; @@ -142,7 +142,7 @@ impl SecretKey { let hram_digest: Scalar; let r: ExtendedPoint; let s: Scalar; - let t: CompressedPoint; + let t: CompressedEdwardsY; let secret_key: &[u8; 32] = array_ref!(&self.0, 0, 32); let public_key: &[u8; 32] = array_ref!(&self.0, 32, 32); @@ -182,11 +182,11 @@ impl SecretKey { /// An ed25519 public key. #[derive(Copy, Clone)] -pub struct PublicKey(pub CompressedPoint); +pub struct PublicKey(pub CompressedEdwardsY); impl Debug for PublicKey { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - write!(f, "PublicKey( CompressedPoint( {:?} ))", self.0) + write!(f, "PublicKey( CompressedEdwardsY( {:?} ))", self.0) } } @@ -202,7 +202,7 @@ impl PublicKey { /// # Warning /// /// The caller is responsible for ensuring that the bytes passed into this - /// method actually represent a `curve25519_dalek::curve::CompressedPoint` + /// method actually represent a `curve25519_dalek::curve::CompressedEdwardsY` /// and that said compressed point is actually a point on the curve. /// /// # Example @@ -224,7 +224,7 @@ impl PublicKey { #[inline] #[allow(dead_code)] fn from_bytes(bytes: &[u8]) -> PublicKey { - PublicKey(CompressedPoint(*array_ref!(bytes, 0, 32))) + PublicKey(CompressedEdwardsY(*array_ref!(bytes, 0, 32))) } /// Convert this public key to its underlying extended twisted Edwards coordinate. @@ -325,7 +325,7 @@ impl Keypair { } Keypair{ - public: PublicKey(CompressedPoint(pk)), + public: PublicKey(CompressedEdwardsY(pk)), secret: SecretKey(sk), } } From 4f81231dc229bde805b954279fd37e54045b844a Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Mon, 6 Feb 2017 13:45:08 -0800 Subject: [PATCH 020/351] Add #![no_std] Use ::core in lieu of ::std, allowing this crate to be usable in #![no_std] environments. Gates features that presently depend on ::std (presently just rand) behind a "std" cargo feature, which is enabled by default. Switches from the "rust-crypto" crate (which is not #![no_std] compatible) to the sha2 crate, which is factored out of the original "rust-crypto" project and being actively maintained. --- Cargo.toml | 18 ++++++++++++++---- src/ed25519.rs | 45 +++++++++++++++++++++++++-------------------- src/lib.rs | 8 +++++++- 3 files changed, 46 insertions(+), 25 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d97887d..bdf1595 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,10 +11,20 @@ exclude = [ ".gitignore", "TESTVECTORS" ] [dependencies] -arrayref = "0.3.2" -rust-crypto = "^0.2" -rand = "^0.3" -curve25519-dalek = "^0.3" +arrayref = "0.3.3" +sha2 = "^0.4" + +[dependencies.curve25519-dalek] +version = "^0.3" +default-features = false + +[dependencies.rand] +optional = true +version = "^0.3" [dev-dependencies] rustc-serialize = "0.3" + +[features] +default = ["std"] +std = ["rand"] diff --git a/src/ed25519.rs b/src/ed25519.rs index d0e3b4e..32abbc7 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -10,11 +10,11 @@ //! A Rust implementation of ed25519 key generation, signing, and verification. -use std::fmt::Debug; +use core::fmt::Debug; -use crypto::digest::Digest; -use crypto::sha2::Sha512; +use sha2::{Digest, Sha512}; +#[cfg(feature = "std")] use rand::Rng; use curve25519_dalek::curve; @@ -24,6 +24,7 @@ use curve25519_dalek::curve::ProjectivePoint; use curve25519_dalek::scalar::Scalar; use curve25519_dalek::util::arrays_equal_ct; +pub const SIGNATURE_LENGTH: usize = 64; /// An ed25519 signature. /// @@ -33,14 +34,14 @@ use curve25519_dalek::util::arrays_equal_ct; /// "detached"—that is, they do **not** include a copy of the message which /// has been signed. #[derive(Copy)] -pub struct Signature(pub [u8; 64]); +pub struct Signature(pub [u8; SIGNATURE_LENGTH]); impl Clone for Signature { fn clone(&self) -> Self { *self } } impl Debug for Signature { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { write!(f, "Signature: {:?}", &self.0[..]) } } @@ -68,16 +69,16 @@ impl PartialEq for Signature { } impl Signature { - /// View this signature as an array of 32 bytes. + /// View this signature as an array of 64 bytes. #[inline] - pub fn to_bytes(&self) -> [u8; 64] { + pub fn to_bytes(&self) -> [u8; SIGNATURE_LENGTH] { self.0 } /// Construct a `Signature` from a slice of bytes. #[inline] pub fn from_bytes(bytes: &[u8]) -> Signature { - Signature(*array_ref!(bytes, 0, 64)) + Signature(*array_ref!(bytes, 0, SIGNATURE_LENGTH)) } } @@ -85,7 +86,7 @@ impl Signature { pub struct SecretKey(pub [u8; 64]); impl Debug for SecretKey { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { write!(f, "SecretKey: {:?}", &self.0[..]) } } @@ -136,7 +137,7 @@ impl SecretKey { pub fn sign(&self, message: &[u8]) -> Signature { let mut h: Sha512 = Sha512::new(); let mut hash: [u8; 64] = [0u8; 64]; - let signature_bytes: Vec; + let mut signature_bytes: [u8; 64] = [0u8; SIGNATURE_LENGTH]; let mut expanded_key_secret: Scalar; let mesg_digest: Scalar; let hram_digest: Scalar; @@ -148,34 +149,35 @@ impl SecretKey { let public_key: &[u8; 32] = array_ref!(&self.0, 32, 32); h.input(secret_key); - h.result(&mut hash); + hash.copy_from_slice(h.result().as_slice()); expanded_key_secret = Scalar(*array_ref!(&hash, 0, 32)); expanded_key_secret[0] &= 248; expanded_key_secret[31] &= 63; expanded_key_secret[31] |= 64; - h.reset(); + h = Sha512::new(); h.input(&hash[32..]); h.input(&message); - h.result(&mut hash); + hash.copy_from_slice(h.result().as_slice()); mesg_digest = Scalar::reduce(&hash); r = ExtendedPoint::basepoint_mult(&mesg_digest); - h.reset(); + h = Sha512::new(); h.input(&r.compress().to_bytes()[..]); h.input(public_key); h.input(&message); - h.result(&mut hash); + hash.copy_from_slice(h.result().as_slice()); hram_digest = Scalar::reduce(&hash); s = Scalar::multiply_add(&hram_digest, &expanded_key_secret, &mesg_digest); t = r.compress(); - signature_bytes = [t.0, s.0].concat(); + signature_bytes[..32].copy_from_slice(&t.0); + signature_bytes[32..64].copy_from_slice(&s.0); Signature(*array_ref!(&signature_bytes, 0, 64)) } } @@ -185,8 +187,8 @@ impl SecretKey { pub struct PublicKey(pub CompressedEdwardsY); impl Debug for PublicKey { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - write!(f, "PublicKey( CompressedEdwardsY( {:?} ))", self.0) + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "PublicKey( CompressedPoint( {:?} ))", self.0) } } @@ -267,7 +269,7 @@ impl PublicKey { h.input(&bottom_half[..]); h.input(&self.to_bytes()); h.input(&message); - h.result(&mut digest); + digest.copy_from_slice(h.result().as_slice()); digest_reduced = Scalar::reduce(&digest); r = curve::double_scalar_mult_vartime(&digest_reduced, &a, &Scalar(*top_half)); @@ -297,6 +299,7 @@ impl Keypair { /// A CSPRING with a `fill_bytes()` method, e.g. the one returned /// from `rand::OsRng::new()` (in the `rand` crate). // we reassign 0 bytes to the temp variable t to overwrite it + #[cfg(feature = "std")] #[allow(unused_assignments)] pub fn generate(cspring: &mut T) -> Keypair { let mut h: Sha512 = Sha512::new(); @@ -309,7 +312,7 @@ impl Keypair { cspring.fill_bytes(&mut t); h.input(&t); - h.result(&mut hash); + hash.copy_from_slice(h.result().as_slice()); digest = array_mut_ref!(&mut hash, 0, 32); digest[0] &= 248; @@ -346,6 +349,8 @@ mod test { use std::io::BufReader; use std::io::BufRead; use std::fs::File; + use std::string::String; + use std::vec::Vec; use test::Bencher; use curve25519_dalek::curve::ExtendedPoint; use rand::OsRng; diff --git a/src/lib.rs b/src/lib.rs index 9f2f1e5..7548c36 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,16 +58,22 @@ //! assert!(verified); //! ``` +#![no_std] #![feature(rand)] #![allow(unused_features)] #![feature(test)] #[macro_use] extern crate arrayref; -extern crate crypto; +extern crate sha2; extern crate curve25519_dalek; + +#[cfg(feature = "std")] extern crate rand; +#[cfg(test)] +#[macro_use] +extern crate std; #[cfg(test)] extern crate test; #[cfg(test)] From 4b12789bcc3a589954d50002ec4ad09d6a8fc4ac Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 8 Feb 2017 20:13:26 +0000 Subject: [PATCH 021/351] Bump version to 0.2.0. --- Cargo.toml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bdf1595..74db71d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "0.1.0" +version = "0.2.0" authors = ["Isis Lovecruft "] readme = "README.md" license = "CC0-1.0" diff --git a/README.md b/README.md index 55b5c31..3397bbb 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ Documentation is available [here](https://docs.rs/ed25519-dalek). To install, add the following to the dependencies section of your project's `Cargo.toml`: - ed25519-dalek = "0.1.0" + ed25519-dalek = "^0.2" Then, in your library or executable source, add: From 409f329890c99f6855d09dfece05013a98303579 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 21 Feb 2017 21:18:40 +0000 Subject: [PATCH 022/351] Change link to repository to github. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 74db71d..d3726c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.2.0" authors = ["Isis Lovecruft "] readme = "README.md" license = "CC0-1.0" -repository = "https://code.ciph.re/isis/ed25519-dalek" +repository = "https://github.com/isislovecruft/ed25519-dalek" keywords = ["cryptography", "ed25519", "signature", "ECC"] description = "Fast and efficient ed25519 signing and verification." exclude = [ ".gitignore", "TESTVECTORS" ] From d87930e7b704fe9b685fd77cace87d1e43e01fce Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 21 Feb 2017 21:19:00 +0000 Subject: [PATCH 023/351] Bump curve25519-dalek version to use 0.4.0. --- Cargo.toml | 2 +- src/ed25519.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d3726c4..a35b2e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ arrayref = "0.3.3" sha2 = "^0.4" [dependencies.curve25519-dalek] -version = "^0.3" +version = "^0.4" default-features = false [dependencies.rand] diff --git a/src/ed25519.rs b/src/ed25519.rs index 32abbc7..a2ac9d2 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -22,7 +22,7 @@ use curve25519_dalek::curve::CompressedEdwardsY; use curve25519_dalek::curve::ExtendedPoint; use curve25519_dalek::curve::ProjectivePoint; use curve25519_dalek::scalar::Scalar; -use curve25519_dalek::util::arrays_equal_ct; +use curve25519_dalek::subtle::arrays_equal_ct; pub const SIGNATURE_LENGTH: usize = 64; From c6c04bf20754d50b145ea19cf80e176ff443ff58 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 21 Feb 2017 21:19:20 +0000 Subject: [PATCH 024/351] Bump version to 0.2.1. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a35b2e5..0095a98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "0.2.0" +version = "0.2.1" authors = ["Isis Lovecruft "] readme = "README.md" license = "CC0-1.0" From 85c77f5da00f9904586eee04c5f1b6349e61fe1b Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 21 Feb 2017 21:26:44 +0000 Subject: [PATCH 025/351] Add a homepage and links to documentation in Cargo.toml. --- Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0095a98..2fa8950 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,11 +5,12 @@ authors = ["Isis Lovecruft "] readme = "README.md" license = "CC0-1.0" repository = "https://github.com/isislovecruft/ed25519-dalek" +homepage = "https://code.ciph.re/isis/ed25519-dalek" +documentation = "https://docs.rs/ed25519-dalek" keywords = ["cryptography", "ed25519", "signature", "ECC"] description = "Fast and efficient ed25519 signing and verification." exclude = [ ".gitignore", "TESTVECTORS" ] - [dependencies] arrayref = "0.3.3" sha2 = "^0.4" From 86c29ff6e7577d387e7bb7ace530ac1ae5aa6b2f Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 21 Feb 2017 21:27:12 +0000 Subject: [PATCH 026/351] Add Cargo.toml keywords, categories, and revise description. --- Cargo.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2fa8950..4bee212 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,8 +7,9 @@ license = "CC0-1.0" repository = "https://github.com/isislovecruft/ed25519-dalek" homepage = "https://code.ciph.re/isis/ed25519-dalek" documentation = "https://docs.rs/ed25519-dalek" -keywords = ["cryptography", "ed25519", "signature", "ECC"] -description = "Fast and efficient ed25519 signing and verification." +keywords = ["cryptography", "ed25519", "curve25519", "signature", "ECC"] +categories = ["cryptography", "no-std"] +description = "Fast and efficient ed25519 signing and verification in pure Rust." exclude = [ ".gitignore", "TESTVECTORS" ] [dependencies] From 11bc81da894c8740ea63128cc843ddd157d834ba Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 21 Feb 2017 21:27:54 +0000 Subject: [PATCH 027/351] Bump the version to 0.2.2. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4bee212..ffc9d7a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "0.2.1" +version = "0.2.2" authors = ["Isis Lovecruft "] readme = "README.md" license = "CC0-1.0" From e2a649eddb7ab76fb371f736dfe9d6a52c29e487 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 9 Mar 2017 01:11:37 +0000 Subject: [PATCH 028/351] Add a missing `use ed25519::Signature` in a docstring example. * THANKS to Tony Arcieri (@tarcieri) for pointing out the mistake. --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 7548c36..59e545a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,6 +27,7 @@ //! use rand::Rng; //! use rand::OsRng; //! use ed25519::Keypair; +//! use ed25519::Signature; //! //! let mut cspring: OsRng = OsRng::new().unwrap(); //! let keypair: Keypair = Keypair::generate(&mut cspring); From 4f1447314f2d0648302aff351fef4422e611fbc8 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Mar 2017 20:16:20 +0000 Subject: [PATCH 029/351] Bump curve25519-dalek version to ^0.6. --- Cargo.toml | 2 +- src/ed25519.rs | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ffc9d7a..f48a5e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ arrayref = "0.3.3" sha2 = "^0.4" [dependencies.curve25519-dalek] -version = "^0.4" +version = "^0.6" default-features = false [dependencies.rand] diff --git a/src/ed25519.rs b/src/ed25519.rs index a2ac9d2..f431563 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -18,6 +18,7 @@ use sha2::{Digest, Sha512}; use rand::Rng; use curve25519_dalek::curve; +use curve25519_dalek::curve::BasepointMult; use curve25519_dalek::curve::CompressedEdwardsY; use curve25519_dalek::curve::ExtendedPoint; use curve25519_dalek::curve::ProjectivePoint; @@ -166,7 +167,7 @@ impl SecretKey { r = ExtendedPoint::basepoint_mult(&mesg_digest); h = Sha512::new(); - h.input(&r.compress().to_bytes()[..]); + h.input(&r.compress_edwards().to_bytes()[..]); h.input(public_key); h.input(&message); hash.copy_from_slice(h.result().as_slice()); @@ -174,7 +175,7 @@ impl SecretKey { hram_digest = Scalar::reduce(&hash); s = Scalar::multiply_add(&hram_digest, &expanded_key_secret, &mesg_digest); - t = r.compress(); + t = r.compress_edwards(); signature_bytes[..32].copy_from_slice(&t.0); signature_bytes[32..64].copy_from_slice(&s.0); @@ -274,7 +275,7 @@ impl PublicKey { digest_reduced = Scalar::reduce(&digest); r = curve::double_scalar_mult_vartime(&digest_reduced, &a, &Scalar(*top_half)); - if arrays_equal_ct(bottom_half, &r.compress().to_bytes()) == 1 { + if arrays_equal_ct(bottom_half, &r.compress_edwards().to_bytes()) == 1 { return true } else { return false @@ -319,7 +320,7 @@ impl Keypair { digest[31] &= 127; digest[31] |= 64; - pk = ExtendedPoint::basepoint_mult(&Scalar(*digest)).compress().to_bytes(); + pk = ExtendedPoint::basepoint_mult(&Scalar(*digest)).compress_edwards().to_bytes(); for i in 0..32 { sk[i] = t[i]; @@ -397,7 +398,7 @@ mod test { break; } } - public = PublicKey(a.compress()); + public = PublicKey(a.compress_edwards()); assert!(keypair.public.0 == public.0); } From 5f8f05dad97fe367dd77e6bf05743697a907559a Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Mar 2017 20:42:44 +0000 Subject: [PATCH 030/351] Bump ed25519-dalek version to 0.2.3. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f48a5e1..06e004d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "0.2.2" +version = "0.2.3" authors = ["Isis Lovecruft "] readme = "README.md" license = "CC0-1.0" From 304d591756c4ef90dd261c164bba0c4905087f3d Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Mar 2017 21:36:35 +0000 Subject: [PATCH 031/351] Make key generation generic to hash function choice. --- Cargo.toml | 7 +++++++ src/ed25519.rs | 49 +++++++++++++++++++++++++++++++++++++++++-------- src/lib.rs | 3 +++ 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 06e004d..f4628cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,13 @@ default-features = false optional = true version = "^0.3" +[dependencies.digest] +version = "0.4" + +[dependencies.generic-array] +# same version that digest depends on +version = "^0.6" + [dev-dependencies] rustc-serialize = "0.3" diff --git a/src/ed25519.rs b/src/ed25519.rs index f431563..1109572 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -12,11 +12,14 @@ use core::fmt::Debug; -use sha2::{Digest, Sha512}; +use sha2::Sha512; #[cfg(feature = "std")] use rand::Rng; +use digest::Digest; +use generic_array::typenum::U64; + use curve25519_dalek::curve; use curve25519_dalek::curve::BasepointMult; use curve25519_dalek::curve::CompressedEdwardsY; @@ -295,15 +298,45 @@ pub struct Keypair { impl Keypair { /// Generate an ed25519 keypair. /// + /// # Example + /// + /// ``` + /// extern crate rand; + /// extern crate ed25519; + /// extern crate sha2; + /// + /// # fn main() { + /// + /// use rand::Rng; + /// use rand::OsRng; + /// use sha2::Sha512; + /// use ed25519::Keypair; + /// use ed25519::Signature; + /// + /// let mut cspring: OsRng = OsRng::new().unwrap(); + /// let keypair: Keypair = Keypair::generate::(&mut cspring); + /// + /// # } + /// ``` + /// /// # Input /// /// A CSPRING with a `fill_bytes()` method, e.g. the one returned /// from `rand::OsRng::new()` (in the `rand` crate). + /// + /// The caller must also supply a hash function which implements the + /// `Digest` and `Default` traits, and which returns 512 bits of output. + /// The standard hash function used for most ed25519 libraries is SHA-512, + /// which is available with `use sha2::Sha512` as in the example above. + /// Other suitable hash functions include Keccak-512 and Blake2b-512. + /// // we reassign 0 bytes to the temp variable t to overwrite it #[cfg(feature = "std")] #[allow(unused_assignments)] - pub fn generate(cspring: &mut T) -> Keypair { - let mut h: Sha512 = Sha512::new(); + pub fn generate(cspring: &mut Rng) -> Keypair + where D: Digest + Default { + + let mut h: D = D::default(); let mut hash: [u8; 64] = [0u8; 64]; let mut t: [u8; 32] = [0u8; 32]; let mut sk: [u8; 64] = [0u8; 64]; @@ -390,7 +423,7 @@ mod test { // from_bytes() fails if vx²-u=0 and vx²+u=0 loop { - keypair = Keypair::generate(&mut cspring); + keypair = Keypair::generate::(&mut cspring); x = keypair.public.decompress(); if x.is_some() { @@ -414,7 +447,7 @@ mod test { let bad: &[u8] = "wrong message".as_bytes(); cspring = OsRng::new().unwrap(); - keypair = Keypair::generate(&mut cspring); + keypair = Keypair::generate::(&mut cspring); good_sig = keypair.sign(&good); bad_sig = keypair.sign(&bad); @@ -479,7 +512,7 @@ mod test { #[bench] fn bench_sign(b: &mut Bencher) { let mut cspring: OsRng = OsRng::new().unwrap(); - let keypair: Keypair = Keypair::generate(&mut cspring); + let keypair: Keypair = Keypair::generate::(&mut cspring); let msg: &[u8] = "test message".as_bytes(); b.iter(| | keypair.sign(msg)); @@ -488,7 +521,7 @@ mod test { #[bench] fn bench_verify(b: &mut Bencher) { let mut cspring: OsRng = OsRng::new().unwrap(); - let keypair: Keypair = Keypair::generate(&mut cspring); + let keypair: Keypair = Keypair::generate::(&mut cspring); let msg: &[u8] = "test message".as_bytes(); let sig: Signature = keypair.sign(msg); @@ -499,6 +532,6 @@ mod test { fn bench_key_generation(b: &mut Bencher) { let mut rng: ZeroRng = ZeroRng::new(); - b.iter(| | Keypair::generate(&mut rng)); + b.iter(| | Keypair::generate::(&mut rng)); } } diff --git a/src/lib.rs b/src/lib.rs index 59e545a..a5c655c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -72,6 +72,9 @@ extern crate curve25519_dalek; #[cfg(feature = "std")] extern crate rand; +extern crate generic_array; +extern crate digest; + #[cfg(test)] #[macro_use] extern crate std; From 85b0cd933cbe77589bb57e5d4cb07987aef944c7 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Mar 2017 21:37:46 +0000 Subject: [PATCH 032/351] Split off benchmarks into separate module and require --features=bench. This allows us to compile (and test) on the rustc stable and beta channels. Benchmarking is only available on nightly, with the --features=bench flag. --- Cargo.toml | 1 + src/ed25519.rs | 53 +++++++++++++++++++++++++++----------------------- src/lib.rs | 6 ++++-- 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f4628cb..3b823a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,3 +37,4 @@ rustc-serialize = "0.3" [features] default = ["std"] std = ["rand"] +bench = [] diff --git a/src/ed25519.rs b/src/ed25519.rs index 1109572..acf5fd0 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -385,32 +385,11 @@ mod test { use std::fs::File; use std::string::String; use std::vec::Vec; - use test::Bencher; use curve25519_dalek::curve::ExtendedPoint; use rand::OsRng; - use rand::Rng; use rustc_serialize::hex::FromHex; use super::*; - /// A fake RNG which simply returns zeroes. - struct ZeroRng; - - impl ZeroRng { - fn new() -> ZeroRng { - ZeroRng - } - } - - impl Rng for ZeroRng { - fn next_u32(&mut self) -> u32 { 0u32 } - - fn fill_bytes(&mut self, bytes: &mut [u8]) { - for i in 0 .. bytes.len() { - bytes[i] = 0; - } - } - } - #[test] fn test_unmarshal_marshal() { // TestUnmarshalMarshal let mut cspring: OsRng; @@ -508,9 +487,35 @@ mod test { } } +} + +#[cfg(all(test, feature = "bench"))] +mod bench { + use test::Bencher; + use rand::OsRng; + use super::*; + + /// A fake RNG which simply returns zeroes. + pub struct ZeroRng; + + impl ZeroRng { + pub fn new() -> ZeroRng { + ZeroRng + } + } + + impl Rng for ZeroRng { + fn next_u32(&mut self) -> u32 { 0u32 } + + fn fill_bytes(&mut self, bytes: &mut [u8]) { + for i in 0 .. bytes.len() { + bytes[i] = 0; + } + } + } #[bench] - fn bench_sign(b: &mut Bencher) { + fn sign(b: &mut Bencher) { let mut cspring: OsRng = OsRng::new().unwrap(); let keypair: Keypair = Keypair::generate::(&mut cspring); let msg: &[u8] = "test message".as_bytes(); @@ -519,7 +524,7 @@ mod test { } #[bench] - fn bench_verify(b: &mut Bencher) { + fn verify(b: &mut Bencher) { let mut cspring: OsRng = OsRng::new().unwrap(); let keypair: Keypair = Keypair::generate::(&mut cspring); let msg: &[u8] = "test message".as_bytes(); @@ -529,7 +534,7 @@ mod test { } #[bench] - fn bench_key_generation(b: &mut Bencher) { + fn key_generation(b: &mut Bencher) { let mut rng: ZeroRng = ZeroRng::new(); b.iter(| | Keypair::generate::(&mut rng)); diff --git a/src/lib.rs b/src/lib.rs index a5c655c..f2f6345 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,7 +62,7 @@ #![no_std] #![feature(rand)] #![allow(unused_features)] -#![feature(test)] +#![cfg_attr(feature = "bench", feature(test))] #[macro_use] extern crate arrayref; @@ -78,8 +78,10 @@ extern crate digest; #[cfg(test)] #[macro_use] extern crate std; -#[cfg(test)] + +#[cfg(all(test, feature = "bench"))] extern crate test; + #[cfg(test)] extern crate rustc_serialize; From 807d52f655906d8092e77d067baa7c7ff7b9a64e Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Mar 2017 21:39:28 +0000 Subject: [PATCH 033/351] Refuse to compile if documentation is missing. --- src/ed25519.rs | 1 + src/lib.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/ed25519.rs b/src/ed25519.rs index acf5fd0..81e9c36 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -28,6 +28,7 @@ use curve25519_dalek::curve::ProjectivePoint; use curve25519_dalek::scalar::Scalar; use curve25519_dalek::subtle::arrays_equal_ct; +/// The length of an ed25519 `Signature`, in bytes. pub const SIGNATURE_LENGTH: usize = 64; /// An ed25519 signature. diff --git a/src/lib.rs b/src/lib.rs index f2f6345..3744e91 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,6 +63,7 @@ #![feature(rand)] #![allow(unused_features)] #![cfg_attr(feature = "bench", feature(test))] +#![deny(missing_docs)] // refuse to compile if documentation is missing #[macro_use] extern crate arrayref; From 9dc9bbed4eacac0d0cdf8eb4568a464efff82a98 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Mar 2017 21:48:36 +0000 Subject: [PATCH 034/351] Add a nightly feature which depends on curve25519-dalek/nightly. --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 3b823a6..2b27b9e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,3 +38,4 @@ rustc-serialize = "0.3" default = ["std"] std = ["rand"] bench = [] +nightly = ["curve25519-dalek/nightly"] From 5a30f4eb0f55cae7090f65d6b0a37c1eec057579 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Mar 2017 21:49:07 +0000 Subject: [PATCH 035/351] Make from_bytes() for keys public. --- src/ed25519.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 81e9c36..31dbac8 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -133,8 +133,7 @@ impl SecretKey { /// /// A `SecretKey`. #[inline] - #[allow(dead_code)] - fn from_bytes(bytes: &[u8]) -> SecretKey { + pub fn from_bytes(bytes: &[u8]) -> SecretKey { SecretKey(*array_ref!(bytes, 0, 64)) } @@ -229,8 +228,7 @@ impl PublicKey { /// /// A `PublicKey`. #[inline] - #[allow(dead_code)] - fn from_bytes(bytes: &[u8]) -> PublicKey { + pub fn from_bytes(bytes: &[u8]) -> PublicKey { PublicKey(CompressedEdwardsY(*array_ref!(bytes, 0, 32))) } From d00c3f9f3c1043c739a390bb99d264e5f2cbc250 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Mar 2017 21:50:17 +0000 Subject: [PATCH 036/351] Make all the doctests actually run. --- src/ed25519.rs | 6 ++--- src/lib.rs | 71 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 61 insertions(+), 16 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 31dbac8..d131033 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -301,16 +301,16 @@ impl Keypair { /// /// ``` /// extern crate rand; - /// extern crate ed25519; /// extern crate sha2; + /// extern crate ed25519_dalek; /// /// # fn main() { /// /// use rand::Rng; /// use rand::OsRng; /// use sha2::Sha512; - /// use ed25519::Keypair; - /// use ed25519::Signature; + /// use ed25519_dalek::Keypair; + /// use ed25519_dalek::Signature; /// /// let mut cspring: OsRng = OsRng::new().unwrap(); /// let keypair: Keypair = Keypair::generate::(&mut cspring); diff --git a/src/lib.rs b/src/lib.rs index 3744e91..480d9d8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,49 +14,94 @@ //! //! Creating an ed25519 signature on a message is simple. //! -//! First, we need to generate a `Keypair`, which includes both public -//! and secret halves of an asymmetric key. To do so, we need a -//! cryptographically secure random number generator (CSPRING). For -//! this example, we'll use the operating system's builtin PRNG to -//! generate a keypair: +//! First, we need to generate a `Keypair`, which includes both public and +//! secret halves of an asymmetric key. To do so, we need a cryptographically +//! secure pseudorandom number generator (CSPRING), and a hash function which +//! has 512 bits of output. For this example, we'll use the operating +//! system's builtin PRNG and SHA-512 to generate a keypair: //! -//! ```ignore +//! ``` //! extern crate rand; -//! extern crate ed25519; +//! extern crate sha2; +//! extern crate ed25519_dalek; //! +//! # fn main() { //! use rand::Rng; //! use rand::OsRng; -//! use ed25519::Keypair; -//! use ed25519::Signature; +//! use sha2::Sha512; +//! use ed25519_dalek::Keypair; +//! use ed25519_dalek::Signature; //! //! let mut cspring: OsRng = OsRng::new().unwrap(); -//! let keypair: Keypair = Keypair::generate(&mut cspring); +//! let keypair: Keypair = Keypair::generate::(&mut cspring); +//! # } //! ``` //! //! We can now use this `keypair` to sign a message: //! -//! ```ignore +//! ``` +//! # extern crate rand; +//! # extern crate sha2; +//! # extern crate ed25519_dalek; +//! # fn main() { +//! # use rand::Rng; +//! # use rand::OsRng; +//! # use sha2::Sha512; +//! # use ed25519_dalek::Keypair; +//! # use ed25519_dalek::Signature; +//! # let mut cspring: OsRng = OsRng::new().unwrap(); +//! # let keypair: Keypair = Keypair::generate::(&mut cspring); //! let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); //! let signature: Signature = keypair.sign(message); +//! # } //! ``` //! //! As well as to verify that this is, indeed, a valid signature on //! that `message`: //! -//! ```ignore +//! ``` +//! # extern crate rand; +//! # extern crate sha2; +//! # extern crate ed25519_dalek; +//! # fn main() { +//! # use rand::Rng; +//! # use rand::OsRng; +//! # use sha2::Sha512; +//! # use ed25519_dalek::Keypair; +//! # use ed25519_dalek::Signature; +//! # let mut cspring: OsRng = OsRng::new().unwrap(); +//! # let keypair: Keypair = Keypair::generate::(&mut cspring); +//! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); +//! # let signature: Signature = keypair.sign(message); //! let verified: bool = keypair.verify(message, &signature); //! //! assert!(verified); +//! # } //! ``` //! //! Anyone else, given the `public` half of the `keypair` can also easily //! verify this signature: //! -//! ```ignore +//! ``` +//! # extern crate rand; +//! # extern crate sha2; +//! # extern crate ed25519_dalek; +//! # fn main() { +//! # use rand::Rng; +//! # use rand::OsRng; +//! # use sha2::Sha512; +//! # use ed25519_dalek::Keypair; +//! # use ed25519_dalek::Signature; +//! use ed25519_dalek::PublicKey; +//! # let mut cspring: OsRng = OsRng::new().unwrap(); +//! # let keypair: Keypair = Keypair::generate::(&mut cspring); +//! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); +//! # let signature: Signature = keypair.sign(message); //! let public_key: PublicKey = keypair.public; //! let verified: bool = public_key.verify(message, &signature); //! //! assert!(verified); +//! # } //! ``` #![no_std] From 6522761ca640baf21326c01b3a4de2ac5b071df3 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Mar 2017 21:50:35 +0000 Subject: [PATCH 037/351] Add a .travis.yml file. --- .travis.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..881c0b3 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,30 @@ +language: rust + +rust: + - stable + - beta + - nightly + +env: + - TEST_COMMAND=test FEATURES='' + - TEST_COMMAND=test FEATURES='nightly' + - TEST_COMMAND=bench FEATURES='bench' + - TEST_COMMAND=bench FEATURES='nightly bench' + +matrix: + exclude: + - rust: stable + env: TEST_COMMAND=bench FEATURES='bench' + - rust: beta + env: TEST_COMMAND=bench FEATURES='bench' + - rust: stable + env: TEST_COMMAND=bench FEATURES='nightly bench' + - rust: beta + env: TEST_COMMAND=bench FEATURES='nightly bench' + - rust: stable + env: TEST_COMMAND=test FEATURES='nightly' + - rust: beta + env: TEST_COMMAND=test FEATURES='nightly' + +script: + - cargo $TEST_COMMAND --features="$FEATURES" From 4a4460a8ad283d68b716b070a201701504d9dead Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Mar 2017 22:09:13 +0000 Subject: [PATCH 038/351] Test both "std" and "no-std" in CI. --- .travis.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 881c0b3..b40c613 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,8 @@ rust: - nightly env: - - TEST_COMMAND=test FEATURES='' + - TEST_COMMAND=test FEATURES='no-std' + - TEST_COMMAND=test FEATURES='std' - TEST_COMMAND=test FEATURES='nightly' - TEST_COMMAND=bench FEATURES='bench' - TEST_COMMAND=bench FEATURES='nightly bench' @@ -18,13 +19,13 @@ matrix: - rust: beta env: TEST_COMMAND=bench FEATURES='bench' - rust: stable - env: TEST_COMMAND=bench FEATURES='nightly bench' + env: TEST_COMMAND=test FEATURES='nightly' - rust: beta - env: TEST_COMMAND=bench FEATURES='nightly bench' + env: TEST_COMMAND=test FEATURES='nightly' - rust: stable - env: TEST_COMMAND=test FEATURES='nightly' + env: TEST_COMMAND=bench FEATURES='nightly bench' - rust: beta - env: TEST_COMMAND=test FEATURES='nightly' + env: TEST_COMMAND=bench FEATURES='nightly bench' script: - cargo $TEST_COMMAND --features="$FEATURES" From ad4e726e495dec59b5dcf83946a29da94cab29fe Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Mar 2017 22:19:59 +0000 Subject: [PATCH 039/351] Change .travis.yml to use include directives. --- .travis.yml | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/.travis.yml b/.travis.yml index b40c613..acc4c6c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,26 +6,17 @@ rust: - nightly env: - - TEST_COMMAND=test FEATURES='no-std' - - TEST_COMMAND=test FEATURES='std' - - TEST_COMMAND=test FEATURES='nightly' - - TEST_COMMAND=bench FEATURES='bench' - - TEST_COMMAND=bench FEATURES='nightly bench' + - TEST_COMMAND=test FEATURES='' + - TEST_COMMAND=test FEATURES='--features="no-std"' matrix: - exclude: - - rust: stable - env: TEST_COMMAND=bench FEATURES='bench' - - rust: beta - env: TEST_COMMAND=bench FEATURES='bench' - - rust: stable - env: TEST_COMMAND=test FEATURES='nightly' - - rust: beta - env: TEST_COMMAND=test FEATURES='nightly' - - rust: stable - env: TEST_COMMAND=bench FEATURES='nightly bench' - - rust: beta - env: TEST_COMMAND=bench FEATURES='nightly bench' + include: + - rust: nightly + env: TEST_COMMAND=test FEATURES='--features="nightly"' + - rust: nightly + env: TEST_COMMAND=bench FEATURES='--features="bench"' + - rust: nightly + env: TEST_COMMAND=bench FEATURES='--features="nightly bench"' script: - - cargo $TEST_COMMAND --features="$FEATURES" + - cargo $TEST_COMMAND $FEATURES From 030c3e537337ccf434a395c5207028087c3539e2 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Mar 2017 23:00:26 +0000 Subject: [PATCH 040/351] Make std feature depend on curve25519-dalek/std. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 2b27b9e..6753c3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,6 @@ rustc-serialize = "0.3" [features] default = ["std"] -std = ["rand"] +std = ["rand", "curve25519-dalek/std"] bench = [] nightly = ["curve25519-dalek/nightly"] From 9ea46e0a7c1a64831de18f870e80f3b235319026 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Mar 2017 23:04:56 +0000 Subject: [PATCH 041/351] Change CI test for --features=no-std to --no-default-features. --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index acc4c6c..d3b9866 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,10 +7,11 @@ rust: env: - TEST_COMMAND=test FEATURES='' - - TEST_COMMAND=test FEATURES='--features="no-std"' matrix: include: + - rust: nightly + env: TEST_COMMAND=build FEATURES='--no-default-features' - rust: nightly env: TEST_COMMAND=test FEATURES='--features="nightly"' - rust: nightly From 23a14cebb4305e429c77a87e63e6c8d1ed0b645f Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Mar 2017 23:11:47 +0000 Subject: [PATCH 042/351] Add a Travis badge to Cargo.toml. --- Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 6753c3f..98dd770 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,9 @@ categories = ["cryptography", "no-std"] description = "Fast and efficient ed25519 signing and verification in pure Rust." exclude = [ ".gitignore", "TESTVECTORS" ] +[badges] +travis-ci = { repository = "isislovecruft/ed25519-dalek", branch = "master"} + [dependencies] arrayref = "0.3.3" sha2 = "^0.4" From 4bdbf89eebb95912905f891abd0f6ca8d80a4b0b Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Mar 2017 23:13:07 +0000 Subject: [PATCH 043/351] Add Travis badge to README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3397bbb..afafb66 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ed25519-dalek ![](https://img.shields.io/crates/v/ed25519-dalek.svg) ![](https://docs.rs/ed25519-dalek/badge.svg) +# ed25519-dalek ![](https://img.shields.io/crates/v/ed25519-dalek.svg) ![](https://docs.rs/ed25519-dalek/badge.svg) ![](https://travis-ci.org/isislovecruft/ed25519-dalek.svg?branch=master) Fast and efficient Rust implementation of ed25519 key generation, signing, and verification in Rust. From 4eaa1321eee64c39bc0a8ff63779858c815c3de2 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Mar 2017 23:15:19 +0000 Subject: [PATCH 044/351] Feature gate rand on nightly. We can't use features on non-nightly channels. --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 480d9d8..73daa41 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -105,7 +105,7 @@ //! ``` #![no_std] -#![feature(rand)] +#![cfg_attr(feature = "nightly", feature(rand))] #![allow(unused_features)] #![cfg_attr(feature = "bench", feature(test))] #![deny(missing_docs)] // refuse to compile if documentation is missing From b5531c125712de615d91ceeedf81b4a2e5cfbb1c Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Mar 2017 23:19:38 +0000 Subject: [PATCH 045/351] Remove test_ prefix from test functions. --- src/ed25519.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index d131033..4e3c468 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -390,7 +390,7 @@ mod test { use super::*; #[test] - fn test_unmarshal_marshal() { // TestUnmarshalMarshal + fn unmarshal_marshal() { // TestUnmarshalMarshal let mut cspring: OsRng; let mut keypair: Keypair; let mut x: Option; @@ -415,7 +415,7 @@ mod test { } #[test] - fn test_sign_verify() { // TestSignVerify + fn sign_verify() { // TestSignVerify let mut cspring: OsRng; let keypair: Keypair; let good_sig: Signature; @@ -443,7 +443,7 @@ mod test { #[cfg(test)] #[cfg(not(release))] #[test] - fn test_golden() { // TestGolden + fn golden() { // TestGolden let mut line: String; let mut lineno: usize = 0; From b1105618e717d61e03859c73e3b497e52e777a29 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Mar 2017 23:43:02 +0000 Subject: [PATCH 046/351] Make verification dependent on hash function. --- src/ed25519.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 4e3c468..4f9665b 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -244,8 +244,10 @@ impl PublicKey { /// /// Returns true if the signature was successfully verified, and /// false otherwise. - pub fn verify(&self, message: &[u8], signature: &Signature) -> bool { - let mut h: Sha512 = Sha512::new(); + pub fn verify(&self, message: &[u8], signature: &Signature) -> bool + where D: Digest + Default { + + let mut h: D = D::default(); let mut a: ExtendedPoint; let ao: Option; let r: ProjectivePoint; @@ -372,8 +374,9 @@ impl Keypair { } /// Verify a signature on a message with this keypair's public key. - pub fn verify(&self, message: &[u8], signature: &Signature) -> bool { - self.public.verify(message, signature) + pub fn verify(&self, message: &[u8], signature: &Signature) -> bool + where D: Digest + Default { + self.public.verify::(message, signature) } } @@ -429,11 +432,11 @@ mod test { good_sig = keypair.sign(&good); bad_sig = keypair.sign(&bad); - assert!(keypair.verify(&good, &good_sig) == true, + assert!(keypair.verify::(&good, &good_sig) == true, "Verification of a valid signature failed!"); - assert!(keypair.verify(&good, &bad_sig) == false, + assert!(keypair.verify::(&good, &bad_sig) == false, "Verification of a signature on a different message passed!"); - assert!(keypair.verify(&bad, &good_sig) == false, + assert!(keypair.verify::(&bad, &good_sig) == false, "Verification of a signature on a different message passed!"); } @@ -482,8 +485,8 @@ mod test { println!("{:?}", pub_bytes); assert!(sig1 == sig2, "Signature bytes not equal on line {}", lineno); - assert!(public_key.verify(&message, &sig2), "Signature verification failed on line {}", lineno); - + assert!(public_key.verify::(&message, &sig2), + "Signature verification failed on line {}", lineno); } } } @@ -529,7 +532,7 @@ mod bench { let msg: &[u8] = "test message".as_bytes(); let sig: Signature = keypair.sign(msg); - b.iter(| | keypair.verify(msg, &sig)); + b.iter(| | keypair.verify::(msg, &sig)); } #[bench] From 03f20fdae3eb151186ffa93dd664a1d3b7e646cf Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Mar 2017 23:43:50 +0000 Subject: [PATCH 047/351] ZeroRng in benchmarks doesn't need to be public. --- src/ed25519.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 4f9665b..4a505b5 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -498,7 +498,7 @@ mod bench { use super::*; /// A fake RNG which simply returns zeroes. - pub struct ZeroRng; + struct ZeroRng; impl ZeroRng { pub fn new() -> ZeroRng { From 1e2fa1025e017c53e9d44533be04d62e03147e5e Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Mar 2017 23:44:21 +0000 Subject: [PATCH 048/351] Implement a ZeroDigest for use in benchmarks. --- src/ed25519.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/ed25519.rs b/src/ed25519.rs index 4a505b5..f41a7bc 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -493,6 +493,8 @@ mod test { #[cfg(all(test, feature = "bench"))] mod bench { + use generic_array::GenericArray; + use generic_array::typenum::{U64, U128}; use test::Bencher; use rand::OsRng; use super::*; @@ -516,6 +518,27 @@ mod bench { } } + /// A fake hash function which simply returns zeroes. + struct ZeroDigest; + + impl ZeroDigest { + pub fn new() -> ZeroDigest { + ZeroDigest + } + } + + impl Digest for ZeroDigest { + type OutputSize = U64; + type BlockSize = U128; + + fn input(&mut self, _input: &[u8]) { } + fn result(self) -> GenericArray { GenericArray::default() } + } + + impl Default for ZeroDigest { + fn default() -> Self { Self::new() } + } + #[bench] fn sign(b: &mut Bencher) { let mut cspring: OsRng = OsRng::new().unwrap(); From aa6e0a4324b8ef40949384b54dd02853c51bb901 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Mar 2017 23:44:49 +0000 Subject: [PATCH 049/351] Benchmark signing/verifying blank messages. --- src/ed25519.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index f41a7bc..d80664f 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -543,7 +543,7 @@ mod bench { fn sign(b: &mut Bencher) { let mut cspring: OsRng = OsRng::new().unwrap(); let keypair: Keypair = Keypair::generate::(&mut cspring); - let msg: &[u8] = "test message".as_bytes(); + let msg: &[u8] = "".as_bytes(); b.iter(| | keypair.sign(msg)); } @@ -552,7 +552,7 @@ mod bench { fn verify(b: &mut Bencher) { let mut cspring: OsRng = OsRng::new().unwrap(); let keypair: Keypair = Keypair::generate::(&mut cspring); - let msg: &[u8] = "test message".as_bytes(); + let msg: &[u8] = "".as_bytes(); let sig: Signature = keypair.sign(msg); b.iter(| | keypair.verify::(msg, &sig)); From e4c085706cda56219f5bdc2ff76d20ef648184d9 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Mar 2017 23:54:43 +0000 Subject: [PATCH 050/351] Make signing generic to hash function choice. --- src/ed25519.rs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index d80664f..38c54dc 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -138,8 +138,10 @@ impl SecretKey { } /// Sign a message with this keypair's secret key. - pub fn sign(&self, message: &[u8]) -> Signature { - let mut h: Sha512 = Sha512::new(); + pub fn sign(&self, message: &[u8]) -> Signature + where D: Digest + Default { + + let mut h: D = D::default(); let mut hash: [u8; 64] = [0u8; 64]; let mut signature_bytes: [u8; 64] = [0u8; SIGNATURE_LENGTH]; let mut expanded_key_secret: Scalar; @@ -160,7 +162,7 @@ impl SecretKey { expanded_key_secret[31] &= 63; expanded_key_secret[31] |= 64; - h = Sha512::new(); + h = D::default(); h.input(&hash[32..]); h.input(&message); hash.copy_from_slice(h.result().as_slice()); @@ -169,7 +171,7 @@ impl SecretKey { r = ExtendedPoint::basepoint_mult(&mesg_digest); - h = Sha512::new(); + h = D::default(); h.input(&r.compress_edwards().to_bytes()[..]); h.input(public_key); h.input(&message); @@ -369,8 +371,9 @@ impl Keypair { } /// Sign a message with this keypair's secret key. - pub fn sign(&self, message: &[u8]) -> Signature { - self.secret.sign(message) + pub fn sign(&self, message: &[u8]) -> Signature + where D: Digest + Default { + self.secret.sign::(message) } /// Verify a signature on a message with this keypair's public key. @@ -429,8 +432,8 @@ mod test { cspring = OsRng::new().unwrap(); keypair = Keypair::generate::(&mut cspring); - good_sig = keypair.sign(&good); - bad_sig = keypair.sign(&bad); + good_sig = keypair.sign::(&good); + bad_sig = keypair.sign::(&bad); assert!(keypair.verify::(&good, &good_sig) == true, "Verification of a valid signature failed!"); @@ -479,7 +482,7 @@ mod test { let secret_key: SecretKey = SecretKey::from_bytes(&sec_bytes); let public_key: PublicKey = PublicKey::from_bytes(&pub_bytes); - let sig2: Signature = secret_key.sign(&message); + let sig2: Signature = secret_key.sign::(&message); println!("{:?}", sec_bytes); println!("{:?}", pub_bytes); @@ -545,7 +548,7 @@ mod bench { let keypair: Keypair = Keypair::generate::(&mut cspring); let msg: &[u8] = "".as_bytes(); - b.iter(| | keypair.sign(msg)); + b.iter(| | keypair.sign::(msg)); } #[bench] @@ -553,7 +556,7 @@ mod bench { let mut cspring: OsRng = OsRng::new().unwrap(); let keypair: Keypair = Keypair::generate::(&mut cspring); let msg: &[u8] = "".as_bytes(); - let sig: Signature = keypair.sign(msg); + let sig: Signature = keypair.sign::(msg); b.iter(| | keypair.verify::(msg, &sig)); } From f1dd165208b89f60cd01b93b73042883d1bb5b13 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 15 Mar 2017 18:42:58 +0000 Subject: [PATCH 051/351] Use array_ref! for getting digest during signing. --- src/ed25519.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 38c54dc..215eab8 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -253,7 +253,7 @@ impl PublicKey { let mut a: ExtendedPoint; let ao: Option; let r: ProjectivePoint; - let mut digest: [u8; 64]; + let digest: [u8; 64]; let digest_reduced: Scalar; if signature.0[63] & 224 != 0 { @@ -268,16 +268,15 @@ impl PublicKey { } a = -(&a); - digest = [0u8; 64]; - let top_half: &[u8; 32] = array_ref!(&signature.0, 32, 32); let bottom_half: &[u8; 32] = array_ref!(&signature.0, 0, 32); h.input(&bottom_half[..]); h.input(&self.to_bytes()); h.input(&message); - digest.copy_from_slice(h.result().as_slice()); + let digest_bytes = h.result(); + digest = *array_ref!(digest_bytes, 0, 64); digest_reduced = Scalar::reduce(&digest); r = curve::double_scalar_mult_vartime(&digest_reduced, &a, &Scalar(*top_half)); From 5e84eedfbb29fd0d49ca119eb3716118cf6bae57 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 15 Mar 2017 18:44:59 +0000 Subject: [PATCH 052/351] Add benchmarks with blake2b. --- src/ed25519.rs | 28 ++++++++++++++++++++++++++++ src/lib.rs | 4 ++++ 2 files changed, 32 insertions(+) diff --git a/src/ed25519.rs b/src/ed25519.rs index 215eab8..74863c6 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -495,6 +495,8 @@ mod test { #[cfg(all(test, feature = "bench"))] mod bench { + use blake2::Blake2b; + use digest::Digest; use generic_array::GenericArray; use generic_array::typenum::{U64, U128}; use test::Bencher; @@ -566,4 +568,30 @@ mod bench { b.iter(| | Keypair::generate::(&mut rng)); } + + #[bench] + fn blake2b_sign(b: &mut Bencher) { + let mut cspring: OsRng = OsRng::new().unwrap(); + let keypair: Keypair = Keypair::generate::(&mut cspring); + let msg: &[u8] = "".as_bytes(); + + b.iter(| | keypair.sign::(msg)); + } + + #[bench] + fn blake2b_verify(b: &mut Bencher) { + let mut cspring: OsRng = OsRng::new().unwrap(); + let keypair: Keypair = Keypair::generate::(&mut cspring); + let msg: &[u8] = "".as_bytes(); + let sig: Signature = keypair.sign::(msg); + + b.iter(| | keypair.verify::(msg, &sig)); + } + + #[bench] + fn blake2b_key_generation(b: &mut Bencher) { + let mut rng: ZeroRng = ZeroRng::new(); + + b.iter(| | Keypair::generate::(&mut rng)); + } } diff --git a/src/lib.rs b/src/lib.rs index 73daa41..41b11c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -131,6 +131,10 @@ extern crate test; #[cfg(test)] extern crate rustc_serialize; +#[cfg(all(test, feature = "bench"))] +extern crate blake2; + + mod ed25519; // Export everything public in ed25519. From 394d1face2f525b92bcb7af941e8f84997a0a695 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 15 Mar 2017 18:53:48 +0000 Subject: [PATCH 053/351] Remove the ZeroDigest from the benchmark suite. It turns out that testing scalar_mult_vartime() on all zeroes is fast. --- Cargo.toml | 1 + src/ed25519.rs | 23 ----------------------- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 98dd770..e5af2f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ version = "^0.6" [dev-dependencies] rustc-serialize = "0.3" +blake2 = "^0.4" [features] default = ["std"] diff --git a/src/ed25519.rs b/src/ed25519.rs index 74863c6..87d38d7 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -497,8 +497,6 @@ mod test { mod bench { use blake2::Blake2b; use digest::Digest; - use generic_array::GenericArray; - use generic_array::typenum::{U64, U128}; use test::Bencher; use rand::OsRng; use super::*; @@ -522,27 +520,6 @@ mod bench { } } - /// A fake hash function which simply returns zeroes. - struct ZeroDigest; - - impl ZeroDigest { - pub fn new() -> ZeroDigest { - ZeroDigest - } - } - - impl Digest for ZeroDigest { - type OutputSize = U64; - type BlockSize = U128; - - fn input(&mut self, _input: &[u8]) { } - fn result(self) -> GenericArray { GenericArray::default() } - } - - impl Default for ZeroDigest { - fn default() -> Self { Self::new() } - } - #[bench] fn sign(b: &mut Bencher) { let mut cspring: OsRng = OsRng::new().unwrap(); From e869d14387dd334015c33ad53b8b35b0f14cf2bb Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 15 Mar 2017 18:56:24 +0000 Subject: [PATCH 054/351] Fix doctests to specify hash function choice. --- src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 41b11c0..ab6cb6a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,7 +52,7 @@ //! # let mut cspring: OsRng = OsRng::new().unwrap(); //! # let keypair: Keypair = Keypair::generate::(&mut cspring); //! let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); -//! let signature: Signature = keypair.sign(message); +//! let signature: Signature = keypair.sign::(message); //! # } //! ``` //! @@ -72,8 +72,8 @@ //! # let mut cspring: OsRng = OsRng::new().unwrap(); //! # let keypair: Keypair = Keypair::generate::(&mut cspring); //! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); -//! # let signature: Signature = keypair.sign(message); -//! let verified: bool = keypair.verify(message, &signature); +//! # let signature: Signature = keypair.sign::(message); +//! let verified: bool = keypair.verify::(message, &signature); //! //! assert!(verified); //! # } @@ -96,9 +96,9 @@ //! # let mut cspring: OsRng = OsRng::new().unwrap(); //! # let keypair: Keypair = Keypair::generate::(&mut cspring); //! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); -//! # let signature: Signature = keypair.sign(message); +//! # let signature: Signature = keypair.sign::(message); //! let public_key: PublicKey = keypair.public; -//! let verified: bool = public_key.verify(message, &signature); +//! let verified: bool = public_key.verify::(message, &signature); //! //! assert!(verified); //! # } From 89d246ec153e1c7c6591e0835db4526d82702dc2 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 15 Mar 2017 18:57:03 +0000 Subject: [PATCH 055/351] Rearrange extern crates. --- src/lib.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ab6cb6a..45d96af 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,23 +114,22 @@ extern crate arrayref; extern crate sha2; extern crate curve25519_dalek; +extern crate generic_array; +extern crate digest; #[cfg(feature = "std")] extern crate rand; -extern crate generic_array; -extern crate digest; - #[cfg(test)] #[macro_use] extern crate std; -#[cfg(all(test, feature = "bench"))] -extern crate test; - #[cfg(test)] extern crate rustc_serialize; +#[cfg(all(test, feature = "bench"))] +extern crate test; + #[cfg(all(test, feature = "bench"))] extern crate blake2; From 645d4e0d8c67fcd84c4f4186e768e717f4250048 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 15 Mar 2017 19:01:03 +0000 Subject: [PATCH 056/351] The sha2 crate is no longer a required dependency. --- Cargo.toml | 2 +- src/ed25519.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e5af2f7..c46bffc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,6 @@ travis-ci = { repository = "isislovecruft/ed25519-dalek", branch = "master"} [dependencies] arrayref = "0.3.3" -sha2 = "^0.4" [dependencies.curve25519-dalek] version = "^0.6" @@ -37,6 +36,7 @@ version = "^0.6" [dev-dependencies] rustc-serialize = "0.3" blake2 = "^0.4" +sha2 = "^0.4" [features] default = ["std"] diff --git a/src/ed25519.rs b/src/ed25519.rs index 87d38d7..7c483ba 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -12,8 +12,6 @@ use core::fmt::Debug; -use sha2::Sha512; - #[cfg(feature = "std")] use rand::Rng; @@ -392,6 +390,7 @@ mod test { use curve25519_dalek::curve::ExtendedPoint; use rand::OsRng; use rustc_serialize::hex::FromHex; + use sha2::Sha512; use super::*; #[test] @@ -499,6 +498,7 @@ mod bench { use digest::Digest; use test::Bencher; use rand::OsRng; + use sha2::Sha512; use super::*; /// A fake RNG which simply returns zeroes. From b188516b22bb8da8dd38bbc3afdd65afaa8e89af Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 15 Mar 2017 19:07:21 +0000 Subject: [PATCH 057/351] Remove unused import digest::Digest from bench module. --- src/ed25519.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 7c483ba..f486320 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -495,7 +495,6 @@ mod test { #[cfg(all(test, feature = "bench"))] mod bench { use blake2::Blake2b; - use digest::Digest; use test::Bencher; use rand::OsRng; use sha2::Sha512; From 054e9ce6b85494de7c80c85b88ce9a0c848c4a6a Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 15 Mar 2017 19:09:15 +0000 Subject: [PATCH 058/351] Remove blake2b benchmarks. --- Cargo.toml | 1 - src/ed25519.rs | 27 --------------------------- src/lib.rs | 3 --- 3 files changed, 31 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c46bffc..f61d3c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,6 @@ version = "^0.6" [dev-dependencies] rustc-serialize = "0.3" -blake2 = "^0.4" sha2 = "^0.4" [features] diff --git a/src/ed25519.rs b/src/ed25519.rs index f486320..fda76cd 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -494,7 +494,6 @@ mod test { #[cfg(all(test, feature = "bench"))] mod bench { - use blake2::Blake2b; use test::Bencher; use rand::OsRng; use sha2::Sha512; @@ -544,30 +543,4 @@ mod bench { b.iter(| | Keypair::generate::(&mut rng)); } - - #[bench] - fn blake2b_sign(b: &mut Bencher) { - let mut cspring: OsRng = OsRng::new().unwrap(); - let keypair: Keypair = Keypair::generate::(&mut cspring); - let msg: &[u8] = "".as_bytes(); - - b.iter(| | keypair.sign::(msg)); - } - - #[bench] - fn blake2b_verify(b: &mut Bencher) { - let mut cspring: OsRng = OsRng::new().unwrap(); - let keypair: Keypair = Keypair::generate::(&mut cspring); - let msg: &[u8] = "".as_bytes(); - let sig: Signature = keypair.sign::(msg); - - b.iter(| | keypair.verify::(msg, &sig)); - } - - #[bench] - fn blake2b_key_generation(b: &mut Bencher) { - let mut rng: ZeroRng = ZeroRng::new(); - - b.iter(| | Keypair::generate::(&mut rng)); - } } diff --git a/src/lib.rs b/src/lib.rs index 45d96af..c5ca4a8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -130,9 +130,6 @@ extern crate rustc_serialize; #[cfg(all(test, feature = "bench"))] extern crate test; -#[cfg(all(test, feature = "bench"))] -extern crate blake2; - mod ed25519; From 71c2bc7687bfec00da01723171395f35533bba54 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 15 Mar 2017 21:05:07 +0000 Subject: [PATCH 059/351] Revise README with new benchmarks, warning, and install instructions. --- README.md | 127 +++++++++++++++++++++++++++++++-------- ed25519-malleability.png | Bin 0 -> 44136 bytes 2 files changed, 103 insertions(+), 24 deletions(-) create mode 100644 ed25519-malleability.png diff --git a/README.md b/README.md index afafb66..d9061b9 100644 --- a/README.md +++ b/README.md @@ -3,26 +3,36 @@ Fast and efficient Rust implementation of ed25519 key generation, signing, and verification in Rust. +# Documentation + +Documentation is available [here](https://docs.rs/ed25519-dalek). + # Benchmarks +You need to pass the `--features="bench"` flag to run the benchmarks. The +reason for feature-gating the benchmarks is that Rust's `test::Bencher` is +unstable, and thus only works on the nightly channel. (We'd like people to be +able to compile and test on the stable and beta channels too!) + On an Intel i5 Sandy Bridge running at 2.6 GHz, with TurboBoost enabled (and also running in QubesOS with *lots* of other VMs executing), this code achieves the following performance benchmarks: - ∃!isisⒶwintermute:(release/0.1.0 *$)~/code/rust/ed25519 ∴ cargo bench - Finished release [optimized] target(s) in 0.0 secs - Running target/release/deps/ed25519-0135748522c518d8 + ∃!isisⒶwintermute:(develop *$)~/code/rust/ed25519 ∴ cargo bench --features="bench" + Finished release [optimized] target(s) in 0.0 secs + Running target/release/deps/ed25519_dalek-281c2d7a2379edae - running 5 tests - test ed25519::test::test_sign_verify ... ignored - test ed25519::test::test_unmarshal_marshal ... ignored - test ed25519::test::bench_key_generation ... bench: 54,837 ns/iter (+/- 11,613) - test ed25519::test::bench_sign ... bench: 69,735 ns/iter (+/- 21,902) - test ed25519::test::bench_verify ... bench: 183,891 ns/iter (+/- 75,304) + running 6 tests + test ed25519::test::golden ... ignored + test ed25519::test::sign_verify ... ignored + test ed25519::test::unmarshal_marshal ... ignored + test ed25519::bench::key_generation ... bench: 54,571 ns/iter (+/- 7,861) + test ed25519::bench::sign ... bench: 70,009 ns/iter (+/- 22,812) + test ed25519::bench::verify ... bench: 185,619 ns/iter (+/- 24,117) - test result: ok. 0 passed; 0 failed; 2 ignored; 3 measured + test result: ok. 0 passed; 0 failed; 3 ignored; 3 measured -In comparision, the equivalent package in Golang performs as follows: +In comparison, the equivalent package in Golang performs as follows: ∃!isisⒶwintermute:(master *=)~/code/go/src/github.com/agl/ed25519 ∴ go test -bench . PASS @@ -34,36 +44,105 @@ In comparision, the equivalent package in Golang performs as follows: Making key generation, signing, and verification a rough average of one third faster, one fifth faster, and one eighth faster respectively. Of course, this is just my machine, and these results—nowhere near rigorous—should be taken -with a fistful of salt. +with a handful of salt. -## Warning +Additionally, if you're on the Rust nightly channel, be sure to build with +`cargo build --features="nightly"`, which uses Rust's experimental support for +the `u128` type in curve25519-dalek to speed up field arithmetic by roughly a +factor of two. The benchmarks using nightly (on the same machine as above) +are: -[Our elliptic curve library](https://github.com/isislovecruft/curve25519-dalek) -(which this code uses) has **not** yet received sufficient peer review by -other qualified cryptographers to be considered in any way, shape, or form, -safe. + ∃!isisⒶwintermute:(develop *$)~/code/rust/ed25519 ∴ cargo bench --features="bench nightly" + Finished release [optimized] target(s) in 0.0 secs + Running target/release/deps/ed25519_dalek-9d7f8674ae11ac39 + + running 6 tests + test ed25519::test::golden ... ignored + test ed25519::test::sign_verify ... ignored + test ed25519::test::unmarshal_marshal ... ignored + test ed25519::bench::key_generation ... bench: 31,160 ns/iter (+/- 8,597) + test ed25519::bench::sign ... bench: 40,565 ns/iter (+/- 4,758) + test ed25519::bench::verify ... bench: 106,146 ns/iter (+/- 2,796) + + test result: ok. 0 passed; 0 failed; 3 ignored; 3 measured + +Translating to a rough cycle count: we multiply by a factor of 2.6 to convert +nanoseconds to cycles per second on a 2.6 GHz CPU, that's 275979 cycles for +verification and 105469 for signing, which is +[competitive with the optimised assembly version](https://ed25519.cr.yp.to/) +included in the SUPERCOP benchmarking suite (albeit their numbers are for the +older Nehalem microarchitecture). + +Additionally, thanks to Rust, this implementation has both type and memory +safety. Not to mention that it's readable for everyone, making ours arguable +more readily auditable. We're of the opinion that these features—combined +with speed—are ultimately more valuable than sole cycle count. + +# Warnings + +ed25519-dalek and +[our elliptic curve library](https://github.com/isislovecruft/curve25519-dalek) +(which this code uses) have received *one* formal cryptographic and security +review. Neither have yet received what we would consider *sufficient* peer +review by other qualified cryptographers to be considered in any way, shape, +or form, safe. **USE AT YOUR OWN RISK** -# Documentation +## A Note on Signature Malleability -Documentation is available [here](https://docs.rs/ed25519-dalek). +The signatures produced by this library are malleable, as defined in +[the original paper](https://ed25519.cr.yp.to/ed25519-20110926.pdf): + +![](https://raw.githubusercontent.com/isislovecruft/ed25519-dalek/develop/ed25519-malleability.png) + +We could eliminate the malleability property by multiplying by the curve +cofactor, however, this would cause our implementation to *not* match the +behaviour of every other implementation in existence. While there is, as of +this writing, a +[draft RFC for EdDSA signatures](https://tools.ietf.org/html/rfc8032) which +specifies that the stronger check should be done (and while we agree that the +stronger check should be done), it is our opinion that one doesn't get to +change the definition of "ed25519 verification" a decade after the fact, +declaring every implementation (including one's own) to be non-conformant. + +In short, if malleable signatures are bad for your protocol, don't use them. +Consider using a curve25519-based Verifiable Random Function (VRF), such as +[Trevor Perrin's VXEdDSA](https://www.whispersystems.org/docs/specifications/xeddsa/), +instead. We +[plan](https://github.com/isislovecruft/curve25519-dalek/issues/9) to +eventually support VXEdDSA in curve25519-dalek. # Installation -To install, add the following to the dependencies section of your project's -`Cargo.toml`: +To install, add the following to your project's `Cargo.toml`: - ed25519-dalek = "^0.2" + [dependencies.ed25519-dalek] + version = "^0.3" Then, in your library or executable source, add: extern crate ed25519_dalek +To cause your application to build `ed25519-dalek` with the nightly feature +enabled by default, instead do: + + [dependencies.ed25519-dalek] + version = "^0.3" + features = ["nightly"] + +To cause your application to instead build with the nightly feature enabled +when someone builds with `cargo build --features="nightly"` add the following +to the `Cargo.toml`: + + [features] + nightly = ["ed25519-dalek/nightly"] + + # TODO - * Maybe add methods to make exporting keys for backup easier. - * Benchmark in comparison to the ed25519_ref10 code. + * Maybe add methods to make exporting keys for backup easier. Maybe using + serde? * We can probably make this go even faster if we implement SHA512, rather than using the rust-crypto implementation whose API requires that we allocate memory and memzero it before mutating to store the diff --git a/ed25519-malleability.png b/ed25519-malleability.png new file mode 100644 index 0000000000000000000000000000000000000000..fe5896e99e3f912a8c924ee02bc687ba483eb9a2 GIT binary patch literal 44136 zcmb@ucOce(+dq8DmW1r=l}+}F?3q17Br>uxLiXO-d+)s>LfJwxLx}9`vJx_$L*MJV z@9VjL_wV`ZIsdr6t~xt?&d>Y(K91KqK4EGqayPKZun-8u4F!2=4FuxaEc`ryaSi@= z%b;c+{)6l!si1{{f$?=wbpe5(Mkq*2Xt`xHg_644oA_yUe8)hSl-SuvdPK72)Th$SdtT>r&1U;77>`2qlFnRi60lU{ij!S z*n7XghCk@VkKbKXEnNx5>C^o`w%ITxaPR-?HS)FOumOScuNMM09{<|km%rGxyUaAy ze_!jj{^O64{{Q~tl8+C4qj$wUPj+MXV!M`ikKg@>2}4Ng&}jB+c|GuYLh*zU|E?GP z)s-BlOT}#jcpAN{3jdBplsw^e4KI}2SGawqJFPwYxstZ__jyV9Mv4WT zu2R#C419-ak`EWB>nuhI?%jKD`?W#6RIB!6zRkyHu`{Tuio>m}s;brJ(z~>>Mz10# zhng|I-?Wy9O>g)7_q>v#R!n$!_{fMFQN+abbd%SgqtSf1h`|qw4ljOw8(iKyJ3mLi zNy(-_GHRpPtu`<+){cKKDm1ijc(_=N3B%u^$&i>hS-V^(F)=Yy#8u_Ng9kD)K?AEV zTwILaIv^oNzI|);`tu^?+w`>iN*|t)kx`G#%e|$Zz2)8w=hL&3gY)xq!s^|v#VuYV zGcz+;S+pR@$K0vOoW_i?@}_k%GBTBVwf@SCbQt(}c(qRl$yUQ@tUb+}mOBE_A3uKV zEbP@4d_yUT4KF>UcX@SXMNmYfytFjT_90UMx##ijN zH8wWRzGRkv`t*B${2c=WgJd@S*LHKw-|(gMk55h}CMO*QGkT+`e%rlqBs(t^Is;R51`|#oE-m+YzH0s^EclY-9S(uoZ zSXrOc(to=+*{W`%gx|T+A78FpZHhscb_4&`Eloqiiky<{lx!5$WDcX|7dtab$sBOc z?Up}8!B41&si`S)QMa11vd!^QO*Aw#xoFCu)Hn6@{{H?fEG(Own+E}Ib1Qd zPkP}OynX%pb#AT%GoklA&!D0f*Pq{F>Et>)I}d;TdSYry@6vd9=&Y%!$(@Rg2#t!O z5U|S_W5DBUeLcIlxLE6Rc>$|5w&AScUS-&{ztcERDei5k@mZ3T8dmw|d|NNGthV;V zdyn5ZRASf&w_lqRT_HDzGlT+)bCfF=s=EgVcXk)M%+1YV&tU0r$ZaXEHO_n2)z?WmW2@1*e89`cH#a+LXlRH) z9IOqaBMe(!L=CS+Jhij2G26D=X71Qm$=iNJ=ZAZf zT|z=aWaNOIuZ_qEF|X4=6)}1Gv4xI+VwFrXe0*-l6?ug=HFFvG@*h8b?Cg|ee&eML z_c!9ztE2t>JT)ed-#;o#OG&v*e5Y*m_4VN*c8|~h{EoAG#NuDFm$-_w)OJGvcq9`{EAXrDX`G;OX=t&DRWOvPeUc+ zPF1FjB_}7>(sGD8c-=s&$#C!1j-t8*%UnSO`NT)9f|8QHj~}t_-E;jxK4f5tTN^t~ z!Nkjam!Cga{8=ES9s)7*^{a84_s-^KxDj&pFtJCn3=Y?(`a43nMp0eoTpKo`XY;LI zA2rkxtUecQtgmjTKf|CZ9_sq(QxeDi+KjpPy#OYeL&&AH!DiJ6%hY_tHCVh=aB3cXs> zg?7K!SR>=(&KGAVX=!A(wmYw0;W#-RJV@tHO-=2eQG_Gp=;&x*a66<6389=V<^^xu zCH??=R*#J+zTfcu%U}De0}KkZ2|JUO25ZCFm;0;N5eu*d;AeCMl#P+eNq#Z03;0`y zRF;q~Oe`!%2L}YgX0G`c93u?>PZ>f^s~;0JTwpQNVL$%b`ZDnGW9!NH+bPGZ1IZKR zy7OXvm{A-D#6PeP-~F_6`pZ_xDc^H$G?G^B5l=U+A<(Mn+b^=`6KU zQW~&tJ1>HwSyuLO?cht5QN*|Y7-kiE{cZTtczAf)+S-lw^Ht^Lbu~4|zrGkVXq%dw zCnhDy7Jr8FYin(N3PtSIt5*W{-!Pav&A6c}x7p8&!P-6hQU$g7oyYH?4&~apMg(kU=x0hSg98Ht zp>Plr5owf;mzA*~+JkT0hHu4{X9`!UuC9JtzzzlB{Nly8CMS!p4YpGs!);M<6pux& z$ujt36%`cJOm7DVqvZ;AQ`wmIqHj+UZqt$DtU*_X{lv(~czSx8*T%yz5q(ckzEX#v zRHi#^Z&>_NSy2&gmL{y&%a_9b)D#pHm6eq?{C}XGx@}YQIVWP$Uaz)()X`>PKI}{5 z+q?X(7fFiCtKa#sO+Qipi1KJ!dm8@UOW>!73C8xl?a|BDGWR#DBSa<`vahHxRexvC z-^BL&)i_C7kIg&0f*S8fGga>zZz7S~-6s-D7QlxVO`5S!yQ)1{{wZ9!Md8IvSABe4 z{PTwWvhRttnRmHRh*XG1Z{|JzSjE%gOXHBflXSqi2JfDdtL>T#Rw9&T`ucgSxU{L% z`nz>IE=ner;^2b^gAWGFKKV~7d1eWk^dU?z&VK)~&@s#7D@iTSj25|kyIyM|wX0eo z2b~xR0VO=B7}@~r0CF5TV`HE7kz7wPs-nyN0Zz-<`g&ei;M3EWC=$n>t;NM_0DG@L2OGx+y z1W38N3n>o|4+Cl`9dG>{49hEYgn2c<5VL{E% z@YC`h7kQwZCRx!Cut5Fhp?U#PteGrF@_=dWr%#{!0|IiN!*a{cv-4FD)(A z*3u#%AXu<(EHA(6CD2h$_E&dvF%uPy8tsfcJUpD8>nkcN9=#_sdMoVuV|KACWNl?- z_U&_I1ea+yhG_LMmig!Lab7Mi-`@|Not>eG!E1r;00|iln^M%R!ygsl3uQGw_zFB% z=-188&hEBR^E{hmgPnVP?B@1zZ78$JX-$JE!TsR-#Q1o7&~#K()cR7<(9n?2<)3+0 z-Ks!=cmOJ@;^)rWQ?<}QYJU+E6N|g;<`fm7A#$p##Vv+1;_s*`;xh^eG}hL}4y<-+ zy1KfuvX)m>RY8Ft&KCC>FHz^3>+iP$hzSLZ@Xx?~8-}U4fdYa=#W!_zuyqGMeKKtG zzIWEY^!@u0REZR09Y1JjnHd?dz6bmJ@7}%h^zu45H~@I{%9tfGAtSAv0&adKi}@5D1zIo$RxcWMP(SYBQp zz`ClcSLb56;o(@#Y6+6L?XF)KnD_TrmBg~e;r1eYlai7uD|Y~kn&_u45w(dpOp3bi zn^{;`xNUE2Y%DFkezEfqMM6tkTP}*s{Mj?sQmavH0xTILqfBYq4rCV>7w%M1lXir? z{ZF7-*JXnm8eX`$9Ybl)Eq(kPKP=Ef1go~PZw9Yhhc<e|*>`w5PzK9`f zR+Pq!k9!W-o?Iwyc&)1x;ZLogpa5WyM21l{>kPJGr9mUmELdyUdL`xM$XESxukp{( z&exflhluvRSh`W^+qZ8c+M!06+sdr#&}8JCL616KjU_ZTHU@kO@0-F(9*7DH>o^#f z#l3Ok27JS%r6stJh<4yWi-X#N%YM&H?B`qW*?$vFxrAK+Kf5?PZ=Z6)5@Dku6A_!k zZv+&dFB`Sw*aWu<)(y^lT;TKP&r7SS8s5B_d*|*1-8rB`(0N_W+uQrmqpX&e7L%c{ zTX)1fj>*d(DE1ppKP?Q!YV_=)Ch1J{v(@-fkJRD=b~8u~>e2To{+;1dS4o zo5MIV;+^Noe#_6+fL;xXz+5XYuk(DlXcRsGI@a3Si9l(U854(1U(@Q~b!b0BM#l+! ziA1`rgJCnbH_ldnGX{$C=VI0J@^W>V0_$+Op&PgP3knN!DVdp>J5FBGsU-Y>8l+qM z?=@5_h~^a#xWmXel*%K=)i#1}Bl_$54IH;JLP={p{Sb-TXYU!NB{X7x+{g52j+HL} zaPV`kK*$BB%Jy4;KQp=V$KF8m;sn}LrHDC!I7H?Jq_Ogj7lDggLqH*O_= zugzmS!)s(TcYJ&-6@oMI2N9>6EOq7775Qw0lpuC@M5tU|Fm@V!O z14DlSfwVKa(DA|bs@L6|T;a8~wF}JNno5I0u0IT6lLITI5Ov!Hq6839P7YlZC$50m zlT%rZ5-25MGPBA`LrO&`3vGh`g>MqGT~Pa^(W|t@g-2Ri^v4GZ&!566vYD!Z*TTZW z(voRmn^>iDqhh#Js{h{tUq$OxP*Z~&>TD~tNnD1c`bJWoopx)AgGk{8Zf9@r9dU80 zd$9Koz7MAFHTp+6@~36;J=2kw$H-J^bJ^07kU;t%$&Ww)r8Bs3dVCB}m+;XA9WCub zn%Y?o@dDqo$w*Qj*`~^xX&Z#EqibAAS=meIr$DYp697Jfu+dUcafJhQs|=@GTscoU zDWiLz)-l+wQTP1=grlNc@m$N_VH;QR9s4O^6jmy+jw|Li1qDlf=`%(tS`2%)h6=t z@|wtVlxs|-z&q)=!bc*KuTDt3WG=J^Mp|0a;jDY0EI8btlV(K6M6y3IBdv9gTE}Bi%Jg&)3PDsEg@&QD-)DwZ#prNmiiHNbX zC#`KqZx*GziBN+goSDgfJWnGPtenn&15xAg+YV@fh=@pvO7Z8m%ZoqEnnj|X$7(aA}o8Qr41JjreTPynk_wNEZCF4ptARkJVZ zYAdF6e~x|rytqlk$M=OWnzM`rK@&I@mrfuG)yGF@d}CNcg=Hi z^tqgS^?6Po-;lSntMc>n8}VS`mdI5b5e9%XdI;r}C-oB?n+_Rd$1j`>geS8s3R>7^ zSo{jh0R@wU*l6&r>FDTa=rF#C##DQkPJC3CZt9}u|MBBT*0sYh1x&*;+PAn?!2Za{ z$e@BQ7wTiY{Dbsu3LP=q=0jCw_h)wz`J$!5jQa!@S&QXYwD`sSoeNl{xfa*0jg9`9 zk8oN?LoBW^jkY#FKmX;~n)q`pI?T5pP^-L{IXQ{nuVJR=eBSW41(pLS=)g@hgXg8- zB`(>or$ZT~u-z*wZ%{pz09nipKmx#yoUh`$CudM?Z3p4@2K8|yF^n9ixT*?A zWdanq$F!bqVMI_gGIDI$+1R#scA_anj(Vwla(Qw(rH@x0s)jYLgj0v`{L!w-gIIvYcd$UR42Pa+3r#jU7v(BLFzkdB9z*>dd zIyyQE?9#B=8M^I{P&_(d6;1#fKzoA5keZUhX3)UK$@#Oo?V=gr1kiY38C#p1OazMR z>Z5@7JUu<38;Rb#7li*nSsDHdM1+=(4!)`P)?{UOcehLg(O{;Ct~G0GYpa9%5ioxc z{(fw1xC4v?;G8aC4_c+e##mv?iyaM3&43+AcC72ySy@<$9;7p}e1ak>U5A7K2?x3| zXoMNDu^pzhfcNO@Y3D{pYD!Av<>V~jiq|FmBm&UPEi9axW(U&+@&&{{9e$!e=@}d2 z78E>&uk5dil53+0tY7eXp2<@{Z`ZF!z#4d-9+tj)cLr+?e3zhwF9XO*YU(r~wKs3x zq^G6D#l;={oTnlr{B`>489-ZrzCJ#e(0<{zwa<010$#JW1`G+{zpT6*N(*Sxz*9Ch zH_Iw3--9X&NGCEf^41-dGoW|kXS)G{(r1-91rJ z==!dJ(t-77WRRl;EPnsa7~k%5ak7@gZUEp&QAz3Z=g+XuN6;hU3O)`E5pmCf5L5WE zIzK-?J-r2#dR~1iSO@_+u+O>cu*3-=m$4q7m|clY?X~sx7E^IG zxlL|2o&ZuG9^Uh%?l4Z{R#A_mZ2+}@e*X+%9Yj5Uq@lqLojg|_?5(U$pbtX5QB_qPo}MPnzW{YdAprTsoAp*re`w+Tk?SNf`J=Dv>VOCD z{`9#N;paaBasWsjsv4EJcXz{;j)!Fw)b)V&wHJe6Bhi-~!3-`EY{vM~>S|8s%%o`e zG60DqysPz!bXq-6K;0dg=lz)X{yl{Rt^((&-Aug|a4HP}SF&eeZ|X0g3Fq-8BqoxQ zkc94h#qb9e2>!W}mnaZiSh5nWvcyEfFlhj|K=-#Lzr)8{T3g4-7wqplvLp%vuI>1O zhlk=0a4AT#wx*_CuP4grFH!GAR`J^#571=U&hQj#YiiOzG(M)Fmuulp)zT-@#G11p z66HHMIs5ogk)53#fIwl2f4`ZIj?ODGKE?IB4`gL!pFJzrXieiNCDfmTW>C6-$zQr0 zFg2Noj}1^fNFWAm2>|>HH)P!OV_Uqptyfv|W*^tAZ44^(>nf?J48g(QTw7}dteBCJ z0h<_Dr_| z2CL7E&IfWfT$2g7xVWD8NJvS&PWB6-oHRGT1Yh9&MM0$R{jk2HAyvld{1lAq); zE|U8AnQKWbb0yUG)0t5;Nbgga+*M2U7{G^LjEj3HVdmk9j98#MyJ;-uY38}Ty_v8a zz=v{7gA^w$V`Qv3uh**=BwZ26@Q)owcTf=M`_C&!_ZL^KW5spSfVH(XP@yU40wN-= zpSlNDhk%zv4%JYl7r5EU?fQ$)XC#YtLPS4CVoAt-JY)vwc*->>5 z4_AYA(X}Y*eMV9w&#eM4V4+~5pktGa%+9`Z+cOms>tLE8_g-=8)>XRXBBmuaPS?Owy|`R8E!n?*xs&ya`@W( zI>MwUoDgUs>`gGiz@Ji5R0N?pO~|PTKYfeSp=lPVA`ed;RAHuam0BF_f`7h1=})I7 zLlBanPg)F9Mv6*4o~1zhi39iyYNfM_%P2wZ2Y0w~;Hc1T7B<8$PgDg21PXcYVAe7w zZiYT8Zv1fKZOtJ>H9tN%30Mk?K_!K&qqo-rq=J3b*aQg>RFad4gCqfX+k*rG_S|mr z&SOGQD9a@)Iuh-!fE*s3Y`ZKA=uNW}D#FT0MKFY3g*23MdB} zz@f*CqC|xh;YQq?pI(po<{!8%Trphxp~25xN#2QY@$!NwL!gZJ>`EZHl0{`l#>U9R zz29l1#RuxeE4oNme>Pb56z5CMh_;71Eqx0lQIPcs;^i=*mb4GYXq0LJ#7q5nRD)$Y zc&tq`xp2>h2!vf=nPS*J<(6;(S*2Q3vPahWnA7%8WdMf_fWyOtE%+!g&S16Hi-7J%X}$m0rBy7p-Y* zgmSx)D%+-VdMp@9l`(h8x-oZeW76At>>GYdcG~s6=f(6iG>K={2rDhdiC z0|TW(4J+O>sQsS)zK=g_Z*K!G`n+)n<*ADg7i3Td1_oFVAT^+BTUlAb(JT180e_B- zM#sZ*OCkoegU-?1f&zL`QP1vBJh0hd&jEEO$q&|}Yxj9P{&{R{|Eql)xI@4K_fO8= zJo^H5ci_$u0B9g_5)uIsd`j?vKq^y}zdHy54S~xBBW9+~A{x45NZ0gx4}rs0a&Rbb zd~*nX)=J+)*f7{bJLwB26E06JmO&}$ZA*)BlOt-GEMt5$xsX2|SKfJ0zuA>{527Mi zXFxK*g>wJ>BQq*$u-RoR#{|9%I8e^Q6h&rFpYES{va?qL!!j{YE@Hx$u18ai&eL)N2N>@h;RgnTYMH8tbI!opfx#j=NcxrVZRE>aT`pg|PF zDFY~iA_1WCp@G4)%@G`#5q}c9PD>RwH)WPh4AHnyT7HuP;qvC@hxG&vQ+XQi-S#BK z){cCe2%5|Wf-54_1j%tCdcdg^cV5Sh=mAL>B;>2G!7TJ!Sey<%LtuCys;sWA?(&L@ z&wu-tC7Vx!(fPDaNl6Ln6@YgteEiO}13Z-J2HUToY}BH*m(&3w=rL_-X+cLpfhYv{ zE49ENc-=fax`^;l`a!v@G4azQ1Vjg?9S{{Riu=WxP@*Di9e`^PC!nD`iI0l|o`czA zX=}^J$M@d-ph$~na&mHWV*?chr9A@*B{~i{0`bo0l2QVfC?YXCySl21l7PVZ=ePGI zB}|C>E-rlj)Ua%@oB-i5{C7`00S7_uLV-JbUs$MJq$YjEIPcSyNpt zEeuk_3Vjl6S-1#1;}ZDuA#e7`gB?GAbxpvixIl|$93^k37O&_3>B0uM z3^9!p$fN`{|J`y`o__3d^wcSfQPlEIPIdYDPt45dMZL~X-?X%dH1BJT3mS4H_n5+$ zK}AQGLIn(TMfQoyP2Xa+2-TXTYsJwODAI-(_;$R|0S#eFgTyo@-b)ub+!e^LtWQvwLJDPdHHaD`eQ;=aSPnO^#}t%EWwm$X<62Pv6h0)Fp+vqs z_PnUK`uub_E9zq4%a<=8_-Y&D)W%-&2?px}nX~wm;+ORZP%f;usdhX{MuO+>IaiSv z+sAE}f5f4|$jQkabBaD^&~Z~Q5Y8(pv26E60(BO{pZwlSf^V*V@;69o_x|#2(u~YZ z0lQfyz3xw+l+{Q;KDV2F3)n6Xxn$vu{v;EAM2S@43k4JjpjcO)iNF7X)uTt0h@AR* zf~;?#uV0-1ag+}ooep~XBri^B=K@xB-t!=;;0I@^>-9IR#wG&OMQp^<|);D0#> zcIXJ3#CRo~1~4$BR6*PrrJb_t{G6djpGwe$r-)4={kpD3DJ(pSJ&}CQkZtZUhd$@D zW6nyVm%l*5J{Sr6`?GU%s65IUf_qT!cZ2aii9h{B;_SBwbQuYOjV7gW?zWi_9ucAB z;_?Pu0FVRYIYJHB{<_1xV-hf-7g8$Xif6f6 z4xJVL>f{0K8WkJm2M@AE-Ql4nW|BE{ zZlHIF`_G@(0#73Ebftp)hRE|JX(*)Nhdz6@b$LF2x%2J)AJ}iO+aUR&;+{yj=C-0< z^JiK5NBajV%&Em2O8s+pd;Av}-EJy34I=fwQnPE9cTB2y%&5)M(;~zlo}#pu(kly= z56G^bm*>CpHXsFo@P!`LAk#ERMG!a8~!>nLVYT`UZ4;UduerT&BE5! zNJAqoCWgyyR`6^L?89AXfKVqJ8X6G3|CX8A`XdpDT1`?KE9hBB2ueyyxQ}jT@mB>0 zZZraMJg?C~*Px|V7t?PhF5X55l8C4qmzbD098~TJx}+;vA35WBker+7D@%KOXc@5V zhEVN68z0F|;YN*|9ocY3_)h6MnMcvCF2P>3uy8#;b%DZm=T7leU=9!#1fYP%W(wG6 zdEzhq+|FZ~I!EnTpPR#*k~9esc&CJluB@z_Fh(A%@LZ9WXxle-h3)JyTF;M@ zSCbFEZ{n`Ys|%^rM$|@Oo&RQ=&7sIlvY6F9)}GHskv)DF1aI?^C5+YBeXk(K&jVU2+8546A-2^0fa(sMcwt~^|%m4Kfd9-M_vyZQROrSiL zjkzDiw-+y7fPNr(slD6C8JtdFh0~_6Cr``YHtvhJ#kQ@HXF9-==y~Yd)Ql{2hXEjP zRcR>_!t>%xAH13eArU)^wYj;A(629!m#+ksb^_r{Z$R>Jd^ef${79vP$5#Kz{zcLD7Xf)e*$9+uS9sLAtVpMZL;&qj1wih&aX2}pDw zD#$@}N-I3Aud9PMQi#gpqIly=?luOMyaMVW>asZwdugF1K`rth`MCdTbAoXBSI|{9 zH)mimu(V`4HO(bA4S;Ugpbs(~5!|^ay*M50>7dhtP`k3Wc1Pg>!BqVeX9zQEl`pRx z@XzzZ&#K@wLNefWeLeQgn>R5q`aXR^Ild-q^A58x^hGxGU(jo#t$1eu>2nyq&B@KB zd3Yyy7cd_bVoR*aN;C)!f!w*@v6WUsWCx42XAJVhz#M|Z}y?L*GZ7PL#RZr5$jFpofrrHJ46|b zgwBEZ$Y`|v`Xq(YJxRifeWQai8#qq=FZ+|wPbPbT1Kl9yre|Zbf#3&dI9xrwyFhLx%q!RJ?BO=Y)$ruxFG1IdRkFo)R#y{aVi1>!Sy^J>Xv7( zs#XyA>tN650l$Cvu=CDqxJ5v*x|zk#w6vwAyZ_06oKlv554 z4xlf9xrP=E;E02BPmSLmF(eP6=F8x~dvm4x(DLE0-LEQs4%VRe0RsbG~H^bO4eG5k%}P zY;4^aB#~}^ezg1?%DQKur}x&R;~K;SH8np2g@b*M#{byP?g!|y2gBmMy}cnpK~YrV zlwSF_-?SALu7b2vA%fUVnE&>%n&*NY7Kh{nCEUsAjD6AG?Y9a zp?qv>``&p&14UvII3q}i@S9gx38|w(;MqEOq-O_9x)5E3F+k&_ECK~s3Ta&Q6!jq5uw z1K331iU(HZn=T%lKuB7mEsg=R6wnj;ALIQn_CWf&g5qLez6O&syZPOEJsTJ_1!$2K zDe37{f)0=?8@1$pyMvyk5f>=_@{s8w>hVf`X{kaDrUIIlPKJ^s^BmPB_4J(IjSd|9 z}>K@sTkxKMYmo=W|CbA*nMuHs)_{1!Mq1e2@g` z%%sG484T$ffK`BR90W5!2RO48+nJn${!h)zdkB^a{C;TTP$ac>2Va51#c9-hxK-=p z>gq#V^9L`m?j^RV1}z1OgqY7oMP455Ti?{2JFqkmxbor&^3Ca8pZuYU@O*r$Fo$lrD`*u-R!o>P0UEzS# z2s+*$eN6vW@GY)I z%CEs10!hXw6!*O)8OSpV^dwHzbWkAxD_tjI+uhy0FaCK-VBbwt1^(zZ~L&x zv9O}8^kq99NGrR#RQ}Wu$KNc{F+*+n>2ti>@%8I%MFSlje&{bCqO4vY74c{|=QQMK z%**Ti^vMPoIhGE{#Fz-RC88v^lIm(I2-Yv$QLlRRUf8TJ2GXkA{tl{;sQU#y)uk@d zE!Kdz5cDUIuye}Gzjp0No_Z)_JqI9#K&ad~>Zl=*VT@9P<8((QgXwsbbd{FhW)~fi zJWx1l3GE*&osJ=Z86rJ}iMIc{vX+5G>Of2E2hp9@Rc!9sXnUU<2Y!>oapR$yI@ zo)q)w%PB8$Iy#7{fyIX+!C~BrjR46ZSYw8;Ky{Y+DrycoA++~*Wtg`ZfdWHLSaYw; z0i+k*3cW=rf-o467Y2}-E*?x4)ejsX)d7_T>7zwo_8n7R#8o^U})cz+H?u9YuO&w%NrQ9c&y-4@gF1K|C?TV2`QDSY3pat0D2MP zYu!zt98>(lBrH5LFc2Y`yFJ&^2+=lgXEL8HK?)jy|H#?{*!iHZR*{=vd2hS*>Jp)W zot~WRxxFJ~)gd4v8cO3+V&1_TyTizUTeuwyh&nSSX5styA~$V8W)2Q-NLoS+7xk6` zeH@5Gphskpoeq8X4d2Q$d^yK`&dV4i`0{WarYYp<3$N{f4)(kF;Px(#5x`M|{c zvrOlcKYrqjs4xKopsNw0*!=uSzgi{drT?D2A1f>40>8~a{4`{YCnYDJt?WGHnk&E? zVwS0%f(CY3Ut0^`7!45^5+Wif2qKt-HFIs=QOy|`apNN+zP(o(+;!}aGT*DQ!2LIR z=N79{jE;(Wmn0H21Qdx^l=raca{f^&WKrL@wnWT75_DWbg5?az83>CYBzuba?999Yms-qW0g(l%{8l(2{hNJ9SD-quoA z2UBX~^XC@FRmJ~r#kb+t%Khg?^i9~sbb{nVCkJcbhyj^_EaZu&Se9&9N&<>Mbw@{s z!{{miEwjvo&>KpK`@?cYIr5IrfD+Ij_1}2Lm;jKlnxHfX!x($Wn$SFk#eS-Z=|taBAf$38KqsObKtfrX82RyPe=3k28TYCqgSU4Ap8z)sUz5Oly?2=Mp+G1o#&NQjCksj8C2rDta!1b+)Kwgj$N_RozC zOmSzJC`--GR%MK*ASb^#J5bdd2K5JmhXVrxqiMZSx9_|aX8Yd~*SF3&qpmX6V2uVB zg9-)^6l(LkcZM)MQ5iD$G&v~=+GLe+n;1QP;P4t9#%s7hC|&v5<}jkLz7APE8jSt3 z6DO8LkhcN;fsG|4ini$3w2p-hUNDq>V7gPdDxdzB;B$uw7bv?5^kbZDXV-Sxm-iqe z@*$EG%5C@Z-mZY(EmW5d2U1Eo2pftB3q$qp{0veyq$QX2*A=@9=uMwI0X(u;3)WYd zbmwKii^AhDYWZO~#&B9P0AIP^Bsz{t>EZgXlH^rL#*_CiSxNZwkf&{Od*l%tawV0NOYc*Ua2PvaiD?>n+DtuP6VjG|C2?co4F2 zpeRp&`}T?m?~F5h7#ip-_|~JZ*qxCCp&GzMY75Y|SAe?NMQms4VPoW{0zwDjVbO9O z;uwb?0s{g9^7HA0bw2d;AR*K`AOr)|BY3g`{*dVoB`EEntwWG*FW|DYDT!v_%+LdY+V||A?5Alme`_L2u$Q*b< zkg-zB&?^*HR%&5Rc7nVGe98bsR?nD{Jv9Dn~XfZfecRB$6zb3f z^Tg-kARBftfP~qa#{rFvt{}p`dBcZ*u44uPDz{BA`ykkh@x5*nG~btQZcIwV*vS%H z212YsafM{jY>`CV=iM>q|M+aeag)^2{H}x3>wh>2an~Vi5dpgsd1a5BS~J5TMgz zQt>Zt)JgvPKsAFxv9`KOA?B&ab(O<`^{gx_^B*l?qZg9goT8=Lm&vctetc7hiIkM| zih8T46ls(?KBGr>R8d4Ac9BRFVMYW&&B<8}@sH8n*D#_)*6H!XYGa-AdTX3hY*Df`i zyv3G@ql1`~pS)T>7G69PFIkjIv6Em%TpWJNHM-o~+{ZxTxwV66NsqpIzu&;ey8-e8SObM0aUbd@khF!ig`4?6F5S&9 zses(^g4zIcZXgRo>zR5V|AD zaj{9L$}mblkAmq{Aa=mq*b+F5M_^21dTOemsOa_d_SJ|VI{Kf}Q+{6F1$QY8i8ZX} z(06~fT|Tnr#|nB4sxf%;keUQ}C+Da)zg4S{VB?Wj&r zT&Fa94TWP=8Wp`9(GiZ$R_!hFQbm(3=oGBQ07CL7hv z9mWMh*9oTBw&XMZbg%8HyFNZXmOF0;QU09;P(vV2v#cE)YGCh$g<)oJ=Kmw33emTL zHVCT<*qIC_9d6&=+S!S!Gl3Yug9lM+wEoo5l=mLKu})Ox#?Xh!72OeTsI*UEhMdDl z12k7%-Q?1z8BA5z8wLu98ev9-n;Q~c_{v(lUlw&q;$vWqNO_)OoFDp0H7UpAZ`YvQ z!%xL6v^0Z3TVC7&ih78hL2v+p_|If8<_AJFEMa2A{aiJ*FDu`_FR{IW36kTOgnqNm z+X$>9{ZI{-SOq$Ww1C!;TT3f0ZVWcaF48UUm6|(QwK6)pdKwiad12uc11PSAyv$m@5Ox)RO_bs+A~C$H6VAcl|vZWc5KE zfboWJpz?t01X&QlpDFje&ji`ocfjZL4}(Jwuw0O;D*sCQc!LcQdhhe*9P|GcTw$Ul zwX7!^PI?PAqp6_3WG>uU*ir2sgbQr~_TcCbUEJ@cFY5t|keI`eDFMgTD#2=`8%Jq` zE*3MPI2H_En0Pb6%F0RXhR6v@CaB4N9JY9l9LMvBm`%g&DFh;t@*dem;PKfB&xNfn z8#_DH%JN{dps&7sVFgaj?*bU<}YjEj2>xjVqMm{t9~U9R(9 zy88MjK=$S#Qv&aTYasG^ewT;GX|h6}TbuQk;wWmDxf{73bssb{h(EZ43y_kM0#e1X zbo;~v9|s3cz7dqDD<_BVSrWv`nww8S=p21>2s905b^sKEUd7GFXH;*g4vk66ehE{l zAe~OLiA+XX8e{_u2E|{cdJvaxg*-o;2S~&)+__^9v7^=zRx&`azKez#bMz3^?Tev- z%?OC&nT<`i{V;4;VId*R)|Keo)&e_*ve+dGW1T2)%t6uxH4LPChtoqV=#7DaD96>X z5}zP#r>bfX$^j%KTAbEijn7CM^fL2*fKIECuU?Vc&G_*yfTAa*nVEB-{zHP~&9h`^ z-_R0@i!Gq$_+u?eoKUSS|J5wPY{FH9b1n|JGrBi_)DkA3++MW#QlaOKT5|EgaLLwk zK|@eB&~7NgZ{wK*`^~<=+}{tG0HX$&UXj2BWh*}33GOHy768M2?3mHLtel+5W~nfj z4BGsFQIl3eVT4w)LeTIUR1t`{Q)7&QLGkF(H_!krwytdpx5cGs2?MTl_Az6Lg*+jQ zeffZo!({X-=NzaCfOGgv$OctZbhkYYc%3ouSKI~Ue?bcg6gSb)KCO4dEQR<&y#7ym z;Fg-r5G(3e=5sSm%?HI;E7azNKiKx`C=*$4sz&L$em|NoBp%kE%KKP?*9)Q&P#^Dc zMpHwr11&D77huF(z2@K-Y`pmWQ6D-dxY2Op*JY4STb(d5UM5qX9^LS#hVTSdtHCG* zgq{EZwYeY2a@9g=6o3GH;(G?d6@c`x6Tcsv!0ZU*R<*PfFSVJksfVT{q+i@l1zoG@ z#ZDS|dMi^z>rcaL{@;QEoiEE~;Atg+!NINYaZ*xGz*vXZT(g?7jwSC*Dvt%6mIaTm z;NsZudqBF~VIcewJ%r<-s{#)s$FTs&<8@MAd5%NNNSJFZ_ubg%+y!I?7|HmRgz%be zLP2@2q^hiVii+WXWt7Rt6smej6L=dVjHDmzYL5y$!5^!pDyd<)7LjJ~J{{~V`r2bPz{dd?jM|I?VJbv!`q#ih?*062B^Mn18XJR4h9KL(j*L;a!U;XlS73aFbw1eu8Md z6!a)jj#5sfOSFY~llCGc#;?k<7sC7L@8{da|GYn98}d0CaS>Y=Js&@;6lly+7I9x0 zrn_uDtELeNDM&2Lb-6MtCFeWgX)1cJ&B9(2YGZID8>p*4e)_ZlY(WKgaH#&tpn7Q3 z{0JUcZT0wF)mW$|W=cVJCCbLs+gE6uMa%_`ZMgjI&$X`}h@?_iZ!+Wn(SVy9(tN6% zm_gS^kCQifA zNT1i+mhq1;R^bU@@zx_I=}o(o^Jgu}qGsmizZx*+LSx!s%sZxDc z3j;oH1X?LBb{x7E1`UpBQNX0E`B=rXGq{Ry`ET_gI5RA$xyO3-vDq<8up;IIJ zo0NUqSa`g7KcQaE)W_LaJg$ie!`tJa1<9~!!kIT z!2AFlTR?9<$BXEZHvdF2&A3y$7U9`LE1(KOxEg@~BL^UXu8z)CTHwKh%DTE+Rcmo( z;r|(DwV!^Ciohix(9+Nto0%blaSBsYS_K47h!f0w!8Zl!076FxVapU$y8kJbuJe*g z^}+ni=*A&Du?e_0qmU3>11~>6;EgNp1j9y6Netr@#feeXr7%~Fe6jZ+R zvom>mjfW3sfG%%1zf@Dh4d?(LT^e@`OAF)-#%aJ;>^B?A z78i#c`s>$YFq{rV(v6LXf`VPA1G;|gk9e}pxFsZENk;Ur%?Jv#IJ4k43Rvtm^wa1T z_Z6)P479Y){~dSry)#tH&WV-9!KvU?n&sa|e0dx|by$9!?BeXt6uOyz+BrmbF{fg?D*Q`?g25kI_^Pby8^ED3{Afwg4>5We(Y>_kP}hbIfbb4~2+ z0h^T<6+MLrN%pWs-89%px9^%ttE=NRFuyglv$d^+#PR+6?LNHLRzg=A*aDoExX;tv6}!($LAh9$ZF=VKlg?gqzzUt3=fqQ1lV=`V;Vj06MBN(Z%zx98uw zpnuiMo*R&LFnfD?dPFHX|EDtB1Wz|Xk$^`ycza(>cc`h2Kq-Wn6+9VkVuBneq{nn) zYl}+QIrJ*k3-kLNHt$!6C}|ZWv1{I3#mF|-8M{lMun^bH zm%t(JkFbR)H7Pl{t1&`}7$CWtrQ@(Iq3yt<1(s^z;WKgcs0>Z2)+I{e=tAW5Wt(4*b&lJ)$c_-|3vJlqL;AVS7ExmS$}QhO!ExbDkTKD%`1EElUJnYau_ zJ}q=p2|=IY@i~&WwzuE8{(RIS4P#_Lg7@4+-Gm=^1_Cx-s!$s%Fne++8*j@fDyDj9 zd@wZJlHo!+Qy%|Sh}IJ2SU~XiF?pHaf59!Hrh~CX03#kd6xA$wYs1lY~F!+l;M?R}W6$grwi|;7?oH+Je&k5kzE+ z4bW!S*F&@ZDw|*nE^howEPM^E0;1kh|4Nx&NvY?$F!=^$nWQazmK5!l>m1ly-mAAo6hP(Dd}%X|&lscqmQKLxEYR z56WtSOv79n1`kr|cp{b!3J{$ol5|>2Aq!K#mxh!PFXhUMn4DI(1pGu&Rm(7$GA}Or zj?X3Z2jCib9}EiY_cRq`F<~lnM9X>Q9uw~;copbg!to5$Vp}zvRZT(;F+wM(GnPg;mK>706w@B9yH)N9NERj(J96JYw0>;mcY!GUVv z|Jn6U0_um2%9#ivy%i6tM-^)J)`v^p3(=nsZsLkDS`p9{occ=FMdqOynd|OD#nRdO zRD=5ZXFWYTYt*tT){&WyX^gXoj7i<4-u%(Ou6uZgeRw2o{Y5ocyM8f&aK151czJr# z5PcXuda##Z_jzs~#Vd`o7%0%(7F)+CB5D zb+YY&`@+mt(n0*$59t2S54e5rcow=OIFUQ8 z|M)4H9pMh!r`A8ACdj=kD~6l_33B)D{d~N>_Vq3I?HJCHu?D5q6YZ<_kRZZLI6)=z zB@*Ks5djF~tz1Y*h?!y%nQHTB%`g(1Y>XE==COZ&DEJ=ej{xv5!Pyuy^Bf)tq69$z zA3oHlKoPU{l5Qi9D8KX$Lw%)|D=JD#xKdyaUCUnL{-TYH_+cng{+5M+H3u@FuaH(D zk6~`&&BWkfDoDKPiG>|n-^FmO3A0yEJy?yq1Z+8`lJt+!jCUmHFB!B4n$TWgY>+hc zShb#eDb!Kso5Mn{n z>xTi13K{I%M@8>$1Hz{x5VQl4Z0pJ9-aa?rd6>uZysyVSvg}GFDP2aGRt;(&{wQW@ zLI;c#=6K0R7uM_{bawB~fxZdofsq(&gI<6~G*ZLs!5-=q0pu`gbtPjBw4D6UrGP=NdaNR0G;p>UCq)@rza_9=zuWpH6 z$3_r5KDNO(G&@g$sgt_)4EZQfYUYBo&B{`RQ-&v*n}-J!E&LF^8%ir1Iy>3dzCJMv z-mobj&VBIl{wFOY`Hc1@QZEed5I4|!5 zU_q0IuB%i8brW%HR67t-Nx)l;wx8c_s1=hG={963(<8!9pP-(k{%pJ4ij@(M^1Cn7 z?Gt`oQa%zd{g1n>yF7GpjYO+Nd|xsrAa9%wYasVTH+TbB#lF3JCB((+h0uz_QJoNe zXy?andl zES1;cgt36C~e++ue6j&NapwG=~A6={a{pHqRdfPH=#GADs?L} z1d)FMwgmKg+@6cjtN@HhCPOL&cVw4_Vrv)Ib%49aba%*g>4PuV4P8W9u;D|+!m*Cmday6RcmeVu;t zaBb1HMN8vjlj9@W_eXlad=C;nWx~=~sb&p2d~={I)Ly^{aAcuVJ96ZRZ!ZRVATS=z z&O?KPH{b-0ilWVY$~95Lk+7WSqwKWk%$LKj%|lu~>@Ie=Q`v5GW@=zy&bUT7`^O*M zZ&n{$7ibnTXA&M>3)j#OLk)k7>^!ShHFj_&HVq6c!uHX`%4nklhzx2;2$kSRq6V6+ zcMUvrXuFXSPB%stmJN`(1qK38n4+SWy;aYO_qCnPgzN*qbM@+bN51VPBJ%R~AZyX? z6;5{$?$G!N!yqXkfxB|EbSBVV2iUV<>@P7dgvb|d$XgKYxI6H82?(4!ckVcgcIfPt zJ9kv4YWBC_ZbqTOsEJq)#PbI`p!#Z^%Xj>zNQwrtp|<5(KUWnQih@Eavw1A(4MO2CVNcR>k$7)ZfBee!OO7?y(pcmBYh>EWF7=i$Cs zz-f$^js_IbG4|EL9O{+_6EH^NG(q9RSL0GHo^RGQ>IPk|jC5T?`WMvIg}RwjDfg|8 zTU;u8G`y3!LEB2%d{YVK(%F$secwDXPHW68)E@qD%H^wT`PaOVuddha7N4CtUmm>J zbMTfY%a8RVFlFJLy;|P!^q1ejVEK64YpYw5E@?hSbr4OfI(C zvEr?GCYfV2eFb?7Vxn*c#_EaRT)SFtri3Rcio7 zPE1T>k&AG!f`8{sH1NEGvU4OPgo=Xta4;@gw<9+u#fVapOQvgW*t)i7itfYm1a-#q zJtlGo#EF;rL+o=h$5yKrK95+;j#wukg5aNOdj4o5UoI zG#Say9d(l-sprrC(ObK?^QI=CAl!VDGtN%D#%C77SMZhuy&diY3RP59vpgrtBCmwK>-`PlN1z4 zvItkhw*iWDzG*wzN&~E4G_B;qg{Ho~kdg{lCth3X-|MU|pZ;OcC+kpEe(~aimXeZssKC&81LX!L2Fiqwi&La}aFQF9lgAp1;1Q@n8S(lCFV)0mKd9tK;p` zdQY&dl7|*jK~i!ICK2*x_zC_fOwnRyWu@w>1|&1d2=W-aZ+m9AGP=&}L;l@4dWNEc zEZ0G1%l7R_kIc+dB2~&+YLV&&9RMbL1jqDQ>=kyq)YhqW?}T$@7gBS$x!n&Q)ap*E zK3KQUCWTi~R7z@_Cqtsa(-$vV_Ld>cGDh`caWNe+{Pr!w!PmMvyVW6^1CDDpalJcq zoSAP~Mw}z-0q%>G@BWS!R&hxoc5b}%9y$dM^N3!1{0>2^$ZpDLHt$bB5sZ(I2PLwL0ew4AGF)Vi)o+fxKMxlYmAQUh869!^FdUtl0pnaV zi{$&o*HU$LJjdR3T02XhUH@L((7mLj*f7pHf}X1{7QwvKjI_Z)olp-$oBi^ZhZbdW z#~$VLV207Z+OR*F{N_=Bub&@cj916_G0?OHpaPm*uCKj|8uwqg%d=R##82!I%B(xY9zK&?0}03Td~^ zv+qSC&_V!p1swq!d%ePyva&;90eyQRR|CKY4F0bDL4Y)&6ZaWjA$00C268yeU+&8T zIb8}4T$o)WY+gZLz8w~BctyXAkB3YSMxwKVz0xn+8eJBGeNg7`lp=p%i-AG@D!*xZ z(bcg}EcgW@tgNl!>OYg6&9p2E)&k^8fqkxlQv zLG6L*7kVy5($ArIA**Y*y5uRm)j9xbQ-bhqdLqGq0=g2f z2>jZMmKKO>+5Fb*0cr)2T3$!Ir=_E#e0r;`>~pxzK!%hw(XNVhwnZ6-H-FEbyldAE z0&aQvihKOyN3ikV!xqu=S0Z)%#fzSvnf{)W=L5|vYPC@N1lLu5=0yn zAIvq;f&?`n5bvtVHfg}pCZQ_(SyvYfxL`sd`^utx;g%Evk5oCa_TQ9$$oOQgqw18#0E=qXfW ze<|s9`sp_!&KWl^$a^>jc;g)Ihx8x<2r%{~q4T#+{-p)DG>#HEeLT}h4C-2xN&u!e zkbEzrr6ovAqHRXKfLM#=2qd;ySvE+aeO(O;E(-Wmh`Lg9jGjn*BOkyqZ*MBp3O1F< zzk8SO>V`f;H#5Z;;|{=^sJXNRjNVNyeh?5LHW#8yfJeY&jl`&yA4rbyjyaei)bvvm zz}v%jy*c)Cg^Hk>Z(Uaxj8TzfSz*9Ou&}}M7L|iK4MA)eRLcX-$j579AtBfTe#a>u z1dQ%2V_{(#+G}ch62*f&QSUdEYz(OfkebxAw66aC6O+5q$2dC|oIKf%Y@A;QEvAQc zCTFc29ON>|zeJp6d)ok`^#x`QtW0j!D?k8+KR`ev+emWF&dn9ZTTbUz>2*IkhxR!% zB&5NuPT_dt5Q63q(J%d6H=*F6B$ouA+sX`!Z`}7USn(CfC7G34#uR!O@L6sw5+>8% zWGT2%Rm_`d8BYm)7Zs)U)`=J1WaOVF0R-PxdZ$(A9=}}rgHp0ww_^rEww}XqB*r-; z2XjN5kW=>1Norgb!toLxqMHaga%5YPMP+t+IvoKiwB!Iay8|54WSNhxE0w;w2X9%9 zudw$A%Z}qKk@n7kS8*LjrMWR&<6+~COEeH=jW4v=ow+TFaAvgYSX?}&l7se=C1|I$ zH7x-F)8Ht?fapnf7}BWZme)q{qvE~?VZAe$=kQ#rsduj@BH2OFTA zQnoi#n!tbRgJR?3@l?EJ)s0wBHLujn4<}R6r@+i9K%`&5BTn5%kpKk}lUHQ%0b#2M z6)o~VoOJv0bZ%y54`i)UtPJ({x9(tUev)P+hCEN)R&rA>aL>WjFw|x4dES1kD>ji( z)O8jcl?h24pe&0jv6Mb(q53G{_rqtUnp{Dquxb{ zV(Fxkqb7;hulv9>k7yuJ`haOrNQ2Om2T^6!y}h+F&v$bsUbuSI#KvY0?P-%MMplTI zE?oXu8+}q>6DKkF{(XE9?kvY4l zTvM2x@q&fawF10B2qHJCTBC%u2TBh_jKY{RhKqtO^ERLRF=j9E4_lUc~ zpKhzWv#5@-nVZL4!xn?{7JeC3!WX#=T7?76%`L7T;>%YEkc?t5bk_knk`c0dWs94a z|0PO7UvI_>x<<2sHGw3`e2h-?>0V>? z`?5yU?>g_*Z%%CbRg#h^r@A}b$pu;gB_zr0I~yZsjW2KVT2Dr)23P{2nW1PGZUu~a z$TmHqkqdb~PG3OlyB!_BjE>S1m=EEDWIoV_U;`Qg{VD21d8~&LZd(n(lw1Z(!pK!v z!@&VduLWA4tgQGwA|-~dJ?ak|lRvR{tiWZP@fk<_g9KB6Cf*;i2Ea1meM4cbJwF%? zIS=MtD$!+oBVx7^A>f2gV5wB5En}=+%ra4LmDNONPft;9(vk$`5DAF~-QCmaJG7A1 z7so=Ic??N#$q5Pbm_mSjlDmfP>RI&UV|9l*uCg2R%T;>Lc@!yui0tTdW&Cc=xh*^M{m+pRup zgf-$hp(UW#8VFzd1M`2W)eoGc!7qDp+&S+hh+M6s%-WszCXh4)QyJL93}}mAP{r;1 zPd$FuW)yuDb@d+@yul4Ohvv4D zkEMl$WdJ%cpF@s=0j#zGE=i|=(`{$EJCx^dRR;mFLY-uqnS(iC~44Hbgk%N#z!PE{ZmGEqg%_H*acj=rNEJDV&;3@I?QM_3hZ|M z>5M2zz9xBT@>4=HKv;*tYNdx*qwu+`53&_db0$0v12~UVoZjAbT+A?=Iu+W=9^Ah_ z`tsv1Uk<)tT#9O7i^+Ru=i5qEBIEOf(sS@YFhOJ+pFVTO!_~FWLVujDV5_+o&bb$T zeGvRn4-BO9fQP_i@HFWr&_GP`5qao%;qa~xKladKe1%#$P<*6rA<@bHHXVVv0NJ6o zvT-X*$jv`~{w!Sz-zL6|U6@obT!;FJaH84S!1?!I&@Cd55YK+k5B>X{b*QG1+XugWhdauE$zP>`XFfR!e-B+a?BkY}E42)e=Kk_TtiK-o zVET<%1A?%#O6Cj0s`#u?q@mH0($F|3SyoikjAkEV8R@OX9%yV$&Z*rjDq2o}gFggu zt@PUAfoBE@kDg%t$^cF~96JcNh};eG3&BCl!e@(J)+kO(v#eb4vdM674J9jMw7{N) zFG0O%Sh9jPCf9;pJo<&9;kJr)clPP-NXf7Wl4QdxX^}$}aT~5)ED3e@#wZQCA7L#p zk)Yq|y|RmL!lPLMz|nt)aX!KJ3~?XknG^gQy^z|7G2(XxMT-7J&Cf5Oa`4gc$l19; zU58?VI5x=lbhB~$V@5W`t{)@|oIEKppi#;dLMOEbQ&Navox&B&Qz+EH2kUsPHTr zOuT;&l~}adC%i)(Ag(uU@q4LmWEq)4id>(easye}pa^R#=ui}g_9Ys)`1qvkOIg3LA#FoP7QiTdp+Ebwzg-WxdKRr zHk|q)-ND3sboS-G8@pj<1x&yupq84OE4Xqc9Exko+k?-bDk20q(fNhyn=r)mAjBVI z4zTKy`Kv$%;B)mqp&^K{$Lt?%mW;2YLatLNF1S262QWx{8XMDLE2d8R_2$`|l(IqJ zyP`}306>|A9}cu=p~rcbu^|y@7E7rFCURGp;d+s;W~Q2kCEcaut);7lGpUuYB~T>e zLQs;f@fvBCJlTA%eTK`Du04Nle3=O+2F#5EZ{DoWBm+&sp(Mu1=~82$3i zvuBpOcfWf1lBw3pJPQ$g@I_;u1OojFJhRarY7dax;w*e>8N*fl!*x^ib^&dej*h3K z01<<&R_lshDXp}tNG2K6LW@|5o3J#kTv>VfG^T)^Rr}$6_F54`t!7B205c-T#oWVh z#mZ0ZOs*8-0(G{pD5B+vEoBUeYA7GkU!l91?66{|sZ_%BmI*z{QXZ>|tV&k&C}Zmy z>&|BdD2yt;k8S|A3PUXSo_;NOV}MOyjAu=>CV35wjBxSseb-+4rmcUqg_?)c^($L` z83B!IKN6p&XEN=nWTNU1VM1d`yZ@qtgBh6vBv! zs4lu!RAeZ|dEmtjnt>bb3g_-ZiUt`g{sg2akVWMoeuhG0I$YM`L4A7Iv=@94XVAx4 z!VOE=V%Jt7gjE6Ab}9)d=uo&Ls;T*h(9-jlUtoMd;68E#k&&TH9DqLvPCPc|G|TwnjTM zJEV{5eeIgc8l@|XOd*=Q39|h8+umm;*WBh3T+GBhcjO)OzEr-kAC%!GnjYQO?YB#) zv8Pylf(KkaYqg-@;y~y`{VZA=fH-g)@bUA54_WmWj&m!pTp8B5UX)s}VHXilnBEFe zX6gJ16=YzDal%^w8P;A5#zX|(I=U_lpyS@nu+l6Cj$U}>$`izQ;K2rL6BziVuY%=sIzBz*Vc-mj)VYBLbyxxbp$A}w z5~#?xlF3QHqM%=)8rXh}BgR1{bMZnO&Smt^2x`;*`Kb)j7Vwry&BM5+Q2Uw}Zs7<$ zwc@Iz?*9)#JmzRK9%wiN{w(n4>;V8LH*E#rN9E_I?pH!%2azSpEwTyN&qqhMiJ{u z^>_arJWTn-=^wJw_#&Hd9xumAi9T05*>hNXQt<4!i)@q26tW+&qs$YP9jn10xS*%_ z`HSfHAEpFJ7>;deT=G~`PF&PN96fGvAc~C&SMJ?=1;#9E&#}mRI3vv@@qxd-=^76R zhdGwYk>YkCHCEco=d{T~HHl|kvth!3hE@pPyu3c!r zkdeAN&e`U=Bbs@*#DGRt1y6~_Iirc4ou2mg@Q66SN#JCBJOfe%hG|HRpx199)K|Ko zo+Hr!;~-$(Jz-OZNGB|J2Z91s(%L|Ml8Hn;mYi+a@C~R3Zyd%&-qow&(`FUa8x1s+ z!I6e84ZuUq;*b4mRaVj_rlz^do)$=Hvy$HF#U+~DDQHaw>u4rPVr0aW#mO9NwjxHr z;A?(iN?@{u)x{W5w{0?MMzFgAXiJoeF*`#ZE!j*5@&FGgvccggeSrkGf53(>^hO=| z2lSVvxj3Ziu9dgiB35=2i$Fk?5)rsgH*H@(dsc?<)0HdJA-4t`&6$XPb|bKG%rpU8 zw^}Lc3EdLVH$LN$;(5o{>LOpJtwzXn%j3r~nwo14qT;R-7Ouk$S9GXz-KwHaqje$s zER_1kz6%vZTj!TaMmj&(e++&^C@{qXZ```ocR%#ba3uK^$5S^YY9<>Xj_4p(Ha0xw z@$2Nm%%Sn0ZeFzqq1>cd4(3>_M58WhNs*S9S5Z_%<%rDjR@sc*evJxgMomM$Tr>F2 zD@eIA5^LD4;xll3b{lXq{9fORwkJ<~my2;?y6?b>#qFPJ@wQ;z@JJwsKef%!fs+)I z?c|{_u&)F$N0(WUN&aWhj(7PT89PM)mUy~`b8x!=-m>z{aN#MPR)`83II<5rIJ@6T zng|+9@2oPEl735JRQJT~oY=lqlBBzj^*&!+VV{#NAu=;T4Y12G=>j&T+(jkx1?b8G9{UOktvwqb#`2dE|#cyAs*zP$7X zAnNq&EMyuDtN{H1*%1WFKT4@#CKzv=M%Y0K6^0Ei_p4X;pfWG#rj68*&K&-Vp_Xd+ zR7Ly^gza6-gpQNbAh?4;pFMpu>o^d@gd#iig@2R37?FSH&Z>!ge~%(gkjRs)m?2|m zcUC#1{fShq0YM9@)VR?&Q}8)Opr|!EL2Jbw!cHpE)b8$^R1W61Cla0%WZeYz>aQ<3 zn|5gZiV1U)Xq9|RzQd&ijxH5Ml*%FR1+EI-I2uCUv3hJ`B7C@sN%B2pzc8>~_}42< zCwYNSln3rQbs(SM%@h6rfjiZ4HqSnR)R&!Md?#|UcC_>E#U>CfVAtg3Pqyv7clR#S zGTP?w{&LZ)&}9+|A=4h!WdPh^>8m;NZ$J^`oCJXx27vWsE(R=vO~|NY%tVwT;s@|2 zM-fm99V>z-pdc?;`uuYweFuu#GTQppKGoEb0Wr{^qS zFiP@xz}dgOF_bGh;TnsQvf?>!gG?29KxLQ=Ky1R%(cj;{=h*jmXn(+80%wJA=v{k# z_f{LbW{;z2OEO1!MdvyNpM?yW#mMsDwGl@GI~PXu+&bGYxK*vlT113Yul`?PGo8k+o~QA$4}&!XcYP_)yg1WfZgBOPM)fHF)F;=r9r6a{ zr%bJ=*UIzplG{d8wu+^iNE@504)=KMSc%{t(^3l&Z`Qg|#ZcQu@EFeDO#1BM3Ec(o zi^oKNBi<;;xXD$fuqz_43k~eBIX z4M_XPB-ny2TepJR(ZCV{G{2a5n}fdp{0Tt`yjUxLBDoE45^90;qLqOpmka+JwXHrL zN@I8bKwGnlaT&D8&wG1?6kV2OMsk%ges&(0=sf-F(d|r5w%e9$W-DI4k6KP#o?4V{ z9+n2QrQ9~3p)Z&_XMP#DvZFB|r2meuY*up%NR zNn7UhUy3m#Qb;d0$;F~MLVI=e$A@B%m0Up9{F*&TE3_Z~dW^5lKi&M#_>+CnCiZ4o zy-;4c6K6u@}j`8S$am~v0~9@Y0ZoM4Ur|r z8w_NDG=dWa);6{34CAsWGypekNvqWDHr>@x-nK5FgL>xE1; zTs+R18nm01#*y62%*2FRTCea4_bbLkCMG6y3n-Sj(Rt&B7@z=JZ&wbj6_i@F{@B#;@;GM=K_3tZxVsEq+rcAJ1+gd`8ki#F9IJ8WKKxa^2V{9< z)YJek?RwYA$n@UaYtAxAkr9XE|e~h3o8w%kKw_y^Ld|SuWvfcBTl6$DTsuZ z&tmbJ;Yn`BNhcNvChY#vpDGJky6*SLp1{B`MQRA{kQ4u~Cf4}{KBAvLe-D#^9S@hV&gbA zi(lHfY*(O&#uXl~)&jw!6|X4=?Mv3l8OCn3+TnTJ?H91M55bSQI36I)nBT~?;)LCE zYo(TgnUH$jS9l_~T?yiUc6Rhf>N{TbxfTr-o!Ds4)V&5wkfGuA{hB~=uEY8~voiOA z?#~fXhR@WINStzasddzOMZsN3AY%Wb&WfPl`JJ(Jh?Mg1C-SjMCggel(gF;=M;o8{ zxMt`{s==O>j+JFdM{QI)R;-|fZT`ZITgJn7`Ks0(;_ zl9&6uJ?QC)=>};$qCg5=T%bmQoW8#0*qStIYS*$_`-P2znaPVX#R`r4nbPC+ zANzjOE|YYIo)=%dJPN%TR6q(}B$hLVOjJ;pj;`@W#IAq-$E|}jc@Gr3?G~~q^D&|H zaYOWZ9X8(+{6vkdH&h=Z)l)`brrf^lnabnIG0l6S-blulsT6fx;&U z%P3>zur*V|f2nEj2=R;WaHI6!b&1$3DDAQ*#cqp&#=e;)_sP6odZF0O7-Nwfc1sIn zH5QV#?WG#RZ$mNgTPx;L8Tl5ov~$_ZCWaBD70gGoX(0^cAha5TAN7&c-SEyIXCmU- z)w`<}gm8004uUEboa_<@eWoAC%z3&~ON(>0c)IqxA5Jv*I#^G4cT1m?`V})-O+&8p zI{?UQHL<~kCr+54 z69m_VHE379HeYC@!SRiKt(gC*nB0xqx3NrK^tR;Q7E}sAaei&PWS^tnS15%*hz<4Y z2*2k)eFl*wYDKI8 z>i+$sSn5Qk!|DeYY@Pv9Q*ZOBHYu`1JyHh(f&74*B)4j5*5a%CpaX=75xg_Nj#622 z-K5(2YH2?{=^M_6u6A7JmZ}IxUXawb?b~7U$H|PT4@Zwu_VH$yt9xn(ux_641<{2` z5efNFa)Z94BaqDnLLLqP^n;N3`kM2`p-V!N*WAnuc8MVw9-B@i*OWspuBDaR@g82# zcsv93ukKY> zhTQ?5;QtP^>4dhb1$_}9oM5Xb&jEkK0u1Etfaw5Lh^U3B?+YDTKfUp9agMs&tb}Jv zLSio#{mzS!Gp(eld)1&m_!qe$*W0K->eAJJ=*lcpS^yr7$Z|3+Lx{og#%*c$V1YPNQ)exAoo4woxppuv5JEq)iz5~=-TmP$5(2?IqAaiAh8 z^!rzL2Y&NE|Gm7d6(2J9LmZg{&$fgJaKIxuwXC^&tk>#)abx;{!5EGzn4(#8L zmi?@f`y=E%&4(R>2o2K#5}M+iac4&ZOTB%3=cPn)D954YGo{Cqly6xq7u!%kzUg0I zWQ#RpnNWj3`9vG(ggSt`4l_6OHi)!2O)f$whn!!_i^jSyO1jy{S1#XGSs_@81f^RU z`V48(Zxw zS`&F{GuF{Df$7PYx(oz1WFK#Dv6CASyBJ)79mq(zP}sOp+%fFoJO7v7fzHk%ub;HG zPR-1STfXY*;;8*}5ET&vC|*0pVcs}MYt+=$0a&~8N~E`f!+>B`frQ{5>{03N>iUj8 z1bIXXq;zr-mi}P*7#29={dsJ?j~#VH1a9)wfHSay62xA8WDjCYShXiC?c6!p^i~DZ zD~R)8X`F0#BTF~Doq|1pCDw_T{`##4CC$r+ERgkq5YGi1E##i`=qJeDsfD=_x4R4P z^JmXou^Q#e>rKJb3~3ZXXJ%*K2SdJyD*);${|%%xRKw_&eZN8PkKum4;pi^iH zbK1c6vNOl(6-+`yh728XLACX0c4~uMbgFl%2)mT{Zs5NIu z98&8uF6v}t*!4g|c=5U&CH%B@(BmE5Nm&PR)SL}UDGra!4P%ss^h=5vJFQ`K&+q{soHEHa zrpDxg796CWVQ{*&K4+e%fFb{oyuK!Q_7k7?s$amTc%KDowTnyC8~zP{;*g=Jm&lAx z^lw9SNSi7Rfenl6b}>{+;)aUM!L`K)S-A)V9qzn1vUuX(6^jqF*A7Pomi`vcUA59w z%CI{;J!W1V4Tuzc01);6(dJG5sm+T=lDA2o*7+CI^cny6hAweVOLsXTFwoRI|3b)l zy~eh;+%@QhaQJdEU{0svyOdUg ze&HR6ET=eU(X@odAyUjuvbooKmuqIKUpzc{<3z~Znkz4z>l}$nmhXY$1~gM$2l6DI zJUQ-BXeIr}hvGa48(O>#R#W^BOAkE{xiDhE>U_4Uv-%=ZEaHP(u_v<~bvbNieCPZ} zc0vc5zH7A*na_2A;*JS zxLy^FpYUi_(kX-g47i4^7nPTGz?#G2 zUmqnB^?s=r#WL!!zfndeQJ;>WrK7uUf0uT{IG%AF*=hIOULZrq&!AkZ$t31J>L2R9 z&;6OxD0chz!w`!ESPs2gPerWwm#6C57$7nTl0qCgbqXZH7qLp<&M@27-G}ZW^0B@2 zTMMZj0!i&Q%ghJ8ipniY&XgVuALmP+D=v}a19HYjB2^zlFg>1dhkNuwaa*Qtaf<#8 zOrD5Tu$F5XWe)M%`a~na!muO8>XuwpnfK^!i)WE~BrLUcb zSVIm6?2k-ZX|;tc|E{yJB=-528hp?sA|Fu!4KYm9Vmc}4yC7_?F`Zqpy@bfey<1$&Fp+)wbli5_qqERs0FZ#l4pwGF)Ejl3 z!)OfE8+GgFTqX!vRb^$=nW)O9w45dy0;UXnQ2!aj)vQXv$WGrcTLQFoYt_^0Jig1d4&~C!t9-uM|KyP-!PQB{=Y%6 zLl)7)mEB#Zza>(iDH%hD4W9A(o)5g1LyrCa8;p7f4#;^&J~k0d**cZO#^DkX_uBTV zvAc8;ja%uZb4)$wJK6ux{yvh9!WpSE`CzpVdad$1ch>K|O%fq4monfw=DEsyLk3$md6Ukr(!L8=8-Eq5xI?(jt;m z;7|KE1z=L+AD?}~U)>K4^7EU5IGt+_?ok*n5Xbx)31mfZ<#6IIJ%n^Id3l~h0}yv6 z*lW(l_I@gu9Ih|=AU1A$MeJXS!554|#f;(GqO>=A&e0?@hGIhdcaLq$$Zs$1o|_Iu z=E{@*i|e=VE#y{hpB+91rvdBO5GkxYgRffIX> zQrSc+6y_Q!9V&9R$w4Z?V&{H|ZX})O;~?E?U;y77Z1oVE7hLz;`ItSkWc9%VhF-!N zOv-n~ZNF5o0Oq-K>sCW}ff7|}sA`~zW#2_HkdbhR=OZV*qO2@h#y~(m3*4${AY*lj z8q6&N16oQ-oR{AJ%{6OeHUF=u>;UU}vgqpe{(epD0=aO(z;v6Do?aBfj?oM4%psd> zI|rT}kRE|OeO*5%YWJ9#31^b0auX^$eM@go;@($L!FVsB;{OW>o^OQ5{2qWddxjOY zWv5<-Li_<&EETM7XC)#qcQvrh9|(ZtC3tqADj+%6Tp2o>U62LCgn~dXG+(=9t!-^> zt*v?D8|&RI__D}HhC2hbD0Mw#&-^Dt<5fYz4Z4OqlmH4NiA`$mGf%F=m>fBQbn>Ro znoyDP2WwVT*W_HW>e^5cxG8kj`|#mubh+@-Ap312$ltrHW9iHL*fR?)ndUmTkmbhK zld&5MKG}sQ@BDFwz6X9Z{N^XxILxAiwkz)}-Zfq9`rS}nGss5*q;+@a5J$eqo?T5Q z^VAigPEM&%M6J*wgGO>C2S=oD7aX{d<=zLiBbXAe>cR{CWH8Dvx~tfE%QKXlgKzdY zmr8CElaflD@9%|PU{RV#_0zq_RUpVmW=q>2$qC{m=yP8HL}ti*t8;*W0{w!N6w@N3 zCtfcG@bl+ags0LGe*w0}vH>n}k~AaCG# zBQ~02p_vJA0;9?-BtfkejSmMeotpy+@$TAe>V7^(*Gx*cqnxR`hv($zT7y#2y?U8N z#r{envv(>gkSWm*A{O;m-k}OL(~t{t9v4iWNT5_&y^}vm@H`n~fmE|tH~;jt6cFs# zIr+JEappSoX~1yMo(#|J)Po4_?{?jf0l)0J5o-pH&h?{QQP&;deuY?c5UDH)obddE ze?Y1=$``I_*oUX$l^;e%rr|6b>WAbKM|jv^T_k1?w6yR-NL~tolMx&q@cf{lIk28C zytDAr!h??F7tG_hnxsb4^XgRhF8(hBg^274+NTuf{2S+$xQ+4=3^gEt?G=6IE;obG zcOl7G4TfCkjj&#qn%}|2walkK2B|549qN#Ai@}u&K`+chXNQ8V3OF1(O@w{-TS+su zwzpHa9ANMVW&t-ebXS81Jh|Rd#w?P?IdlcSVyO!pKKYj}dBM(znHPK!%ph=u<{C@j z2DXJ~0ptM=PcSx!85n7TZ2Ir`?{(~>q~gCC&Q5WIx+vhgaErt_gU-VWHt0411~u7m zNSPD>+kjf~8&=>#af#~{2D}ZE4MJ@&1`nA7Q%9P*sfkH{rJpMnGHq$Gbx9IEP8r%s zJ$?TNn0|9FDCLE2(FP7rVVe*(?wDZV*+9Z& zWF*Z1U|nSS;bj+bO3Gh?3=FF!3x>jrJT?KcKnasxieIZQ%)U^&G`9qh6SFj28G~yn z71zW@@ZPbf2t61qlMs$kBPx4)vsOl(OulVTrXw}t5c2_BKQeMbS;&b2)dRR1w)If) z%f}CiiCZXY)?BZ~_d1l`Ao{lhxhLDd}?<6dZb!{L!2x^A7 zxw=L#6<}U2P_`KJE6pgji1gaD2k^oU6{YygA04m8M^ke38+2B)jP+e^-WGSoYG->) z-!L@uXd%EOKA3QT1O^`h{8&`%1viTmHjWL$;p4%p=dki{WxR$3SC@zRSE_{5&c(6XRH%pUDqHj@(0G_}X8=l0MrNmC z4VqvIL+vwK0=3E@jGjbC3ux^>2syO`juilfiIF#vc$6)VVN*Non2n@ZifuvlZtobacAQ_s|+X ztFO+3X4%)eso7AACpnc`^z`4opmFiA45{SqzPxkub)H5S^Wlk!T5#4-;ewl^!uRti zaq!MS@HcfDkJKOU)`!2uYT}!(;2nU50o!tKqkcmcjD^e7yML5Cd-LY{yqA2DdUcX< zt_F2s1D>L$JKUV+)3u^t6Ht?XiBN-@J(m~d+9VHUKlOLpw{L0cq0rgd^g!SIQ*3nc zj}G_9>kINXbMlU4oVIkr>q})A+qlno@-2;-uI@W&(eccQ_o8z9pP)4s zfmZ9S{FwUlkS`Z6Yk`PNq%KRO#qu}}8sXo62G8cWlFkG_y7kT~Tl&le)nH)g$FOo5 zf)z+#o;0an!4L9%<>d8W&Q6La?hkYxX?=}dMo@FY$bbY2+C339m?H5XaR~_^xYXp5 zJ+*=a`|8!;s9*K>pNBej-#&(v&*S4i@L6Nv$2A6X3zZMzEfO0*k|A4f9cN;&hNH3Z z6QpTmj8oCSyb*N_K$C_LP4j@p z6n8IbDRP;h$c(mG5ZO|Eip^hmA%(v22D(D9L%`=TVq@-uI?*NmKd5p)Se$zD&BLZp zx48VNfEuDs@C-Y10ptWRj*Z_a$JV$+H*KrkAM@ZbOX885*{P>TOUjFmrfW{z`N5a# z5Xqdj{3AyJcLVbQZk9~KeCm?Gv|cp!75rzhZ^$;!F9q9)C?n4z4&BQl@#N*~h3Rc2 z(-QiI<$}WW;!a+_aZs7`)(wejQbbI_mn;)2ud3^P`#ASwh&Dta39$JWFcZSK7aV|5+PU+x&2m% z#^&IZL^VX*YoP~Y8s81m3ygyUc#3;^w424=0$Sy@q0TJ2>A z`~mvo$w$2cUi{??Amk-%yB?Is8PCNGFo5b}6^AKTriaFvBRiShG1hc5_L~vZC4sU$ za}cW{XcDeskR=l7Alfl};pw?nRmHEVo*7oB;0<$jrJTpoT5NWPgZ*B>N1oSEzEIO- zAnBQ6MNAOxY#@=majkB*(3Zk+RzzvhNKrXW%9JuWO$Y1*M=%lLQ}dnvOD4GpbsS(L zx)6)fj4HzjrdXWEN zr4B!v86-Z~-U>l-k%g4riCPT*_8x%f)7@pr|B#{oEwJV|; z<_9JxeW6>cjzIMtIQH{E*al2(1lAixn>_&e^7!%gFR5rnhu0u8jmku6Zx`bf63PQ# zcv{jKg#+D*nH~x@)sh{GkwzlOVtQ`w;SLHrhzHQOVH+Ti#Yd~8q5sx$1Z4;XoSfn9 z?p_f(chK;%-x@1BgHh3P8;n2T77)AtI%aM&*6h@`f#}M;9-+xW5G*6x40Nru!*hqn z62cHY*qGhaY+8fcmD@b~ z>6ifCF1ETb)*OKc3hj7j8DlX5y|MOUJ}U{x3bwX)h`N;su%@{taCY7h>^8)?^Ppvr*QT0z(lo}~Syx zmnci_KogJ`c% z;{km}gaScTIy7pWRJiu5E^E@_x<*8!Rq}Cieg#krrjw394&jld#iJ-Gi{WpFhPt3m zfAPY|TrGd^6^l-(6YY^G&ftR3!d0SU3r(h>* ze2bz1lZS1}xVp1&k~{XA;^~}E_-)yi_;`hrzn~xi4_QhI8&L;G3O4VlJk1Snu{cZ4 zmAq6SpAco?$#J4JwWV%(LgasUU^gsP=~DrDf>$#{d4Pg@!o@}J)7l^}8p247v)r~> zf5gt02~Q+J+~t5ViNJWsEJz(h8b|>;Io@t=-Pq2zYSqKNWe@@{qotYIE-CDhj3LvB zWySvqs2t-ey>yAT@0<3ygD3*9Hof_$P{GX?ii$YiQaJk_P|l~cN21lhCMAT&H3C*f z2;m~sWK1>z<#Jf!5~j6BmIrIo8h9$dApS9-Oe%Az<%vN?MBGftwUzV~TYxH>IIw^M zU^xSB31r5cnz3TK-)XkBE4}=^kkTsi#Dvl1CmGqflRb>%QX^T}=}z_~pMqKt$3KS7 zG^4{XtKf%GGXR|~)YHYES^1aAIkD=jlaCJ@G4}D}T9t;ah3aZ*b?cq`j#^0fjo^$# ziWK}SuiM&I5LAag1UgFQD5bztvi2M7zK}Qy#s-cJ7*R2o;kghBo1|tHK@o?Ub(O5G zPUzvLD-7$8_MBdd)e|8IN0Sf#BYZ6qVGcPKVSl2X22uv_e~>1+A>bXNEKsL$pQmLT z6ufbGVTf3ldj{tS)#tf*yEu^!7^1W0rOQ1!24*{UtQQp>2HXWvV<=X*;$i>LTCj$H zT(Z5R`@K+6dWxu(C}mo?fHR4?WMko_X5nQCG+RG&VUZo?>%XV#afMIk78HosYwIPX zi~y*mf|PG;d=b?f`g6%=;+X!~;I~A?o4T@Hy#{y6I4*md7l>WC_TR#U8Bf2W&{s07x*b{=SW4Er1 z?9M(v7R+e4W$hNM1C8lpGc-rG+X1b`*esV|IIxX7C3cA(fPG-BixB8JMrX#SB#zM z8tiV?VN=p8V6SPhG#5>xz!wTeM*I$QXRX)Q#GMZQj0H?$F9kD4y)yY9WA@cApe6_- zs-@iSYSsA?EoCl4fZkhF6tg*>-;Y_B=h51o?m^xlT)eSVVu>hlzP(LiF3a+n58B_P z>XD7v#$zD~loG^$F4Rbxb75bg=^vq+^q!SJemAUk^8JzOsK@Ub4!!SmHLBh{ogKDS zaa7`zcrY7%bOFi?;)7~65G07>3}sl6r2h<2391kM<{2L2Wwo><2s5T-;vie@5WDhg z#F7}-5$G<_F~bkxaaWiT&7fXn!}=p|Y;1YUbZl{<^w|f>Hy!!3@eB{kFu~4PALLI=dmg&~LLc1nMarVKdUfzHxdw8s&Nkt1nU5|-XW9D242*K%7 z@azM|(Y<9a5dnl^CCkIor~qgXxK7tgVQ*^g_{*2yVfe(q143WeWxIR#&!I*JbFRjr z$-26T9+TkZ|7g?5?fjNUwX#DMW-!q!tXxNpf9zPOc46VP~oOR~pedgl*Br%X=gMH=O3cILjQ9#nr&8&T^O=oFfj-0^95aH|U)89q*Hmv~WI1w-<8xVq&<^IHb_= zjnoL-YU5sZuASImy5mU6%IA^3YM9n*`>Wz(&)5VpRw8VV$!r~vRwO^1K zHC1=@EL>sJ%1e3w9m*jyt^kBX&)zo83{S2#c01LmvSk;@vxUjeh+aq_{L3mn(Yp zyr-x16*uvL=|Kj8FJZZo@8>zG|0^ORn)+h|@st*S2w$l@`FMl)7v;q7@3^ph?9ZRk zDaMK~g7BnK2WdnQMl79DH27~FyNG}N6^k^M$E&aKGc%r`e&aSnQ-f=IPEr34zYI23 literal 0 HcmV?d00001 From db965fd5e826648362d71db70f4b93a32df8be1d Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 15 Mar 2017 21:10:00 +0000 Subject: [PATCH 060/351] Move README image resourse to res/ and fix link. --- README.md | 2 +- .../ed25519-malleability.png | Bin 2 files changed, 1 insertion(+), 1 deletion(-) rename ed25519-malleability.png => res/ed25519-malleability.png (100%) diff --git a/README.md b/README.md index d9061b9..1a8affe 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ or form, safe. The signatures produced by this library are malleable, as defined in [the original paper](https://ed25519.cr.yp.to/ed25519-20110926.pdf): -![](https://raw.githubusercontent.com/isislovecruft/ed25519-dalek/develop/ed25519-malleability.png) +![](https://github.com/isislovecruft/ed25519-dalek/blob/develop/res/ed25519-malleability.png) We could eliminate the malleability property by multiplying by the curve cofactor, however, this would cause our implementation to *not* match the diff --git a/ed25519-malleability.png b/res/ed25519-malleability.png similarity index 100% rename from ed25519-malleability.png rename to res/ed25519-malleability.png From fc2725889cdade7a6ad299069bf97cc173fc46c4 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 15 Mar 2017 21:13:09 +0000 Subject: [PATCH 061/351] It's a discussion and not a definition. --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1a8affe..8510b57 100644 --- a/README.md +++ b/README.md @@ -87,11 +87,12 @@ review. Neither have yet received what we would consider *sufficient* peer review by other qualified cryptographers to be considered in any way, shape, or form, safe. -**USE AT YOUR OWN RISK** +**USE AT YOUR OWN RISK.** -## A Note on Signature Malleability -The signatures produced by this library are malleable, as defined in +### A Note on Signature Malleability + +The signatures produced by this library are malleable, as discussed in [the original paper](https://ed25519.cr.yp.to/ed25519-20110926.pdf): ![](https://github.com/isislovecruft/ed25519-dalek/blob/develop/res/ed25519-malleability.png) From 4cd8ffd7976f3b0e0d072b5aa1d8ae556f3a98b7 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 15 Mar 2017 21:19:13 +0000 Subject: [PATCH 062/351] Explain the baseline "readability" in comparison. --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8510b57..e4db7d4 100644 --- a/README.md +++ b/README.md @@ -74,9 +74,10 @@ included in the SUPERCOP benchmarking suite (albeit their numbers are for the older Nehalem microarchitecture). Additionally, thanks to Rust, this implementation has both type and memory -safety. Not to mention that it's readable for everyone, making ours arguable -more readily auditable. We're of the opinion that these features—combined -with speed—are ultimately more valuable than sole cycle count. +safety. It's also easily readable a much larger set of people than those who +can read qhasm, making it more readily and more easily auditable. We're of +the opinion that, ultimately, these features—combined with speed—are more +valuable than simply cycle counts alone. # Warnings From b5ae6c4447a344394efcf0f43aca72224996c5ad Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 15 Mar 2017 21:25:44 +0000 Subject: [PATCH 063/351] RFC8032 isn't a draft anymore. --- README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index e4db7d4..e22a109 100644 --- a/README.md +++ b/README.md @@ -100,13 +100,12 @@ The signatures produced by this library are malleable, as discussed in We could eliminate the malleability property by multiplying by the curve cofactor, however, this would cause our implementation to *not* match the -behaviour of every other implementation in existence. While there is, as of -this writing, a -[draft RFC for EdDSA signatures](https://tools.ietf.org/html/rfc8032) which -specifies that the stronger check should be done (and while we agree that the -stronger check should be done), it is our opinion that one doesn't get to -change the definition of "ed25519 verification" a decade after the fact, -declaring every implementation (including one's own) to be non-conformant. +behaviour of every other implementation in existence. As of this writing, +[RFC 8032](https://tools.ietf.org/html/rfc8032), "Edwards-Curve Digital +Signature Algorithm (EdDSA)," advises that the stronger check should be done. +While we agree that the stronger check should be done, it is our opinion that +one shouldn't get to change the definition of "ed25519 verification" a decade +after the fact, breaking compatibility with every other implementation. In short, if malleable signatures are bad for your protocol, don't use them. Consider using a curve25519-based Verifiable Random Function (VRF), such as From a6e74ba608acb8fbf24b57cddd7fe0b7ddc4fff3 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 15 Mar 2017 22:34:06 +0000 Subject: [PATCH 064/351] Use b"" instead of "".as_bytes(). --- src/ed25519.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index fda76cd..c7f183c 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -522,7 +522,7 @@ mod bench { fn sign(b: &mut Bencher) { let mut cspring: OsRng = OsRng::new().unwrap(); let keypair: Keypair = Keypair::generate::(&mut cspring); - let msg: &[u8] = "".as_bytes(); + let msg: &[u8] = b""; b.iter(| | keypair.sign::(msg)); } @@ -531,7 +531,7 @@ mod bench { fn verify(b: &mut Bencher) { let mut cspring: OsRng = OsRng::new().unwrap(); let keypair: Keypair = Keypair::generate::(&mut cspring); - let msg: &[u8] = "".as_bytes(); + let msg: &[u8] = b""; let sig: Signature = keypair.sign::(msg); b.iter(| | keypair.verify::(msg, &sig)); From 0e535a931843cc45efefe502ac29c52fa333c4e1 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 15 Mar 2017 22:34:46 +0000 Subject: [PATCH 065/351] The sha2 dependency is only for tests. --- src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index c5ca4a8..a10ac2f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,7 +112,6 @@ #[macro_use] extern crate arrayref; -extern crate sha2; extern crate curve25519_dalek; extern crate generic_array; extern crate digest; @@ -124,6 +123,9 @@ extern crate rand; #[macro_use] extern crate std; +#[cfg(test)] +extern crate sha2; + #[cfg(test)] extern crate rustc_serialize; From a3aa6078b4290f5e6dea4a6a026c50e01447a440 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 15 Mar 2017 22:41:36 +0000 Subject: [PATCH 066/351] Add a TODO for adding benchmarks to Brian Smith's crypto-bench. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index e22a109..544b48d 100644 --- a/README.md +++ b/README.md @@ -148,3 +148,5 @@ to the `Cargo.toml`: rather than using the rust-crypto implementation whose API requires that we allocate memory and memzero it before mutating to store the digest. + * Incorporate ed25519-dalek into Brian Smith's + [crypto-bench](https://github.com/briansmith/crypto-bench). From 20c19ac11d89976777aa8530163ff3e8d1551496 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 15 Mar 2017 22:42:38 +0000 Subject: [PATCH 067/351] Bump version to 0.3.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f61d3c3..c7bedd6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "0.2.3" +version = "0.3.0" authors = ["Isis Lovecruft "] readme = "README.md" license = "CC0-1.0" From e93ce2c1e2f539ca6dd7c6b1673bc1b5a5fd78e7 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 15 Mar 2017 22:48:56 +0000 Subject: [PATCH 068/351] Ignore res/ for packaging. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c7bedd6..f5166ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ documentation = "https://docs.rs/ed25519-dalek" keywords = ["cryptography", "ed25519", "curve25519", "signature", "ECC"] categories = ["cryptography", "no-std"] description = "Fast and efficient ed25519 signing and verification in pure Rust." -exclude = [ ".gitignore", "TESTVECTORS" ] +exclude = [ ".gitignore", "TESTVECTORS", "res/*" ] [badges] travis-ci = { repository = "isislovecruft/ed25519-dalek", branch = "master"} From bd9057ceb3e0261dff745d9c0d0bd07b81757b33 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 15 Mar 2017 23:47:00 +0000 Subject: [PATCH 069/351] Fix quoting in Travis env variables. --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index d3b9866..cb15444 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,13 +11,13 @@ env: matrix: include: - rust: nightly - env: TEST_COMMAND=build FEATURES='--no-default-features' + env: TEST_COMMAND=build FEATURES=--no-default-features - rust: nightly - env: TEST_COMMAND=test FEATURES='--features="nightly"' + env: TEST_COMMAND=test FEATURES=--features="nightly" - rust: nightly - env: TEST_COMMAND=bench FEATURES='--features="bench"' + env: TEST_COMMAND=bench FEATURES=--features="bench" - rust: nightly - env: TEST_COMMAND=bench FEATURES='--features="nightly bench"' + env: TEST_COMMAND=bench FEATURES=--features="nightly bench" script: - cargo $TEST_COMMAND $FEATURES From 833a08ca20c0bb037dcbc41a157cf43d0f3e79de Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 15 Mar 2017 23:50:49 +0000 Subject: [PATCH 070/351] Bump to ed25519-dalek version 0.3.1. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f5166ef..c7a9186 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "0.3.0" +version = "0.3.1" authors = ["Isis Lovecruft "] readme = "README.md" license = "CC0-1.0" From a19524946840a804db07fd4ce849e10ff0ee3efb Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 8 May 2017 07:54:56 +0000 Subject: [PATCH 071/351] Switch to using new digest v0.5 API. --- Cargo.toml | 4 ++-- src/ed25519.rs | 43 ++++++++++++++++++++++--------------------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c7a9186..94310d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ optional = true version = "^0.3" [dependencies.digest] -version = "0.4" +version = "^0.5" [dependencies.generic-array] # same version that digest depends on @@ -35,7 +35,7 @@ version = "^0.6" [dev-dependencies] rustc-serialize = "0.3" -sha2 = "^0.4" +sha2 = "^0.5" [features] default = ["std"] diff --git a/src/ed25519.rs b/src/ed25519.rs index c7f183c..57003d1 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -15,7 +15,8 @@ use core::fmt::Debug; #[cfg(feature = "std")] use rand::Rng; -use digest::Digest; +use digest::Input; +use digest::FixedOutput; use generic_array::typenum::U64; use curve25519_dalek::curve; @@ -137,7 +138,7 @@ impl SecretKey { /// Sign a message with this keypair's secret key. pub fn sign(&self, message: &[u8]) -> Signature - where D: Digest + Default { + where D: FixedOutput + Default + Input { let mut h: D = D::default(); let mut hash: [u8; 64] = [0u8; 64]; @@ -152,8 +153,8 @@ impl SecretKey { let secret_key: &[u8; 32] = array_ref!(&self.0, 0, 32); let public_key: &[u8; 32] = array_ref!(&self.0, 32, 32); - h.input(secret_key); - hash.copy_from_slice(h.result().as_slice()); + h.digest(secret_key); + hash.copy_from_slice(h.fixed_result().as_slice()); expanded_key_secret = Scalar(*array_ref!(&hash, 0, 32)); expanded_key_secret[0] &= 248; @@ -161,19 +162,19 @@ impl SecretKey { expanded_key_secret[31] |= 64; h = D::default(); - h.input(&hash[32..]); - h.input(&message); - hash.copy_from_slice(h.result().as_slice()); + h.digest(&hash[32..]); + h.digest(&message); + hash.copy_from_slice(h.fixed_result().as_slice()); mesg_digest = Scalar::reduce(&hash); r = ExtendedPoint::basepoint_mult(&mesg_digest); h = D::default(); - h.input(&r.compress_edwards().to_bytes()[..]); - h.input(public_key); - h.input(&message); - hash.copy_from_slice(h.result().as_slice()); + h.digest(&r.compress_edwards().to_bytes()[..]); + h.digest(public_key); + h.digest(&message); + hash.copy_from_slice(h.fixed_result().as_slice()); hram_digest = Scalar::reduce(&hash); @@ -245,7 +246,7 @@ impl PublicKey { /// Returns true if the signature was successfully verified, and /// false otherwise. pub fn verify(&self, message: &[u8], signature: &Signature) -> bool - where D: Digest + Default { + where D: FixedOutput + Default + Input { let mut h: D = D::default(); let mut a: ExtendedPoint; @@ -269,11 +270,11 @@ impl PublicKey { let top_half: &[u8; 32] = array_ref!(&signature.0, 32, 32); let bottom_half: &[u8; 32] = array_ref!(&signature.0, 0, 32); - h.input(&bottom_half[..]); - h.input(&self.to_bytes()); - h.input(&message); + h.digest(&bottom_half[..]); + h.digest(&self.to_bytes()); + h.digest(&message); - let digest_bytes = h.result(); + let digest_bytes = h.fixed_result(); digest = *array_ref!(digest_bytes, 0, 64); digest_reduced = Scalar::reduce(&digest); r = curve::double_scalar_mult_vartime(&digest_reduced, &a, &Scalar(*top_half)); @@ -334,7 +335,7 @@ impl Keypair { #[cfg(feature = "std")] #[allow(unused_assignments)] pub fn generate(cspring: &mut Rng) -> Keypair - where D: Digest + Default { + where D: FixedOutput + Default + Input { let mut h: D = D::default(); let mut hash: [u8; 64] = [0u8; 64]; @@ -345,8 +346,8 @@ impl Keypair { cspring.fill_bytes(&mut t); - h.input(&t); - hash.copy_from_slice(h.result().as_slice()); + h.digest(&t); + hash.copy_from_slice(h.fixed_result().as_slice()); digest = array_mut_ref!(&mut hash, 0, 32); digest[0] &= 248; @@ -369,13 +370,13 @@ impl Keypair { /// Sign a message with this keypair's secret key. pub fn sign(&self, message: &[u8]) -> Signature - where D: Digest + Default { + where D: FixedOutput + Default + Input { self.secret.sign::(message) } /// Verify a signature on a message with this keypair's public key. pub fn verify(&self, message: &[u8], signature: &Signature) -> bool - where D: Digest + Default { + where D: FixedOutput + Default + Input { self.public.verify::(message, signature) } } From 02e5a940441d5ae15e0e46b5b3d310b1a3697b03 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 14 May 2017 10:37:40 +0000 Subject: [PATCH 072/351] Bump curve25519-dalek version to ^0.7. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 94310d9..d8d2f59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ travis-ci = { repository = "isislovecruft/ed25519-dalek", branch = "master"} arrayref = "0.3.3" [dependencies.curve25519-dalek] -version = "^0.6" +version = "^0.7" default-features = false [dependencies.rand] From 99d342656997bce9f57c8c03575f94bad99b9ca0 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 14 May 2017 10:57:59 +0000 Subject: [PATCH 073/351] Refactor to use new curve25519-dalek APIs. --- src/ed25519.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 57003d1..ab4eff3 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -19,11 +19,9 @@ use digest::Input; use digest::FixedOutput; use generic_array::typenum::U64; -use curve25519_dalek::curve; -use curve25519_dalek::curve::BasepointMult; +use curve25519_dalek::constants; use curve25519_dalek::curve::CompressedEdwardsY; use curve25519_dalek::curve::ExtendedPoint; -use curve25519_dalek::curve::ProjectivePoint; use curve25519_dalek::scalar::Scalar; use curve25519_dalek::subtle::arrays_equal_ct; @@ -168,7 +166,7 @@ impl SecretKey { mesg_digest = Scalar::reduce(&hash); - r = ExtendedPoint::basepoint_mult(&mesg_digest); + r = &mesg_digest * &constants::ED25519_BASEPOINT; h = D::default(); h.digest(&r.compress_edwards().to_bytes()[..]); @@ -251,7 +249,7 @@ impl PublicKey { let mut h: D = D::default(); let mut a: ExtendedPoint; let ao: Option; - let r: ProjectivePoint; + let r: ExtendedPoint; let digest: [u8; 64]; let digest_reduced: Scalar; @@ -277,7 +275,7 @@ impl PublicKey { let digest_bytes = h.fixed_result(); digest = *array_ref!(digest_bytes, 0, 64); digest_reduced = Scalar::reduce(&digest); - r = curve::double_scalar_mult_vartime(&digest_reduced, &a, &Scalar(*top_half)); + r = &(&digest_reduced * &a) + &(&Scalar(*top_half) * &constants::ED25519_BASEPOINT); if arrays_equal_ct(bottom_half, &r.compress_edwards().to_bytes()) == 1 { return true @@ -354,7 +352,7 @@ impl Keypair { digest[31] &= 127; digest[31] |= 64; - pk = ExtendedPoint::basepoint_mult(&Scalar(*digest)).compress_edwards().to_bytes(); + pk = (&Scalar(*digest) * &constants::ED25519_BASEPOINT).compress_edwards().to_bytes(); for i in 0..32 { sk[i] = t[i]; From 6a24e0812b7ce84c005211e1aea2aa0e5bb81492 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 15 May 2017 04:10:25 +0000 Subject: [PATCH 074/351] Bump ed25519-dalek version to 0.3.2. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d8d2f59..621e073 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "0.3.1" +version = "0.3.2" authors = ["Isis Lovecruft "] readme = "README.md" license = "CC0-1.0" From 8ac6ef9ab13577b888e58c705d4757e794604216 Mon Sep 17 00:00:00 2001 From: Nicolas Gailly Date: Wed, 12 Jul 2017 15:24:35 +0200 Subject: [PATCH 075/351] simple typo fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 544b48d..ac77c10 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ included in the SUPERCOP benchmarking suite (albeit their numbers are for the older Nehalem microarchitecture). Additionally, thanks to Rust, this implementation has both type and memory -safety. It's also easily readable a much larger set of people than those who +safety. It's also easily readable by a much larger set of people than those who can read qhasm, making it more readily and more easily auditable. We're of the opinion that, ultimately, these features—combined with speed—are more valuable than simply cycle counts alone. From 13ed8af8de3a7164ef8bef09d462dd056a9ab94e Mon Sep 17 00:00:00 2001 From: Emil Bay Date: Sat, 22 Jul 2017 23:21:16 +0200 Subject: [PATCH 076/351] Fix badge links --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 544b48d..4889a1e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ed25519-dalek ![](https://img.shields.io/crates/v/ed25519-dalek.svg) ![](https://docs.rs/ed25519-dalek/badge.svg) ![](https://travis-ci.org/isislovecruft/ed25519-dalek.svg?branch=master) +# ed25519-dalek [![](https://img.shields.io/crates/v/ed25519-dalek.svg)](https://crates.io/crates/ed25519-dalek) [![](https://docs.rs/ed25519-dalek/badge.svg)](https://docs.rs/ed25519-dalek) [![](https://travis-ci.org/isislovecruft/ed25519-dalek.svg?branch=master)](https://travis-ci.org/isislovecruft/ed25519-dalek?branch=master) Fast and efficient Rust implementation of ed25519 key generation, signing, and verification in Rust. From df3834c03dabc2182cde516625aa360707138bec Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 1 Aug 2017 03:35:38 +0000 Subject: [PATCH 077/351] Change SecretKey bytes to only include the secret, not also public, key. --- Cargo.toml | 2 +- src/ed25519.rs | 356 +++++++++++++++++++++++++++++++------------------ 2 files changed, 228 insertions(+), 130 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 621e073..cc89438 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ homepage = "https://code.ciph.re/isis/ed25519-dalek" documentation = "https://docs.rs/ed25519-dalek" keywords = ["cryptography", "ed25519", "curve25519", "signature", "ECC"] categories = ["cryptography", "no-std"] -description = "Fast and efficient ed25519 signing and verification in pure Rust." +description = "Fast and efficient ed25519 EdDSA key generations, signing, and verification in pure Rust." exclude = [ ".gitignore", "TESTVECTORS", "res/*" ] [badges] diff --git a/src/ed25519.rs b/src/ed25519.rs index ab4eff3..d3b1da2 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -8,7 +8,8 @@ // Authors: // - Isis Agora Lovecruft -//! A Rust implementation of ed25519 key generation, signing, and verification. +//! A Rust implementation of ed25519 EdDSA key generation, signing, and +//! verification. use core::fmt::Debug; @@ -25,17 +26,24 @@ use curve25519_dalek::curve::ExtendedPoint; use curve25519_dalek::scalar::Scalar; use curve25519_dalek::subtle::arrays_equal_ct; -/// The length of an ed25519 `Signature`, in bytes. +/// The length of an ed25519 EdDSA `Signature`, in bytes. pub const SIGNATURE_LENGTH: usize = 64; -/// An ed25519 signature. +/// The length of an ed25519 EdDSA `SecretKey`, in bytes. +pub const SECRET_KEY_LENGTH: usize = 32; + +/// The length of an ed25519 EdDSA `PublicKey`, in bytes. +pub const PUBLIC_KEY_LENGTH: usize = 32; + +/// An EdDSA signature. /// /// # Note /// -/// These signatures, unlike the ed25519 reference implementation, are -/// "detached"—that is, they do **not** include a copy of the message which -/// has been signed. +/// These signatures, unlike the ed25519 signature reference implementation, are +/// "detached"—that is, they do **not** include a copy of the message which has +/// been signed. #[derive(Copy)] +#[repr(C)] pub struct Signature(pub [u8; SIGNATURE_LENGTH]); impl Clone for Signature { @@ -44,17 +52,13 @@ impl Clone for Signature { impl Debug for Signature { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "Signature: {:?}", &self.0[..]) + write!(f, "Signature([{:?}])", &self.0[..]) } } impl Eq for Signature {} impl PartialEq for Signature { - /// # Note - /// - /// This function happens to be constant time, even though that is not - /// really necessary. fn eq(&self, other: &Signature) -> bool { let mut equal: u8 = 0; @@ -71,12 +75,18 @@ impl PartialEq for Signature { } impl Signature { - /// View this signature as an array of 64 bytes. + /// View this `Signature` as a byte array. #[inline] pub fn to_bytes(&self) -> [u8; SIGNATURE_LENGTH] { self.0 } + /// View this `Signature` as a byte array. + #[inline] + pub fn as_bytes<'a>(&'a self) -> &'a [u8; SIGNATURE_LENGTH] { + &self.0 + } + /// Construct a `Signature` from a slice of bytes. #[inline] pub fn from_bytes(bytes: &[u8]) -> Signature { @@ -84,8 +94,9 @@ impl Signature { } } -/// An ed25519 private key. -pub struct SecretKey(pub [u8; 64]); +/// An EdDSA secret key. +#[repr(C)] +pub struct SecretKey(pub [u8; SECRET_KEY_LENGTH]); impl Debug for SecretKey { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { @@ -94,99 +105,117 @@ impl Debug for SecretKey { } impl SecretKey { - /// View this secret key as an array of 32 bytes. + /// Convert this secret key to a byte array. #[inline] - pub fn to_bytes(&self) -> [u8; 64] { + pub fn to_bytes(&self) -> [u8; SECRET_KEY_LENGTH] { self.0 } + /// View this secret key as a byte array. + #[inline] + pub fn as_bytes<'a>(&'a self) -> &'a [u8; SECRET_KEY_LENGTH] { + &self.0 + } + /// Construct a `SecretKey` from a slice of bytes. /// - /// # Warning - /// - /// **The caller is responsible for ensuring that the bytes represent a - /// *masked* secret key. If you do not understand what this means, DO NOT - /// USE THIS CONSTRUCTOR.** - /// /// # Example /// - /// ```ignore + /// ``` + /// # extern crate ed25519_dalek; + /// # fn main() { /// use ed25519_dalek::SecretKey; + /// use ed25519_dalek::SECRET_KEY_LENGTH; /// - /// let secret_key_bytes: [u8; 64] = [ - /// 157, 97, 177, 157, 239, 253, 90, 96, 186, 132, 74, 244, 146, 236, 44, 196, - /// 68, 73, 197, 105, 123, 50, 105, 25, 112, 59, 172, 3, 28, 174, 127, 96, - /// 215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, - /// 14, 225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26]; - /// let public_key_bytes: [u8; 32] = [ - /// 215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, - /// 14, 225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26]; + /// let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = [ + /// 157, 097, 177, 157, 239, 253, 090, 096, + /// 186, 132, 074, 244, 146, 236, 044, 196, + /// 068, 073, 197, 105, 123, 050, 105, 025, + /// 112, 059, 172, 003, 028, 174, 127, 096, ]; /// - /// let secret_key: SecretKey = SecretKey::from_bytes(&[&secret_key_bytes[..32], - /// &public_key_bytes[..32]].concat()[..]); + /// let secret_key: SecretKey = SecretKey::from_bytes(&secret_key_bytes[..]); + /// # } /// ``` /// /// # Returns /// - /// A `SecretKey`. + /// An EdDSA `SecretKey`. #[inline] pub fn from_bytes(bytes: &[u8]) -> SecretKey { - SecretKey(*array_ref!(bytes, 0, 64)) + SecretKey(*array_ref!(bytes, 0, SECRET_KEY_LENGTH)) } - /// Sign a message with this keypair's secret key. - pub fn sign(&self, message: &[u8]) -> Signature - where D: FixedOutput + Default + Input { + /// Generate a `SecretKey` from a `csprng`. + /// + /// # Example + /// + /// ``` + /// extern crate rand; + /// extern crate sha2; + /// extern crate ed25519_dalek; + /// + /// # fn main() { + /// + /// use rand::Rng; + /// use rand::OsRng; + /// use sha2::Sha512; + /// use ed25519_dalek::PublicKey; + /// use ed25519_dalek::SecretKey; + /// use ed25519_dalek::Signature; + /// + /// let mut csprng: OsRng = OsRng::new().unwrap(); + /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); + /// + /// # } + /// ``` + /// + /// Afterwards, you can generate the corresponding public—provided you also + /// supply a hash function which implements the `Digest` and `Default` + /// traits, and which returns 512 bits of output—via: + /// + /// ``` + /// # extern crate rand; + /// # extern crate sha2; + /// # extern crate ed25519_dalek; + /// # + /// # fn main() { + /// # + /// # use rand::Rng; + /// # use rand::OsRng; + /// # use sha2::Sha512; + /// # use ed25519_dalek::PublicKey; + /// # use ed25519_dalek::SecretKey; + /// # use ed25519_dalek::Signature; + /// # + /// # let mut csprng: OsRng = OsRng::new().unwrap(); + /// # let secret_key: SecretKey = SecretKey::generate(&mut csprng); + /// + /// let public_key: PublicKey = PublicKey::from_secret::(&secret_key); + /// # } + /// ``` + /// + /// The standard hash function used for most ed25519 libraries is SHA-512, + /// which is available with `use sha2::Sha512` as in the example above. + /// Other suitable hash functions include Keccak-512 and Blake2b-512. + /// + /// # Input + /// + /// A CSPRING with a `fill_bytes()` method, e.g. the one returned + /// from `rand::OsRng::new()` (in the `rand` crate). + /// + #[cfg(feature = "std")] + pub fn generate(csprng: &mut Rng) -> SecretKey { + let mut sk: SecretKey = SecretKey([0u8; 32]); - let mut h: D = D::default(); - let mut hash: [u8; 64] = [0u8; 64]; - let mut signature_bytes: [u8; 64] = [0u8; SIGNATURE_LENGTH]; - let mut expanded_key_secret: Scalar; - let mesg_digest: Scalar; - let hram_digest: Scalar; - let r: ExtendedPoint; - let s: Scalar; - let t: CompressedEdwardsY; + csprng.fill_bytes(&mut sk.0); - let secret_key: &[u8; 32] = array_ref!(&self.0, 0, 32); - let public_key: &[u8; 32] = array_ref!(&self.0, 32, 32); - - h.digest(secret_key); - hash.copy_from_slice(h.fixed_result().as_slice()); - - expanded_key_secret = Scalar(*array_ref!(&hash, 0, 32)); - expanded_key_secret[0] &= 248; - expanded_key_secret[31] &= 63; - expanded_key_secret[31] |= 64; - - h = D::default(); - h.digest(&hash[32..]); - h.digest(&message); - hash.copy_from_slice(h.fixed_result().as_slice()); - - mesg_digest = Scalar::reduce(&hash); - - r = &mesg_digest * &constants::ED25519_BASEPOINT; - - h = D::default(); - h.digest(&r.compress_edwards().to_bytes()[..]); - h.digest(public_key); - h.digest(&message); - hash.copy_from_slice(h.fixed_result().as_slice()); - - hram_digest = Scalar::reduce(&hash); - - s = Scalar::multiply_add(&hram_digest, &expanded_key_secret, &mesg_digest); - t = r.compress_edwards(); - - signature_bytes[..32].copy_from_slice(&t.0); - signature_bytes[32..64].copy_from_slice(&s.0); - Signature(*array_ref!(&signature_bytes, 0, 64)) + sk } } /// An ed25519 public key. #[derive(Copy, Clone)] +#[repr(C)] pub struct PublicKey(pub CompressedEdwardsY); impl Debug for PublicKey { @@ -196,12 +225,18 @@ impl Debug for PublicKey { } impl PublicKey { - /// View this public key as an array of 32 bytes. + /// Convert this public key to a byte array. #[inline] - pub fn to_bytes(&self) -> [u8; 32] { + pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_LENGTH] { self.0.to_bytes() } + /// View this public key as a byte array. + #[inline] + pub fn as_bytes<'a>(&'a self) -> &'a [u8; PUBLIC_KEY_LENGTH] { + &(self.0).0 + } + /// Construct a `PublicKey` from a slice of bytes. /// /// # Warning @@ -212,15 +247,18 @@ impl PublicKey { /// /// # Example /// - /// ```ignore + /// ``` + /// # extern crate ed25519_dalek; + /// # fn main() { /// use ed25519_dalek::PublicKey; + /// use ed25519_dalek::PUBLIC_KEY_LENGTH; /// - /// let public_key_bytes: [u8; 32] = [ + /// let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = [ /// 215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, /// 14, 225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26]; /// /// let public_key: PublicKey = PublicKey::from_bytes(&public_key_bytes); - /// + /// # } /// ``` /// /// # Returns @@ -237,6 +275,30 @@ impl PublicKey { self.0.decompress() } + /// Derive this public key from its corresponding `SecretKey`. + #[cfg(feature = "std")] + #[allow(unused_assignments)] + pub fn from_secret(secret_key: &SecretKey) -> PublicKey + where D: FixedOutput + Default + Input { + + let mut h: D = D::default(); + let mut hash: [u8; 64] = [0u8; 64]; + let pk: [u8; 32]; + let mut digest: &mut [u8; 32]; + + h.digest(secret_key.as_bytes()); + hash.copy_from_slice(h.fixed_result().as_slice()); + + digest = array_mut_ref!(&mut hash, 0, 32); + digest[0] &= 248; + digest[31] &= 127; + digest[31] |= 64; + + pk = (&Scalar(*digest) * &constants::ED25519_BASEPOINT).compress_edwards().to_bytes(); + + PublicKey(CompressedEdwardsY(pk)) + } + /// Verify a signature on a message with this keypair's public key. /// /// # Return @@ -287,6 +349,7 @@ impl PublicKey { /// An ed25519 keypair. #[derive(Debug)] +#[repr(C)] pub struct Keypair { /// The public half of this keypair. pub public: PublicKey, @@ -295,6 +358,29 @@ pub struct Keypair { } impl Keypair { + /// Construct a `Keypair` from the bytes of a `PublicKey` and `SecretKey`. + /// + /// # Inputs + /// + /// * `public`: a `[u8; 32]` representing the compressed Edwards-Y + /// coordinate of a point on curve25519. + /// * `secret`: a `[u8; 32]` representing the corresponding secret key. + /// + /// # Warning + /// + /// Absolutely no validation is done on the key. If you give this function + /// bytes which do not represent a valid point, or which do not represent + /// corresponding parts of the key, then your `Keypair` will be broken and + /// it will be your fault. + /// + /// # Returns + /// + /// A `Keypair`. + pub fn from_bytes<'a>(public: &'a [u8; 32], secret: &'a [u8; 32]) -> Keypair { + Keypair{ public: PublicKey::from_bytes(public), + secret: SecretKey::from_bytes(secret), } + } + /// Generate an ed25519 keypair. /// /// # Example @@ -320,7 +406,7 @@ impl Keypair { /// /// # Input /// - /// A CSPRING with a `fill_bytes()` method, e.g. the one returned + /// A CSPRNG with a `fill_bytes()` method, e.g. the one returned /// from `rand::OsRng::new()` (in the `rand` crate). /// /// The caller must also supply a hash function which implements the @@ -328,48 +414,63 @@ impl Keypair { /// The standard hash function used for most ed25519 libraries is SHA-512, /// which is available with `use sha2::Sha512` as in the example above. /// Other suitable hash functions include Keccak-512 and Blake2b-512. - /// - // we reassign 0 bytes to the temp variable t to overwrite it #[cfg(feature = "std")] - #[allow(unused_assignments)] - pub fn generate(cspring: &mut Rng) -> Keypair + pub fn generate(csprng: &mut Rng) -> Keypair where D: FixedOutput + Default + Input { + let sk: SecretKey = SecretKey::generate(csprng); + let pk: PublicKey = PublicKey::from_secret::(&sk); - let mut h: D = D::default(); - let mut hash: [u8; 64] = [0u8; 64]; - let mut t: [u8; 32] = [0u8; 32]; - let mut sk: [u8; 64] = [0u8; 64]; - let pk: [u8; 32]; - let mut digest: &mut [u8; 32]; - - cspring.fill_bytes(&mut t); - - h.digest(&t); - hash.copy_from_slice(h.fixed_result().as_slice()); - - digest = array_mut_ref!(&mut hash, 0, 32); - digest[0] &= 248; - digest[31] &= 127; - digest[31] |= 64; - - pk = (&Scalar(*digest) * &constants::ED25519_BASEPOINT).compress_edwards().to_bytes(); - - for i in 0..32 { - sk[i] = t[i]; - sk[i+32] = pk[i]; - t[i] = 0; - } - - Keypair{ - public: PublicKey(CompressedEdwardsY(pk)), - secret: SecretKey(sk), - } + Keypair{ public: pk, secret: sk } } /// Sign a message with this keypair's secret key. pub fn sign(&self, message: &[u8]) -> Signature where D: FixedOutput + Default + Input { - self.secret.sign::(message) + + let mut h: D = D::default(); + let mut hash: [u8; 64] = [0u8; 64]; + let mut signature_bytes: [u8; 64] = [0u8; SIGNATURE_LENGTH]; + let mut expanded_key_secret: Scalar; + let mesg_digest: Scalar; + let hram_digest: Scalar; + let r: ExtendedPoint; + let s: Scalar; + let t: CompressedEdwardsY; + + let secret_key: &[u8; 32] = self.secret.as_bytes(); + let public_key: &[u8; 32] = self.public.as_bytes(); + + h.digest(secret_key); + hash.copy_from_slice(h.fixed_result().as_slice()); + + expanded_key_secret = Scalar(*array_ref!(&hash, 0, 32)); + expanded_key_secret[0] &= 248; + expanded_key_secret[31] &= 63; + expanded_key_secret[31] |= 64; + + h = D::default(); + h.digest(&hash[32..]); + h.digest(&message); + hash.copy_from_slice(h.fixed_result().as_slice()); + + mesg_digest = Scalar::reduce(&hash); + + r = &mesg_digest * &constants::ED25519_BASEPOINT; + + h = D::default(); + h.digest(&r.compress_edwards().to_bytes()[..]); + h.digest(public_key); + h.digest(&message); + hash.copy_from_slice(h.fixed_result().as_slice()); + + hram_digest = Scalar::reduce(&hash); + + s = Scalar::multiply_add(&hram_digest, &expanded_key_secret, &mesg_digest); + t = r.compress_edwards(); + + signature_bytes[..32].copy_from_slice(&t.0); + signature_bytes[32..64].copy_from_slice(&s.0); + Signature(*array_ref!(&signature_bytes, 0, 64)) } /// Verify a signature on a message with this keypair's public key. @@ -475,17 +576,14 @@ mod test { // at the end, but we just want R and S. let sig1: Signature = Signature::from_bytes(sig_bytes); - assert_eq!(pub_bytes.len(), 32); + let keypair: Keypair = Keypair::from_bytes( + array_ref!(*pub_bytes, 0, PUBLIC_KEY_LENGTH), + array_ref!(*sec_bytes, 0, SECRET_KEY_LENGTH)); - let secret_key: SecretKey = SecretKey::from_bytes(&sec_bytes); - let public_key: PublicKey = PublicKey::from_bytes(&pub_bytes); - let sig2: Signature = secret_key.sign::(&message); - - println!("{:?}", sec_bytes); - println!("{:?}", pub_bytes); + let sig2: Signature = keypair.sign::(&message); assert!(sig1 == sig2, "Signature bytes not equal on line {}", lineno); - assert!(public_key.verify::(&message, &sig2), + assert!(keypair.verify::(&message, &sig2), "Signature verification failed on line {}", lineno); } } From 27753235ae0ba892c92486beb42c70c509e95cf9 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 1 Aug 2017 06:52:10 +0000 Subject: [PATCH 078/351] Upgrade curve25519-dalek, generic-array, and digest dependencies. As well as adding a dependency on subtle and upgrading dev-dependency sha2. --- Cargo.toml | 12 ++++++++---- src/ed25519.rs | 38 +++++++++++++++++++++----------------- src/lib.rs | 1 + 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cc89438..f6db6c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,11 @@ travis-ci = { repository = "isislovecruft/ed25519-dalek", branch = "master"} arrayref = "0.3.3" [dependencies.curve25519-dalek] -version = "^0.7" +version = "^0.10" +default-features = false + +[dependencies.subtle] +version = "^0.2" default-features = false [dependencies.rand] @@ -27,15 +31,15 @@ optional = true version = "^0.3" [dependencies.digest] -version = "^0.5" +version = "^0.6" [dependencies.generic-array] # same version that digest depends on -version = "^0.6" +version = "^0.8" [dev-dependencies] rustc-serialize = "0.3" -sha2 = "^0.5" +sha2 = "^0.6" [features] default = ["std"] diff --git a/src/ed25519.rs b/src/ed25519.rs index d3b1da2..e699de1 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -16,15 +16,19 @@ use core::fmt::Debug; #[cfg(feature = "std")] use rand::Rng; +use digest::BlockInput; +use digest::Digest; use digest::Input; use digest::FixedOutput; + use generic_array::typenum::U64; use curve25519_dalek::constants; use curve25519_dalek::curve::CompressedEdwardsY; use curve25519_dalek::curve::ExtendedPoint; use curve25519_dalek::scalar::Scalar; -use curve25519_dalek::subtle::arrays_equal_ct; + +use subtle::slices_equal; /// The length of an ed25519 EdDSA `Signature`, in bytes. pub const SIGNATURE_LENGTH: usize = 64; @@ -279,14 +283,14 @@ impl PublicKey { #[cfg(feature = "std")] #[allow(unused_assignments)] pub fn from_secret(secret_key: &SecretKey) -> PublicKey - where D: FixedOutput + Default + Input { + where D: Digest + Default { let mut h: D = D::default(); let mut hash: [u8; 64] = [0u8; 64]; let pk: [u8; 32]; let mut digest: &mut [u8; 32]; - h.digest(secret_key.as_bytes()); + h.input(secret_key.as_bytes()); hash.copy_from_slice(h.fixed_result().as_slice()); digest = array_mut_ref!(&mut hash, 0, 32); @@ -306,7 +310,7 @@ impl PublicKey { /// Returns true if the signature was successfully verified, and /// false otherwise. pub fn verify(&self, message: &[u8], signature: &Signature) -> bool - where D: FixedOutput + Default + Input { + where D: Digest + Default { let mut h: D = D::default(); let mut a: ExtendedPoint; @@ -330,16 +334,16 @@ impl PublicKey { let top_half: &[u8; 32] = array_ref!(&signature.0, 32, 32); let bottom_half: &[u8; 32] = array_ref!(&signature.0, 0, 32); - h.digest(&bottom_half[..]); - h.digest(&self.to_bytes()); - h.digest(&message); + h.input(&bottom_half[..]); + h.input(&self.to_bytes()); + h.input(&message); let digest_bytes = h.fixed_result(); digest = *array_ref!(digest_bytes, 0, 64); digest_reduced = Scalar::reduce(&digest); r = &(&digest_reduced * &a) + &(&Scalar(*top_half) * &constants::ED25519_BASEPOINT); - if arrays_equal_ct(bottom_half, &r.compress_edwards().to_bytes()) == 1 { + if slices_equal(bottom_half, &r.compress_edwards().to_bytes()) == 1 { return true } else { return false @@ -416,7 +420,7 @@ impl Keypair { /// Other suitable hash functions include Keccak-512 and Blake2b-512. #[cfg(feature = "std")] pub fn generate(csprng: &mut Rng) -> Keypair - where D: FixedOutput + Default + Input { + where D: Digest + Default { let sk: SecretKey = SecretKey::generate(csprng); let pk: PublicKey = PublicKey::from_secret::(&sk); @@ -425,7 +429,7 @@ impl Keypair { /// Sign a message with this keypair's secret key. pub fn sign(&self, message: &[u8]) -> Signature - where D: FixedOutput + Default + Input { + where D: Digest + Default { let mut h: D = D::default(); let mut hash: [u8; 64] = [0u8; 64]; @@ -440,7 +444,7 @@ impl Keypair { let secret_key: &[u8; 32] = self.secret.as_bytes(); let public_key: &[u8; 32] = self.public.as_bytes(); - h.digest(secret_key); + h.input(secret_key); hash.copy_from_slice(h.fixed_result().as_slice()); expanded_key_secret = Scalar(*array_ref!(&hash, 0, 32)); @@ -449,8 +453,8 @@ impl Keypair { expanded_key_secret[31] |= 64; h = D::default(); - h.digest(&hash[32..]); - h.digest(&message); + h.input(&hash[32..]); + h.input(&message); hash.copy_from_slice(h.fixed_result().as_slice()); mesg_digest = Scalar::reduce(&hash); @@ -458,9 +462,9 @@ impl Keypair { r = &mesg_digest * &constants::ED25519_BASEPOINT; h = D::default(); - h.digest(&r.compress_edwards().to_bytes()[..]); - h.digest(public_key); - h.digest(&message); + h.input(&r.compress_edwards().to_bytes()[..]); + h.input(public_key); + h.input(&message); hash.copy_from_slice(h.fixed_result().as_slice()); hram_digest = Scalar::reduce(&hash); @@ -475,7 +479,7 @@ impl Keypair { /// Verify a signature on a message with this keypair's public key. pub fn verify(&self, message: &[u8], signature: &Signature) -> bool - where D: FixedOutput + Default + Input { + where D: FixedOutput + BlockInput + Default + Input { self.public.verify::(message, signature) } } diff --git a/src/lib.rs b/src/lib.rs index a10ac2f..2f53b72 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -115,6 +115,7 @@ extern crate arrayref; extern crate curve25519_dalek; extern crate generic_array; extern crate digest; +extern crate subtle; #[cfg(feature = "std")] extern crate rand; From a6e5333cb5b5fe74f14c776debe654e56dd2316e Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 1 Aug 2017 22:21:59 +0000 Subject: [PATCH 079/351] Add Cargo.toml feature to optionally use sha2-asm. --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index f6db6c0..4704345 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,3 +46,4 @@ default = ["std"] std = ["rand", "curve25519-dalek/std"] bench = [] nightly = ["curve25519-dalek/nightly"] +asm = ["sha2/asm"] From 23756b4c74d08001af40990b979910f6cd0cdf3f Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 2 Aug 2017 19:39:28 +0000 Subject: [PATCH 080/351] Bump ed25519-dalek version to 0.4.0. --- Cargo.toml | 2 +- README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4704345..bf99220 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "0.3.2" +version = "0.4.0" authors = ["Isis Lovecruft "] readme = "README.md" license = "CC0-1.0" diff --git a/README.md b/README.md index 544b48d..4275507 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ eventually support VXEdDSA in curve25519-dalek. To install, add the following to your project's `Cargo.toml`: [dependencies.ed25519-dalek] - version = "^0.3" + version = "^0.4" Then, in your library or executable source, add: @@ -129,7 +129,7 @@ To cause your application to build `ed25519-dalek` with the nightly feature enabled by default, instead do: [dependencies.ed25519-dalek] - version = "^0.3" + version = "^0.4" features = ["nightly"] To cause your application to instead build with the nightly feature enabled From 07dc9f4ba789b038ac6e48d6dc9c00f9041b29b6 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 15 Aug 2017 05:30:53 +0000 Subject: [PATCH 081/351] Bump curve25519-dalek dependency to 0.11.0. --- Cargo.toml | 2 +- src/ed25519.rs | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bf99220..cb9c597 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ travis-ci = { repository = "isislovecruft/ed25519-dalek", branch = "master"} arrayref = "0.3.3" [dependencies.curve25519-dalek] -version = "^0.10" +version = "^0.11" default-features = false [dependencies.subtle] diff --git a/src/ed25519.rs b/src/ed25519.rs index e699de1..d4a88a8 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -24,8 +24,8 @@ use digest::FixedOutput; use generic_array::typenum::U64; use curve25519_dalek::constants; -use curve25519_dalek::curve::CompressedEdwardsY; -use curve25519_dalek::curve::ExtendedPoint; +use curve25519_dalek::edwards::CompressedEdwardsY; +use curve25519_dalek::edwards::ExtendedPoint; use curve25519_dalek::scalar::Scalar; use subtle::slices_equal; @@ -298,7 +298,7 @@ impl PublicKey { digest[31] &= 127; digest[31] |= 64; - pk = (&Scalar(*digest) * &constants::ED25519_BASEPOINT).compress_edwards().to_bytes(); + pk = (&Scalar(*digest) * &constants::ED25519_BASEPOINT_TABLE).compress_edwards().to_bytes(); PublicKey(CompressedEdwardsY(pk)) } @@ -312,6 +312,8 @@ impl PublicKey { pub fn verify(&self, message: &[u8], signature: &Signature) -> bool where D: Digest + Default { + use curve25519_dalek::edwards::vartime; + let mut h: D = D::default(); let mut a: ExtendedPoint; let ao: Option; @@ -341,7 +343,7 @@ impl PublicKey { let digest_bytes = h.fixed_result(); digest = *array_ref!(digest_bytes, 0, 64); digest_reduced = Scalar::reduce(&digest); - r = &(&digest_reduced * &a) + &(&Scalar(*top_half) * &constants::ED25519_BASEPOINT); + r = vartime::double_scalar_mult_basepoint(&digest_reduced, &a, &Scalar(*top_half)); if slices_equal(bottom_half, &r.compress_edwards().to_bytes()) == 1 { return true @@ -459,7 +461,7 @@ impl Keypair { mesg_digest = Scalar::reduce(&hash); - r = &mesg_digest * &constants::ED25519_BASEPOINT; + r = &mesg_digest * &constants::ED25519_BASEPOINT_TABLE; h = D::default(); h.input(&r.compress_edwards().to_bytes()[..]); @@ -491,7 +493,7 @@ mod test { use std::fs::File; use std::string::String; use std::vec::Vec; - use curve25519_dalek::curve::ExtendedPoint; + use curve25519_dalek::edwards::ExtendedPoint; use rand::OsRng; use rustc_serialize::hex::FromHex; use sha2::Sha512; From 178ebba08e475b7ceef9bf2553742d856cac05c6 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 15 Aug 2017 05:39:50 +0000 Subject: [PATCH 082/351] Bump arrayref dependency to 0.3.4. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index cb9c597..21aeeb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ exclude = [ ".gitignore", "TESTVECTORS", "res/*" ] travis-ci = { repository = "isislovecruft/ed25519-dalek", branch = "master"} [dependencies] -arrayref = "0.3.3" +arrayref = "0.3.4" [dependencies.curve25519-dalek] version = "^0.11" From bc63dcb315f764f7223b3dd3983d2aba84ec7191 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 16 Aug 2017 02:29:24 +0000 Subject: [PATCH 083/351] Bump ed25519-dalek version to 0.4.1. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 21aeeb4..f999309 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "0.4.0" +version = "0.4.1" authors = ["Isis Lovecruft "] readme = "README.md" license = "CC0-1.0" From 039533d3494068467c5011d4e9679f7f2e624084 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 16 Aug 2017 04:19:04 +0000 Subject: [PATCH 084/351] Add additional benchmark for further comparison with ed25519-donna. ed25519-donna includes a "curved25519_scalarmult_basepoint" [sic] function. See https://github.com/isislovecruft/dalek-benchmarks. --- src/ed25519.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/ed25519.rs b/src/ed25519.rs index d4a88a8..17f1a41 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -646,4 +646,16 @@ mod bench { b.iter(| | Keypair::generate::(&mut rng)); } + + #[bench] + fn underlying_scalar_mult_basepoint(b: &mut Bencher) { + use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; + + let scalar: Scalar = Scalar([ 20, 130, 129, 196, 247, 182, 211, 102, + 11, 168, 169, 131, 159, 69, 126, 35, + 109, 193, 175, 54, 118, 234, 138, 81, + 60, 183, 80, 186, 92, 248, 132, 13, ]); + + b.iter(| | &scalar * &ED25519_BASEPOINT_TABLE); + } } From 2ae13d75a0b1e1c89b1218694648542d9ce33e7c Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 16 Aug 2017 04:22:50 +0000 Subject: [PATCH 085/351] Bump ed25519-dalek version to 0.4.2. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f999309..c36de7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "0.4.1" +version = "0.4.2" authors = ["Isis Lovecruft "] readme = "README.md" license = "CC0-1.0" From 52ecf1669e3dad584b0a4023e0e74b25518d7fb9 Mon Sep 17 00:00:00 2001 From: greyspectrum Date: Wed, 6 Sep 2017 13:31:52 -0400 Subject: [PATCH 086/351] Fix a small typo in the license url. --- src/ed25519.rs | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 17f1a41..dacef12 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -3,7 +3,7 @@ // To the extent possible under law, the authors have waived all copyright and // related or neighboring rights to curve25519-dalek, using the Creative // Commons "CC0" public domain dedication. See -// for full details. +// for full details. // // Authors: // - Isis Agora Lovecruft diff --git a/src/lib.rs b/src/lib.rs index 2f53b72..cae6fa3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ // To the extent possible under law, the authors have waived all copyright and // related or neighboring rights to curve25519-dalek, using the Creative // Commons "CC0" public domain dedication. See -// for full details. +// for full details. // // Authors: // - Isis Agora Lovecruft From a1caa4dfda2f5789394e9f9d0a7650f30c24f4e1 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 21 Sep 2017 22:06:48 +0000 Subject: [PATCH 087/351] Fix typo in README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4275507..c4cda86 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ included in the SUPERCOP benchmarking suite (albeit their numbers are for the older Nehalem microarchitecture). Additionally, thanks to Rust, this implementation has both type and memory -safety. It's also easily readable a much larger set of people than those who +safety. It's also easily readable for a much larger set of people than those who can read qhasm, making it more readily and more easily auditable. We're of the opinion that, ultimately, these features—combined with speed—are more valuable than simply cycle counts alone. From 8accff36b3a4743e88c67992b1d9ce5681b1d698 Mon Sep 17 00:00:00 2001 From: Zaki Manian Date: Fri, 23 Jun 2017 15:42:58 -0700 Subject: [PATCH 088/351] Replaces the depracted rustc_serialize with hex --- Cargo.toml | 2 +- src/ed25519.rs | 12 ++++++------ src/lib.rs | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c36de7e..bf1a294 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ version = "^0.6" version = "^0.8" [dev-dependencies] -rustc-serialize = "0.3" +hex = "0.2" sha2 = "^0.6" [features] diff --git a/src/ed25519.rs b/src/ed25519.rs index 17f1a41..4af72d8 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -495,7 +495,7 @@ mod test { use std::vec::Vec; use curve25519_dalek::edwards::ExtendedPoint; use rand::OsRng; - use rustc_serialize::hex::FromHex; + use hex::FromHex; use sha2::Sha512; use super::*; @@ -573,14 +573,14 @@ mod test { let parts: Vec<&str> = line.split(':').collect(); assert_eq!(parts.len(), 5, "wrong number of fields in line {}", lineno); - let sec_bytes: &[u8] = &parts[0].from_hex().unwrap(); - let pub_bytes: &[u8] = &parts[1].from_hex().unwrap(); - let message: &[u8] = &parts[2].from_hex().unwrap(); - let sig_bytes: &[u8] = &parts[3].from_hex().unwrap(); + let sec_bytes: Vec= FromHex::from_hex(&parts[0]).unwrap(); + let pub_bytes: Vec = FromHex::from_hex(&parts[1]).unwrap(); + let message: Vec = FromHex::from_hex(&parts[2]).unwrap(); + let sig_bytes: Vec = FromHex::from_hex(&parts[3]).unwrap(); // The signatures in the test vectors also include the message // at the end, but we just want R and S. - let sig1: Signature = Signature::from_bytes(sig_bytes); + let sig1: Signature = Signature::from_bytes(sig_bytes.as_ref()); let keypair: Keypair = Keypair::from_bytes( array_ref!(*pub_bytes, 0, PUBLIC_KEY_LENGTH), diff --git a/src/lib.rs b/src/lib.rs index 2f53b72..638d9fa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -128,7 +128,7 @@ extern crate std; extern crate sha2; #[cfg(test)] -extern crate rustc_serialize; +extern crate hex; #[cfg(all(test, feature = "bench"))] extern crate test; From 3596e5ec879d046fddafa04981b6aa957111c099 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 5 Oct 2017 06:19:55 +0000 Subject: [PATCH 089/351] Add licence. --- Cargo.toml | 2 +- LICENSE | 28 ++++++++++++++++++++++++++++ src/ed25519.rs | 7 +++---- src/lib.rs | 7 +++---- 4 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 LICENSE diff --git a/Cargo.toml b/Cargo.toml index bf1a294..a29b055 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "ed25519-dalek" version = "0.4.2" authors = ["Isis Lovecruft "] readme = "README.md" -license = "CC0-1.0" +license = "BSD-3-Clause" repository = "https://github.com/isislovecruft/ed25519-dalek" homepage = "https://code.ciph.re/isis/ed25519-dalek" documentation = "https://docs.rs/ed25519-dalek" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..20dcc41 --- /dev/null +++ b/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2017 Isis Agora Lovecruft. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/ed25519.rs b/src/ed25519.rs index 16ddec4..c69641e 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -1,9 +1,8 @@ // -*- mode: rust; -*- // -// To the extent possible under law, the authors have waived all copyright and -// related or neighboring rights to curve25519-dalek, using the Creative -// Commons "CC0" public domain dedication. See -// for full details. +// This file is part of ed25519-dalek. +// Copyright (c) 2017 Isis Lovecruft +// See LICENSE for licensing information. // // Authors: // - Isis Agora Lovecruft diff --git a/src/lib.rs b/src/lib.rs index c2ca6bc..8bc87a5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,8 @@ // -*- mode: rust; -*- // -// To the extent possible under law, the authors have waived all copyright and -// related or neighboring rights to curve25519-dalek, using the Creative -// Commons "CC0" public domain dedication. See -// for full details. +// This file is part of ed25519-dalek. +// Copyright (c) 2017 Isis Lovecruft +// See LICENSE for licensing information. // // Authors: // - Isis Agora Lovecruft From b78487132c6ab27c345475f0467f0b487fce48d1 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 5 Oct 2017 06:58:17 +0000 Subject: [PATCH 090/351] Bump subtle dependency version. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a29b055..d21f611 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ version = "^0.11" default-features = false [dependencies.subtle] -version = "^0.2" +version = "^0.3" default-features = false [dependencies.rand] From e9cd9a2264b02139836e83fcadc3da77fabb6d8d Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 5 Oct 2017 07:03:35 +0000 Subject: [PATCH 091/351] Changes for bumping curve25519-dalek dependency to 0.12.0. --- Cargo.toml | 2 +- src/ed25519.rs | 14 +++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d21f611..7ef20d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ travis-ci = { repository = "isislovecruft/ed25519-dalek", branch = "master"} arrayref = "0.3.4" [dependencies.curve25519-dalek] -version = "^0.11" +version = "^0.12" default-features = false [dependencies.subtle] diff --git a/src/ed25519.rs b/src/ed25519.rs index c69641e..e47cc79 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -297,7 +297,7 @@ impl PublicKey { digest[31] &= 127; digest[31] |= 64; - pk = (&Scalar(*digest) * &constants::ED25519_BASEPOINT_TABLE).compress_edwards().to_bytes(); + pk = (&Scalar(*digest) * &constants::ED25519_BASEPOINT_TABLE).compress().to_bytes(); PublicKey(CompressedEdwardsY(pk)) } @@ -344,11 +344,7 @@ impl PublicKey { digest_reduced = Scalar::reduce(&digest); r = vartime::double_scalar_mult_basepoint(&digest_reduced, &a, &Scalar(*top_half)); - if slices_equal(bottom_half, &r.compress_edwards().to_bytes()) == 1 { - return true - } else { - return false - } + slices_equal(bottom_half, &r.compress().to_bytes()) == 1 } } @@ -463,7 +459,7 @@ impl Keypair { r = &mesg_digest * &constants::ED25519_BASEPOINT_TABLE; h = D::default(); - h.input(&r.compress_edwards().to_bytes()[..]); + h.input(&r.compress().to_bytes()[..]); h.input(public_key); h.input(&message); hash.copy_from_slice(h.fixed_result().as_slice()); @@ -471,7 +467,7 @@ impl Keypair { hram_digest = Scalar::reduce(&hash); s = Scalar::multiply_add(&hram_digest, &expanded_key_secret, &mesg_digest); - t = r.compress_edwards(); + t = r.compress(); signature_bytes[..32].copy_from_slice(&t.0); signature_bytes[32..64].copy_from_slice(&s.0); @@ -518,7 +514,7 @@ mod test { break; } } - public = PublicKey(a.compress_edwards()); + public = PublicKey(a.compress()); assert!(keypair.public.0 == public.0); } From 3b3848fc0ca7ce22a87426f781a227064812b1c1 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 5 Oct 2017 07:05:08 +0000 Subject: [PATCH 092/351] Bump ed25519-dalek version to 0.4.3. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 7ef20d7..370170f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "0.4.2" +version = "0.4.3" authors = ["Isis Lovecruft "] readme = "README.md" license = "BSD-3-Clause" From 8d0dda08479fb6baa38544d8a236715563575df0 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 5 Nov 2017 23:20:04 +0000 Subject: [PATCH 093/351] Implement serde serialisation support and pre-expanded secret keys. * ADD support for serialisation with serde. * ADD a new pre-expanded secret key type, `ExpandedSecretKey`. * FIXES Issue #13: https://github.com/isislovecruft/ed25519-dalek/issues/13 --- .travis.yml | 6 + Cargo.toml | 10 + src/ed25519.rs | 723 +++++++++++++++++++++++++++++++++++++++++-------- src/lib.rs | 157 ++++++++++- 4 files changed, 784 insertions(+), 112 deletions(-) diff --git a/.travis.yml b/.travis.yml index cb15444..1828597 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,12 @@ env: matrix: include: + - rust: stable + env: TEST_COMMAND=test FEATURES=--features="serde" + - rust: beta + env: TEST_COMMAND=test FEATURES=--features="serde" + - rust: nightly + env: TEST_COMMAND=test FEATURES=--features="serde" - rust: nightly env: TEST_COMMAND=build FEATURES=--no-default-features - rust: nightly diff --git a/Cargo.toml b/Cargo.toml index 370170f..9e64a9b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,9 +37,18 @@ version = "^0.6" # same version that digest depends on version = "^0.8" +[dependencies.serde] +version = "^1.0" +optional = true + +[dependencies.sha2] +version = "^0.6" +optional = true + [dev-dependencies] hex = "0.2" sha2 = "^0.6" +bincode = "^0.9" [features] default = ["std"] @@ -47,3 +56,4 @@ std = ["rand", "curve25519-dalek/std"] bench = [] nightly = ["curve25519-dalek/nightly"] asm = ["sha2/asm"] + diff --git a/src/ed25519.rs b/src/ed25519.rs index e47cc79..6dd4099 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -15,6 +15,18 @@ use core::fmt::Debug; #[cfg(feature = "std")] use rand::Rng; +#[cfg(feature = "serde")] +use serde::{Serialize, Deserialize}; +#[cfg(feature = "serde")] +use serde::{Serializer, Deserializer}; +#[cfg(feature = "serde")] +use serde::de::Error as SerdeError; +#[cfg(feature = "serde")] +use serde::de::Visitor; + +#[cfg(feature = "sha2")] +use sha2::Sha512; + use digest::BlockInput; use digest::Digest; use digest::Input; @@ -38,6 +50,9 @@ pub const SECRET_KEY_LENGTH: usize = 32; /// The length of an ed25519 EdDSA `PublicKey`, in bytes. pub const PUBLIC_KEY_LENGTH: usize = 32; +/// The length of an ed25519 EdDSA `Keypair`, in bytes. +pub const KEYPAIR_LENGTH: usize = SECRET_KEY_LENGTH + PUBLIC_KEY_LENGTH; + /// An EdDSA signature. /// /// # Note @@ -47,7 +62,29 @@ pub const PUBLIC_KEY_LENGTH: usize = 32; /// been signed. #[derive(Copy)] #[repr(C)] -pub struct Signature(pub [u8; SIGNATURE_LENGTH]); +pub struct Signature { + /// `r` is an `ExtendedPoint`, formed by using an hash function with + /// 512-bits output to produce the digest of: + /// + /// - the nonce half of the `ExpandedSecretKey`, and + /// - the message to be signed. + /// + /// This digest is then interpreted as a `Scalar` and reduced into an + /// element in ℤ/lℤ. The scalar is then multiplied by the distinguished + /// basepoint to produce `r`, and `ExtendedPoint`. + pub (crate) r: CompressedEdwardsY, + + /// `s` is a `Scalar`, formed by using an hash function with 512-bits output + /// to produce the digest of: + /// + /// - the `r` portion of this `Signature`, + /// - the `PublicKey` which should be used to verify this `Signature`, and + /// - the message to be signed. + /// + /// This digest is then interpreted as a `Scalar` and reduced into an + /// element in ℤ/lℤ. + pub (crate) s: Scalar, +} impl Clone for Signature { fn clone(&self) -> Self { *self } @@ -55,7 +92,7 @@ impl Clone for Signature { impl Debug for Signature { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "Signature([{:?}])", &self.0[..]) + write!(f, "Signature( r: {:?}, s: {:?} )", &self.r, &self.s) } } @@ -65,41 +102,69 @@ impl PartialEq for Signature { fn eq(&self, other: &Signature) -> bool { let mut equal: u8 = 0; - for i in 0..64 { - equal |= self.0[i] ^ other.0[i]; - } - - if equal == 0 { - return true; - } else { - return false; + for i in 0..32 { + equal |= self.r.0[i] ^ other.r.0[i]; + equal |= self.s[i] ^ other.s[i]; } + equal == 0 } } impl Signature { - /// View this `Signature` as a byte array. + /// Convert this `Signature` to a byte array. #[inline] pub fn to_bytes(&self) -> [u8; SIGNATURE_LENGTH] { - self.0 - } + let mut signature_bytes: [u8; SIGNATURE_LENGTH] = [0u8; SIGNATURE_LENGTH]; - /// View this `Signature` as a byte array. - #[inline] - pub fn as_bytes<'a>(&'a self) -> &'a [u8; SIGNATURE_LENGTH] { - &self.0 + signature_bytes[..32].copy_from_slice(&self.r.as_bytes()[..]); + signature_bytes[32..].copy_from_slice(&self.s.as_bytes()[..]); + signature_bytes } /// Construct a `Signature` from a slice of bytes. #[inline] - pub fn from_bytes(bytes: &[u8]) -> Signature { - Signature(*array_ref!(bytes, 0, SIGNATURE_LENGTH)) + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != SIGNATURE_LENGTH { + return Err("Wrong length of bytes for signature! Need 64 bytes.") + } + + let lower: &[u8; 32] = array_ref!(bytes, 0, 32); + let upper: &[u8; 32] = array_ref!(bytes, 32, 32); + + Ok(Signature{ r: CompressedEdwardsY(*lower), s: Scalar(*upper) }) + } +} + +#[cfg(feature = "serde")] +impl Serialize for Signature { + fn serialize(&self, serializer: S) -> Result where S: Serializer { + serializer.serialize_bytes(&self.to_bytes()[..]) + } +} + +#[cfg(feature = "serde")] +impl<'d> Deserialize<'d> for Signature { + fn deserialize(deserializer: D) -> Result where D: Deserializer<'d> { + struct SignatureVisitor; + + impl<'d> Visitor<'d> for SignatureVisitor { + type Value = Signature; + + fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + formatter.write_str("An ed25519 signature as 64 bytes, as specified in RFC8032.") + } + + fn visit_bytes(self, bytes: &[u8]) -> Result where E: SerdeError{ + Signature::from_bytes(bytes).or(Err(SerdeError::invalid_length(bytes.len(), &self))) + } + } + deserializer.deserialize_bytes(SignatureVisitor) } } /// An EdDSA secret key. #[repr(C)] -pub struct SecretKey(pub [u8; SECRET_KEY_LENGTH]); +pub struct SecretKey(pub (crate) [u8; SECRET_KEY_LENGTH]); impl Debug for SecretKey { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { @@ -108,6 +173,11 @@ impl Debug for SecretKey { } impl SecretKey { + /// Expand this `SecretKey` into an `ExpandedSecretKey`. + pub fn expand(&self) -> ExpandedSecretKey where D: Digest + Default { + ExpandedSecretKey::from_secret_key::(&self) + } + /// Convert this secret key to a byte array. #[inline] pub fn to_bytes(&self) -> [u8; SECRET_KEY_LENGTH] { @@ -126,26 +196,38 @@ impl SecretKey { /// /// ``` /// # extern crate ed25519_dalek; - /// # fn main() { + /// # /// use ed25519_dalek::SecretKey; /// use ed25519_dalek::SECRET_KEY_LENGTH; /// + /// # fn doctest() -> Result { /// let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = [ /// 157, 097, 177, 157, 239, 253, 090, 096, /// 186, 132, 074, 244, 146, 236, 044, 196, /// 068, 073, 197, 105, 123, 050, 105, 025, /// 112, 059, 172, 003, 028, 174, 127, 096, ]; /// - /// let secret_key: SecretKey = SecretKey::from_bytes(&secret_key_bytes[..]); + /// let secret_key: SecretKey = SecretKey::from_bytes(&secret_key_bytes)?; + /// # + /// # Ok(secret_key) + /// # } + /// # + /// # fn main() { + /// # let result = doctest(); + /// # assert!(result.is_ok()); /// # } /// ``` /// /// # Returns /// - /// An EdDSA `SecretKey`. + /// A `Result` whose okay value is an EdDSA `SecretKey` or whose error value + /// is an `&'static str` describing the error that occurred. #[inline] - pub fn from_bytes(bytes: &[u8]) -> SecretKey { - SecretKey(*array_ref!(bytes, 0, SECRET_KEY_LENGTH)) + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != SECRET_KEY_LENGTH { + return Err("Wrong length of bytes for creating secret key!"); + } + Ok(SecretKey(*array_ref!(bytes, 0, SECRET_KEY_LENGTH))) } /// Generate a `SecretKey` from a `csprng`. @@ -203,7 +285,7 @@ impl SecretKey { /// /// # Input /// - /// A CSPRING with a `fill_bytes()` method, e.g. the one returned + /// A CSPRNG with a `fill_bytes()` method, e.g. the one returned /// from `rand::OsRng::new()` (in the `rand` crate). /// #[cfg(feature = "std")] @@ -216,14 +298,299 @@ impl SecretKey { } } -/// An ed25519 public key. -#[derive(Copy, Clone)] +#[cfg(feature = "serde")] +impl Serialize for SecretKey { + fn serialize(&self, serializer: S) -> Result where S: Serializer { + serializer.serialize_bytes(self.as_bytes()) + } +} + +#[cfg(feature = "serde")] +impl<'d> Deserialize<'d> for SecretKey { + fn deserialize(deserializer: D) -> Result where D: Deserializer<'d> { + struct SecretKeyVisitor; + + impl<'d> Visitor<'d> for SecretKeyVisitor { + type Value = SecretKey; + + fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + formatter.write_str("An ed25519 secret key as 32 bytes, as specified in RFC8032.") + } + + fn visit_bytes(self, bytes: &[u8]) -> Result where E: SerdeError { + SecretKey::from_bytes(bytes).or(Err(SerdeError::invalid_length(bytes.len(), &self))) + } + } + deserializer.deserialize_bytes(SecretKeyVisitor) + } +} + +/// An "expanded" secret key. +/// +/// This is produced by using an hash function with 512-bits output to digest a +/// `SecretKey`. The output digest is then split in half, the lower half being +/// the actual `key` used to sign messages, after twiddling with some bits.¹ The +/// upper half is used a sort of half-baked, ill-designed² pseudo-domain-separation +/// "nonce"-like thing, which is used during signature production by +/// concatenating it with the message to be signed before the message is hashed. +// +// ¹ This results in a slight bias towards non-uniformity at one spectrum of +// the range of valid keys. Oh well: not my idea; not my problem. +// +// ² It is the author's view (specifically, isis agora lovecruft, in the event +// you'd like to complain about me, again) that this is "ill-designed" because +// this doesn't actually provide true hash domain separation, in that in many +// real-world applications a user wishes to have one key which is used in +// several contexts (such as within tor, which does does domain separation +// manually by pre-concatenating static strings to messages to achieve more +// robust domain separation). In other real-world applications, such as +// bitcoind, a user might wish to have one master keypair from which others are +// derived (à la BIP32) and different domain separators between keys derived at +// different levels (and similarly for tree-based key derivation constructions, +// such as hash-based signatures). Leaving the domain separation to +// application designers, who thus far have produced incompatible, +// slightly-differing, ad hoc domain separation (at least those application +// designers who knew enough cryptographic theory to do so!), is therefore a +// bad design choice on the part of the cryptographer designing primitives +// which should be simple and as foolproof as possible to use for +// non-cryptographers. Further, later in the ed25519 signature scheme, as +// specified in RFC8032, the public key is added into *another* hash digest +// (along with the message, again); it is unclear to this author why there's +// not only one but two poorly-thought-out attempts at domain separation in the +// same signature scheme, and which both fail in exactly the same way. For a +// better-designed, Schnorr-based signature scheme, see Trevor Perrin's work on +// "generalised EdDSA" and "VXEdDSA". #[repr(C)] -pub struct PublicKey(pub CompressedEdwardsY); +pub struct ExpandedSecretKey { + pub (crate) key: Scalar, + pub (crate) nonce: [u8; 32], +} + +#[cfg(feature = "sha2")] +impl<'a> From<&'a SecretKey> for ExpandedSecretKey { + /// Construct an `ExpandedSecretKey` from a `SecretKey`. + /// + /// # Examples + /// + /// ``` + /// # extern crate rand; + /// # extern crate sha2; + /// # extern crate ed25519_dalek; + /// # + /// # fn main() { + /// # + /// use rand::{Rng, OsRng}; + /// use sha2::Sha512; + /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; + /// + /// let mut csprng: OsRng = OsRng::new().unwrap(); + /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); + /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key); + /// # } + /// ``` + fn from(secret_key: &'a SecretKey) -> ExpandedSecretKey { + ExpandedSecretKey::from_secret_key::(&secret_key) + } +} + +impl ExpandedSecretKey { + /// Convert this `ExpandedSecretKey` into an array of 64 bytes. + /// + /// # Returns + /// + /// An array of 64 bytes. The first 32 bytes represent the "expanded" + /// secret key, and the last 32 bytes represent the "domain-separation" + /// "nonce". + /// + /// # Examples + /// + /// ``` + /// # extern crate rand; + /// # extern crate sha2; + /// # extern crate ed25519_dalek; + /// # + /// # #[cfg(feature = "sha2")] + /// # fn main() { + /// # + /// use rand::{Rng, OsRng}; + /// use sha2::Sha512; + /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; + /// + /// let mut csprng: OsRng = OsRng::new().unwrap(); + /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); + /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key); + /// let expanded_secret_key_bytes: [u8; 64] = expanded_secret_key.to_bytes(); + /// + /// assert!(&expanded_secret_key_bytes[..] != &[0u8; 64][..]); + /// # } + /// # + /// # #[cfg(not(feature = "sha2"))] + /// # fn main() { } + /// ``` + #[inline] + pub fn to_bytes(&self) -> [u8; 64] { + let mut bytes: [u8; 64] = [0u8; 64]; + + bytes[..32].copy_from_slice(&self.key.0[..]); + bytes[32..].copy_from_slice(&self.nonce[..]); + bytes + } + + /// Construct an `ExpandedSecretKey` from a slice of bytes. + /// + /// # Returns + /// + /// A `Result` whose okay value is an EdDSA `ExpandedSecretKey` or whose + /// error value is an `&'static str` describing the error that occurred. + /// + /// # Examples + /// + /// ``` + /// # extern crate rand; + /// # extern crate sha2; + /// # extern crate ed25519_dalek; + /// # + /// use rand::{Rng, OsRng}; + /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; + /// + /// # #[cfg(feature = "sha2")] + /// # fn do_test() -> Result { + /// # + /// let mut csprng: OsRng = OsRng::new().unwrap(); + /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); + /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key); + /// let bytes: [u8; 64] = expanded_secret_key.to_bytes(); + /// let expanded_secret_key_again = ExpandedSecretKey::from_bytes(&bytes)?; + /// # + /// # Ok(expanded_secret_key_again) + /// # } + /// # + /// # #[cfg(feature = "sha2")] + /// # fn main() { + /// # let result = do_test(); + /// # assert!(result.is_ok()); + /// # } + /// # + /// # #[cfg(not(feature = "sha2"))] + /// # fn main() {} + /// ``` + #[inline] + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != 64 { + return Err("Wrong length of bytes for creating expanded secret key!"); + } + Ok(ExpandedSecretKey{ key: Scalar(*array_ref!(bytes, 0, 32)), + nonce: *array_ref!(bytes, 32, 32), }) + } + + /// Construct an `ExpandedSecretKey` from a `SecretKey`, using hash function `D`. + /// + /// # Examples + /// + /// ``` + /// # extern crate rand; + /// # extern crate sha2; + /// # extern crate ed25519_dalek; + /// # + /// # fn do_test() { + /// # + /// use rand::{Rng, OsRng}; + /// use sha2::Sha512; + /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; + /// + /// let mut csprng: OsRng = OsRng::new().unwrap(); + /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); + /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from_secret_key::(&secret_key); + /// # } + /// # + /// # fn main() { do_test(); } + /// ``` + pub fn from_secret_key(secret_key: &SecretKey) -> ExpandedSecretKey + where D: Digest + Default { + + let mut h: D = D::default(); + let mut hash: [u8; 64] = [0u8; 64]; + let mut expanded_key: Scalar; + + h.input(secret_key.as_bytes()); + hash.copy_from_slice(h.fixed_result().as_slice()); + + expanded_key = Scalar(*array_ref!(&hash, 0, 32)); + expanded_key[0] &= 248; + expanded_key[31] &= 63; + expanded_key[31] |= 64; + + ExpandedSecretKey{ key: expanded_key, nonce: *array_ref!(&hash, 32, 32) } + } + + /// Sign a message with this `ExpandedSecretKey`. + pub fn sign(&self, message: &[u8], public_key: &PublicKey) -> Signature + where D: Digest + Default { + + let mut h: D = D::default(); + let mut hash: [u8; 64] = [0u8; 64]; + let mesg_digest: Scalar; + let hram_digest: Scalar; + let r: ExtendedPoint; + let s: Scalar; + + h.input(&self.nonce); + h.input(&message); + hash.copy_from_slice(h.fixed_result().as_slice()); + + mesg_digest = Scalar::reduce(&hash); + + r = &mesg_digest * &constants::ED25519_BASEPOINT_TABLE; + + h = D::default(); + h.input(r.compress().as_bytes()); + h.input(public_key.as_bytes()); + h.input(&message); + hash.copy_from_slice(h.fixed_result().as_slice()); + + hram_digest = Scalar::reduce(&hash); + + s = Scalar::multiply_add(&hram_digest, &self.key, &mesg_digest); + + Signature{ r: r.compress(), s: s } + } +} + +#[cfg(feature = "serde")] +impl Serialize for ExpandedSecretKey { + fn serialize(&self, serializer: S) -> Result where S: Serializer { + serializer.serialize_bytes(&self.to_bytes()[..]) + } +} + +#[cfg(feature = "serde")] +impl<'d> Deserialize<'d> for ExpandedSecretKey { + fn deserialize(deserializer: D) -> Result where D: Deserializer<'d> { + struct ExpandedSecretKeyVisitor; + + impl<'d> Visitor<'d> for ExpandedSecretKeyVisitor { + type Value = ExpandedSecretKey; + + fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + formatter.write_str("An ed25519 expanded secret key as 64 bytes, as specified in RFC8032.") + } + + fn visit_bytes(self, bytes: &[u8]) -> Result where E: SerdeError { + ExpandedSecretKey::from_bytes(bytes).or(Err(SerdeError::invalid_length(bytes.len(), &self))) + } + } + deserializer.deserialize_bytes(ExpandedSecretKeyVisitor) + } +} + +/// An ed25519 public key. +#[derive(Copy, Clone, Eq, PartialEq)] +#[repr(C)] +pub struct PublicKey(pub (crate) CompressedEdwardsY); impl Debug for PublicKey { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "PublicKey( CompressedPoint( {:?} ))", self.0) + write!(f, "PublicKey( CompressedEdwardsY( {:?} ))", self.0) } } @@ -252,24 +619,35 @@ impl PublicKey { /// /// ``` /// # extern crate ed25519_dalek; - /// # fn main() { + /// # /// use ed25519_dalek::PublicKey; /// use ed25519_dalek::PUBLIC_KEY_LENGTH; /// + /// # fn doctest() -> Result { /// let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = [ /// 215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, /// 14, 225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26]; /// - /// let public_key: PublicKey = PublicKey::from_bytes(&public_key_bytes); + /// let public_key = PublicKey::from_bytes(&public_key_bytes)?; + /// # + /// # Ok(public_key) + /// # } + /// # + /// # fn main() { + /// # doctest(); /// # } /// ``` /// /// # Returns /// - /// A `PublicKey`. + /// A `Result` whose okay value is an EdDSA `PublicKey` or whose error value + /// is an `&'static str` describing the error that occurred. #[inline] - pub fn from_bytes(bytes: &[u8]) -> PublicKey { - PublicKey(CompressedEdwardsY(*array_ref!(bytes, 0, 32))) + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != PUBLIC_KEY_LENGTH { + return Err("Wrong length of bytes for creating public key!"); + } + Ok(PublicKey(CompressedEdwardsY(*array_ref!(bytes, 0, 32)))) } /// Convert this public key to its underlying extended twisted Edwards coordinate. @@ -320,7 +698,7 @@ impl PublicKey { let digest: [u8; 64]; let digest_reduced: Scalar; - if signature.0[63] & 224 != 0 { + if signature.s[31] & 224 != 0 { return false; } ao = self.decompress(); @@ -332,40 +710,81 @@ impl PublicKey { } a = -(&a); - let top_half: &[u8; 32] = array_ref!(&signature.0, 32, 32); - let bottom_half: &[u8; 32] = array_ref!(&signature.0, 0, 32); - - h.input(&bottom_half[..]); - h.input(&self.to_bytes()); + h.input(signature.r.as_bytes()); + h.input(self.as_bytes()); h.input(&message); let digest_bytes = h.fixed_result(); digest = *array_ref!(digest_bytes, 0, 64); digest_reduced = Scalar::reduce(&digest); - r = vartime::double_scalar_mult_basepoint(&digest_reduced, &a, &Scalar(*top_half)); + r = vartime::double_scalar_mult_basepoint(&digest_reduced, &a, &signature.s); - slices_equal(bottom_half, &r.compress().to_bytes()) == 1 + slices_equal(signature.r.as_bytes(), r.compress().as_bytes()) == 1 + } +} + +#[cfg(feature = "serde")] +impl Serialize for PublicKey { + fn serialize(&self, serializer: S) -> Result where S: Serializer { + serializer.serialize_bytes(self.as_bytes()) + } +} + +#[cfg(feature = "serde")] +impl<'d> Deserialize<'d> for PublicKey { + fn deserialize(deserializer: D) -> Result where D: Deserializer<'d> { + + struct PublicKeyVisitor; + + impl<'d> Visitor<'d> for PublicKeyVisitor { + type Value = PublicKey; + + fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + formatter.write_str("An ed25519 signature as specified in RFC8032") + } + + fn visit_bytes(self, bytes: &[u8]) -> Result where E: SerdeError { + PublicKey::from_bytes(bytes).or(Err(SerdeError::invalid_length(bytes.len(), &self))) + } + } + deserializer.deserialize_bytes(PublicKeyVisitor) } } /// An ed25519 keypair. #[derive(Debug)] -#[repr(C)] pub struct Keypair { - /// The public half of this keypair. - pub public: PublicKey, /// The secret half of this keypair. pub secret: SecretKey, + /// The public half of this keypair. + pub public: PublicKey, } impl Keypair { + /// Convert this keypair to bytes. + /// + /// # Returns + /// + /// An array of bytes, `[u8; KEYPAIR_LENGTH]`. The first + /// `SECRET_KEY_LENGTH` of bytes is the `SecretKey`, and the next + /// `PUBLIC_KEY_LENGTH` bytes is the `PublicKey` (the same as other + /// libraries, such as [Adam Langley's ed25519 Golang + /// implementation](https://github.com/agl/ed25519/)). + pub fn to_bytes(&self) -> [u8; KEYPAIR_LENGTH] { + let mut bytes: [u8; KEYPAIR_LENGTH] = [0u8; KEYPAIR_LENGTH]; + + bytes[..SECRET_KEY_LENGTH].copy_from_slice(self.secret.as_bytes()); + bytes[SECRET_KEY_LENGTH..].copy_from_slice(self.public.as_bytes()); + bytes + } + /// Construct a `Keypair` from the bytes of a `PublicKey` and `SecretKey`. /// /// # Inputs /// - /// * `public`: a `[u8; 32]` representing the compressed Edwards-Y - /// coordinate of a point on curve25519. - /// * `secret`: a `[u8; 32]` representing the corresponding secret key. + /// * `bytes`: an `&[u8]` representing the scalar for the secret key, and a + /// compressed Edwards-Y coordinate of a point on curve25519, both as bytes. + /// (As obtained from `Keypair::to_bytes()`.) /// /// # Warning /// @@ -376,10 +795,16 @@ impl Keypair { /// /// # Returns /// - /// A `Keypair`. - pub fn from_bytes<'a>(public: &'a [u8; 32], secret: &'a [u8; 32]) -> Keypair { - Keypair{ public: PublicKey::from_bytes(public), - secret: SecretKey::from_bytes(secret), } + /// A `Result` whose okay value is an EdDSA `Keypair` or whose error value + /// is an `&'static str` describing the error that occurred. + pub fn from_bytes<'a>(bytes: &'a [u8]) -> Result { + if bytes.len() != KEYPAIR_LENGTH { + return Err("Wrong length of bytes for creating keypair!"); + } + let secret = SecretKey::from_bytes(&bytes[..SECRET_KEY_LENGTH])?; + let public = PublicKey::from_bytes(&bytes[SECRET_KEY_LENGTH..])?; + + Ok(Keypair{ secret: secret, public: public }) } /// Generate an ed25519 keypair. @@ -425,53 +850,8 @@ impl Keypair { } /// Sign a message with this keypair's secret key. - pub fn sign(&self, message: &[u8]) -> Signature - where D: Digest + Default { - - let mut h: D = D::default(); - let mut hash: [u8; 64] = [0u8; 64]; - let mut signature_bytes: [u8; 64] = [0u8; SIGNATURE_LENGTH]; - let mut expanded_key_secret: Scalar; - let mesg_digest: Scalar; - let hram_digest: Scalar; - let r: ExtendedPoint; - let s: Scalar; - let t: CompressedEdwardsY; - - let secret_key: &[u8; 32] = self.secret.as_bytes(); - let public_key: &[u8; 32] = self.public.as_bytes(); - - h.input(secret_key); - hash.copy_from_slice(h.fixed_result().as_slice()); - - expanded_key_secret = Scalar(*array_ref!(&hash, 0, 32)); - expanded_key_secret[0] &= 248; - expanded_key_secret[31] &= 63; - expanded_key_secret[31] |= 64; - - h = D::default(); - h.input(&hash[32..]); - h.input(&message); - hash.copy_from_slice(h.fixed_result().as_slice()); - - mesg_digest = Scalar::reduce(&hash); - - r = &mesg_digest * &constants::ED25519_BASEPOINT_TABLE; - - h = D::default(); - h.input(&r.compress().to_bytes()[..]); - h.input(public_key); - h.input(&message); - hash.copy_from_slice(h.fixed_result().as_slice()); - - hram_digest = Scalar::reduce(&hash); - - s = Scalar::multiply_add(&hram_digest, &expanded_key_secret, &mesg_digest); - t = r.compress(); - - signature_bytes[..32].copy_from_slice(&t.0); - signature_bytes[32..64].copy_from_slice(&s.0); - Signature(*array_ref!(&signature_bytes, 0, 64)) + pub fn sign(&self, message: &[u8]) -> Signature where D: Digest + Default { + self.secret.expand::().sign::(&message, &self.public) } /// Verify a signature on a message with this keypair's public key. @@ -481,6 +861,41 @@ impl Keypair { } } +#[cfg(feature = "serde")] +impl Serialize for Keypair { + fn serialize(&self, serializer: S) -> Result where S: Serializer { + serializer.serialize_bytes(&self.to_bytes()[..]) + } +} + +#[cfg(feature = "serde")] +impl<'d> Deserialize<'d> for Keypair { + fn deserialize(deserializer: D) -> Result where D: Deserializer<'d> { + + struct KeypairVisitor; + + impl<'d> Visitor<'d> for KeypairVisitor { + type Value = Keypair; + + fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + formatter.write_str("An ed25519 signature as specified in RFC8032") + } + + fn visit_bytes(self, bytes: &[u8]) -> Result where E: SerdeError { + let secret_key = SecretKey::from_bytes(&bytes[..SECRET_KEY_LENGTH]); + let public_key = PublicKey::from_bytes(&bytes[SECRET_KEY_LENGTH..]); + + if secret_key.is_ok() && public_key.is_ok() { + Ok(Keypair{ secret: secret_key.unwrap(), public: public_key.unwrap() }) + } else { + Err(SerdeError::invalid_length(bytes.len(), &self)) + } + } + } + deserializer.deserialize_bytes(KeypairVisitor) + } +} + #[cfg(test)] mod test { use std::io::BufReader; @@ -494,6 +909,32 @@ mod test { use sha2::Sha512; use super::*; + #[cfg(all(test, feature = "serde"))] + static PUBLIC_KEY: PublicKey = PublicKey(CompressedEdwardsY([ + 130, 039, 155, 015, 062, 076, 188, 063, + 124, 122, 026, 251, 233, 253, 225, 220, + 014, 041, 166, 120, 108, 035, 254, 077, + 160, 083, 172, 058, 219, 042, 086, 120, ])); + + #[cfg(all(test, feature = "serde"))] + static SECRET_KEY: SecretKey = SecretKey([ + 062, 070, 027, 163, 092, 182, 011, 003, + 077, 234, 098, 004, 011, 127, 079, 228, + 243, 187, 150, 073, 201, 137, 076, 022, + 085, 251, 152, 002, 241, 042, 072, 054, ]); + + /// Signature with the above keypair of a blank message. + #[cfg(all(test, feature = "serde"))] + static SIGNATURE_BYTES: [u8; SIGNATURE_LENGTH] = [ + 010, 126, 151, 143, 157, 064, 047, 001, + 196, 140, 179, 058, 226, 152, 018, 102, + 160, 123, 080, 016, 210, 086, 196, 028, + 053, 231, 012, 157, 169, 019, 158, 063, + 045, 154, 238, 007, 053, 185, 227, 229, + 079, 108, 213, 080, 124, 252, 084, 167, + 216, 085, 134, 144, 129, 149, 041, 081, + 063, 120, 126, 100, 092, 059, 050, 011, ]; + #[test] fn unmarshal_marshal() { // TestUnmarshalMarshal let mut cspring: OsRng; @@ -568,26 +1009,78 @@ mod test { let parts: Vec<&str> = line.split(':').collect(); assert_eq!(parts.len(), 5, "wrong number of fields in line {}", lineno); - let sec_bytes: Vec= FromHex::from_hex(&parts[0]).unwrap(); + let sec_bytes: Vec = FromHex::from_hex(&parts[0]).unwrap(); let pub_bytes: Vec = FromHex::from_hex(&parts[1]).unwrap(); - let message: Vec = FromHex::from_hex(&parts[2]).unwrap(); + let msg_bytes: Vec = FromHex::from_hex(&parts[2]).unwrap(); let sig_bytes: Vec = FromHex::from_hex(&parts[3]).unwrap(); + let secret: SecretKey = SecretKey::from_bytes(&sec_bytes[..SECRET_KEY_LENGTH]).unwrap(); + let public: PublicKey = PublicKey::from_bytes(&pub_bytes[..PUBLIC_KEY_LENGTH]).unwrap(); + let keypair: Keypair = Keypair{ secret: secret, public: public }; + // The signatures in the test vectors also include the message // at the end, but we just want R and S. - let sig1: Signature = Signature::from_bytes(sig_bytes.as_ref()); - - let keypair: Keypair = Keypair::from_bytes( - array_ref!(*pub_bytes, 0, PUBLIC_KEY_LENGTH), - array_ref!(*sec_bytes, 0, SECRET_KEY_LENGTH)); - - let sig2: Signature = keypair.sign::(&message); + let sig1: Signature = Signature::from_bytes(&sig_bytes[..64]).unwrap(); + let sig2: Signature = keypair.sign::(&msg_bytes); assert!(sig1 == sig2, "Signature bytes not equal on line {}", lineno); - assert!(keypair.verify::(&message, &sig2), + assert!(keypair.verify::(&msg_bytes, &sig2), "Signature verification failed on line {}", lineno); } } + + #[test] + fn public_key_from_bytes() { + // Make another function so that we can test the ? operator. + fn do_the_test() -> Result { + let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = [ + 215, 090, 152, 001, 130, 177, 010, 183, + 213, 075, 254, 211, 201, 100, 007, 058, + 014, 225, 114, 243, 218, 166, 035, 037, + 175, 002, 026, 104, 247, 007, 081, 026, ]; + let public_key = PublicKey::from_bytes(&public_key_bytes)?; + + Ok(public_key) + } + assert_eq!(do_the_test(), Ok(PublicKey(CompressedEdwardsY([ + 215, 090, 152, 001, 130, 177, 010, 183, + 213, 075, 254, 211, 201, 100, 007, 058, + 014, 225, 114, 243, 218, 166, 035, 037, + 175, 002, 026, 104, 247, 007, 081, 026, ])))) + } + + #[cfg(all(test, feature = "serde"))] + use bincode::{serialize, deserialize, Infinite}; + + #[cfg(all(test, feature = "serde"))] + #[test] + fn serialize_deserialize_signature() { + let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES).unwrap(); + let encoded_signature: Vec = serialize(&signature, Infinite).unwrap(); + let decoded_signature: Signature = deserialize(&encoded_signature).unwrap(); + + assert_eq!(signature, decoded_signature); + } + + #[cfg(all(test, feature = "serde"))] + #[test] + fn serialize_deserialize_public_key() { + let encoded_public_key: Vec = serialize(&PUBLIC_KEY, Infinite).unwrap(); + let decoded_public_key: PublicKey = deserialize(&encoded_public_key).unwrap(); + + assert_eq!(PUBLIC_KEY, decoded_public_key); + } + + #[cfg(all(test, feature = "serde"))] + #[test] + fn serialize_deserialize_secret_key() { + let encoded_secret_key: Vec = serialize(&SECRET_KEY, Infinite).unwrap(); + let decoded_secret_key: SecretKey = deserialize(&encoded_secret_key).unwrap(); + + for i in 0..32 { + assert_eq!(SECRET_KEY.0[i], decoded_secret_key.0[i]); + } + } } #[cfg(all(test, feature = "bench"))] @@ -625,6 +1118,16 @@ mod bench { b.iter(| | keypair.sign::(msg)); } + #[bench] + fn sign_expanded_key(b: &mut Bencher) { + let mut cspring: OsRng = OsRng::new().unwrap(); + let keypair: Keypair = Keypair::generate::(&mut cspring); + let expanded: ExpandedSecretKey = keypair.secret.expand::(); + let msg: &[u8] = b""; + + b.iter(| | expanded.sign::(msg, &keypair.public)); + } + #[bench] fn verify(b: &mut Bencher) { let mut cspring: OsRng = OsRng::new().unwrap(); diff --git a/src/lib.rs b/src/lib.rs index 8bc87a5..78ec572 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -96,17 +96,165 @@ //! # let keypair: Keypair = Keypair::generate::(&mut cspring); //! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); //! # let signature: Signature = keypair.sign::(message); +//! //! let public_key: PublicKey = keypair.public; //! let verified: bool = public_key.verify::(message, &signature); //! //! assert!(verified); //! # } //! ``` +//! +//! ## Serialisation +//! +//! `PublicKey`s, `SecretKey`s, `Keypair`s, and `Signature`s can be serialised +//! into byte-arrays by calling `.to_bytes()`. It's perfectly acceptible and +//! safe to transfer and/or store those bytes. (Of course, never transfer your +//! secret key to anyone else, since they will only need the public key to +//! verify your signatures!) +//! +//! ``` +//! # extern crate rand; +//! # extern crate sha2; +//! # extern crate ed25519_dalek; +//! # fn main() { +//! # use rand::{Rng, OsRng}; +//! # use sha2::Sha512; +//! # use ed25519_dalek::{Keypair, Signature, PublicKey}; +//! use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; +//! # let mut cspring: OsRng = OsRng::new().unwrap(); +//! # let keypair: Keypair = Keypair::generate::(&mut cspring); +//! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); +//! # let signature: Signature = keypair.sign::(message); +//! # let public_key: PublicKey = keypair.public; +//! # let verified: bool = public_key.verify::(message, &signature); +//! +//! let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = public_key.to_bytes(); +//! let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = keypair.secret.to_bytes(); +//! let keypair_bytes: [u8; KEYPAIR_LENGTH] = keypair.to_bytes(); +//! let signature_bytes: [u8; SIGNATURE_LENGTH] = signature.to_bytes(); +//! # } +//! ``` +//! +//! And similarly, decoded from bytes with `::from_bytes()`: +//! +//! ``` +//! # extern crate rand; +//! # extern crate sha2; +//! # extern crate ed25519_dalek; +//! # use rand::{Rng, OsRng}; +//! # use sha2::Sha512; +//! # use ed25519_dalek::{Keypair, Signature, PublicKey, SecretKey}; +//! # use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; +//! # fn do_test() -> Result<(SecretKey, PublicKey, Keypair, Signature), &'static str> { +//! # let mut cspring: OsRng = OsRng::new().unwrap(); +//! # let keypair_orig: Keypair = Keypair::generate::(&mut cspring); +//! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); +//! # let signature_orig: Signature = keypair_orig.sign::(message); +//! # let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = keypair_orig.public.to_bytes(); +//! # let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = keypair_orig.secret.to_bytes(); +//! # let keypair_bytes: [u8; KEYPAIR_LENGTH] = keypair_orig.to_bytes(); +//! # let signature_bytes: [u8; SIGNATURE_LENGTH] = signature_orig.to_bytes(); +//! # +//! let public_key: PublicKey = PublicKey::from_bytes(&public_key_bytes)?; +//! let secret_key: SecretKey = SecretKey::from_bytes(&secret_key_bytes)?; +//! let keypair: Keypair = Keypair::from_bytes(&keypair_bytes)?; +//! let signature: Signature = Signature::from_bytes(&signature_bytes)?; +//! # +//! # Ok((secret_key, public_key, keypair, signature)) +//! # } +//! # fn main() { +//! # do_test(); +//! # } +//! ``` +//! +//! ### Using Serde +//! +//! If you prefer the bytes to be wrapped in another serialisation format, all +//! types additionally come with built-in [serde](https://serde.rs) support by +//! building `ed25519-dalek` via: +//! +//! ```ignore,bash +//! $ cargo build --features="serde" +//! ``` +//! +//! They can be then serialised into any of the wire formats which serde supports. +//! For example, using [bincode](https://github.com/TyOverby/bincode): +//! +//! ``` +//! # extern crate rand; +//! # extern crate sha2; +//! # extern crate ed25519_dalek; +//! # #[cfg(feature = "serde")] +//! extern crate serde; +//! # #[cfg(feature = "serde")] +//! extern crate bincode; +//! +//! # #[cfg(feature = "serde")] +//! # fn main() { +//! # use rand::{Rng, OsRng}; +//! # use sha2::Sha512; +//! # use ed25519_dalek::{Keypair, Signature, PublicKey}; +//! use bincode::{serialize, Infinite}; +//! # let mut cspring: OsRng = OsRng::new().unwrap(); +//! # let keypair: Keypair = Keypair::generate::(&mut cspring); +//! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); +//! # let signature: Signature = keypair.sign::(message); +//! # let public_key: PublicKey = keypair.public; +//! # let verified: bool = public_key.verify::(message, &signature); +//! +//! let encoded_public_key: Vec = serialize(&public_key, Infinite).unwrap(); +//! let encoded_signature: Vec = serialize(&signature, Infinite).unwrap(); +//! # } +//! # #[cfg(not(feature = "serde"))] +//! # fn main() {} +//! ``` +//! +//! After sending the `encoded_public_key` and `encoded_signature`, the +//! recipient may deserialise them and verify: +//! +//! ``` +//! # extern crate rand; +//! # extern crate sha2; +//! # extern crate ed25519_dalek; +//! # #[cfg(feature = "serde")] +//! # extern crate serde; +//! # #[cfg(feature = "serde")] +//! # extern crate bincode; +//! # +//! # #[cfg(feature = "serde")] +//! # fn main() { +//! # use rand::{Rng, OsRng}; +//! # use sha2::Sha512; +//! # use ed25519_dalek::{Keypair, Signature, PublicKey}; +//! # use bincode::{serialize, Infinite}; +//! use bincode::{deserialize}; +//! +//! # let mut cspring: OsRng = OsRng::new().unwrap(); +//! # let keypair: Keypair = Keypair::generate::(&mut cspring); +//! let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); +//! # let signature: Signature = keypair.sign::(message); +//! # let public_key: PublicKey = keypair.public; +//! # let verified: bool = public_key.verify::(message, &signature); +//! # let encoded_public_key: Vec = serialize(&public_key, Infinite).unwrap(); +//! # let encoded_signature: Vec = serialize(&signature, Infinite).unwrap(); +//! let decoded_public_key: PublicKey = deserialize(&encoded_public_key).unwrap(); +//! let decoded_signature: Signature = deserialize(&encoded_signature).unwrap(); +//! +//! # assert_eq!(public_key, decoded_public_key); +//! # assert_eq!(signature, decoded_signature); +//! # +//! let verified: bool = decoded_public_key.verify::(&message, &decoded_signature); +//! +//! assert!(verified); +//! # } +//! # #[cfg(not(feature = "serde"))] +//! # fn main() {} +//! ``` #![no_std] #![cfg_attr(feature = "nightly", feature(rand))] -#![allow(unused_features)] #![cfg_attr(feature = "bench", feature(test))] +#![allow(unused_features)] #![deny(missing_docs)] // refuse to compile if documentation is missing #[macro_use] @@ -123,7 +271,7 @@ extern crate rand; #[macro_use] extern crate std; -#[cfg(test)] +#[cfg(any(test, feature = "sha2"))] extern crate sha2; #[cfg(test)] @@ -132,6 +280,11 @@ extern crate hex; #[cfg(all(test, feature = "bench"))] extern crate test; +#[cfg(feature = "serde")] +extern crate serde; + +#[cfg(all(test, feature = "serde"))] +extern crate bincode; mod ed25519; From 1cc60aa3ee0e7cfa9711ba4b0df2897fbf27cd86 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 6 Nov 2017 19:09:12 +0000 Subject: [PATCH 094/351] Bump ed25519-dalek version to 0.5.0. --- Cargo.toml | 2 +- README.md | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9e64a9b..5402d2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "0.4.3" +version = "0.5.0" authors = ["Isis Lovecruft "] readme = "README.md" license = "BSD-3-Clause" diff --git a/README.md b/README.md index f659b3d..5ea1c7b 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ eventually support VXEdDSA in curve25519-dalek. To install, add the following to your project's `Cargo.toml`: [dependencies.ed25519-dalek] - version = "^0.4" + version = "^0.5" Then, in your library or executable source, add: @@ -129,7 +129,7 @@ To cause your application to build `ed25519-dalek` with the nightly feature enabled by default, instead do: [dependencies.ed25519-dalek] - version = "^0.4" + version = "^0.5" features = ["nightly"] To cause your application to instead build with the nightly feature enabled @@ -139,6 +139,15 @@ to the `Cargo.toml`: [features] nightly = ["ed25519-dalek/nightly"] +Using the `nightly` feature will nearly double the latency of signing and +verification. + +To enable [serde](https://serde.rs) support, build `ed25519-dalek` with: + + [dependencies.ed25519-dalek] + version = "^0.5" + features = ["serde"] + # TODO From 7888bfe3f851cda161da3a6b7d4eaf77fe481513 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 4 Dec 2017 00:50:17 +0000 Subject: [PATCH 095/351] Fix serde expecting string. --- src/ed25519.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 6dd4099..cd9afcf 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -740,7 +740,7 @@ impl<'d> Deserialize<'d> for PublicKey { type Value = PublicKey; fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - formatter.write_str("An ed25519 signature as specified in RFC8032") + formatter.write_str("An ed25519 public key as a 32-byte compressed point, as specified in RFC8032") } fn visit_bytes(self, bytes: &[u8]) -> Result where E: SerdeError { From 65b7a210628a03660de64b48a5b302272aaa768c Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 4 Dec 2017 00:50:39 +0000 Subject: [PATCH 096/351] Make the Keypair struct inherit repr(C). --- src/ed25519.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ed25519.rs b/src/ed25519.rs index cd9afcf..2844923 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -753,6 +753,7 @@ impl<'d> Deserialize<'d> for PublicKey { /// An ed25519 keypair. #[derive(Debug)] +#[repr(C)] pub struct Keypair { /// The secret half of this keypair. pub secret: SecretKey, From 9d89e2f66079e78ea7eae5cc722415e218c7e7ad Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 4 Dec 2017 02:11:27 +0000 Subject: [PATCH 097/351] Upgrade to using curve25519-dalek-0.14.0. --- Cargo.toml | 5 +-- src/ed25519.rs | 101 +++++++++++++++++++++++++++++-------------------- src/lib.rs | 2 - 3 files changed, 62 insertions(+), 46 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5402d2a..0bbd8de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,11 +15,8 @@ exclude = [ ".gitignore", "TESTVECTORS", "res/*" ] [badges] travis-ci = { repository = "isislovecruft/ed25519-dalek", branch = "master"} -[dependencies] -arrayref = "0.3.4" - [dependencies.curve25519-dalek] -version = "^0.12" +version = "^0.14" default-features = false [dependencies.subtle] diff --git a/src/ed25519.rs b/src/ed25519.rs index 2844923..c38276e 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -128,10 +128,17 @@ impl Signature { return Err("Wrong length of bytes for signature! Need 64 bytes.") } - let lower: &[u8; 32] = array_ref!(bytes, 0, 32); - let upper: &[u8; 32] = array_ref!(bytes, 32, 32); + let mut lower: [u8; 32] = [0u8; 32]; + let mut upper: [u8; 32] = [0u8; 32]; - Ok(Signature{ r: CompressedEdwardsY(*lower), s: Scalar(*upper) }) + lower.copy_from_slice(&bytes[..32]); + upper.copy_from_slice(&bytes[32..]); + + if upper[31] & 224 != 0 { + return Err("High-bit of scalar 's' in signature must not be set.") + } + + Ok(Signature{ r: CompressedEdwardsY(lower), s: Scalar::from_bits(upper) }) } } @@ -227,7 +234,11 @@ impl SecretKey { if bytes.len() != SECRET_KEY_LENGTH { return Err("Wrong length of bytes for creating secret key!"); } - Ok(SecretKey(*array_ref!(bytes, 0, SECRET_KEY_LENGTH))) + let mut bits: [u8; 32] = [0u8; 32]; + + bits.copy_from_slice(&bytes[..32]); + + Ok(SecretKey(bits)) } /// Generate a `SecretKey` from a `csprng`. @@ -431,7 +442,7 @@ impl ExpandedSecretKey { pub fn to_bytes(&self) -> [u8; 64] { let mut bytes: [u8; 64] = [0u8; 64]; - bytes[..32].copy_from_slice(&self.key.0[..]); + bytes[..32].copy_from_slice(self.key.as_bytes()); bytes[32..].copy_from_slice(&self.nonce[..]); bytes } @@ -479,8 +490,15 @@ impl ExpandedSecretKey { if bytes.len() != 64 { return Err("Wrong length of bytes for creating expanded secret key!"); } - Ok(ExpandedSecretKey{ key: Scalar(*array_ref!(bytes, 0, 32)), - nonce: *array_ref!(bytes, 32, 32), }) + + let mut lower: [u8; 32] = [0u8; 32]; + let mut upper: [u8; 32] = [0u8; 32]; + + lower.copy_from_slice(&bytes[00..32]); + upper.copy_from_slice(&bytes[32..64]); + + Ok(ExpandedSecretKey{ key: Scalar::from_bits(lower), + nonce: upper }) } /// Construct an `ExpandedSecretKey` from a `SecretKey`, using hash function `D`. @@ -509,18 +527,21 @@ impl ExpandedSecretKey { where D: Digest + Default { let mut h: D = D::default(); - let mut hash: [u8; 64] = [0u8; 64]; - let mut expanded_key: Scalar; + let mut hash: [u8; 64] = [0u8; 64]; + let mut lower: [u8; 32] = [0u8; 32]; + let mut upper: [u8; 32] = [0u8; 32]; h.input(secret_key.as_bytes()); hash.copy_from_slice(h.fixed_result().as_slice()); - expanded_key = Scalar(*array_ref!(&hash, 0, 32)); - expanded_key[0] &= 248; - expanded_key[31] &= 63; - expanded_key[31] |= 64; + lower.copy_from_slice(&hash[00..32]); + upper.copy_from_slice(&hash[32..64]); - ExpandedSecretKey{ key: expanded_key, nonce: *array_ref!(&hash, 32, 32) } + lower[0] &= 248; + lower[31] &= 63; + lower[31] |= 64; + + ExpandedSecretKey{ key: Scalar::from_bits(lower), nonce: upper, } } /// Sign a message with this `ExpandedSecretKey`. @@ -538,7 +559,7 @@ impl ExpandedSecretKey { h.input(&message); hash.copy_from_slice(h.fixed_result().as_slice()); - mesg_digest = Scalar::reduce(&hash); + mesg_digest = Scalar::from_bytes_mod_order_wide(&hash); r = &mesg_digest * &constants::ED25519_BASEPOINT_TABLE; @@ -548,9 +569,9 @@ impl ExpandedSecretKey { h.input(&message); hash.copy_from_slice(h.fixed_result().as_slice()); - hram_digest = Scalar::reduce(&hash); + hram_digest = Scalar::from_bytes_mod_order_wide(&hash); - s = Scalar::multiply_add(&hram_digest, &self.key, &mesg_digest); + s = &(&hram_digest * &self.key) + &mesg_digest; Signature{ r: r.compress(), s: s } } @@ -647,7 +668,11 @@ impl PublicKey { if bytes.len() != PUBLIC_KEY_LENGTH { return Err("Wrong length of bytes for creating public key!"); } - Ok(PublicKey(CompressedEdwardsY(*array_ref!(bytes, 0, 32)))) + let mut bits: [u8; 32] = [0u8; 32]; + + bits.copy_from_slice(&bytes[..32]); + + Ok(PublicKey(CompressedEdwardsY(bits))) } /// Convert this public key to its underlying extended twisted Edwards coordinate. @@ -662,20 +687,20 @@ impl PublicKey { pub fn from_secret(secret_key: &SecretKey) -> PublicKey where D: Digest + Default { - let mut h: D = D::default(); - let mut hash: [u8; 64] = [0u8; 64]; - let pk: [u8; 32]; - let mut digest: &mut [u8; 32]; + let mut h: D = D::default(); + let mut hash: [u8; 64] = [0u8; 64]; + let mut digest: [u8; 32] = [0u8; 32]; + let pk: [u8; 32]; h.input(secret_key.as_bytes()); hash.copy_from_slice(h.fixed_result().as_slice()); - digest = array_mut_ref!(&mut hash, 0, 32); + digest.copy_from_slice(&hash[..32]); digest[0] &= 248; digest[31] &= 127; digest[31] |= 64; - pk = (&Scalar(*digest) * &constants::ED25519_BASEPOINT_TABLE).compress().to_bytes(); + pk = (&Scalar::from_bits(digest) * &constants::ED25519_BASEPOINT_TABLE).compress().to_bytes(); PublicKey(CompressedEdwardsY(pk)) } @@ -687,20 +712,15 @@ impl PublicKey { /// Returns true if the signature was successfully verified, and /// false otherwise. pub fn verify(&self, message: &[u8], signature: &Signature) -> bool - where D: Digest + Default { - + where D: Digest + Default + { use curve25519_dalek::edwards::vartime; let mut h: D = D::default(); let mut a: ExtendedPoint; let ao: Option; - let r: ExtendedPoint; - let digest: [u8; 64]; - let digest_reduced: Scalar; + let mut digest: [u8; 64] = [0u8; 64]; - if signature.s[31] & 224 != 0 { - return false; - } ao = self.decompress(); if ao.is_some() { @@ -714,10 +734,10 @@ impl PublicKey { h.input(self.as_bytes()); h.input(&message); - let digest_bytes = h.fixed_result(); - digest = *array_ref!(digest_bytes, 0, 64); - digest_reduced = Scalar::reduce(&digest); - r = vartime::double_scalar_mult_basepoint(&digest_reduced, &a, &signature.s); + digest.copy_from_slice(h.fixed_result().as_slice()); + + let digest_reduced: Scalar = Scalar::from_bytes_mod_order_wide(&digest); + let r: ExtendedPoint = vartime::double_scalar_mult_basepoint(&digest_reduced, &a, &signature.s); slices_equal(signature.r.as_bytes(), r.compress().as_bytes()) == 1 } @@ -1150,10 +1170,11 @@ mod bench { fn underlying_scalar_mult_basepoint(b: &mut Bencher) { use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; - let scalar: Scalar = Scalar([ 20, 130, 129, 196, 247, 182, 211, 102, - 11, 168, 169, 131, 159, 69, 126, 35, - 109, 193, 175, 54, 118, 234, 138, 81, - 60, 183, 80, 186, 92, 248, 132, 13, ]); + let scalar: Scalar = Scalar::from_bits([ + 20, 130, 129, 196, 247, 182, 211, 102, + 11, 168, 169, 131, 159, 69, 126, 35, + 109, 193, 175, 54, 118, 234, 138, 81, + 60, 183, 80, 186, 92, 248, 132, 13, ]); b.iter(| | &scalar * &ED25519_BASEPOINT_TABLE); } diff --git a/src/lib.rs b/src/lib.rs index 78ec572..617f579 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -257,8 +257,6 @@ #![allow(unused_features)] #![deny(missing_docs)] // refuse to compile if documentation is missing -#[macro_use] -extern crate arrayref; extern crate curve25519_dalek; extern crate generic_array; extern crate digest; From b5b295e414a0ee859750b9800cd912a7568530e5 Mon Sep 17 00:00:00 2001 From: Without Boats Date: Tue, 5 Dec 2017 18:13:43 -0800 Subject: [PATCH 098/351] Use a custom error type instead of &'static str. Advantages of a custom error type: - It can be more easily integrated into other error types by clients; they can implement From for their error types, or they can use a library like failure. - It is a zero-sized type, which can enable some representational optimizations. - It can be easier and more stable to test for. --- src/ed25519.rs | 86 +++++++++++++++++++++++++++++++++----------------- src/lib.rs | 6 ++-- 2 files changed, 60 insertions(+), 32 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 6dd4099..b790d91 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -10,7 +10,7 @@ //! A Rust implementation of ed25519 EdDSA key generation, signing, and //! verification. -use core::fmt::Debug; +use core::fmt::{self, Debug, Display}; #[cfg(feature = "std")] use rand::Rng; @@ -123,10 +123,8 @@ impl Signature { /// Construct a `Signature` from a slice of bytes. #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != SIGNATURE_LENGTH { - return Err("Wrong length of bytes for signature! Need 64 bytes.") - } + pub fn from_bytes(bytes: &[u8]) -> Result { + check_bytes_len(bytes, SIGNATURE_LENGTH)?; let lower: &[u8; 32] = array_ref!(bytes, 0, 32); let upper: &[u8; 32] = array_ref!(bytes, 32, 32); @@ -199,8 +197,9 @@ impl SecretKey { /// # /// use ed25519_dalek::SecretKey; /// use ed25519_dalek::SECRET_KEY_LENGTH; + /// use ed25519_dalek::FromBytesError; /// - /// # fn doctest() -> Result { + /// # fn doctest() -> Result { /// let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = [ /// 157, 097, 177, 157, 239, 253, 090, 096, /// 186, 132, 074, 244, 146, 236, 044, 196, @@ -221,12 +220,11 @@ impl SecretKey { /// # Returns /// /// A `Result` whose okay value is an EdDSA `SecretKey` or whose error value - /// is an `&'static str` describing the error that occurred. + /// is an `FromBytesError` describing the error that occurred. #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != SECRET_KEY_LENGTH { - return Err("Wrong length of bytes for creating secret key!"); - } + pub fn from_bytes(bytes: &[u8]) -> Result { + check_bytes_len(bytes, SECRET_KEY_LENGTH)?; + Ok(SecretKey(*array_ref!(bytes, 0, SECRET_KEY_LENGTH))) } @@ -441,7 +439,7 @@ impl ExpandedSecretKey { /// # Returns /// /// A `Result` whose okay value is an EdDSA `ExpandedSecretKey` or whose - /// error value is an `&'static str` describing the error that occurred. + /// error value is an `FromBytesError` describing the error that occurred. /// /// # Examples /// @@ -452,9 +450,10 @@ impl ExpandedSecretKey { /// # /// use rand::{Rng, OsRng}; /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; + /// use ed25519_dalek::FromBytesError; /// /// # #[cfg(feature = "sha2")] - /// # fn do_test() -> Result { + /// # fn do_test() -> Result { /// # /// let mut csprng: OsRng = OsRng::new().unwrap(); /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); @@ -475,10 +474,9 @@ impl ExpandedSecretKey { /// # fn main() {} /// ``` #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != 64 { - return Err("Wrong length of bytes for creating expanded secret key!"); - } + pub fn from_bytes(bytes: &[u8]) -> Result { + check_bytes_len(bytes, 64)?; + Ok(ExpandedSecretKey{ key: Scalar(*array_ref!(bytes, 0, 32)), nonce: *array_ref!(bytes, 32, 32), }) } @@ -622,8 +620,9 @@ impl PublicKey { /// # /// use ed25519_dalek::PublicKey; /// use ed25519_dalek::PUBLIC_KEY_LENGTH; + /// use ed25519_dalek::FromBytesError; /// - /// # fn doctest() -> Result { + /// # fn doctest() -> Result { /// let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = [ /// 215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, /// 14, 225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26]; @@ -641,12 +640,11 @@ impl PublicKey { /// # Returns /// /// A `Result` whose okay value is an EdDSA `PublicKey` or whose error value - /// is an `&'static str` describing the error that occurred. + /// is an `FromBytesError` describing the error that occurred. #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != PUBLIC_KEY_LENGTH { - return Err("Wrong length of bytes for creating public key!"); - } + pub fn from_bytes(bytes: &[u8]) -> Result { + check_bytes_len(bytes, PUBLIC_KEY_LENGTH)?; + Ok(PublicKey(CompressedEdwardsY(*array_ref!(bytes, 0, 32)))) } @@ -796,11 +794,10 @@ impl Keypair { /// # Returns /// /// A `Result` whose okay value is an EdDSA `Keypair` or whose error value - /// is an `&'static str` describing the error that occurred. - pub fn from_bytes<'a>(bytes: &'a [u8]) -> Result { - if bytes.len() != KEYPAIR_LENGTH { - return Err("Wrong length of bytes for creating keypair!"); - } + /// is an `FromBytesError` describing the error that occurred. + pub fn from_bytes<'a>(bytes: &'a [u8]) -> Result { + check_bytes_len(bytes, KEYPAIR_LENGTH)?; + let secret = SecretKey::from_bytes(&bytes[..SECRET_KEY_LENGTH])?; let public = PublicKey::from_bytes(&bytes[SECRET_KEY_LENGTH..])?; @@ -896,6 +893,37 @@ impl<'d> Deserialize<'d> for Keypair { } } +/// An error which occurred when using the `from_bytes` constructor. +/// +/// This error will be returned if the byte slice given was not the correct +/// length for constructing that kind of object. +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +pub struct FromBytesError { + _private: (), +} + +impl Display for FromBytesError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "wrong length of bytes when constructing ed25519 object") + } +} + +#[cfg(feature = "std")] +impl ::std::error::Error for FromBytesError { + fn description(&self) -> &str { + "wrong length of bytes when constructing ed25519 object" + } +} + +#[inline(always)] +fn check_bytes_len(bytes: &[u8], len: usize) -> Result<(), FromBytesError> { + if bytes.len() != len { + Err(FromBytesError { _private: () }) + } else { + Ok(()) + } +} + #[cfg(test)] mod test { use std::io::BufReader; @@ -1032,7 +1060,7 @@ mod test { #[test] fn public_key_from_bytes() { // Make another function so that we can test the ? operator. - fn do_the_test() -> Result { + fn do_the_test() -> Result { let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = [ 215, 090, 152, 001, 130, 177, 010, 183, 213, 075, 254, 211, 201, 100, 007, 058, diff --git a/src/lib.rs b/src/lib.rs index 78ec572..d862d23 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -143,9 +143,9 @@ //! # extern crate ed25519_dalek; //! # use rand::{Rng, OsRng}; //! # use sha2::Sha512; -//! # use ed25519_dalek::{Keypair, Signature, PublicKey, SecretKey}; +//! # use ed25519_dalek::{Keypair, Signature, PublicKey, SecretKey, FromBytesError}; //! # use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; -//! # fn do_test() -> Result<(SecretKey, PublicKey, Keypair, Signature), &'static str> { +//! # fn do_test() -> Result<(SecretKey, PublicKey, Keypair, Signature), FromBytesError> { //! # let mut cspring: OsRng = OsRng::new().unwrap(); //! # let keypair_orig: Keypair = Keypair::generate::(&mut cspring); //! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); @@ -267,7 +267,7 @@ extern crate subtle; #[cfg(feature = "std")] extern crate rand; -#[cfg(test)] +#[cfg(any(feature = "std", test))] #[macro_use] extern crate std; From 96246506c07dd9ee1017ed7a222258f1c33d6ca7 Mon Sep 17 00:00:00 2001 From: Without Boats Date: Tue, 5 Dec 2017 18:18:34 -0800 Subject: [PATCH 099/351] This is a breaking change. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 5402d2a..1de19d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "0.5.0" +version = "0.6.0" authors = ["Isis Lovecruft "] readme = "README.md" license = "BSD-3-Clause" From 6c1acaca7c40877af5eca3c2eb191821baf0ab45 Mon Sep 17 00:00:00 2001 From: Without Boats Date: Wed, 6 Dec 2017 15:25:12 -0800 Subject: [PATCH 100/351] Use failure instead of std::error::Error. failure is no_std compatible, whereas std::error::Error is not. --- Cargo.toml | 6 +++++- src/ed25519.rs | 7 +------ src/lib.rs | 1 + 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1de19d7..c64887b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,10 @@ optional = true version = "^0.6" optional = true +[dependencies.failure] +version = "^0.1.1" +default-features = false + [dev-dependencies] hex = "0.2" sha2 = "^0.6" @@ -52,7 +56,7 @@ bincode = "^0.9" [features] default = ["std"] -std = ["rand", "curve25519-dalek/std"] +std = ["rand", "curve25519-dalek/std", "failure/std"] bench = [] nightly = ["curve25519-dalek/nightly"] asm = ["sha2/asm"] diff --git a/src/ed25519.rs b/src/ed25519.rs index b790d91..da8c2cb 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -908,12 +908,7 @@ impl Display for FromBytesError { } } -#[cfg(feature = "std")] -impl ::std::error::Error for FromBytesError { - fn description(&self) -> &str { - "wrong length of bytes when constructing ed25519 object" - } -} +impl ::failure::Fail for FromBytesError { } #[inline(always)] fn check_bytes_len(bytes: &[u8], len: usize) -> Result<(), FromBytesError> { diff --git a/src/lib.rs b/src/lib.rs index d862d23..4d20d9e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -263,6 +263,7 @@ extern crate curve25519_dalek; extern crate generic_array; extern crate digest; extern crate subtle; +extern crate failure; #[cfg(feature = "std")] extern crate rand; From f64b20b351fddeb802ed0eb8ed9a92cfd83ac101 Mon Sep 17 00:00:00 2001 From: Oleg Andreev Date: Tue, 12 Dec 2017 15:41:39 -0800 Subject: [PATCH 101/351] Do not require feature=std for PublicKey::from_secret --- src/ed25519.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 6dd4099..5412278 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -657,7 +657,6 @@ impl PublicKey { } /// Derive this public key from its corresponding `SecretKey`. - #[cfg(feature = "std")] #[allow(unused_assignments)] pub fn from_secret(secret_key: &SecretKey) -> PublicKey where D: Digest + Default { From c7b69c656246b0ed9783afa7a95825e1616ba3bf Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 23 Dec 2017 22:33:59 +0000 Subject: [PATCH 102/351] Expand boats' error types to give more detailed reasons for failures. This code was significantly based off without boats' error types in commit 6c1acaca7c40877af5eca3c2eb191821baf0ab45, and also upon conversation with them. Please target them with praise, and blame me for whatever mistakes I might have made. --- src/ed25519.rs | 112 +++++++++++++++++++++++-------------------------- src/errors.rs | 82 ++++++++++++++++++++++++++++++++++++ src/lib.rs | 7 +++- 3 files changed, 140 insertions(+), 61 deletions(-) create mode 100644 src/errors.rs diff --git a/src/ed25519.rs b/src/ed25519.rs index 988c278..15b6c4b 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -10,7 +10,7 @@ //! A Rust implementation of ed25519 EdDSA key generation, signing, and //! verification. -use core::fmt::{self, Debug, Display}; +use core::fmt::{Debug}; #[cfg(feature = "std")] use rand::Rng; @@ -41,10 +41,13 @@ use curve25519_dalek::scalar::Scalar; use subtle::slices_equal; -/// The length of an ed25519 EdDSA `Signature`, in bytes. +use errors::DecodingError; +use errors::InternalError; + +/// The length of a curve25519 EdDSA `Signature`, in bytes. pub const SIGNATURE_LENGTH: usize = 64; -/// The length of an ed25519 EdDSA `SecretKey`, in bytes. +/// The length of a curve25519 EdDSA `SecretKey`, in bytes. pub const SECRET_KEY_LENGTH: usize = 32; /// The length of an ed25519 EdDSA `PublicKey`, in bytes. @@ -53,6 +56,15 @@ pub const PUBLIC_KEY_LENGTH: usize = 32; /// The length of an ed25519 EdDSA `Keypair`, in bytes. pub const KEYPAIR_LENGTH: usize = SECRET_KEY_LENGTH + PUBLIC_KEY_LENGTH; +/// The length of the "key" portion of an "expanded" curve25519 EdDSA secret key, in bytes. +const EXPANDED_SECRET_KEY_KEY_LENGTH: usize = 32; + +/// The length of the "nonce" portion of an "expanded" curve25519 EdDSA secret key, in bytes. +const EXPANDED_SECRET_KEY_NONCE_LENGTH: usize = 32; + +/// The length of an "expanded" curve25519 EdDSA key, `ExpandedSecretKey`, in bytes. +pub const EXPANDED_SECRET_KEY_LENGTH: usize = EXPANDED_SECRET_KEY_KEY_LENGTH + EXPANDED_SECRET_KEY_NONCE_LENGTH; + /// An EdDSA signature. /// /// # Note @@ -123,9 +135,11 @@ impl Signature { /// Construct a `Signature` from a slice of bytes. #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { - check_bytes_len(bytes, SIGNATURE_LENGTH)?; - + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != SIGNATURE_LENGTH { + return Err(DecodingError(InternalError::BytesLengthError{ + name: "Signature", length: SIGNATURE_LENGTH })); + } let mut lower: [u8; 32] = [0u8; 32]; let mut upper: [u8; 32] = [0u8; 32]; @@ -133,7 +147,7 @@ impl Signature { upper.copy_from_slice(&bytes[32..]); if upper[31] & 224 != 0 { - return Err("High-bit of scalar 's' in signature must not be set.") + return Err(DecodingError(InternalError::ScalarFormatError)); } Ok(Signature{ r: CompressedEdwardsY(lower), s: Scalar::from_bits(upper) }) @@ -204,9 +218,9 @@ impl SecretKey { /// # /// use ed25519_dalek::SecretKey; /// use ed25519_dalek::SECRET_KEY_LENGTH; - /// use ed25519_dalek::FromBytesError; + /// use ed25519_dalek::DecodingError; /// - /// # fn doctest() -> Result { + /// # fn doctest() -> Result { /// let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = [ /// 157, 097, 177, 157, 239, 253, 090, 096, /// 186, 132, 074, 244, 146, 236, 044, 196, @@ -227,13 +241,14 @@ impl SecretKey { /// # Returns /// /// A `Result` whose okay value is an EdDSA `SecretKey` or whose error value - /// is an `FromBytesError` describing the error that occurred. + /// is an `DecodingError` wrapping the internal error that occurred. #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { - check_bytes_len(bytes, SECRET_KEY_LENGTH)?; - + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != SECRET_KEY_LENGTH { + return Err(DecodingError(InternalError::BytesLengthError{ + name: "SecretKey", length: SECRET_KEY_LENGTH })); + } let mut bits: [u8; 32] = [0u8; 32]; - bits.copy_from_slice(&bytes[..32]); Ok(SecretKey(bits)) @@ -437,7 +452,7 @@ impl ExpandedSecretKey { /// # fn main() { } /// ``` #[inline] - pub fn to_bytes(&self) -> [u8; 64] { + pub fn to_bytes(&self) -> [u8; EXPANDED_SECRET_KEY_LENGTH] { let mut bytes: [u8; 64] = [0u8; 64]; bytes[..32].copy_from_slice(self.key.as_bytes()); @@ -450,7 +465,7 @@ impl ExpandedSecretKey { /// # Returns /// /// A `Result` whose okay value is an EdDSA `ExpandedSecretKey` or whose - /// error value is an `FromBytesError` describing the error that occurred. + /// error value is an `DecodingError` describing the error that occurred. /// /// # Examples /// @@ -461,10 +476,10 @@ impl ExpandedSecretKey { /// # /// use rand::{Rng, OsRng}; /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; - /// use ed25519_dalek::FromBytesError; + /// use ed25519_dalek::DecodingError; /// /// # #[cfg(feature = "sha2")] - /// # fn do_test() -> Result { + /// # fn do_test() -> Result { /// # /// let mut csprng: OsRng = OsRng::new().unwrap(); /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); @@ -485,9 +500,11 @@ impl ExpandedSecretKey { /// # fn main() {} /// ``` #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { - check_bytes_len(bytes, 64)?; - + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != EXPANDED_SECRET_KEY_LENGTH { + return Err(DecodingError(InternalError::BytesLengthError{ + name: "ExpandedSecretKey", length: EXPANDED_SECRET_KEY_LENGTH })); + } let mut lower: [u8; 32] = [0u8; 32]; let mut upper: [u8; 32] = [0u8; 32]; @@ -640,9 +657,9 @@ impl PublicKey { /// # /// use ed25519_dalek::PublicKey; /// use ed25519_dalek::PUBLIC_KEY_LENGTH; - /// use ed25519_dalek::FromBytesError; + /// use ed25519_dalek::DecodingError; /// - /// # fn doctest() -> Result { + /// # fn doctest() -> Result { /// let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = [ /// 215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, /// 14, 225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26]; @@ -660,13 +677,14 @@ impl PublicKey { /// # Returns /// /// A `Result` whose okay value is an EdDSA `PublicKey` or whose error value - /// is an `FromBytesError` describing the error that occurred. + /// is an `DecodingError` describing the error that occurred. #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { - check_bytes_len(bytes, PUBLIC_KEY_LENGTH)?; - + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != PUBLIC_KEY_LENGTH { + return Err(DecodingError(InternalError::BytesLengthError{ + name: "PublicKey", length: PUBLIC_KEY_LENGTH })); + } let mut bits: [u8; 32] = [0u8; 32]; - bits.copy_from_slice(&bytes[..32]); Ok(PublicKey(CompressedEdwardsY(bits))) @@ -814,10 +832,12 @@ impl Keypair { /// # Returns /// /// A `Result` whose okay value is an EdDSA `Keypair` or whose error value - /// is an `FromBytesError` describing the error that occurred. - pub fn from_bytes<'a>(bytes: &'a [u8]) -> Result { - check_bytes_len(bytes, KEYPAIR_LENGTH)?; - + /// is an `DecodingError` describing the error that occurred. + pub fn from_bytes<'a>(bytes: &'a [u8]) -> Result { + if bytes.len() != KEYPAIR_LENGTH { + return Err(DecodingError(InternalError::BytesLengthError{ + name: "Keypair", length: KEYPAIR_LENGTH})); + } let secret = SecretKey::from_bytes(&bytes[..SECRET_KEY_LENGTH])?; let public = PublicKey::from_bytes(&bytes[SECRET_KEY_LENGTH..])?; @@ -913,32 +933,6 @@ impl<'d> Deserialize<'d> for Keypair { } } -/// An error which occurred when using the `from_bytes` constructor. -/// -/// This error will be returned if the byte slice given was not the correct -/// length for constructing that kind of object. -#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] -pub struct FromBytesError { - _private: (), -} - -impl Display for FromBytesError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "wrong length of bytes when constructing ed25519 object") - } -} - -impl ::failure::Fail for FromBytesError { } - -#[inline(always)] -fn check_bytes_len(bytes: &[u8], len: usize) -> Result<(), FromBytesError> { - if bytes.len() != len { - Err(FromBytesError { _private: () }) - } else { - Ok(()) - } -} - #[cfg(test)] mod test { use std::io::BufReader; @@ -1075,7 +1069,7 @@ mod test { #[test] fn public_key_from_bytes() { // Make another function so that we can test the ? operator. - fn do_the_test() -> Result { + fn do_the_test() -> Result { let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = [ 215, 090, 152, 001, 130, 177, 010, 183, 213, 075, 254, 211, 201, 100, 007, 058, diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 0000000..ca672ec --- /dev/null +++ b/src/errors.rs @@ -0,0 +1,82 @@ +// -*- mode: rust; -*- +// +// This file is part of ed25519-dalek. +// Copyright (c) 2017 Isis Lovecruft +// See LICENSE for licensing information. +// +// Authors: +// - Isis Agora Lovecruft + +//! Errors which may occur when parsing keys and/or signatures to or from wire formats. + +// rustc seems to think the typenames in match statements (e.g. in +// Display) should be snake cased, for some reason. +#![allow(non_snake_case)] + +use core::fmt; +use core::fmt::Display; + +/// Internal errors. Most application-level developer will likely not +/// need to pay any attention to these. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +pub (crate) enum InternalError { + PointDecompressionError, + ScalarFormatError, + /// An error in the length of bytes handed to a constructor. + /// + /// To use this, pass a string specifying the `name` of the type which is + /// returning the error, and the `length` in bytes which its constructor + /// expects. + BytesLengthError{ name: &'static str, length: usize }, +} + +impl Display for InternalError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + InternalError::PointDecompressionError + => write!(f, "Cannot decompress extended twisted edwards point"), + InternalError::ScalarFormatError + => write!(f, "Cannot use scalar with high-bit set"), + InternalError::BytesLengthError{ name: n, length: l} + => write!(f, "{} must be {} bytes in length", n, l), + } + } +} + +impl ::failure::Fail for InternalError {} + +/// Errors which may occur in the `from_bytes()` constructors of `PublicKey`, +/// `SecretKey`, `ExpandedSecretKey`, `Keypair`, and `Signature`. +/// +/// There was an internal problem due to parsing the `Signature`. +/// +/// This error may arise due to: +/// +/// * A problem decompressing `r`, a curve point, in the `Signature`, or the +/// curve point for a `PublicKey`. +/// * A problem with the format of `s`, a scalar, in the `Signature`. This +/// is only raised if the high-bit of the scalar was set. (Scalars must +/// only be constructed from 255-bit integers.) +/// * Being given bytes with a length different to what was expected. +#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)] +pub struct DecodingError(pub (crate) InternalError); + +impl Display for DecodingError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.0 { + InternalError::PointDecompressionError => write!(f, "{}", self.0), + InternalError::ScalarFormatError => write!(f, "{}", self.0), + InternalError::BytesLengthError{ name: _, length: _ } => write!(f, "{}", self.0), + } + } +} + +impl ::failure::Fail for DecodingError { + fn cause(&self) -> Option<&::failure::Fail> { + match self.0 { + InternalError::PointDecompressionError => Some(&self.0), + InternalError::ScalarFormatError => Some(&self.0), + InternalError::BytesLengthError{ name: _, length: _} => Some(&self.0), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 4e4da19..a9a34e4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -143,9 +143,9 @@ //! # extern crate ed25519_dalek; //! # use rand::{Rng, OsRng}; //! # use sha2::Sha512; -//! # use ed25519_dalek::{Keypair, Signature, PublicKey, SecretKey, FromBytesError}; +//! # use ed25519_dalek::{Keypair, Signature, PublicKey, SecretKey, DecodingError}; //! # use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; -//! # fn do_test() -> Result<(SecretKey, PublicKey, Keypair, Signature), FromBytesError> { +//! # fn do_test() -> Result<(SecretKey, PublicKey, Keypair, Signature), DecodingError> { //! # let mut cspring: OsRng = OsRng::new().unwrap(); //! # let keypair_orig: Keypair = Keypair::generate::(&mut cspring); //! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); @@ -287,5 +287,8 @@ extern crate bincode; mod ed25519; +pub mod errors; + // Export everything public in ed25519. pub use ed25519::*; +pub use errors::*; From fff5deddf8ba06f6ae6ada0ddb0c4cb80477c63f Mon Sep 17 00:00:00 2001 From: Jacob Hughes Date: Wed, 17 Jan 2018 14:33:41 -0500 Subject: [PATCH 103/351] Update dependencies Update rand to verion 0.4 Update sha2 and digest to version 0.7 Update hex to version 0.3 --- Cargo.toml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5402d2a..15a0640 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,10 +28,10 @@ default-features = false [dependencies.rand] optional = true -version = "^0.3" +version = "^0.4" [dependencies.digest] -version = "^0.6" +version = "^0.7" [dependencies.generic-array] # same version that digest depends on @@ -42,12 +42,12 @@ version = "^1.0" optional = true [dependencies.sha2] -version = "^0.6" +version = "^0.7" optional = true [dev-dependencies] -hex = "0.2" -sha2 = "^0.6" +hex = "^0.3" +sha2 = "^0.7" bincode = "^0.9" [features] From e356e476d89e95c81eaa536093d154baa359cf0a Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 20 Jan 2018 02:33:53 +0000 Subject: [PATCH 104/351] Enable slack notifications. --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 1828597..7a99953 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,3 +27,8 @@ matrix: script: - cargo $TEST_COMMAND $FEATURES + +notifications: + slack: + rooms: + - dalek-cryptography:Xxv9WotKYWdSoKlgKNqXiHoD#dalek-bots From 04c8574106fd954bfa5faf5c22bb40a9357eb59c Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 20 Jan 2018 02:36:42 +0000 Subject: [PATCH 105/351] Bump versions for several dependencies. --- Cargo.toml | 12 ++++++------ src/ed25519.rs | 10 +++------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 103cb9a..ad944c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,30 +16,30 @@ exclude = [ ".gitignore", "TESTVECTORS", "res/*" ] travis-ci = { repository = "isislovecruft/ed25519-dalek", branch = "master"} [dependencies.curve25519-dalek] -version = "^0.14" +version = "0.14" default-features = false [dependencies.subtle] -version = "^0.3" +version = "0.5" default-features = false [dependencies.rand] optional = true -version = "^0.3" +version = "0.4" [dependencies.digest] -version = "^0.6" +version = "0.6" [dependencies.generic-array] # same version that digest depends on -version = "^0.8" +version = "0.9" [dependencies.serde] version = "^1.0" optional = true [dependencies.sha2] -version = "^0.6" +version = "0.7" optional = true [dependencies.failure] diff --git a/src/ed25519.rs b/src/ed25519.rs index 15b6c4b..b3b2f2b 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -27,10 +27,7 @@ use serde::de::Visitor; #[cfg(feature = "sha2")] use sha2::Sha512; -use digest::BlockInput; use digest::Digest; -use digest::Input; -use digest::FixedOutput; use generic_array::typenum::U64; @@ -539,7 +536,6 @@ impl ExpandedSecretKey { /// ``` pub fn from_secret_key(secret_key: &SecretKey) -> ExpandedSecretKey where D: Digest + Default { - let mut h: D = D::default(); let mut hash: [u8; 64] = [0u8; 64]; let mut lower: [u8; 32] = [0u8; 32]; @@ -561,7 +557,6 @@ impl ExpandedSecretKey { /// Sign a message with this `ExpandedSecretKey`. pub fn sign(&self, message: &[u8], public_key: &PublicKey) -> Signature where D: Digest + Default { - let mut h: D = D::default(); let mut hash: [u8; 64] = [0u8; 64]; let mesg_digest: Scalar; @@ -887,13 +882,14 @@ impl Keypair { } /// Sign a message with this keypair's secret key. - pub fn sign(&self, message: &[u8]) -> Signature where D: Digest + Default { + pub fn sign(&self, message: &[u8]) -> Signature + where D: Digest + Default { self.secret.expand::().sign::(&message, &self.public) } /// Verify a signature on a message with this keypair's public key. pub fn verify(&self, message: &[u8], signature: &Signature) -> bool - where D: FixedOutput + BlockInput + Default + Input { + where D: Digest + Default { self.public.verify::(message, signature) } } From 6724268ea112a31f952b3548ee4b726668cb5566 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 20 Jan 2018 02:49:53 +0000 Subject: [PATCH 106/351] Update website and repo links. --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ad944c9..953ad88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "ed25519-dalek" version = "0.6.0" -authors = ["Isis Lovecruft "] +authors = ["Isis Lovecruft "] readme = "README.md" license = "BSD-3-Clause" -repository = "https://github.com/isislovecruft/ed25519-dalek" -homepage = "https://code.ciph.re/isis/ed25519-dalek" +repository = "https://github.com/dalek-cryptography/ed25519-dalek" +homepage = "https://dalek.rs" documentation = "https://docs.rs/ed25519-dalek" keywords = ["cryptography", "ed25519", "curve25519", "signature", "ECC"] categories = ["cryptography", "no-std"] From b650ae0c28cf242eaa74fa07200628efddfe9d3a Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 20 Jan 2018 02:54:42 +0000 Subject: [PATCH 107/351] Update dev dependency versions. --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 953ad88..65c8fc8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,8 +47,8 @@ version = "^0.1.1" default-features = false [dev-dependencies] -hex = "0.2" -sha2 = "^0.6" +hex = "0.3" +sha2 = "0.7" bincode = "^0.9" [features] From 4c633acaf93e5c07952bf6721d9eb7c946771b8f Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 20 Jan 2018 02:56:35 +0000 Subject: [PATCH 108/351] Bump ed25519-dalek version to 0.6.0. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5ea1c7b..b580cf8 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ eventually support VXEdDSA in curve25519-dalek. To install, add the following to your project's `Cargo.toml`: [dependencies.ed25519-dalek] - version = "^0.5" + version = "^0.6" Then, in your library or executable source, add: @@ -129,7 +129,7 @@ To cause your application to build `ed25519-dalek` with the nightly feature enabled by default, instead do: [dependencies.ed25519-dalek] - version = "^0.5" + version = "^0.6" features = ["nightly"] To cause your application to instead build with the nightly feature enabled @@ -145,7 +145,7 @@ verification. To enable [serde](https://serde.rs) support, build `ed25519-dalek` with: [dependencies.ed25519-dalek] - version = "^0.5" + version = "^0.6" features = ["serde"] From 20fd237d35d10352dd553fa766af3afb5059fc63 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 20 Jan 2018 03:00:47 +0000 Subject: [PATCH 109/351] Revert to using sha2^=0.6. --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 65c8fc8..53040ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ version = "^1.0" optional = true [dependencies.sha2] -version = "0.7" +version = "0.6" optional = true [dependencies.failure] @@ -48,7 +48,7 @@ default-features = false [dev-dependencies] hex = "0.3" -sha2 = "0.7" +sha2 = "0.6" bincode = "^0.9" [features] From 08a5fc34ae22c14bf70bb17ff8ce93334745eb65 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 26 Jan 2018 22:19:55 +0000 Subject: [PATCH 110/351] Fix serde expecting() string for Keypair. --- src/ed25519.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index b01077b..57b5dcb 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -910,7 +910,9 @@ impl<'d> Deserialize<'d> for Keypair { type Value = Keypair; fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - formatter.write_str("An ed25519 signature as specified in RFC8032") + formatter.write_str("An ed25519 keypair, 64 bytes in total where the secret key is \ + the first 32 bytes and is in unexpanded form, and the second \ + 32 bytes is a compressed point for a public key.") } fn visit_bytes(self, bytes: &[u8]) -> Result where E: SerdeError { From e648c641cca393ad77da595f6b5fbc7460294b8c Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 2 Feb 2018 22:17:01 +0000 Subject: [PATCH 111/351] Fix typo in benchmark variable name. --- src/ed25519.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 57b5dcb..dcfec7f 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -1145,8 +1145,8 @@ mod bench { #[bench] fn sign(b: &mut Bencher) { - let mut cspring: OsRng = OsRng::new().unwrap(); - let keypair: Keypair = Keypair::generate::(&mut cspring); + let mut csprng: OsRng = OsRng::new().unwrap(); + let keypair: Keypair = Keypair::generate::(&mut csprng); let msg: &[u8] = b""; b.iter(| | keypair.sign::(msg)); @@ -1154,8 +1154,8 @@ mod bench { #[bench] fn sign_expanded_key(b: &mut Bencher) { - let mut cspring: OsRng = OsRng::new().unwrap(); - let keypair: Keypair = Keypair::generate::(&mut cspring); + let mut csprng: OsRng = OsRng::new().unwrap(); + let keypair: Keypair = Keypair::generate::(&mut csprng); let expanded: ExpandedSecretKey = keypair.secret.expand::(); let msg: &[u8] = b""; @@ -1164,8 +1164,8 @@ mod bench { #[bench] fn verify(b: &mut Bencher) { - let mut cspring: OsRng = OsRng::new().unwrap(); - let keypair: Keypair = Keypair::generate::(&mut cspring); + let mut csprng: OsRng = OsRng::new().unwrap(); + let keypair: Keypair = Keypair::generate::(&mut csprng); let msg: &[u8] = b""; let sig: Signature = keypair.sign::(msg); From 40e887ce54ebcb09479b544ed8a2259ba5c01ba7 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 2 Feb 2018 22:17:36 +0000 Subject: [PATCH 112/351] Bump ed25519-dalek version to 0.6.1. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4782c02..c63b3b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "0.6.0" +version = "0.6.1" authors = ["Isis Lovecruft "] readme = "README.md" license = "BSD-3-Clause" From ac3d974f70f849839d64041be1087638347c9815 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 26 Mar 2018 02:11:43 +0000 Subject: [PATCH 113/351] Update Travis badge to point to dalek-cryptography repo. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c63b3b3..8403122 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ description = "Fast and efficient ed25519 EdDSA key generations, signing, and ve exclude = [ ".gitignore", "TESTVECTORS", "res/*" ] [badges] -travis-ci = { repository = "isislovecruft/ed25519-dalek", branch = "master"} +travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master"} [dependencies.curve25519-dalek] version = "0.14" From f790bd2ce1cca283add6676e05fe620f5a366235 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 26 Mar 2018 02:13:39 +0000 Subject: [PATCH 114/351] Update subtle and curve25519-dalek dependencies. --- Cargo.toml | 10 +++++----- src/ed25519.rs | 26 +++++++++++++------------- src/errors.rs | 3 ++- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8403122..4adfde6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,11 +16,11 @@ exclude = [ ".gitignore", "TESTVECTORS", "res/*" ] travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master"} [dependencies.curve25519-dalek] -version = "0.14" +version = "0.16" default-features = false [dependencies.subtle] -version = "0.5" +version = "0.6" default-features = false [dependencies.rand] @@ -53,8 +53,8 @@ bincode = "^0.9" [features] default = ["std"] -std = ["rand", "curve25519-dalek/std", "failure/std"] +std = ["rand", "subtle/std", "curve25519-dalek/std", "failure/std"] bench = [] -nightly = ["curve25519-dalek/nightly"] +nightly = ["curve25519-dalek/nightly", "subtle/nightly"] asm = ["sha2/asm"] - +yolocrypto = ["curve25519-dalek/yolocrypto"] diff --git a/src/ed25519.rs b/src/ed25519.rs index dcfec7f..e7a656c 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -33,10 +33,10 @@ use generic_array::typenum::U64; use curve25519_dalek::constants; use curve25519_dalek::edwards::CompressedEdwardsY; -use curve25519_dalek::edwards::ExtendedPoint; +use curve25519_dalek::edwards::EdwardsPoint; use curve25519_dalek::scalar::Scalar; -use subtle::slices_equal; +use subtle::ConstantTimeEq; use errors::DecodingError; use errors::InternalError; @@ -72,7 +72,7 @@ pub const EXPANDED_SECRET_KEY_LENGTH: usize = EXPANDED_SECRET_KEY_KEY_LENGTH + E #[derive(Copy)] #[repr(C)] pub struct Signature { - /// `r` is an `ExtendedPoint`, formed by using an hash function with + /// `r` is an `EdwardsPoint`, formed by using an hash function with /// 512-bits output to produce the digest of: /// /// - the nonce half of the `ExpandedSecretKey`, and @@ -80,7 +80,7 @@ pub struct Signature { /// /// This digest is then interpreted as a `Scalar` and reduced into an /// element in ℤ/lℤ. The scalar is then multiplied by the distinguished - /// basepoint to produce `r`, and `ExtendedPoint`. + /// basepoint to produce `r`, and `EdwardsPoint`. pub (crate) r: CompressedEdwardsY, /// `s` is a `Scalar`, formed by using an hash function with 512-bits output @@ -561,7 +561,7 @@ impl ExpandedSecretKey { let mut hash: [u8; 64] = [0u8; 64]; let mesg_digest: Scalar; let hram_digest: Scalar; - let r: ExtendedPoint; + let r: EdwardsPoint; let s: Scalar; h.input(&self.nonce); @@ -687,7 +687,7 @@ impl PublicKey { /// Convert this public key to its underlying extended twisted Edwards coordinate. #[inline] - fn decompress(&self) -> Option { + fn decompress(&self) -> Option { self.0.decompress() } @@ -726,8 +726,8 @@ impl PublicKey { use curve25519_dalek::edwards::vartime; let mut h: D = D::default(); - let mut a: ExtendedPoint; - let ao: Option; + let mut a: EdwardsPoint; + let ao: Option; let mut digest: [u8; 64] = [0u8; 64]; ao = self.decompress(); @@ -746,9 +746,9 @@ impl PublicKey { digest.copy_from_slice(h.fixed_result().as_slice()); let digest_reduced: Scalar = Scalar::from_bytes_mod_order_wide(&digest); - let r: ExtendedPoint = vartime::double_scalar_mult_basepoint(&digest_reduced, &a, &signature.s); + let r: EdwardsPoint = vartime::double_scalar_mul_basepoint(&digest_reduced, &a, &signature.s); - slices_equal(signature.r.as_bytes(), r.compress().as_bytes()) == 1 + (signature.r.as_bytes()).ct_eq(r.compress().as_bytes()).unwrap_u8() == 1 } } @@ -937,7 +937,7 @@ mod test { use std::fs::File; use std::string::String; use std::vec::Vec; - use curve25519_dalek::edwards::ExtendedPoint; + use curve25519_dalek::edwards::EdwardsPoint; use rand::OsRng; use hex::FromHex; use sha2::Sha512; @@ -973,8 +973,8 @@ mod test { fn unmarshal_marshal() { // TestUnmarshalMarshal let mut cspring: OsRng; let mut keypair: Keypair; - let mut x: Option; - let a: ExtendedPoint; + let mut x: Option; + let a: EdwardsPoint; let public: PublicKey; cspring = OsRng::new().unwrap(); diff --git a/src/errors.rs b/src/errors.rs index ca672ec..57968dd 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -16,10 +16,11 @@ use core::fmt; use core::fmt::Display; -/// Internal errors. Most application-level developer will likely not +/// Internal errors. Most application-level developers will likely not /// need to pay any attention to these. #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub (crate) enum InternalError { + #[allow(dead_code)] PointDecompressionError, ScalarFormatError, /// An error in the length of bytes handed to a constructor. From d61808cb084d3f6e314bebfb22d5e9e3566de760 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 26 Mar 2018 02:32:01 +0000 Subject: [PATCH 115/351] Remove done TODO item from README. --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index b580cf8..869b755 100644 --- a/README.md +++ b/README.md @@ -151,8 +151,6 @@ To enable [serde](https://serde.rs) support, build `ed25519-dalek` with: # TODO - * Maybe add methods to make exporting keys for backup easier. Maybe using - serde? * We can probably make this go even faster if we implement SHA512, rather than using the rust-crypto implementation whose API requires that we allocate memory and memzero it before mutating to store the From e3dfc8843c79f5f04099142257623b8ec97494d0 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 26 Mar 2018 02:32:38 +0000 Subject: [PATCH 116/351] Bump ed25519-dalek version to 0.6.2. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4adfde6..55d6173 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "0.6.1" +version = "0.6.2" authors = ["Isis Lovecruft "] readme = "README.md" license = "BSD-3-Clause" From 51f176007e56bcbce822748938ca12eb93886e53 Mon Sep 17 00:00:00 2001 From: Robin Millette Date: Mon, 2 Apr 2018 17:25:40 -0400 Subject: [PATCH 117/351] fix links image and #9 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 869b755..04139e7 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ or form, safe. The signatures produced by this library are malleable, as discussed in [the original paper](https://ed25519.cr.yp.to/ed25519-20110926.pdf): -![](https://github.com/isislovecruft/ed25519-dalek/blob/develop/res/ed25519-malleability.png) +![](https://github.com/dalek-cryptography/ed25519-dalek/blob/master/res/ed25519-malleability.png) We could eliminate the malleability property by multiplying by the curve cofactor, however, this would cause our implementation to *not* match the @@ -111,7 +111,7 @@ In short, if malleable signatures are bad for your protocol, don't use them. Consider using a curve25519-based Verifiable Random Function (VRF), such as [Trevor Perrin's VXEdDSA](https://www.whispersystems.org/docs/specifications/xeddsa/), instead. We -[plan](https://github.com/isislovecruft/curve25519-dalek/issues/9) to +[plan](https://github.com/dalek-cryptography/curve25519-dalek/issues/9) to eventually support VXEdDSA in curve25519-dalek. # Installation From f6a631c2298183b8cb893c92a0b1666937c5c6ca Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 15 May 2018 23:34:01 +0000 Subject: [PATCH 118/351] WIP Update curve25519-dalek dependency to 0.17. --- Cargo.toml | 9 ++++++--- src/ed25519.rs | 23 ++++++++++++++--------- src/lib.rs | 12 ++++++------ 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 55d6173..f0ee015 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ exclude = [ ".gitignore", "TESTVECTORS", "res/*" ] travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master"} [dependencies.curve25519-dalek] -version = "0.16" +version = "0.17" default-features = false [dependencies.subtle] @@ -25,7 +25,7 @@ default-features = false [dependencies.rand] optional = true -version = "0.4" +version = "0.5.0-pre.2" [dependencies.digest] version = "^0.7" @@ -52,9 +52,12 @@ sha2 = "^0.7" bincode = "^0.9" [features] -default = ["std"] +default = ["std", "u64_backend"] std = ["rand", "subtle/std", "curve25519-dalek/std", "failure/std"] bench = [] nightly = ["curve25519-dalek/nightly", "subtle/nightly"] asm = ["sha2/asm"] yolocrypto = ["curve25519-dalek/yolocrypto"] +u64_backend = ["curve25519-dalek/u64_backend"] +u32_backend = ["curve25519-dalek/u32_backend"] +avx2_backend = ["curve25519-dalek/avx2_backend"] diff --git a/src/ed25519.rs b/src/ed25519.rs index e7a656c..1803aa1 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -12,6 +12,8 @@ use core::fmt::{Debug}; +#[cfg(feature = "std")] +use rand::CryptoRng; #[cfg(feature = "std")] use rand::Rng; @@ -310,7 +312,9 @@ impl SecretKey { /// from `rand::OsRng::new()` (in the `rand` crate). /// #[cfg(feature = "std")] - pub fn generate(csprng: &mut Rng) -> SecretKey { + pub fn generate(csprng: &mut T) -> SecretKey + where T: CryptoRng + Rng, + { let mut sk: SecretKey = SecretKey([0u8; 32]); csprng.fill_bytes(&mut sk.0); @@ -723,8 +727,6 @@ impl PublicKey { pub fn verify(&self, message: &[u8], signature: &Signature) -> bool where D: Digest + Default { - use curve25519_dalek::edwards::vartime; - let mut h: D = D::default(); let mut a: EdwardsPoint; let ao: Option; @@ -746,7 +748,8 @@ impl PublicKey { digest.copy_from_slice(h.fixed_result().as_slice()); let digest_reduced: Scalar = Scalar::from_bytes_mod_order_wide(&digest); - let r: EdwardsPoint = vartime::double_scalar_mul_basepoint(&digest_reduced, &a, &signature.s); + let r: EdwardsPoint = EdwardsPoint::vartime_double_scalar_mul_basepoint(&digest_reduced, + &a, &signature.s); (signature.r.as_bytes()).ct_eq(r.compress().as_bytes()).unwrap_u8() == 1 } @@ -856,7 +859,7 @@ impl Keypair { /// use ed25519_dalek::Signature; /// /// let mut cspring: OsRng = OsRng::new().unwrap(); - /// let keypair: Keypair = Keypair::generate::(&mut cspring); + /// let keypair: Keypair = Keypair::generate::(&mut cspring); /// /// # } /// ``` @@ -872,8 +875,10 @@ impl Keypair { /// which is available with `use sha2::Sha512` as in the example above. /// Other suitable hash functions include Keccak-512 and Blake2b-512. #[cfg(feature = "std")] - pub fn generate(csprng: &mut Rng) -> Keypair - where D: Digest + Default { + pub fn generate(csprng: &mut R) -> Keypair + where D: Digest + Default, + R: CryptoRng + Rng, + { let sk: SecretKey = SecretKey::generate(csprng); let pk: PublicKey = PublicKey::from_secret::(&sk); @@ -981,7 +986,7 @@ mod test { // from_bytes() fails if vx²-u=0 and vx²+u=0 loop { - keypair = Keypair::generate::(&mut cspring); + keypair = Keypair::generate::(&mut cspring); x = keypair.public.decompress(); if x.is_some() { @@ -1005,7 +1010,7 @@ mod test { let bad: &[u8] = "wrong message".as_bytes(); cspring = OsRng::new().unwrap(); - keypair = Keypair::generate::(&mut cspring); + keypair = Keypair::generate::(&mut cspring); good_sig = keypair.sign::(&good); bad_sig = keypair.sign::(&bad); diff --git a/src/lib.rs b/src/lib.rs index a9a34e4..99011e2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,7 +32,7 @@ //! use ed25519_dalek::Signature; //! //! let mut cspring: OsRng = OsRng::new().unwrap(); -//! let keypair: Keypair = Keypair::generate::(&mut cspring); +//! let keypair: Keypair = Keypair::generate::(&mut cspring); //! # } //! ``` //! @@ -49,7 +49,7 @@ //! # use ed25519_dalek::Keypair; //! # use ed25519_dalek::Signature; //! # let mut cspring: OsRng = OsRng::new().unwrap(); -//! # let keypair: Keypair = Keypair::generate::(&mut cspring); +//! # let keypair: Keypair = Keypair::generate::(&mut cspring); //! let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); //! let signature: Signature = keypair.sign::(message); //! # } @@ -69,7 +69,7 @@ //! # use ed25519_dalek::Keypair; //! # use ed25519_dalek::Signature; //! # let mut cspring: OsRng = OsRng::new().unwrap(); -//! # let keypair: Keypair = Keypair::generate::(&mut cspring); +//! # let keypair: Keypair = Keypair::generate::(&mut cspring); //! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); //! # let signature: Signature = keypair.sign::(message); //! let verified: bool = keypair.verify::(message, &signature); @@ -93,7 +93,7 @@ //! # use ed25519_dalek::Signature; //! use ed25519_dalek::PublicKey; //! # let mut cspring: OsRng = OsRng::new().unwrap(); -//! # let keypair: Keypair = Keypair::generate::(&mut cspring); +//! # let keypair: Keypair = Keypair::generate::(&mut cspring); //! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); //! # let signature: Signature = keypair.sign::(message); //! @@ -122,7 +122,7 @@ //! # use ed25519_dalek::{Keypair, Signature, PublicKey}; //! use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; //! # let mut cspring: OsRng = OsRng::new().unwrap(); -//! # let keypair: Keypair = Keypair::generate::(&mut cspring); +//! # let keypair: Keypair = Keypair::generate::(&mut cspring); //! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); //! # let signature: Signature = keypair.sign::(message); //! # let public_key: PublicKey = keypair.public; @@ -147,7 +147,7 @@ //! # use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; //! # fn do_test() -> Result<(SecretKey, PublicKey, Keypair, Signature), DecodingError> { //! # let mut cspring: OsRng = OsRng::new().unwrap(); -//! # let keypair_orig: Keypair = Keypair::generate::(&mut cspring); +//! # let keypair_orig: Keypair = Keypair::generate::(&mut cspring); //! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); //! # let signature_orig: Signature = keypair_orig.sign::(message); //! # let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = keypair_orig.public.to_bytes(); From 3a54e970a6e02c2aa37f204dc66056dfebf49155 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 16 May 2018 18:49:08 +0000 Subject: [PATCH 119/351] Change travis config to test various backends. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7a99953..dc3f978 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ matrix: - rust: nightly env: TEST_COMMAND=test FEATURES=--features="serde" - rust: nightly - env: TEST_COMMAND=build FEATURES=--no-default-features + env: TEST_COMMAND=build FEATURES="--no-default-features --features=u32_backend" - rust: nightly env: TEST_COMMAND=test FEATURES=--features="nightly" - rust: nightly From 85a3776d2721f2fc174b076150b0d53e424fa638 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 16 May 2018 19:56:09 +0000 Subject: [PATCH 120/351] Collapse part of the .travis build matrix. --- .travis.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index dc3f978..753186e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,15 +7,10 @@ rust: env: - TEST_COMMAND=test FEATURES='' + - TEST_COMMAND=test FEATURES=--features="serde" matrix: include: - - rust: stable - env: TEST_COMMAND=test FEATURES=--features="serde" - - rust: beta - env: TEST_COMMAND=test FEATURES=--features="serde" - - rust: nightly - env: TEST_COMMAND=test FEATURES=--features="serde" - rust: nightly env: TEST_COMMAND=build FEATURES="--no-default-features --features=u32_backend" - rust: nightly From 47c1d869eceb3a90806f67bf6e9aaeb20e90d983 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 16 May 2018 22:09:31 +0000 Subject: [PATCH 121/351] Make key generation work with no_std. --- Cargo.toml | 7 ++-- src/ed25519.rs | 88 +++++++++++++++++++++++++++----------------------- src/lib.rs | 59 +++++++++++++++++---------------- 3 files changed, 84 insertions(+), 70 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f0ee015..6a46b9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,8 +24,8 @@ version = "0.6" default-features = false [dependencies.rand] -optional = true version = "0.5.0-pre.2" +default-features = false [dependencies.digest] version = "^0.7" @@ -53,9 +53,10 @@ bincode = "^0.9" [features] default = ["std", "u64_backend"] -std = ["rand", "subtle/std", "curve25519-dalek/std", "failure/std"] +# We don't add "rand/std" here because it would enable a bunch of Fuchsia dependencies. +std = ["subtle/std", "curve25519-dalek/std", "failure/std"] bench = [] -nightly = ["curve25519-dalek/nightly", "subtle/nightly"] +nightly = ["curve25519-dalek/nightly", "subtle/nightly", "rand/nightly"] asm = ["sha2/asm"] yolocrypto = ["curve25519-dalek/yolocrypto"] u64_backend = ["curve25519-dalek/u64_backend"] diff --git a/src/ed25519.rs b/src/ed25519.rs index 1803aa1..e01ec94 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -12,9 +12,7 @@ use core::fmt::{Debug}; -#[cfg(feature = "std")] use rand::CryptoRng; -#[cfg(feature = "std")] use rand::Rng; #[cfg(feature = "serde")] @@ -262,8 +260,9 @@ impl SecretKey { /// extern crate sha2; /// extern crate ed25519_dalek; /// + /// # #[cfg(feature = "std")] /// # fn main() { - /// + /// # /// use rand::Rng; /// use rand::OsRng; /// use sha2::Sha512; @@ -273,8 +272,10 @@ impl SecretKey { /// /// let mut csprng: OsRng = OsRng::new().unwrap(); /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); - /// /// # } + /// # + /// # #[cfg(not(feature = "std"))] + /// # fn main() { } /// ``` /// /// Afterwards, you can generate the corresponding public—provided you also @@ -289,13 +290,14 @@ impl SecretKey { /// # fn main() { /// # /// # use rand::Rng; - /// # use rand::OsRng; + /// # use rand::ChaChaRng; + /// # use rand::SeedableRng; /// # use sha2::Sha512; /// # use ed25519_dalek::PublicKey; /// # use ed25519_dalek::SecretKey; /// # use ed25519_dalek::Signature; /// # - /// # let mut csprng: OsRng = OsRng::new().unwrap(); + /// # let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]); /// # let secret_key: SecretKey = SecretKey::generate(&mut csprng); /// /// let public_key: PublicKey = PublicKey::from_secret::(&secret_key); @@ -308,10 +310,7 @@ impl SecretKey { /// /// # Input /// - /// A CSPRNG with a `fill_bytes()` method, e.g. the one returned - /// from `rand::OsRng::new()` (in the `rand` crate). - /// - #[cfg(feature = "std")] + /// A CSPRNG with a `fill_bytes()` method, e.g. `rand::ChaChaRng` pub fn generate(csprng: &mut T) -> SecretKey where T: CryptoRng + Rng, { @@ -402,6 +401,7 @@ impl<'a> From<&'a SecretKey> for ExpandedSecretKey { /// # extern crate sha2; /// # extern crate ed25519_dalek; /// # + /// # #[cfg(all(feature = "std", feature = "sha2"))] /// # fn main() { /// # /// use rand::{Rng, OsRng}; @@ -412,6 +412,9 @@ impl<'a> From<&'a SecretKey> for ExpandedSecretKey { /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key); /// # } + /// # + /// # #[cfg(any(not(feature = "std"), not(feature = "sha2")))] + /// # fn main() {} /// ``` fn from(secret_key: &'a SecretKey) -> ExpandedSecretKey { ExpandedSecretKey::from_secret_key::(&secret_key) @@ -434,7 +437,7 @@ impl ExpandedSecretKey { /// # extern crate sha2; /// # extern crate ed25519_dalek; /// # - /// # #[cfg(feature = "sha2")] + /// # #[cfg(all(feature = "sha2", feature = "std"))] /// # fn main() { /// # /// use rand::{Rng, OsRng}; @@ -449,7 +452,7 @@ impl ExpandedSecretKey { /// assert!(&expanded_secret_key_bytes[..] != &[0u8; 64][..]); /// # } /// # - /// # #[cfg(not(feature = "sha2"))] + /// # #[cfg(any(not(feature = "sha2"), not(feature = "std")))] /// # fn main() { } /// ``` #[inline] @@ -475,13 +478,13 @@ impl ExpandedSecretKey { /// # extern crate sha2; /// # extern crate ed25519_dalek; /// # + /// # #[cfg(all(feature = "sha2", feature = "std"))] + /// # fn do_test() -> Result { + /// # /// use rand::{Rng, OsRng}; /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; /// use ed25519_dalek::DecodingError; /// - /// # #[cfg(feature = "sha2")] - /// # fn do_test() -> Result { - /// # /// let mut csprng: OsRng = OsRng::new().unwrap(); /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key); @@ -491,14 +494,14 @@ impl ExpandedSecretKey { /// # Ok(expanded_secret_key_again) /// # } /// # - /// # #[cfg(feature = "sha2")] + /// # #[cfg(all(feature = "sha2", feature = "std"))] /// # fn main() { /// # let result = do_test(); /// # assert!(result.is_ok()); /// # } /// # - /// # #[cfg(not(feature = "sha2"))] - /// # fn main() {} + /// # #[cfg(any(not(feature = "sha2"), not(feature = "std")))] + /// # fn main() { } /// ``` #[inline] pub fn from_bytes(bytes: &[u8]) -> Result { @@ -525,7 +528,8 @@ impl ExpandedSecretKey { /// # extern crate sha2; /// # extern crate ed25519_dalek; /// # - /// # fn do_test() { + /// # #[cfg(all(feature = "std", feature = "sha2"))] + /// # fn main() { /// # /// use rand::{Rng, OsRng}; /// use sha2::Sha512; @@ -536,7 +540,8 @@ impl ExpandedSecretKey { /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from_secret_key::(&secret_key); /// # } /// # - /// # fn main() { do_test(); } + /// # #[cfg(any(not(feature = "sha2"), not(feature = "std")))] + /// # fn main() { } /// ``` pub fn from_secret_key(secret_key: &SecretKey) -> ExpandedSecretKey where D: Digest + Default { @@ -850,6 +855,7 @@ impl Keypair { /// extern crate sha2; /// extern crate ed25519_dalek; /// + /// # #[cfg(all(feature = "std", feature = "sha2"))] /// # fn main() { /// /// use rand::Rng; @@ -858,23 +864,24 @@ impl Keypair { /// use ed25519_dalek::Keypair; /// use ed25519_dalek::Signature; /// - /// let mut cspring: OsRng = OsRng::new().unwrap(); - /// let keypair: Keypair = Keypair::generate::(&mut cspring); + /// let mut csprng: OsRng = OsRng::new().unwrap(); + /// let keypair: Keypair = Keypair::generate::(&mut csprng); /// /// # } + /// # + /// # #[cfg(any(not(feature = "sha2"), not(feature = "std")))] + /// # fn main() { } /// ``` /// /// # Input /// - /// A CSPRNG with a `fill_bytes()` method, e.g. the one returned - /// from `rand::OsRng::new()` (in the `rand` crate). + /// A CSPRNG with a `fill_bytes()` method, e.g. `rand::ChaChaRng`. /// /// The caller must also supply a hash function which implements the /// `Digest` and `Default` traits, and which returns 512 bits of output. /// The standard hash function used for most ed25519 libraries is SHA-512, /// which is available with `use sha2::Sha512` as in the example above. /// Other suitable hash functions include Keccak-512 and Blake2b-512. - #[cfg(feature = "std")] pub fn generate(csprng: &mut R) -> Keypair where D: Digest + Default, R: CryptoRng + Rng, @@ -943,7 +950,8 @@ mod test { use std::string::String; use std::vec::Vec; use curve25519_dalek::edwards::EdwardsPoint; - use rand::OsRng; + use rand::ChaChaRng; + use rand::SeedableRng; use hex::FromHex; use sha2::Sha512; use super::*; @@ -976,17 +984,17 @@ mod test { #[test] fn unmarshal_marshal() { // TestUnmarshalMarshal - let mut cspring: OsRng; + let mut csprng: ChaChaRng; let mut keypair: Keypair; let mut x: Option; let a: EdwardsPoint; let public: PublicKey; - cspring = OsRng::new().unwrap(); + csprng = ChaChaRng::from_seed([0u8; 32]); // from_bytes() fails if vx²-u=0 and vx²+u=0 loop { - keypair = Keypair::generate::(&mut cspring); + keypair = Keypair::generate::(&mut csprng); x = keypair.public.decompress(); if x.is_some() { @@ -1001,7 +1009,7 @@ mod test { #[test] fn sign_verify() { // TestSignVerify - let mut cspring: OsRng; + let mut csprng: ChaChaRng; let keypair: Keypair; let good_sig: Signature; let bad_sig: Signature; @@ -1009,8 +1017,8 @@ mod test { let good: &[u8] = "test message".as_bytes(); let bad: &[u8] = "wrong message".as_bytes(); - cspring = OsRng::new().unwrap(); - keypair = Keypair::generate::(&mut cspring); + csprng = ChaChaRng::from_seed([0u8; 32]); + keypair = Keypair::generate::(&mut csprng); good_sig = keypair.sign::(&good); bad_sig = keypair.sign::(&bad); @@ -1125,7 +1133,7 @@ mod test { #[cfg(all(test, feature = "bench"))] mod bench { use test::Bencher; - use rand::OsRng; + use rand::ChaChaRng; use sha2::Sha512; use super::*; @@ -1150,8 +1158,8 @@ mod bench { #[bench] fn sign(b: &mut Bencher) { - let mut csprng: OsRng = OsRng::new().unwrap(); - let keypair: Keypair = Keypair::generate::(&mut csprng); + let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]).unwrap(); + let keypair: Keypair = Keypair::generate::(&mut csprng); let msg: &[u8] = b""; b.iter(| | keypair.sign::(msg)); @@ -1159,8 +1167,8 @@ mod bench { #[bench] fn sign_expanded_key(b: &mut Bencher) { - let mut csprng: OsRng = OsRng::new().unwrap(); - let keypair: Keypair = Keypair::generate::(&mut csprng); + let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]).unwrap(); + let keypair: Keypair = Keypair::generate::(&mut csprng); let expanded: ExpandedSecretKey = keypair.secret.expand::(); let msg: &[u8] = b""; @@ -1169,8 +1177,8 @@ mod bench { #[bench] fn verify(b: &mut Bencher) { - let mut csprng: OsRng = OsRng::new().unwrap(); - let keypair: Keypair = Keypair::generate::(&mut csprng); + let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]).unwrap(); + let keypair: Keypair = Keypair::generate::(&mut csprng); let msg: &[u8] = b""; let sig: Signature = keypair.sign::(msg); @@ -1181,7 +1189,7 @@ mod bench { fn key_generation(b: &mut Bencher) { let mut rng: ZeroRng = ZeroRng::new(); - b.iter(| | Keypair::generate::(&mut rng)); + b.iter(| | Keypair::generate::(&mut rng)); } #[bench] diff --git a/src/lib.rs b/src/lib.rs index 99011e2..d516597 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,7 +15,7 @@ //! //! First, we need to generate a `Keypair`, which includes both public and //! secret halves of an asymmetric key. To do so, we need a cryptographically -//! secure pseudorandom number generator (CSPRING), and a hash function which +//! secure pseudorandom number generator (CSPRNG), and a hash function which //! has 512 bits of output. For this example, we'll use the operating //! system's builtin PRNG and SHA-512 to generate a keypair: //! @@ -24,6 +24,7 @@ //! extern crate sha2; //! extern crate ed25519_dalek; //! +//! # #[cfg(all(feature = "std", feature = "sha2"))] //! # fn main() { //! use rand::Rng; //! use rand::OsRng; @@ -31,9 +32,12 @@ //! use ed25519_dalek::Keypair; //! use ed25519_dalek::Signature; //! -//! let mut cspring: OsRng = OsRng::new().unwrap(); -//! let keypair: Keypair = Keypair::generate::(&mut cspring); +//! let mut csprng: OsRng = OsRng::new().unwrap(); +//! let keypair: Keypair = Keypair::generate::(&mut csprng); //! # } +//! # +//! # #[cfg(any(not(feature = "std"), not(feature = "sha2")))] +//! # fn main() { } //! ``` //! //! We can now use this `keypair` to sign a message: @@ -44,12 +48,13 @@ //! # extern crate ed25519_dalek; //! # fn main() { //! # use rand::Rng; -//! # use rand::OsRng; +//! # use rand::ChaChaRng; +//! # use rand::SeedableRng; //! # use sha2::Sha512; //! # use ed25519_dalek::Keypair; //! # use ed25519_dalek::Signature; -//! # let mut cspring: OsRng = OsRng::new().unwrap(); -//! # let keypair: Keypair = Keypair::generate::(&mut cspring); +//! # let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]); +//! # let keypair: Keypair = Keypair::generate::(&mut csprng); //! let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); //! let signature: Signature = keypair.sign::(message); //! # } @@ -64,12 +69,13 @@ //! # extern crate ed25519_dalek; //! # fn main() { //! # use rand::Rng; -//! # use rand::OsRng; +//! # use rand::ChaChaRng; +//! # use rand::SeedableRng; //! # use sha2::Sha512; //! # use ed25519_dalek::Keypair; //! # use ed25519_dalek::Signature; -//! # let mut cspring: OsRng = OsRng::new().unwrap(); -//! # let keypair: Keypair = Keypair::generate::(&mut cspring); +//! # let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]); +//! # let keypair: Keypair = Keypair::generate::(&mut csprng); //! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); //! # let signature: Signature = keypair.sign::(message); //! let verified: bool = keypair.verify::(message, &signature); @@ -87,13 +93,14 @@ //! # extern crate ed25519_dalek; //! # fn main() { //! # use rand::Rng; -//! # use rand::OsRng; +//! # use rand::ChaChaRng; +//! # use rand::SeedableRng; //! # use sha2::Sha512; //! # use ed25519_dalek::Keypair; //! # use ed25519_dalek::Signature; //! use ed25519_dalek::PublicKey; -//! # let mut cspring: OsRng = OsRng::new().unwrap(); -//! # let keypair: Keypair = Keypair::generate::(&mut cspring); +//! # let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]); +//! # let keypair: Keypair = Keypair::generate::(&mut csprng); //! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); //! # let signature: Signature = keypair.sign::(message); //! @@ -117,12 +124,12 @@ //! # extern crate sha2; //! # extern crate ed25519_dalek; //! # fn main() { -//! # use rand::{Rng, OsRng}; +//! # use rand::{Rng, ChaChaRng, SeedableRng}; //! # use sha2::Sha512; //! # use ed25519_dalek::{Keypair, Signature, PublicKey}; //! use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; -//! # let mut cspring: OsRng = OsRng::new().unwrap(); -//! # let keypair: Keypair = Keypair::generate::(&mut cspring); +//! # let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]); +//! # let keypair: Keypair = Keypair::generate::(&mut csprng); //! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); //! # let signature: Signature = keypair.sign::(message); //! # let public_key: PublicKey = keypair.public; @@ -141,13 +148,13 @@ //! # extern crate rand; //! # extern crate sha2; //! # extern crate ed25519_dalek; -//! # use rand::{Rng, OsRng}; +//! # use rand::{Rng, ChaChaRng, SeedableRng}; //! # use sha2::Sha512; //! # use ed25519_dalek::{Keypair, Signature, PublicKey, SecretKey, DecodingError}; //! # use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; //! # fn do_test() -> Result<(SecretKey, PublicKey, Keypair, Signature), DecodingError> { -//! # let mut cspring: OsRng = OsRng::new().unwrap(); -//! # let keypair_orig: Keypair = Keypair::generate::(&mut cspring); +//! # let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]); +//! # let keypair_orig: Keypair = Keypair::generate::(&mut csprng); //! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); //! # let signature_orig: Signature = keypair_orig.sign::(message); //! # let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = keypair_orig.public.to_bytes(); @@ -173,7 +180,7 @@ //! types additionally come with built-in [serde](https://serde.rs) support by //! building `ed25519-dalek` via: //! -//! ```ignore,bash +//! ```bash //! $ cargo build --features="serde" //! ``` //! @@ -191,12 +198,12 @@ //! //! # #[cfg(feature = "serde")] //! # fn main() { -//! # use rand::{Rng, OsRng}; +//! # use rand::{Rng, ChaChaRng, SeedableRng}; //! # use sha2::Sha512; //! # use ed25519_dalek::{Keypair, Signature, PublicKey}; //! use bincode::{serialize, Infinite}; -//! # let mut cspring: OsRng = OsRng::new().unwrap(); -//! # let keypair: Keypair = Keypair::generate::(&mut cspring); +//! # let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]); +//! # let keypair: Keypair = Keypair::generate::(&mut csprng); //! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); //! # let signature: Signature = keypair.sign::(message); //! # let public_key: PublicKey = keypair.public; @@ -223,14 +230,14 @@ //! # //! # #[cfg(feature = "serde")] //! # fn main() { -//! # use rand::{Rng, OsRng}; +//! # use rand::{Rng, ChaChaRng, SeedableRng}; //! # use sha2::Sha512; //! # use ed25519_dalek::{Keypair, Signature, PublicKey}; //! # use bincode::{serialize, Infinite}; //! use bincode::{deserialize}; //! -//! # let mut cspring: OsRng = OsRng::new().unwrap(); -//! # let keypair: Keypair = Keypair::generate::(&mut cspring); +//! # let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]); +//! # let keypair: Keypair = Keypair::generate::(&mut csprng); //! let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); //! # let signature: Signature = keypair.sign::(message); //! # let public_key: PublicKey = keypair.public; @@ -262,8 +269,6 @@ extern crate generic_array; extern crate digest; extern crate subtle; extern crate failure; - -#[cfg(feature = "std")] extern crate rand; #[cfg(any(feature = "std", test))] From 196fd24b638ff4c12633c0a11db0dfad33b86d1c Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 30 May 2018 20:59:44 +0000 Subject: [PATCH 122/351] Update README to explain features and update benches. --- README.md | 109 +++++++++++++++++++++++++++++------------------------- 1 file changed, 58 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index 869b755..ce3ce08 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ed25519-dalek [![](https://img.shields.io/crates/v/ed25519-dalek.svg)](https://crates.io/crates/ed25519-dalek) [![](https://docs.rs/ed25519-dalek/badge.svg)](https://docs.rs/ed25519-dalek) [![](https://travis-ci.org/isislovecruft/ed25519-dalek.svg?branch=master)](https://travis-ci.org/isislovecruft/ed25519-dalek?branch=master) +# ed25519-dalek [![](https://img.shields.io/crates/v/ed25519-dalek.svg)](https://crates.io/crates/ed25519-dalek) [![](https://docs.rs/ed25519-dalek/badge.svg)](https://docs.rs/ed25519-dalek) [![](https://travis-ci.org/isislovecruft/ed25519-dalek.svg?branch=master)](https://travis-ci.org/dalek-cryptography/ed25519-dalek?branch=master) Fast and efficient Rust implementation of ed25519 key generation, signing, and verification in Rust. @@ -18,19 +18,23 @@ On an Intel i5 Sandy Bridge running at 2.6 GHz, with TurboBoost enabled (and also running in QubesOS with *lots* of other VMs executing), this code achieves the following performance benchmarks: - ∃!isisⒶwintermute:(develop *$)~/code/rust/ed25519 ∴ cargo bench --features="bench" - Finished release [optimized] target(s) in 0.0 secs - Running target/release/deps/ed25519_dalek-281c2d7a2379edae + ∃!isisⒶwintermute:(master *=)~/code/rust/ed25519-dalek ∴ cargo bench --features="nightly bench" + Compiling ed25519-dalek v0.7.0 (file:///home/isis/code/rust/ed25519-dalek) + Finished release [optimized] target(s) in 3.11s + Running target/release/deps/ed25519_dalek-ae92163eefd0cc80 - running 6 tests + running 9 tests test ed25519::test::golden ... ignored + test ed25519::test::public_key_from_bytes ... ignored test ed25519::test::sign_verify ... ignored test ed25519::test::unmarshal_marshal ... ignored - test ed25519::bench::key_generation ... bench: 54,571 ns/iter (+/- 7,861) - test ed25519::bench::sign ... bench: 70,009 ns/iter (+/- 22,812) - test ed25519::bench::verify ... bench: 185,619 ns/iter (+/- 24,117) + test ed25519::bench::key_generation ... bench: 30,711 ns/iter (+/- 10,936) + test ed25519::bench::sign ... bench: 39,432 ns/iter (+/- 21,387) + test ed25519::bench::sign_expanded_key ... bench: 45,753 ns/iter (+/- 25,261) + test ed25519::bench::underlying_scalar_mult_basepoint ... bench: 25,455 ns/iter (+/- 10,587) + test ed25519::bench::verify ... bench: 91,408 ns/iter (+/- 31,193) - test result: ok. 0 passed; 0 failed; 3 ignored; 3 measured + test result: ok. 0 passed; 0 failed; 4 ignored; 5 measured; 0 filtered out In comparison, the equivalent package in Golang performs as follows: @@ -41,37 +45,22 @@ In comparison, the equivalent package in Golang performs as follows: BenchmarkVerification 10000 212585 ns/op ok github.com/agl/ed25519 7.500s -Making key generation, signing, and verification a rough average of one third -faster, one fifth faster, and one eighth faster respectively. Of course, this +Making key generation, signing, and verification a rough average of 33% +faster, 44% faster, and 43% faster respectively. Of course, this is just my machine, and these results—nowhere near rigorous—should be taken with a handful of salt. -Additionally, if you're on the Rust nightly channel, be sure to build with -`cargo build --features="nightly"`, which uses Rust's experimental support for -the `u128` type in curve25519-dalek to speed up field arithmetic by roughly a -factor of two. The benchmarks using nightly (on the same machine as above) -are: - - ∃!isisⒶwintermute:(develop *$)~/code/rust/ed25519 ∴ cargo bench --features="bench nightly" - Finished release [optimized] target(s) in 0.0 secs - Running target/release/deps/ed25519_dalek-9d7f8674ae11ac39 - - running 6 tests - test ed25519::test::golden ... ignored - test ed25519::test::sign_verify ... ignored - test ed25519::test::unmarshal_marshal ... ignored - test ed25519::bench::key_generation ... bench: 31,160 ns/iter (+/- 8,597) - test ed25519::bench::sign ... bench: 40,565 ns/iter (+/- 4,758) - test ed25519::bench::verify ... bench: 106,146 ns/iter (+/- 2,796) - - test result: ok. 0 passed; 0 failed; 3 ignored; 3 measured - Translating to a rough cycle count: we multiply by a factor of 2.6 to convert -nanoseconds to cycles per second on a 2.6 GHz CPU, that's 275979 cycles for -verification and 105469 for signing, which is -[competitive with the optimised assembly version](https://ed25519.cr.yp.to/) -included in the SUPERCOP benchmarking suite (albeit their numbers are for the -older Nehalem microarchitecture). +nanoseconds to cycles per second on a 2591 Mhz CPU, that's 237660 cycles for +verification and 102523 for signing, which for signing is competitive +with optimised assembly versions. + +Additionally, if you're on the Rust nightly channel, be sure to build with +`cargo build --features="nightly"` which enables more secure compiler +optimisation protections in the +[subtle](https://github.com/dalek-cryptography/subtle) crate. Additionally, if +you're using a CSPRNG from the `rand` crate, the `nightly` feature will enable +`u128`/`i128` features there, resulting in potentially faster performance. Additionally, thanks to Rust, this implementation has both type and memory safety. It's also easily readable by a much larger set of people than those who @@ -118,42 +107,60 @@ eventually support VXEdDSA in curve25519-dalek. To install, add the following to your project's `Cargo.toml`: - [dependencies.ed25519-dalek] - version = "^0.6" +```toml +[dependencies.ed25519-dalek] +version = "^0.7" +``` Then, in your library or executable source, add: - extern crate ed25519_dalek +```rust +extern crate ed25519_dalek; +``` + +# Features To cause your application to build `ed25519-dalek` with the nightly feature enabled by default, instead do: - [dependencies.ed25519-dalek] - version = "^0.6" - features = ["nightly"] +```toml +[dependencies.ed25519-dalek] +version = "^0.7" +features = ["nightly"] +``` To cause your application to instead build with the nightly feature enabled when someone builds with `cargo build --features="nightly"` add the following to the `Cargo.toml`: - [features] - nightly = ["ed25519-dalek/nightly"] - -Using the `nightly` feature will nearly double the latency of signing and -verification. +```toml +[features] +nightly = ["ed25519-dalek/nightly"] +``` To enable [serde](https://serde.rs) support, build `ed25519-dalek` with: - [dependencies.ed25519-dalek] - version = "^0.6" - features = ["serde"] +```toml +[dependencies.ed25519-dalek] +version = "^0.7" +features = ["serde"] +``` +By default, `ed25519-dalek` builds against `curve25519-dalek`'s `u64_backend` +feature, which uses Rust's `i128` feature to achieve roughly double the speed as +the `u32_backend` feature. When targetting 32-bit systems, however, you'll +likely want to compile with + `cargo build --no-default-features --features="u32_backend"`. +If you're building for a machine with avx2 instructions, there's also the +experimental `avx2_backend`. To use it, compile with +`RUSTFLAGS="-C target_cpu=native" cargo build --no-default-features --features="avx2_backend"` # TODO + * Batch signature verification, maybe? * We can probably make this go even faster if we implement SHA512, rather than using the rust-crypto implementation whose API requires - that we allocate memory and memzero it before mutating to store the + that we allocate memory and bzero it before mutating to store the digest. * Incorporate ed25519-dalek into Brian Smith's [crypto-bench](https://github.com/briansmith/crypto-bench). From a7c318da6ee405c048253ad8130f677feb6a83ad Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 30 May 2018 21:01:21 +0000 Subject: [PATCH 123/351] Fix benchmarks to use new rand_core traits. --- src/ed25519.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index e01ec94..fc79054 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -1134,6 +1134,9 @@ mod test { mod bench { use test::Bencher; use rand::ChaChaRng; + use rand::Error; + use rand::RngCore; + use rand::SeedableRng; use sha2::Sha512; use super::*; @@ -1146,19 +1149,27 @@ mod bench { } } - impl Rng for ZeroRng { + impl RngCore for ZeroRng { fn next_u32(&mut self) -> u32 { 0u32 } + fn next_u64(&mut self) -> u64 { 0u64 } + fn fill_bytes(&mut self, bytes: &mut [u8]) { for i in 0 .. bytes.len() { bytes[i] = 0; } } + + fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> Result<(), Error> { + Ok(self.fill_bytes(bytes)) + } } + impl CryptoRng for ZeroRng { } + #[bench] fn sign(b: &mut Bencher) { - let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]).unwrap(); + let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]); let keypair: Keypair = Keypair::generate::(&mut csprng); let msg: &[u8] = b""; @@ -1167,7 +1178,7 @@ mod bench { #[bench] fn sign_expanded_key(b: &mut Bencher) { - let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]).unwrap(); + let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]); let keypair: Keypair = Keypair::generate::(&mut csprng); let expanded: ExpandedSecretKey = keypair.secret.expand::(); let msg: &[u8] = b""; @@ -1177,7 +1188,7 @@ mod bench { #[bench] fn verify(b: &mut Bencher) { - let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]).unwrap(); + let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]); let keypair: Keypair = Keypair::generate::(&mut csprng); let msg: &[u8] = b""; let sig: Signature = keypair.sign::(msg); From 1dfe211aa1317fc6757b29e72d578937874999bf Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 11 Jul 2018 20:32:12 +0000 Subject: [PATCH 124/351] Remove failure/std from the default enabled features. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6a46b9d..5370a7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,7 +54,7 @@ bincode = "^0.9" [features] default = ["std", "u64_backend"] # We don't add "rand/std" here because it would enable a bunch of Fuchsia dependencies. -std = ["subtle/std", "curve25519-dalek/std", "failure/std"] +std = ["subtle/std", "curve25519-dalek/std"] bench = [] nightly = ["curve25519-dalek/nightly", "subtle/nightly", "rand/nightly"] asm = ["sha2/asm"] From a4be92b27123218c8daccbb0a938a02d016d76de Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 11 Jul 2018 20:36:17 +0000 Subject: [PATCH 125/351] Update curve25519-dalek dependency to 0.18. --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5370a7f..06af84d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ exclude = [ ".gitignore", "TESTVECTORS", "res/*" ] travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master"} [dependencies.curve25519-dalek] -version = "0.17" +version = "0.18" default-features = false [dependencies.subtle] @@ -24,7 +24,7 @@ version = "0.6" default-features = false [dependencies.rand] -version = "0.5.0-pre.2" +version = "0.5" default-features = false [dependencies.digest] From 164303eeacb1b14d3bdeeb2611670e7a9063360a Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 11 Jul 2018 22:41:14 +0000 Subject: [PATCH 126/351] Switch to using criterion for benchmarks. --- .travis.yml | 4 +- Cargo.toml | 6 +- benches/ed25519_benchmarks.rs | 114 ++++++++++++++++++++++++++++++++++ src/ed25519.rs | 87 -------------------------- 4 files changed, 121 insertions(+), 90 deletions(-) create mode 100644 benches/ed25519_benchmarks.rs diff --git a/.travis.yml b/.travis.yml index 753186e..b14a0d4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,9 +16,9 @@ matrix: - rust: nightly env: TEST_COMMAND=test FEATURES=--features="nightly" - rust: nightly - env: TEST_COMMAND=bench FEATURES=--features="bench" + env: TEST_COMMAND=bench FEATURES='' - rust: nightly - env: TEST_COMMAND=bench FEATURES=--features="nightly bench" + env: TEST_COMMAND=bench FEATURES=--features="nightly" script: - cargo $TEST_COMMAND $FEATURES diff --git a/Cargo.toml b/Cargo.toml index 06af84d..b22e552 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,12 +50,16 @@ default-features = false hex = "^0.3" sha2 = "^0.7" bincode = "^0.9" +criterion = "0.2" + +[[bench]] +name = "ed25519_benchmarks" +harness = false [features] default = ["std", "u64_backend"] # We don't add "rand/std" here because it would enable a bunch of Fuchsia dependencies. std = ["subtle/std", "curve25519-dalek/std"] -bench = [] nightly = ["curve25519-dalek/nightly", "subtle/nightly", "rand/nightly"] asm = ["sha2/asm"] yolocrypto = ["curve25519-dalek/yolocrypto"] diff --git a/benches/ed25519_benchmarks.rs b/benches/ed25519_benchmarks.rs new file mode 100644 index 0000000..b813b12 --- /dev/null +++ b/benches/ed25519_benchmarks.rs @@ -0,0 +1,114 @@ +// -*- mode: rust; -*- +// +// This file is part of ed25519-dalek. +// Copyright (c) 2018 Isis Lovecruft +// See LICENSE for licensing information. +// +// Authors: +// - Isis Agora Lovecruft + +#[macro_use] +extern crate criterion; +extern crate ed25519_dalek; +extern crate rand; +extern crate sha2; + +use criterion::Criterion; + +mod helpers { + use rand::CryptoRng; + use rand::Error; + use rand::RngCore; + + /// A fake RNG which simply returns zeroes. + pub struct ZeroRng; + + impl ZeroRng { + pub fn new() -> ZeroRng { + ZeroRng + } + } + + impl RngCore for ZeroRng { + fn next_u32(&mut self) -> u32 { 0u32 } + + fn next_u64(&mut self) -> u64 { 0u64 } + + fn fill_bytes(&mut self, bytes: &mut [u8]) { + for i in 0 .. bytes.len() { + bytes[i] = 0; + } + } + + fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> Result<(), Error> { + Ok(self.fill_bytes(bytes)) + } + } + + impl CryptoRng for ZeroRng { } +} + +mod ed25519_benches { + use super::*; + use super::helpers::ZeroRng; + use ed25519_dalek::ExpandedSecretKey; + use ed25519_dalek::Keypair; + use ed25519_dalek::Signature; + use rand::thread_rng; + use rand::ThreadRng; + use sha2::Sha512; + + fn sign(c: &mut Criterion) { + let mut csprng: ThreadRng = thread_rng(); + let keypair: Keypair = Keypair::generate::(&mut csprng); + let msg: &[u8] = b""; + + c.bench_function("Ed25519 signing", move |b| { + b.iter(| | keypair.sign::(msg)) + }); + } + + fn sign_expanded_key(c: &mut Criterion) { + let mut csprng: ThreadRng = thread_rng(); + let keypair: Keypair = Keypair::generate::(&mut csprng); + let expanded: ExpandedSecretKey = keypair.secret.expand::(); + let msg: &[u8] = b""; + + c.bench_function("Ed25519 signing with an expanded secret key", move |b| { + b.iter(| | expanded.sign::(msg, &keypair.public)) + }); + } + + fn verify(c: &mut Criterion) { + let mut csprng: ThreadRng = thread_rng(); + let keypair: Keypair = Keypair::generate::(&mut csprng); + let msg: &[u8] = b""; + let sig: Signature = keypair.sign::(msg); + + c.bench_function("Ed25519 signature verification", move |b| { + b.iter(| | keypair.verify::(msg, &sig)) + }); + } + + fn key_generation(c: &mut Criterion) { + let mut rng: ZeroRng = ZeroRng::new(); + + c.bench_function("Ed25519 keypair generation", move |b| { + b.iter(| | Keypair::generate::(&mut rng)) + }); + } + + criterion_group!{ + name = ed25519_benches; + config = Criterion::default(); + targets = + sign, + sign_expanded_key, + verify, + key_generation, + } +} + +criterion_main!( + ed25519_benches::ed25519_benches, +); diff --git a/src/ed25519.rs b/src/ed25519.rs index fc79054..e4d5701 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -1129,90 +1129,3 @@ mod test { } } } - -#[cfg(all(test, feature = "bench"))] -mod bench { - use test::Bencher; - use rand::ChaChaRng; - use rand::Error; - use rand::RngCore; - use rand::SeedableRng; - use sha2::Sha512; - use super::*; - - /// A fake RNG which simply returns zeroes. - struct ZeroRng; - - impl ZeroRng { - pub fn new() -> ZeroRng { - ZeroRng - } - } - - impl RngCore for ZeroRng { - fn next_u32(&mut self) -> u32 { 0u32 } - - fn next_u64(&mut self) -> u64 { 0u64 } - - fn fill_bytes(&mut self, bytes: &mut [u8]) { - for i in 0 .. bytes.len() { - bytes[i] = 0; - } - } - - fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> Result<(), Error> { - Ok(self.fill_bytes(bytes)) - } - } - - impl CryptoRng for ZeroRng { } - - #[bench] - fn sign(b: &mut Bencher) { - let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]); - let keypair: Keypair = Keypair::generate::(&mut csprng); - let msg: &[u8] = b""; - - b.iter(| | keypair.sign::(msg)); - } - - #[bench] - fn sign_expanded_key(b: &mut Bencher) { - let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]); - let keypair: Keypair = Keypair::generate::(&mut csprng); - let expanded: ExpandedSecretKey = keypair.secret.expand::(); - let msg: &[u8] = b""; - - b.iter(| | expanded.sign::(msg, &keypair.public)); - } - - #[bench] - fn verify(b: &mut Bencher) { - let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]); - let keypair: Keypair = Keypair::generate::(&mut csprng); - let msg: &[u8] = b""; - let sig: Signature = keypair.sign::(msg); - - b.iter(| | keypair.verify::(msg, &sig)); - } - - #[bench] - fn key_generation(b: &mut Bencher) { - let mut rng: ZeroRng = ZeroRng::new(); - - b.iter(| | Keypair::generate::(&mut rng)); - } - - #[bench] - fn underlying_scalar_mult_basepoint(b: &mut Bencher) { - use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; - - let scalar: Scalar = Scalar::from_bits([ - 20, 130, 129, 196, 247, 182, 211, 102, - 11, 168, 169, 131, 159, 69, 126, 35, - 109, 193, 175, 54, 118, 234, 138, 81, - 60, 183, 80, 186, 92, 248, 132, 13, ]); - - b.iter(| | &scalar * &ED25519_BASEPOINT_TABLE); - } -} From bda9bba9d5d48d740ec33c8e56e1ac6894761c34 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 12 Jul 2018 20:19:42 +0000 Subject: [PATCH 127/351] Remove ZeroRng from benchmarks. --- benches/ed25519_benchmarks.rs | 38 ++--------------------------------- 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/benches/ed25519_benchmarks.rs b/benches/ed25519_benchmarks.rs index b813b12..b9ac890 100644 --- a/benches/ed25519_benchmarks.rs +++ b/benches/ed25519_benchmarks.rs @@ -15,42 +15,8 @@ extern crate sha2; use criterion::Criterion; -mod helpers { - use rand::CryptoRng; - use rand::Error; - use rand::RngCore; - - /// A fake RNG which simply returns zeroes. - pub struct ZeroRng; - - impl ZeroRng { - pub fn new() -> ZeroRng { - ZeroRng - } - } - - impl RngCore for ZeroRng { - fn next_u32(&mut self) -> u32 { 0u32 } - - fn next_u64(&mut self) -> u64 { 0u64 } - - fn fill_bytes(&mut self, bytes: &mut [u8]) { - for i in 0 .. bytes.len() { - bytes[i] = 0; - } - } - - fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> Result<(), Error> { - Ok(self.fill_bytes(bytes)) - } - } - - impl CryptoRng for ZeroRng { } -} - mod ed25519_benches { use super::*; - use super::helpers::ZeroRng; use ed25519_dalek::ExpandedSecretKey; use ed25519_dalek::Keypair; use ed25519_dalek::Signature; @@ -91,10 +57,10 @@ mod ed25519_benches { } fn key_generation(c: &mut Criterion) { - let mut rng: ZeroRng = ZeroRng::new(); + let mut csprng: ThreadRng = thread_rng(); c.bench_function("Ed25519 keypair generation", move |b| { - b.iter(| | Keypair::generate::(&mut rng)) + b.iter(| | Keypair::generate::(&mut csprng)) }); } From 9d58954578b0ebf73b15551002573ee0899cb2bf Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 12 Jul 2018 21:47:52 +0000 Subject: [PATCH 128/351] Avoid compressing R twice. --- src/ed25519.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index e4d5701..156f89a 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -570,7 +570,7 @@ impl ExpandedSecretKey { let mut hash: [u8; 64] = [0u8; 64]; let mesg_digest: Scalar; let hram_digest: Scalar; - let r: EdwardsPoint; + let r: CompressedEdwardsY; let s: Scalar; h.input(&self.nonce); @@ -579,10 +579,10 @@ impl ExpandedSecretKey { mesg_digest = Scalar::from_bytes_mod_order_wide(&hash); - r = &mesg_digest * &constants::ED25519_BASEPOINT_TABLE; + r = (&mesg_digest * &constants::ED25519_BASEPOINT_TABLE).compress(); h = D::default(); - h.input(r.compress().as_bytes()); + h.input(r.as_bytes()); h.input(public_key.as_bytes()); h.input(&message); hash.copy_from_slice(h.fixed_result().as_slice()); @@ -591,7 +591,7 @@ impl ExpandedSecretKey { s = &(&hram_digest * &self.key) + &mesg_digest; - Signature{ r: r.compress(), s: s } + Signature{ r: r, s: s } } } From 68d2ff93f562dcca2611d9f63b1edb3532ec7950 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 13 Jul 2018 20:14:31 +0000 Subject: [PATCH 129/351] =?UTF-8?q?Implement=20ed25519ph=20from=20RFC8032?= =?UTF-8?q?=20=C2=A75.1.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * FIXES #21: https://github.com/dalek-cryptography/ed25519-dalek/issues/21 --- src/ed25519.rs | 267 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 266 insertions(+), 1 deletion(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 156f89a..529e88a 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -593,6 +593,91 @@ impl ExpandedSecretKey { Signature{ r: r, s: s } } + + /// Sign a `prehashed_message` with this `ExpandedSecretKey` using the + /// Ed25519ph algorithm defined in [RFC8032 §5.1][rfc8032]. + /// + /// # Inputs + /// + /// * `prehashed_message` is an instantiated hash digest with 512-bits of + /// output which has had the message to be signed previously fed into its + /// state. + /// * `public_key` is a [`PublicKey`] which corresponds to this secret key. + /// * `context` is an optional context string, up to 255 bytes inclusive, + /// which may be used to provide additional domain separation. If not + /// set, this will default to an empty string. + /// + /// # Returns + /// + /// An Ed25519ph [`Signature`] on the `prehashed_message`. + /// + /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 + pub fn sign_prehashed(&self, + prehashed_message: D, + public_key: &PublicKey, + context: Option<&'static [u8]>) -> Signature + where D: Digest + Default + { + let mut h: D = D::default(); + let mut hash: [u8; 64] = [0u8; 64]; + let mut prehash: [u8; 64] = [0u8; 64]; + let mesg_digest: Scalar; + let hram_digest: Scalar; + let r: CompressedEdwardsY; + let s: Scalar; + + let ctx: &[u8] = match context { + Some(x) => x, + None => b"", // By default, the context is an empty string. + }; + debug_assert!(ctx.len() <= 255, "The context must not be longer than 255 octets."); + + let ctx_len: u8 = ctx.len() as u8; + + // Get the result of the pre-hashed message. + prehash.copy_from_slice(prehashed_message.fixed_result().as_slice()); + + // This is the dumbest, ten-years-late, non-admission of fucking up the + // domain separation I have ever seen. Why am I still required to put + // the upper half "prefix" of the hashed "secret key" in here? Why + // can't the user just supply their own nonce and decide for themselves + // whether or not they want a deterministic signature scheme? Why does + // the message go into what's ostensibly the signature domain separation + // hash? Why wasn't there always a way to provide a context string? + // + // ... + // + // This is a really fucking stupid bandaid, and the damned scheme is + // still bleeding from malleability, for fuck's sake. + h.input(b"SigEd25519 no Ed25519 collisions"); + h.input(&[1]); // Ed25519ph + h.input(&[ctx_len]); + h.input(ctx); + h.input(&self.nonce); + h.input(&prehash); + hash.copy_from_slice(h.fixed_result().as_slice()); + + mesg_digest = Scalar::from_bytes_mod_order_wide(&hash); + + r = (&mesg_digest * &constants::ED25519_BASEPOINT_TABLE).compress(); + + h = D::default(); + h.input(b"SigEd25519 no Ed25519 collisions"); + h.input(&[1]); // Ed25519ph + h.input(&[ctx_len]); + h.input(ctx); + h.input(r.as_bytes()); + h.input(public_key.as_bytes()); + h.input(&prehash); + hash.copy_from_slice(h.fixed_result().as_slice()); + + hram_digest = Scalar::from_bytes_mod_order_wide(&hash); + + s = &(&hram_digest * &self.key) + &mesg_digest; + + Signature{ r: r, s: s } + } + } #[cfg(feature = "serde")] @@ -758,6 +843,63 @@ impl PublicKey { (signature.r.as_bytes()).ct_eq(r.compress().as_bytes()).unwrap_u8() == 1 } + + /// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm. + /// + /// # Inputs + /// + /// * `prehashed_message` is an instantiated hash digest with 512-bits of + /// output which has had the message to be signed previously fed into its + /// state. + /// * `context` is an optional context string, up to 255 bytes inclusive, + /// which may be used to provide additional domain separation. If not + /// set, this will default to an empty string. + /// * `signature` is a purported Ed25519ph [`Signature`] on the `prehashed_message`. + /// + /// # Returns + /// + /// Returns `true` if the `signature` was a valid signature created by this + /// `Keypair` on the `prehashed_message`. + /// + /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 + pub fn verify_prehashed(&self, + prehashed_message: D, + context: Option<&[u8]>, + signature: &Signature) -> bool + where D: Digest + Default + { + let mut h: D = D::default(); + let mut hash: [u8; 64] = [0u8; 64]; + + let mut a: EdwardsPoint = match self.decompress() { + Some(x) => x, + None => return false, + }; + a = -(&a); + + let ctx: &[u8] = match context { + Some(x) => x, + None => b"", // By default, the context is an empty string. + }; + debug_assert!(ctx.len() <= 255, "The context must not be longer than 255 octets."); + + let ctx_len: u8 = ctx.len() as u8; + + h.input(b"SigEd25519 no Ed25519 collisions"); + h.input(&[1]); // Ed25519ph + h.input(&[ctx_len]); + h.input(ctx); + h.input(signature.r.as_bytes()); + h.input(self.as_bytes()); + h.input(prehashed_message.fixed_result().as_slice()); + hash.copy_from_slice(h.fixed_result().as_slice()); + + let digest_reduced: Scalar = Scalar::from_bytes_mod_order_wide(&hash); + let r: EdwardsPoint = EdwardsPoint::vartime_double_scalar_mul_basepoint(&digest_reduced, + &a, &signature.s); + + (signature.r.as_bytes()).ct_eq(r.compress().as_bytes()).unwrap_u8() == 1 + } } #[cfg(feature = "serde")] @@ -898,11 +1040,63 @@ impl Keypair { self.secret.expand::().sign::(&message, &self.public) } + /// Sign a `prehashed_message` with this `Keypair` using the + /// Ed25519ph algorithm defined in [RFC8032 §5.1][rfc8032]. + /// + /// # Inputs + /// + /// * `prehashed_message` is an instantiated hash digest with 512-bits of + /// output which has had the message to be signed previously fed into its + /// state. + /// * `context` is an optional context string, up to 255 bytes inclusive, + /// which may be used to provide additional domain separation. If not + /// set, this will default to an empty string. + /// + /// # Returns + /// + /// An Ed25519ph [`Signature`] on the `prehashed_message`. + /// + /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 + pub fn sign_prehashed(&self, + prehashed_message: D, + context: Option<&'static [u8]>) -> Signature + where D: Digest + Default + { + self.secret.expand::().sign_prehashed::(prehashed_message, &self.public, context) + } + /// Verify a signature on a message with this keypair's public key. pub fn verify(&self, message: &[u8], signature: &Signature) -> bool where D: Digest + Default { self.public.verify::(message, signature) } + + /// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm. + /// + /// # Inputs + /// + /// * `prehashed_message` is an instantiated hash digest with 512-bits of + /// output which has had the message to be signed previously fed into its + /// state. + /// * `context` is an optional context string, up to 255 bytes inclusive, + /// which may be used to provide additional domain separation. If not + /// set, this will default to an empty string. + /// * `signature` is a purported Ed25519ph [`Signature`] on the `prehashed_message`. + /// + /// # Returns + /// + /// Returns `true` if the `signature` was a valid signature created by this + /// `Keypair` on the `prehashed_message`. + /// + /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 + pub fn verify_prehashed(&self, + prehashed_message: D, + context: Option<&[u8]>, + signature: &Signature) -> bool + where D: Digest + Default + { + self.public.verify_prehashed::(prehashed_message, context, signature) + } } #[cfg(feature = "serde")] @@ -1008,7 +1202,7 @@ mod test { } #[test] - fn sign_verify() { // TestSignVerify + fn ed25519_sign_verify() { // TestSignVerify let mut csprng: ChaChaRng; let keypair: Keypair; let good_sig: Signature; @@ -1076,6 +1270,77 @@ mod test { } } + // From https://tools.ietf.org/html/rfc8032#section-7.3 + #[test] + fn ed25519ph_rf8032_test_vector() { + let secret_key: &[u8] = b"833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42"; + let public_key: &[u8] = b"ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf"; + let message: &[u8] = b"616263"; + let signature: &[u8] = b"98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406"; + + let sec_bytes: Vec = FromHex::from_hex(secret_key).unwrap(); + let pub_bytes: Vec = FromHex::from_hex(public_key).unwrap(); + let msg_bytes: Vec = FromHex::from_hex(message).unwrap(); + let sig_bytes: Vec = FromHex::from_hex(signature).unwrap(); + + let secret: SecretKey = SecretKey::from_bytes(&sec_bytes[..SECRET_KEY_LENGTH]).unwrap(); + let public: PublicKey = PublicKey::from_bytes(&pub_bytes[..PUBLIC_KEY_LENGTH]).unwrap(); + let keypair: Keypair = Keypair{ secret: secret, public: public }; + let sig1: Signature = Signature::from_bytes(&sig_bytes[..]).unwrap(); + + let mut prehash_for_signing: Sha512 = Sha512::default(); + let mut prehash_for_verifying: Sha512 = Sha512::default(); + + prehash_for_signing.input(&msg_bytes[..]); + prehash_for_verifying.input(&msg_bytes[..]); + + let sig2: Signature = keypair.sign_prehashed(prehash_for_signing, None); + + assert!(sig1 == sig2, + "Original signature from test vectors doesn't equal signature produced:\ + \noriginal:\n{:?}\nproduced:\n{:?}", sig1, sig2); + assert!(keypair.verify_prehashed(prehash_for_verifying, None, &sig2), + "Could not verify ed25519ph signature!"); + } + + #[test] + fn ed25519ph_sign_verify() { + let mut csprng: ChaChaRng; + let keypair: Keypair; + let good_sig: Signature; + let bad_sig: Signature; + + let good: &[u8] = b"test message"; + let bad: &[u8] = b"wrong message"; + + // ugh… there's no `impl Copy for Sha512`… i hope we can all agree these are the same hashes + let mut prehashed_good1: Sha512 = Sha512::default(); + prehashed_good1.input(good); + let mut prehashed_good2: Sha512 = Sha512::default(); + prehashed_good2.input(good); + let mut prehashed_good3: Sha512 = Sha512::default(); + prehashed_good3.input(good); + + let mut prehashed_bad1: Sha512 = Sha512::default(); + prehashed_bad1.input(bad); + let mut prehashed_bad2: Sha512 = Sha512::default(); + prehashed_bad2.input(bad); + + let context: &[u8] = b"testing testing 1 2 3"; + + csprng = ChaChaRng::from_seed([0u8; 32]); + keypair = Keypair::generate::(&mut csprng); + good_sig = keypair.sign_prehashed::(prehashed_good1, Some(context)); + bad_sig = keypair.sign_prehashed::(prehashed_bad1, Some(context)); + + assert!(keypair.verify_prehashed::(prehashed_good2, Some(context), &good_sig) == true, + "Verification of a valid signature failed!"); + assert!(keypair.verify_prehashed::(prehashed_good3, Some(context), &bad_sig) == false, + "Verification of a signature on a different message passed!"); + assert!(keypair.verify_prehashed::(prehashed_bad2, Some(context), &good_sig) == false, + "Verification of a signature on a different message passed!"); + } + #[test] fn public_key_from_bytes() { // Make another function so that we can test the ? operator. From fad39851aa80c3b6cdcfe2c064c53abe92609f17 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 13 Jul 2018 21:57:31 +0000 Subject: [PATCH 130/351] Add doctests for sign_prehashed() and verify_prehashed(). --- src/ed25519.rs | 118 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/src/ed25519.rs b/src/ed25519.rs index 529e88a..13e2133 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -1056,7 +1056,86 @@ impl Keypair { /// /// An Ed25519ph [`Signature`] on the `prehashed_message`. /// + /// # Examples + /// + /// ``` + /// extern crate ed25519_dalek; + /// extern crate rand; + /// extern crate sha2; + /// + /// use ed25519_dalek::Keypair; + /// use ed25519_dalek::Signature; + /// use rand::thread_rng; + /// use rand::ThreadRng; + /// use sha2::Sha512; + /// + /// # #[cfg(all(feature = "std", feature = "sha2"))] + /// # fn main() { + /// let mut csprng: ThreadRng = thread_rng(); + /// let keypair: Keypair = Keypair::generate::(&mut csprng); + /// let message: &[u8] = b"All I want is to pet all of the dogs."; + /// + /// // Create a hash digest object which we'll feed the message into: + /// let prehashed: Sha512 = Sha512::default(); + /// + /// prehashed.input(message); + /// # } + /// # + /// # #[cfg(any(not(feature = "sha2"), not(feature = "std")))] + /// # fn main() { } + /// ``` + /// + /// If you want, you can optionally pass a "context". It is generally a + /// good idea to choose a context and try to make it unique to your project + /// and this specific usage of signatures. + /// + /// For example, without this, if you were to [convert your OpenPGP key + /// to a Bitcoin key][terrible_idea] (just as an example, and also Don't + /// Ever Do That) and someone tricked you into signing an "email" which was + /// actually a Bitcoin transaction moving all your magic internet money to + /// their address, it'd be a valid transaction. + /// + /// By adding a context, this trick becomes impossible, because the context + /// is concatenated into the hash, which is then signed. So, going with the + /// previous example, if your bitcoin wallet used a context of + /// "BitcoinWalletAppTxnSigning" and OpenPGP used a context (this is likely + /// the least of their safety problems) of "GPGsCryptoIsntConstantTimeLol", + /// then the signatures produced by both could never match the other, even + /// if they signed the exact same message with the same key. + /// + /// Let's add a context for good measure (remember, you'll want to choose + /// your own!): + /// + /// ``` + /// # extern crate ed25519_dalek; + /// # extern crate rand; + /// # extern crate sha2; + /// # + /// # use ed25519_dalek::Keypair; + /// # use ed25519_dalek::Signature; + /// # use rand::thread_rng; + /// # use rand::ThreadRng; + /// # use sha2::Sha512; + /// # + /// # #[cfg(all(feature = "std", feature = "sha2"))] + /// # fn main() { + /// # let mut csprng: ThreadRng = thread_rng(); + /// # let keypair: Keypair = Keypair::generate::(&mut csprng); + /// # let message: &[u8] = b"All I want is to pet all of the dogs."; + /// # let prehashed: Sha512 = Sha512::default(); + /// # prehashed.input(message); + /// # + /// let context: &[u8] = "Ed25519DalekSignPrehashedDoctest"; + /// + /// let sig: Signature = keypair.sign_prehashed(prehashed, Some(context)); + /// # } + /// # + /// # #[cfg(any(not(feature = "sha2"), not(feature = "std")))] + /// # fn main() { } + /// ``` + /// /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 + /// [terrible_idea]: https://github.com/isislovecruft/scripts/blob/master/gpgkey2bc.py pub fn sign_prehashed(&self, prehashed_message: D, context: Option<&'static [u8]>) -> Signature @@ -1088,6 +1167,45 @@ impl Keypair { /// Returns `true` if the `signature` was a valid signature created by this /// `Keypair` on the `prehashed_message`. /// + /// # Examples + /// + /// ``` + /// extern crate ed25519_dalek; + /// extern crate rand; + /// extern crate sha2; + /// + /// use ed25519_dalek::Keypair; + /// use ed25519_dalek::Signature; + /// use rand::thread_rng; + /// use rand::ThreadRng; + /// use sha2::Sha512; + /// + /// # #[cfg(all(feature = "std", feature = "sha2"))] + /// # fn main() { + /// let mut csprng: ThreadRng = thread_rng(); + /// let keypair: Keypair = Keypair::generate::(&mut csprng); + /// let message: &[u8] = b"All I want is to pet all of the dogs."; + /// + /// let prehashed: Sha512 = Sha512::default(); + /// prehashed.input(message); + /// + /// let context: &[u8] = "Ed25519DalekSignPrehashedDoctest"; + /// + /// let sig: Signature = keypair.sign_prehashed(prehashed, Some(context)); + /// + /// // The sha2::Sha512 struct doesn't implement Copy, so we'll have to create a new one: + /// let prehashed_again: Sha512 = Sha512::default(); + /// prehashed_again.input(message); + /// + /// let valid: bool = keypair.public.verify_prehashed(prehashed_again, context, sig); + /// + /// assert!(valid); + /// # } + /// # + /// # #[cfg(any(not(feature = "sha2"), not(feature = "std")))] + /// # fn main() { } + /// ``` + /// /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 pub fn verify_prehashed(&self, prehashed_message: D, From f8373a9e70278a17f1910f8b33cafb32c50e9e07 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 13 Jul 2018 23:57:27 +0000 Subject: [PATCH 131/351] Fix two more links in the README. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2cb81ac..342e395 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ed25519-dalek [![](https://img.shields.io/crates/v/ed25519-dalek.svg)](https://crates.io/crates/ed25519-dalek) [![](https://docs.rs/ed25519-dalek/badge.svg)](https://docs.rs/ed25519-dalek) [![](https://travis-ci.org/isislovecruft/ed25519-dalek.svg?branch=master)](https://travis-ci.org/dalek-cryptography/ed25519-dalek?branch=master) +# ed25519-dalek [![](https://img.shields.io/crates/v/ed25519-dalek.svg)](https://crates.io/crates/ed25519-dalek) [![](https://docs.rs/ed25519-dalek/badge.svg)](https://docs.rs/ed25519-dalek) [![](https://travis-ci.org/dalek-cryptography/ed25519-dalek.svg?branch=master)](https://travis-ci.org/dalek-cryptography/ed25519-dalek?branch=master) Fast and efficient Rust implementation of ed25519 key generation, signing, and verification in Rust. @@ -71,7 +71,7 @@ valuable than simply cycle counts alone. # Warnings ed25519-dalek and -[our elliptic curve library](https://github.com/isislovecruft/curve25519-dalek) +[our elliptic curve library](https://github.com/dalek-cryptography/curve25519-dalek) (which this code uses) have received *one* formal cryptographic and security review. Neither have yet received what we would consider *sufficient* peer review by other qualified cryptographers to be considered in any way, shape, From 030eff547f918e99e17d0d9e81692cb244208d0c Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Fri, 13 Jul 2018 12:39:45 -0700 Subject: [PATCH 132/351] Make `verify` return a `Result`. This also simplifies the verification logic. Because the verification check happens in variable time, we don't need to do a constant-time eq check at the end, so we can drop the `subtle` dependency entirely. The `DecodingError` type becomes `SignatureError` and is also used to signal failing verifications. --- Cargo.toml | 8 +-- README.md | 9 +-- src/ed25519.rs | 172 ++++++++++++++++++------------------------------- src/errors.rs | 36 +++++------ src/lib.rs | 14 ++-- 5 files changed, 86 insertions(+), 153 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b22e552..3da42c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,10 +19,6 @@ travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master" version = "0.18" default-features = false -[dependencies.subtle] -version = "0.6" -default-features = false - [dependencies.rand] version = "0.5" default-features = false @@ -59,8 +55,8 @@ harness = false [features] default = ["std", "u64_backend"] # We don't add "rand/std" here because it would enable a bunch of Fuchsia dependencies. -std = ["subtle/std", "curve25519-dalek/std"] -nightly = ["curve25519-dalek/nightly", "subtle/nightly", "rand/nightly"] +std = ["curve25519-dalek/std"] +nightly = ["curve25519-dalek/nightly", "rand/nightly"] asm = ["sha2/asm"] yolocrypto = ["curve25519-dalek/yolocrypto"] u64_backend = ["curve25519-dalek/u64_backend"] diff --git a/README.md b/README.md index 342e395..037eeb6 100644 --- a/README.md +++ b/README.md @@ -55,12 +55,9 @@ nanoseconds to cycles per second on a 2591 Mhz CPU, that's 237660 cycles for verification and 102523 for signing, which for signing is competitive with optimised assembly versions. -Additionally, if you're on the Rust nightly channel, be sure to build with -`cargo build --features="nightly"` which enables more secure compiler -optimisation protections in the -[subtle](https://github.com/dalek-cryptography/subtle) crate. Additionally, if -you're using a CSPRNG from the `rand` crate, the `nightly` feature will enable -`u128`/`i128` features there, resulting in potentially faster performance. +Additionally, if you're using a CSPRNG from the `rand` crate, the `nightly` +feature will enable `u128`/`i128` features there, resulting in potentially +faster performance. Additionally, thanks to Rust, this implementation has both type and memory safety. It's also easily readable by a much larger set of people than those who diff --git a/src/ed25519.rs b/src/ed25519.rs index 13e2133..3bd43ac 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -36,9 +36,7 @@ use curve25519_dalek::edwards::CompressedEdwardsY; use curve25519_dalek::edwards::EdwardsPoint; use curve25519_dalek::scalar::Scalar; -use subtle::ConstantTimeEq; - -use errors::DecodingError; +use errors::SignatureError; use errors::InternalError; /// The length of a curve25519 EdDSA `Signature`, in bytes. @@ -132,9 +130,9 @@ impl Signature { /// Construct a `Signature` from a slice of bytes. #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { + pub fn from_bytes(bytes: &[u8]) -> Result { if bytes.len() != SIGNATURE_LENGTH { - return Err(DecodingError(InternalError::BytesLengthError{ + return Err(SignatureError(InternalError::BytesLengthError{ name: "Signature", length: SIGNATURE_LENGTH })); } let mut lower: [u8; 32] = [0u8; 32]; @@ -144,7 +142,7 @@ impl Signature { upper.copy_from_slice(&bytes[32..]); if upper[31] & 224 != 0 { - return Err(DecodingError(InternalError::ScalarFormatError)); + return Err(SignatureError(InternalError::ScalarFormatError)); } Ok(Signature{ r: CompressedEdwardsY(lower), s: Scalar::from_bits(upper) }) @@ -215,9 +213,9 @@ impl SecretKey { /// # /// use ed25519_dalek::SecretKey; /// use ed25519_dalek::SECRET_KEY_LENGTH; - /// use ed25519_dalek::DecodingError; + /// use ed25519_dalek::SignatureError; /// - /// # fn doctest() -> Result { + /// # fn doctest() -> Result { /// let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = [ /// 157, 097, 177, 157, 239, 253, 090, 096, /// 186, 132, 074, 244, 146, 236, 044, 196, @@ -238,11 +236,11 @@ impl SecretKey { /// # Returns /// /// A `Result` whose okay value is an EdDSA `SecretKey` or whose error value - /// is an `DecodingError` wrapping the internal error that occurred. + /// is an `SignatureError` wrapping the internal error that occurred. #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { + pub fn from_bytes(bytes: &[u8]) -> Result { if bytes.len() != SECRET_KEY_LENGTH { - return Err(DecodingError(InternalError::BytesLengthError{ + return Err(SignatureError(InternalError::BytesLengthError{ name: "SecretKey", length: SECRET_KEY_LENGTH })); } let mut bits: [u8; 32] = [0u8; 32]; @@ -469,7 +467,7 @@ impl ExpandedSecretKey { /// # Returns /// /// A `Result` whose okay value is an EdDSA `ExpandedSecretKey` or whose - /// error value is an `DecodingError` describing the error that occurred. + /// error value is an `SignatureError` describing the error that occurred. /// /// # Examples /// @@ -479,11 +477,11 @@ impl ExpandedSecretKey { /// # extern crate ed25519_dalek; /// # /// # #[cfg(all(feature = "sha2", feature = "std"))] - /// # fn do_test() -> Result { + /// # fn do_test() -> Result { /// # /// use rand::{Rng, OsRng}; /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; - /// use ed25519_dalek::DecodingError; + /// use ed25519_dalek::SignatureError; /// /// let mut csprng: OsRng = OsRng::new().unwrap(); /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); @@ -504,9 +502,9 @@ impl ExpandedSecretKey { /// # fn main() { } /// ``` #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { + pub fn from_bytes(bytes: &[u8]) -> Result { if bytes.len() != EXPANDED_SECRET_KEY_LENGTH { - return Err(DecodingError(InternalError::BytesLengthError{ + return Err(SignatureError(InternalError::BytesLengthError{ name: "ExpandedSecretKey", length: EXPANDED_SECRET_KEY_LENGTH })); } let mut lower: [u8; 32] = [0u8; 32]; @@ -746,9 +744,9 @@ impl PublicKey { /// # /// use ed25519_dalek::PublicKey; /// use ed25519_dalek::PUBLIC_KEY_LENGTH; - /// use ed25519_dalek::DecodingError; + /// use ed25519_dalek::SignatureError; /// - /// # fn doctest() -> Result { + /// # fn doctest() -> Result { /// let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = [ /// 215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, /// 14, 225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26]; @@ -766,11 +764,11 @@ impl PublicKey { /// # Returns /// /// A `Result` whose okay value is an EdDSA `PublicKey` or whose error value - /// is an `DecodingError` describing the error that occurred. + /// is an `SignatureError` describing the error that occurred. #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { + pub fn from_bytes(bytes: &[u8]) -> Result { if bytes.len() != PUBLIC_KEY_LENGTH { - return Err(DecodingError(InternalError::BytesLengthError{ + return Err(SignatureError(InternalError::BytesLengthError{ name: "PublicKey", length: PUBLIC_KEY_LENGTH })); } let mut bits: [u8; 32] = [0u8; 32]; @@ -779,12 +777,6 @@ impl PublicKey { Ok(PublicKey(CompressedEdwardsY(bits))) } - /// Convert this public key to its underlying extended twisted Edwards coordinate. - #[inline] - fn decompress(&self) -> Option { - self.0.decompress() - } - /// Derive this public key from its corresponding `SecretKey`. #[allow(unused_assignments)] pub fn from_secret(secret_key: &SecretKey) -> PublicKey @@ -812,36 +804,27 @@ impl PublicKey { /// /// # Return /// - /// Returns true if the signature was successfully verified, and - /// false otherwise. - pub fn verify(&self, message: &[u8], signature: &Signature) -> bool + /// Returns `Ok(())` if the signature is valid, and `Err` otherwise. + #[allow(non_snake_case)] + pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> where D: Digest + Default { - let mut h: D = D::default(); - let mut a: EdwardsPoint; - let ao: Option; - let mut digest: [u8; 64] = [0u8; 64]; - - ao = self.decompress(); - - if ao.is_some() { - a = ao.unwrap(); - } else { - return false; - } - a = -(&a); + let A = self.0.decompress() + .ok_or_else(|| SignatureError(InternalError::PointDecompressionError))?; + let mut h = D::default(); h.input(signature.r.as_bytes()); h.input(self.as_bytes()); h.input(&message); + let k = Scalar::from_hash(h); - digest.copy_from_slice(h.fixed_result().as_slice()); + let R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(-A), &signature.s); - let digest_reduced: Scalar = Scalar::from_bytes_mod_order_wide(&digest); - let r: EdwardsPoint = EdwardsPoint::vartime_double_scalar_mul_basepoint(&digest_reduced, - &a, &signature.s); - - (signature.r.as_bytes()).ct_eq(r.compress().as_bytes()).unwrap_u8() == 1 + if R.compress() == signature.r { + Ok(()) + } else { + Err(SignatureError(InternalError::VerifyError)) + } } /// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm. @@ -862,43 +845,36 @@ impl PublicKey { /// `Keypair` on the `prehashed_message`. /// /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 + #[allow(non_snake_case)] pub fn verify_prehashed(&self, prehashed_message: D, context: Option<&[u8]>, - signature: &Signature) -> bool + signature: &Signature) -> Result<(), SignatureError> where D: Digest + Default { - let mut h: D = D::default(); - let mut hash: [u8; 64] = [0u8; 64]; - - let mut a: EdwardsPoint = match self.decompress() { - Some(x) => x, - None => return false, - }; - a = -(&a); - - let ctx: &[u8] = match context { - Some(x) => x, - None => b"", // By default, the context is an empty string. - }; + let ctx = context.unwrap_or(b""); debug_assert!(ctx.len() <= 255, "The context must not be longer than 255 octets."); - let ctx_len: u8 = ctx.len() as u8; + let A = self.0.decompress() + .ok_or_else(|| SignatureError(InternalError::PointDecompressionError))?; + let mut h = D::default(); h.input(b"SigEd25519 no Ed25519 collisions"); h.input(&[1]); // Ed25519ph - h.input(&[ctx_len]); + h.input(&[ctx.len() as u8]); h.input(ctx); h.input(signature.r.as_bytes()); h.input(self.as_bytes()); h.input(prehashed_message.fixed_result().as_slice()); - hash.copy_from_slice(h.fixed_result().as_slice()); + let k = Scalar::from_hash(h); - let digest_reduced: Scalar = Scalar::from_bytes_mod_order_wide(&hash); - let r: EdwardsPoint = EdwardsPoint::vartime_double_scalar_mul_basepoint(&digest_reduced, - &a, &signature.s); + let R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(-A), &signature.s); - (signature.r.as_bytes()).ct_eq(r.compress().as_bytes()).unwrap_u8() == 1 + if R.compress() == signature.r { + Ok(()) + } else { + Err(SignatureError(InternalError::VerifyError)) + } } } @@ -976,10 +952,10 @@ impl Keypair { /// # Returns /// /// A `Result` whose okay value is an EdDSA `Keypair` or whose error value - /// is an `DecodingError` describing the error that occurred. - pub fn from_bytes<'a>(bytes: &'a [u8]) -> Result { + /// is an `SignatureError` describing the error that occurred. + pub fn from_bytes<'a>(bytes: &'a [u8]) -> Result { if bytes.len() != KEYPAIR_LENGTH { - return Err(DecodingError(InternalError::BytesLengthError{ + return Err(SignatureError(InternalError::BytesLengthError{ name: "Keypair", length: KEYPAIR_LENGTH})); } let secret = SecretKey::from_bytes(&bytes[..SECRET_KEY_LENGTH])?; @@ -1145,7 +1121,7 @@ impl Keypair { } /// Verify a signature on a message with this keypair's public key. - pub fn verify(&self, message: &[u8], signature: &Signature) -> bool + pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> where D: Digest + Default { self.public.verify::(message, signature) } @@ -1210,7 +1186,7 @@ impl Keypair { pub fn verify_prehashed(&self, prehashed_message: D, context: Option<&[u8]>, - signature: &Signature) -> bool + signature: &Signature) -> Result<(), SignatureError> where D: Digest + Default { self.public.verify_prehashed::(prehashed_message, context, signature) @@ -1261,7 +1237,6 @@ mod test { use std::fs::File; use std::string::String; use std::vec::Vec; - use curve25519_dalek::edwards::EdwardsPoint; use rand::ChaChaRng; use rand::SeedableRng; use hex::FromHex; @@ -1295,32 +1270,7 @@ mod test { 063, 120, 126, 100, 092, 059, 050, 011, ]; #[test] - fn unmarshal_marshal() { // TestUnmarshalMarshal - let mut csprng: ChaChaRng; - let mut keypair: Keypair; - let mut x: Option; - let a: EdwardsPoint; - let public: PublicKey; - - csprng = ChaChaRng::from_seed([0u8; 32]); - - // from_bytes() fails if vx²-u=0 and vx²+u=0 - loop { - keypair = Keypair::generate::(&mut csprng); - x = keypair.public.decompress(); - - if x.is_some() { - a = x.unwrap(); - break; - } - } - public = PublicKey(a.compress()); - - assert!(keypair.public.0 == public.0); - } - - #[test] - fn ed25519_sign_verify() { // TestSignVerify + fn sign_verify() { // TestSignVerify let mut csprng: ChaChaRng; let keypair: Keypair; let good_sig: Signature; @@ -1334,11 +1284,11 @@ mod test { good_sig = keypair.sign::(&good); bad_sig = keypair.sign::(&bad); - assert!(keypair.verify::(&good, &good_sig) == true, + assert!(keypair.verify::(&good, &good_sig).is_ok(), "Verification of a valid signature failed!"); - assert!(keypair.verify::(&good, &bad_sig) == false, + assert!(keypair.verify::(&good, &bad_sig).is_err(), "Verification of a signature on a different message passed!"); - assert!(keypair.verify::(&bad, &good_sig) == false, + assert!(keypair.verify::(&bad, &good_sig).is_err(), "Verification of a signature on a different message passed!"); } @@ -1383,7 +1333,7 @@ mod test { let sig2: Signature = keypair.sign::(&msg_bytes); assert!(sig1 == sig2, "Signature bytes not equal on line {}", lineno); - assert!(keypair.verify::(&msg_bytes, &sig2), + assert!(keypair.verify::(&msg_bytes, &sig2).is_ok(), "Signature verification failed on line {}", lineno); } } @@ -1417,7 +1367,7 @@ mod test { assert!(sig1 == sig2, "Original signature from test vectors doesn't equal signature produced:\ \noriginal:\n{:?}\nproduced:\n{:?}", sig1, sig2); - assert!(keypair.verify_prehashed(prehash_for_verifying, None, &sig2), + assert!(keypair.verify_prehashed(prehash_for_verifying, None, &sig2).is_ok(), "Could not verify ed25519ph signature!"); } @@ -1451,18 +1401,18 @@ mod test { good_sig = keypair.sign_prehashed::(prehashed_good1, Some(context)); bad_sig = keypair.sign_prehashed::(prehashed_bad1, Some(context)); - assert!(keypair.verify_prehashed::(prehashed_good2, Some(context), &good_sig) == true, + assert!(keypair.verify_prehashed::(prehashed_good2, Some(context), &good_sig).is_ok(), "Verification of a valid signature failed!"); - assert!(keypair.verify_prehashed::(prehashed_good3, Some(context), &bad_sig) == false, + assert!(keypair.verify_prehashed::(prehashed_good3, Some(context), &bad_sig).is_err(), "Verification of a signature on a different message passed!"); - assert!(keypair.verify_prehashed::(prehashed_bad2, Some(context), &good_sig) == false, + assert!(keypair.verify_prehashed::(prehashed_bad2, Some(context), &good_sig).is_err(), "Verification of a signature on a different message passed!"); } #[test] fn public_key_from_bytes() { // Make another function so that we can test the ? operator. - fn do_the_test() -> Result { + fn do_the_test() -> Result { let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = [ 215, 090, 152, 001, 130, 177, 010, 183, 213, 075, 254, 211, 201, 100, 007, 058, diff --git a/src/errors.rs b/src/errors.rs index 57968dd..bf568a6 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -20,7 +20,6 @@ use core::fmt::Display; /// need to pay any attention to these. #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub (crate) enum InternalError { - #[allow(dead_code)] PointDecompressionError, ScalarFormatError, /// An error in the length of bytes handed to a constructor. @@ -29,55 +28,52 @@ pub (crate) enum InternalError { /// returning the error, and the `length` in bytes which its constructor /// expects. BytesLengthError{ name: &'static str, length: usize }, + /// The verification equation wasn't satisfied + VerifyError, } impl Display for InternalError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { InternalError::PointDecompressionError - => write!(f, "Cannot decompress extended twisted edwards point"), + => write!(f, "Cannot decompress Edwards point"), InternalError::ScalarFormatError => write!(f, "Cannot use scalar with high-bit set"), InternalError::BytesLengthError{ name: n, length: l} => write!(f, "{} must be {} bytes in length", n, l), + InternalError::VerifyError + => write!(f, "Verification equation was not satisfied"), } } } impl ::failure::Fail for InternalError {} -/// Errors which may occur in the `from_bytes()` constructors of `PublicKey`, -/// `SecretKey`, `ExpandedSecretKey`, `Keypair`, and `Signature`. -/// -/// There was an internal problem due to parsing the `Signature`. +/// Errors which may occur while processing signatures and keypairs. /// /// This error may arise due to: /// +/// * Being given bytes with a length different to what was expected. +/// /// * A problem decompressing `r`, a curve point, in the `Signature`, or the /// curve point for a `PublicKey`. +/// /// * A problem with the format of `s`, a scalar, in the `Signature`. This /// is only raised if the high-bit of the scalar was set. (Scalars must /// only be constructed from 255-bit integers.) -/// * Being given bytes with a length different to what was expected. +/// +/// * Failure of a signature to satisfy the verification equation. #[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)] -pub struct DecodingError(pub (crate) InternalError); +pub struct SignatureError(pub (crate) InternalError); -impl Display for DecodingError { +impl Display for SignatureError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.0 { - InternalError::PointDecompressionError => write!(f, "{}", self.0), - InternalError::ScalarFormatError => write!(f, "{}", self.0), - InternalError::BytesLengthError{ name: _, length: _ } => write!(f, "{}", self.0), - } + write!(f, "{}", self.0) } } -impl ::failure::Fail for DecodingError { +impl ::failure::Fail for SignatureError { fn cause(&self) -> Option<&::failure::Fail> { - match self.0 { - InternalError::PointDecompressionError => Some(&self.0), - InternalError::ScalarFormatError => Some(&self.0), - InternalError::BytesLengthError{ name: _, length: _} => Some(&self.0), - } + Some(&self.0) } } diff --git a/src/lib.rs b/src/lib.rs index d516597..b74999a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -78,9 +78,7 @@ //! # let keypair: Keypair = Keypair::generate::(&mut csprng); //! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); //! # let signature: Signature = keypair.sign::(message); -//! let verified: bool = keypair.verify::(message, &signature); -//! -//! assert!(verified); +//! assert!(keypair.verify::(message, &signature).is_ok()); //! # } //! ``` //! @@ -105,9 +103,7 @@ //! # let signature: Signature = keypair.sign::(message); //! //! let public_key: PublicKey = keypair.public; -//! let verified: bool = public_key.verify::(message, &signature); -//! -//! assert!(verified); +//! assert!(public_key.verify::(message, &signature).is_ok()); //! # } //! ``` //! @@ -133,7 +129,6 @@ //! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); //! # let signature: Signature = keypair.sign::(message); //! # let public_key: PublicKey = keypair.public; -//! # let verified: bool = public_key.verify::(message, &signature); //! //! let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = public_key.to_bytes(); //! let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = keypair.secret.to_bytes(); @@ -150,9 +145,9 @@ //! # extern crate ed25519_dalek; //! # use rand::{Rng, ChaChaRng, SeedableRng}; //! # use sha2::Sha512; -//! # use ed25519_dalek::{Keypair, Signature, PublicKey, SecretKey, DecodingError}; +//! # use ed25519_dalek::{Keypair, Signature, PublicKey, SecretKey, SignatureError}; //! # use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; -//! # fn do_test() -> Result<(SecretKey, PublicKey, Keypair, Signature), DecodingError> { +//! # fn do_test() -> Result<(SecretKey, PublicKey, Keypair, Signature), SignatureError> { //! # let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]); //! # let keypair_orig: Keypair = Keypair::generate::(&mut csprng); //! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); @@ -267,7 +262,6 @@ extern crate curve25519_dalek; extern crate generic_array; extern crate digest; -extern crate subtle; extern crate failure; extern crate rand; From 80075a0b41b5dedf6053ac012b2c24edefc81345 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 15 Jul 2018 21:38:10 +0000 Subject: [PATCH 133/351] Cleanup signing code to use new dalek APIs and Rust syntax. --- src/ed25519.rs | 73 ++++++++++++++++++++++---------------------------- 1 file changed, 32 insertions(+), 41 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 3bd43ac..3cb59d7 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -67,6 +67,7 @@ pub const EXPANDED_SECRET_KEY_LENGTH: usize = EXPANDED_SECRET_KEY_KEY_LENGTH + E /// These signatures, unlike the ed25519 signature reference implementation, are /// "detached"—that is, they do **not** include a copy of the message which has /// been signed. +#[allow(non_snake_case)] #[derive(Copy)] #[repr(C)] pub struct Signature { @@ -79,7 +80,7 @@ pub struct Signature { /// This digest is then interpreted as a `Scalar` and reduced into an /// element in ℤ/lℤ. The scalar is then multiplied by the distinguished /// basepoint to produce `r`, and `EdwardsPoint`. - pub (crate) r: CompressedEdwardsY, + pub (crate) R: CompressedEdwardsY, /// `s` is a `Scalar`, formed by using an hash function with 512-bits output /// to produce the digest of: @@ -99,7 +100,7 @@ impl Clone for Signature { impl Debug for Signature { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "Signature( r: {:?}, s: {:?} )", &self.r, &self.s) + write!(f, "Signature( R: {:?}, s: {:?} )", &self.R, &self.s) } } @@ -110,7 +111,7 @@ impl PartialEq for Signature { let mut equal: u8 = 0; for i in 0..32 { - equal |= self.r.0[i] ^ other.r.0[i]; + equal |= self.R.0[i] ^ other.R.0[i]; equal |= self.s[i] ^ other.s[i]; } equal == 0 @@ -123,7 +124,7 @@ impl Signature { pub fn to_bytes(&self) -> [u8; SIGNATURE_LENGTH] { let mut signature_bytes: [u8; SIGNATURE_LENGTH] = [0u8; SIGNATURE_LENGTH]; - signature_bytes[..32].copy_from_slice(&self.r.as_bytes()[..]); + signature_bytes[..32].copy_from_slice(&self.R.as_bytes()[..]); signature_bytes[32..].copy_from_slice(&self.s.as_bytes()[..]); signature_bytes } @@ -145,7 +146,7 @@ impl Signature { return Err(SignatureError(InternalError::ScalarFormatError)); } - Ok(Signature{ r: CompressedEdwardsY(lower), s: Scalar::from_bits(upper) }) + Ok(Signature{ R: CompressedEdwardsY(lower), s: Scalar::from_bits(upper) }) } } @@ -562,34 +563,30 @@ impl ExpandedSecretKey { } /// Sign a message with this `ExpandedSecretKey`. + #[allow(non_snake_case)] pub fn sign(&self, message: &[u8], public_key: &PublicKey) -> Signature where D: Digest + Default { let mut h: D = D::default(); - let mut hash: [u8; 64] = [0u8; 64]; - let mesg_digest: Scalar; - let hram_digest: Scalar; - let r: CompressedEdwardsY; + let R: CompressedEdwardsY; + let r: Scalar; let s: Scalar; + let k: Scalar; h.input(&self.nonce); h.input(&message); - hash.copy_from_slice(h.fixed_result().as_slice()); - mesg_digest = Scalar::from_bytes_mod_order_wide(&hash); - - r = (&mesg_digest * &constants::ED25519_BASEPOINT_TABLE).compress(); + r = Scalar::from_hash(h); + R = (&r * &constants::ED25519_BASEPOINT_TABLE).compress(); h = D::default(); - h.input(r.as_bytes()); + h.input(R.as_bytes()); h.input(public_key.as_bytes()); h.input(&message); - hash.copy_from_slice(h.fixed_result().as_slice()); - hram_digest = Scalar::from_bytes_mod_order_wide(&hash); + k = Scalar::from_hash(h); + s = &(&k * &self.key) + &r; - s = &(&hram_digest * &self.key) + &mesg_digest; - - Signature{ r: r, s: s } + Signature{ R, s } } /// Sign a `prehashed_message` with this `ExpandedSecretKey` using the @@ -610,6 +607,7 @@ impl ExpandedSecretKey { /// An Ed25519ph [`Signature`] on the `prehashed_message`. /// /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 + #[allow(non_snake_case)] pub fn sign_prehashed(&self, prehashed_message: D, public_key: &PublicKey, @@ -617,17 +615,14 @@ impl ExpandedSecretKey { where D: Digest + Default { let mut h: D = D::default(); - let mut hash: [u8; 64] = [0u8; 64]; let mut prehash: [u8; 64] = [0u8; 64]; - let mesg_digest: Scalar; - let hram_digest: Scalar; - let r: CompressedEdwardsY; + let R: CompressedEdwardsY; + let r: Scalar; let s: Scalar; + let k: Scalar; + + let ctx: &[u8] = context.unwrap_or(b""); // By default, the context is an empty string. - let ctx: &[u8] = match context { - Some(x) => x, - None => b"", // By default, the context is an empty string. - }; debug_assert!(ctx.len() <= 255, "The context must not be longer than 255 octets."); let ctx_len: u8 = ctx.len() as u8; @@ -653,27 +648,23 @@ impl ExpandedSecretKey { h.input(ctx); h.input(&self.nonce); h.input(&prehash); - hash.copy_from_slice(h.fixed_result().as_slice()); - mesg_digest = Scalar::from_bytes_mod_order_wide(&hash); - - r = (&mesg_digest * &constants::ED25519_BASEPOINT_TABLE).compress(); + r = Scalar::from_hash(h); + R = (&r * &constants::ED25519_BASEPOINT_TABLE).compress(); h = D::default(); h.input(b"SigEd25519 no Ed25519 collisions"); h.input(&[1]); // Ed25519ph h.input(&[ctx_len]); h.input(ctx); - h.input(r.as_bytes()); + h.input(R.as_bytes()); h.input(public_key.as_bytes()); h.input(&prehash); - hash.copy_from_slice(h.fixed_result().as_slice()); - hram_digest = Scalar::from_bytes_mod_order_wide(&hash); + k = Scalar::from_hash(h); + s = &(&k * &self.key) + &r; - s = &(&hram_digest * &self.key) + &mesg_digest; - - Signature{ r: r, s: s } + Signature{ R, s } } } @@ -813,14 +804,14 @@ impl PublicKey { .ok_or_else(|| SignatureError(InternalError::PointDecompressionError))?; let mut h = D::default(); - h.input(signature.r.as_bytes()); + h.input(signature.R.as_bytes()); h.input(self.as_bytes()); h.input(&message); let k = Scalar::from_hash(h); let R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(-A), &signature.s); - if R.compress() == signature.r { + if R.compress() == signature.R { Ok(()) } else { Err(SignatureError(InternalError::VerifyError)) @@ -863,14 +854,14 @@ impl PublicKey { h.input(&[1]); // Ed25519ph h.input(&[ctx.len() as u8]); h.input(ctx); - h.input(signature.r.as_bytes()); + h.input(signature.R.as_bytes()); h.input(self.as_bytes()); h.input(prehashed_message.fixed_result().as_slice()); let k = Scalar::from_hash(h); let R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(-A), &signature.s); - if R.compress() == signature.r { + if R.compress() == signature.R { Ok(()) } else { Err(SignatureError(InternalError::VerifyError)) From 5c26349b6c8961c615ef2d041965d656cf8b4be2 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 15 Jul 2018 21:50:20 +0000 Subject: [PATCH 134/351] Derive Eq, PartialEq for Signature. --- src/ed25519.rs | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 3cb59d7..9fe85c3 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -68,7 +68,7 @@ pub const EXPANDED_SECRET_KEY_LENGTH: usize = EXPANDED_SECRET_KEY_KEY_LENGTH + E /// "detached"—that is, they do **not** include a copy of the message which has /// been signed. #[allow(non_snake_case)] -#[derive(Copy)] +#[derive(Copy, Eq, PartialEq)] #[repr(C)] pub struct Signature { /// `r` is an `EdwardsPoint`, formed by using an hash function with @@ -104,20 +104,6 @@ impl Debug for Signature { } } -impl Eq for Signature {} - -impl PartialEq for Signature { - fn eq(&self, other: &Signature) -> bool { - let mut equal: u8 = 0; - - for i in 0..32 { - equal |= self.R.0[i] ^ other.R.0[i]; - equal |= self.s[i] ^ other.s[i]; - } - equal == 0 - } -} - impl Signature { /// Convert this `Signature` to a byte array. #[inline] From cdbc302da784aee8b831b34ebee77abc6f899e06 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 15 Jul 2018 21:51:23 +0000 Subject: [PATCH 135/351] Fix a couple docstrings which mentioned `r` instead of `R`. --- src/ed25519.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 9fe85c3..5cc628b 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -71,7 +71,7 @@ pub const EXPANDED_SECRET_KEY_LENGTH: usize = EXPANDED_SECRET_KEY_KEY_LENGTH + E #[derive(Copy, Eq, PartialEq)] #[repr(C)] pub struct Signature { - /// `r` is an `EdwardsPoint`, formed by using an hash function with + /// `R` is an `EdwardsPoint`, formed by using an hash function with /// 512-bits output to produce the digest of: /// /// - the nonce half of the `ExpandedSecretKey`, and @@ -79,7 +79,7 @@ pub struct Signature { /// /// This digest is then interpreted as a `Scalar` and reduced into an /// element in ℤ/lℤ. The scalar is then multiplied by the distinguished - /// basepoint to produce `r`, and `EdwardsPoint`. + /// basepoint to produce `R`, and `EdwardsPoint`. pub (crate) R: CompressedEdwardsY, /// `s` is a `Scalar`, formed by using an hash function with 512-bits output From 2e5363c6792adbee6cff01641d9fa042f5d4de2e Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 15 Jul 2018 22:04:55 +0000 Subject: [PATCH 136/351] Cleanup verification variable declarations. --- src/ed25519.rs | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 3bd43ac..c01edcd 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -809,16 +809,21 @@ impl PublicKey { pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> where D: Digest + Default { - let A = self.0.decompress() - .ok_or_else(|| SignatureError(InternalError::PointDecompressionError))?; + let mut h: D = D::default(); + let R: EdwardsPoint; + let k: Scalar; + + let A: EdwardsPoint = match self.0.decompress() { + Ok(x) => x, + Err => Err(SignatureError(InternalError::PointDecompressionError))), + }; - let mut h = D::default(); h.input(signature.r.as_bytes()); h.input(self.as_bytes()); h.input(&message); - let k = Scalar::from_hash(h); - let R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(-A), &signature.s); + k = Scalar::from_hash(h); + R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(-A), &signature.s); if R.compress() == signature.r { Ok(()) @@ -852,13 +857,18 @@ impl PublicKey { signature: &Signature) -> Result<(), SignatureError> where D: Digest + Default { - let ctx = context.unwrap_or(b""); + let mut h: D = D::default(); + let R: EdwardsPoint; + let k: Scalar; + + let ctx: &[u8] = context.unwrap_or(b""); debug_assert!(ctx.len() <= 255, "The context must not be longer than 255 octets."); - let A = self.0.decompress() - .ok_or_else(|| SignatureError(InternalError::PointDecompressionError))?; + let A: EdwardsPoint = match self.0.decompress() { + Ok(x) => x, + Err => Err(SignatureError(InternalError::PointDecompressionError))), + }; - let mut h = D::default(); h.input(b"SigEd25519 no Ed25519 collisions"); h.input(&[1]); // Ed25519ph h.input(&[ctx.len() as u8]); @@ -866,9 +876,9 @@ impl PublicKey { h.input(signature.r.as_bytes()); h.input(self.as_bytes()); h.input(prehashed_message.fixed_result().as_slice()); - let k = Scalar::from_hash(h); - let R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(-A), &signature.s); + k = Scalar::from_hash(h); + R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(-A), &signature.s); if R.compress() == signature.r { Ok(()) From 535f4752a686338ebfbc731bdb5d7f26edc9a4b7 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 15 Jul 2018 22:07:42 +0000 Subject: [PATCH 137/351] Don't run `cargo bench` on Travis CI servers. --- .travis.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index b14a0d4..4c8d19e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,10 +15,6 @@ matrix: env: TEST_COMMAND=build FEATURES="--no-default-features --features=u32_backend" - rust: nightly env: TEST_COMMAND=test FEATURES=--features="nightly" - - rust: nightly - env: TEST_COMMAND=bench FEATURES='' - - rust: nightly - env: TEST_COMMAND=bench FEATURES=--features="nightly" script: - cargo $TEST_COMMAND $FEATURES From 6c8ae573d90f58f946df063c17387c91826cb415 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 15 Jul 2018 22:15:54 +0000 Subject: [PATCH 138/351] Bump ed25519-dalek version to 0.7.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 3da42c9..e37ea8d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "0.6.2" +version = "0.7.0" authors = ["Isis Lovecruft "] readme = "README.md" license = "BSD-3-Clause" From b32e39c801940bb5b8f2b84a52742cc3438370f9 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 15 Jul 2018 22:21:32 +0000 Subject: [PATCH 139/351] Fix match statements on public key decompression. --- src/ed25519.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 6cb2308..919ecb4 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -791,8 +791,8 @@ impl PublicKey { let k: Scalar; let A: EdwardsPoint = match self.0.decompress() { - Ok(x) => x, - Err => Err(SignatureError(InternalError::PointDecompressionError))), + Some(x) => x, + None => return Err(SignatureError(InternalError::PointDecompressionError)), }; h.input(signature.R.as_bytes()); @@ -842,8 +842,8 @@ impl PublicKey { debug_assert!(ctx.len() <= 255, "The context must not be longer than 255 octets."); let A: EdwardsPoint = match self.0.decompress() { - Ok(x) => x, - Err => Err(SignatureError(InternalError::PointDecompressionError))), + Some(x) => x, + None => return Err(SignatureError(InternalError::PointDecompressionError)), }; h.input(b"SigEd25519 no Ed25519 collisions"); From 5050049a6c870470af9a7e70c39e7d4b06d4b296 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 15 Jul 2018 23:22:22 +0000 Subject: [PATCH 140/351] Update benchmarks in README. --- README.md | 55 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 037eeb6..3ba0546 100644 --- a/README.md +++ b/README.md @@ -14,46 +14,47 @@ reason for feature-gating the benchmarks is that Rust's `test::Bencher` is unstable, and thus only works on the nightly channel. (We'd like people to be able to compile and test on the stable and beta channels too!) -On an Intel i5 Sandy Bridge running at 2.6 GHz, with TurboBoost enabled (and -also running in QubesOS with *lots* of other VMs executing), this code -achieves the following performance benchmarks: +On an Intel i9-7900X running at 3.30 GHz, without TurboBoost, this code achieves +the following performance benchmarks: - ∃!isisⒶwintermute:(master *=)~/code/rust/ed25519-dalek ∴ cargo bench --features="nightly bench" + ∃!isisⒶmistakenot:(master *=)~/code/rust/ed25519-dalek ∴ cargo bench Compiling ed25519-dalek v0.7.0 (file:///home/isis/code/rust/ed25519-dalek) Finished release [optimized] target(s) in 3.11s - Running target/release/deps/ed25519_dalek-ae92163eefd0cc80 + Running target/release/deps/ed25519_benchmarks-721332beed423bce - running 9 tests - test ed25519::test::golden ... ignored - test ed25519::test::public_key_from_bytes ... ignored - test ed25519::test::sign_verify ... ignored - test ed25519::test::unmarshal_marshal ... ignored - test ed25519::bench::key_generation ... bench: 30,711 ns/iter (+/- 10,936) - test ed25519::bench::sign ... bench: 39,432 ns/iter (+/- 21,387) - test ed25519::bench::sign_expanded_key ... bench: 45,753 ns/iter (+/- 25,261) - test ed25519::bench::underlying_scalar_mult_basepoint ... bench: 25,455 ns/iter (+/- 10,587) - test ed25519::bench::verify ... bench: 91,408 ns/iter (+/- 31,193) + Ed25519 signing time: [15.617 us 15.630 us 15.647 us] + Ed25519 signature verification time: [45.930 us 45.968 us 46.011 us] + Ed25519 keypair generation time: [15.440 us 15.465 us 15.492 us] - test result: ok. 0 passed; 0 failed; 4 ignored; 5 measured; 0 filtered out +By enabling the avx2 backend (on machines with compatible microarchitectures), +the performance for signature verification is greatly improved: + + ∃!isisⒶmistakenot:(master *=)~/code/rust/ed25519-dalek ∴ export RUSTFLAGS="-C target_cpu=native" + ∃!isisⒶmistakenot:(master *=)~/code/rust/ed25519-dalek ∴ cargo bench --no-default-features --features "std avx2_backend" + Compiling ed25519-dalek v0.7.0 (file:///home/isis/code/rust/ed25519-dalek) + Finished release [optimized] target(s) in 4.28s + Running target/release/deps/ed25519_benchmarks-e4866664de39c84d + Ed25519 signing time: [15.923 us 15.945 us 15.967 us] + Ed25519 signature verification time: [33.382 us 33.411 us 33.445 us] + Ed25519 keypair generation time: [15.246 us 15.260 us 15.275 us] In comparison, the equivalent package in Golang performs as follows: ∃!isisⒶwintermute:(master *=)~/code/go/src/github.com/agl/ed25519 ∴ go test -bench . - PASS - BenchmarkKeyGeneration 20000 85880 ns/op - BenchmarkSigning 20000 89115 ns/op - BenchmarkVerification 10000 212585 ns/op - ok github.com/agl/ed25519 7.500s + BenchmarkKeyGeneration 30000 47007 ns/op + BenchmarkSigning 30000 48820 ns/op + BenchmarkVerification 10000 119701 ns/op + ok github.com/agl/ed25519 5.775s -Making key generation, signing, and verification a rough average of 33% -faster, 44% faster, and 43% faster respectively. Of course, this +Making key generation and signing a rough average of 2x faster, and +verification 2.5-3x faster depending on the availability of avx2. Of course, this is just my machine, and these results—nowhere near rigorous—should be taken with a handful of salt. -Translating to a rough cycle count: we multiply by a factor of 2.6 to convert -nanoseconds to cycles per second on a 2591 Mhz CPU, that's 237660 cycles for -verification and 102523 for signing, which for signing is competitive -with optimised assembly versions. +Translating to a rough cycle count: we multiply by a factor of 3.3 to convert +nanoseconds to cycles per second on a 3300 Mhz CPU, that's 110256 cycles for +verification and 52618 for signing, which is competitive with hand-optimised +assembly implementations. Additionally, if you're using a CSPRNG from the `rand` crate, the `nightly` feature will enable `u128`/`i128` features there, resulting in potentially From c024b671c03677c658569d232f2c4c4403ea1678 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 16 Jul 2018 20:44:54 +0000 Subject: [PATCH 141/351] Remove outdated paragraph about the bench deature in README. --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index 3ba0546..be88add 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,6 @@ Documentation is available [here](https://docs.rs/ed25519-dalek). # Benchmarks -You need to pass the `--features="bench"` flag to run the benchmarks. The -reason for feature-gating the benchmarks is that Rust's `test::Bencher` is -unstable, and thus only works on the nightly channel. (We'd like people to be -able to compile and test on the stable and beta channels too!) - On an Intel i9-7900X running at 3.30 GHz, without TurboBoost, this code achieves the following performance benchmarks: From 494c4628c22d77c38bc6430b343e4984f1021477 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 16 Jul 2018 20:45:03 +0000 Subject: [PATCH 142/351] Fix a typo in the README. This wasn't the machine I ran it on, or else it wouldn't have been a remotely fair comparison because my laptop is a 10-year-old piece of crap x220 running Qubes. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index be88add..76ed1e0 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ the performance for signature verification is greatly improved: In comparison, the equivalent package in Golang performs as follows: - ∃!isisⒶwintermute:(master *=)~/code/go/src/github.com/agl/ed25519 ∴ go test -bench . + ∃!isisⒶmistakenot:(master *=)~/code/go/src/github.com/agl/ed25519 ∴ go test -bench . BenchmarkKeyGeneration 30000 47007 ns/op BenchmarkSigning 30000 48820 ns/op BenchmarkVerification 10000 119701 ns/op From ce46a12d92a06e441df0806fb4b51ed2b3cef58d Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 16 Jul 2018 23:41:09 +0000 Subject: [PATCH 143/351] Implement batch verification. The API for this isn't the greatest and I apologise for that. Suggestions for improvement welcome. One thing which @hdevalence and I considered was to change the function signature to: pub fn verify_batch(messages: M, signatures: S, public_keys: K, csprng: &mut C) -> Result<(), SignatureError> where D: Digest + Default, C: Rng + CryptoRng, M: IntoIterator, S: IntoIterator, S::Item: Borrow, K: IntoIterator, K::Item: Borrow, The other improvement which could be made is to implement 128-bit scalars for the randomnesses. * CLOSES #27 --- Cargo.toml | 1 + benches/ed25519_benchmarks.rs | 22 ++++++ src/ed25519.rs | 142 ++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index e37ea8d..245f3ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,6 +56,7 @@ harness = false default = ["std", "u64_backend"] # We don't add "rand/std" here because it would enable a bunch of Fuchsia dependencies. std = ["curve25519-dalek/std"] +alloc = ["curve25519-dalek/alloc"] nightly = ["curve25519-dalek/nightly", "rand/nightly"] asm = ["sha2/asm"] yolocrypto = ["curve25519-dalek/yolocrypto"] diff --git a/benches/ed25519_benchmarks.rs b/benches/ed25519_benchmarks.rs index b9ac890..8347ea5 100644 --- a/benches/ed25519_benchmarks.rs +++ b/benches/ed25519_benchmarks.rs @@ -19,7 +19,9 @@ mod ed25519_benches { use super::*; use ed25519_dalek::ExpandedSecretKey; use ed25519_dalek::Keypair; + use ed25519_dalek::PublicKey; use ed25519_dalek::Signature; + use ed25519_dalek::verify_batch; use rand::thread_rng; use rand::ThreadRng; use sha2::Sha512; @@ -56,6 +58,25 @@ mod ed25519_benches { }); } + fn verify_batch_signatures(c: &mut Criterion) { + static BATCH_SIZES: [u8; 6] = [4, 8, 16, 32, 64, 96]; + + c.bench_function_over_inputs( + "Ed25519 batch signature verification", + |b, &&size| { + let mut csprng: ThreadRng = thread_rng(); + let keypairs: Vec = (0..size).map(|_| Keypair::generate::(&mut csprng)).collect(); + let msg: &[u8] = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + let messages: Vec<&[u8]> = (0..size).map(|_| msg).collect(); + let signatures: Vec = keypairs.iter().map(|key| key.sign::(&msg)).collect(); + let public_keys: Vec = keypairs.iter().map(|key| key.public).collect(); + + b.iter(|| verify_batch::(&messages[..], &signatures[..], &public_keys[..], &mut csprng)); + }, + &BATCH_SIZES, + ); + } + fn key_generation(c: &mut Criterion) { let mut csprng: ThreadRng = thread_rng(); @@ -71,6 +92,7 @@ mod ed25519_benches { sign, sign_expanded_key, verify, + verify_batch_signatures, key_generation, } } diff --git a/src/ed25519.rs b/src/ed25519.rs index 919ecb4..9073f7a 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -865,6 +865,120 @@ impl PublicKey { } } +/// Verify a batch of `signatures` on `messages` with their respective `public_keys`. +/// +/// # Inputs +/// +/// * `messages` is a slice of byte slices, one per signed message. +/// * `signatures` is a slice of `Signature`s. +/// * `public_keys` is a slice of `PublicKey`s. +/// * `csprng` is an implementation of `Rng + CryptoRng`, such as `rand::ThreadRng`. +/// +/// # Panics +/// +/// This function will panic if the `messages, `signatures`, and `public_keys` +/// slices are not equal length. +/// +/// # Returns +/// +/// * A `Result` whose `Ok` value is an emtpy tuple and whose `Err` value is a +/// `SignatureError` containing a description of the internal error which +/// occured. +/// +/// # Examples +/// +/// ``` +/// extern crate ed25519_dalek; +/// extern crate rand; +/// extern crate sha2; +/// +/// use ed25519_dalek::verify_batch; +/// use ed25519_dalek::Keypair; +/// use ed25519_dalek::PublicKey; +/// use ed25519_dalek::Signature; +/// use rand::thread_rng; +/// use rand::ThreadRng; +/// use sha2::Sha512; +/// +/// # fn main() { +/// let mut csprng: ThreadRng = thread_rng(); +/// let keypairs: Vec = (0..64).map(|_| Keypair::generate::(&mut csprng)).collect(); +/// let msg: &[u8] = b"They're good dogs Brant"; +/// let messages: Vec<&[u8]> = (0..64).map(|_| msg).collect(); +/// let signatures: Vec = keypairs.iter().map(|key| key.sign::(&msg)).collect(); +/// let public_keys: Vec = keypairs.iter().map(|key| key.public).collect(); +/// +/// let result = verify_batch::(&messages[..], &signatures[..], &public_keys[..], &mut csprng); +/// assert!(result.is_ok()); +/// # } +/// ``` +#[cfg(any(feature = "alloc", feature = "std"))] +#[allow(non_snake_case)] +pub fn verify_batch(messages: &[&[u8]], + signatures: &[Signature], + public_keys: &[PublicKey], + csprng: &mut C) -> Result<(), SignatureError> + where D: Digest + Default, + C: Rng + CryptoRng, +{ + const ASSERT_MESSAGE: &'static [u8] = b"The number of messages, signatures, and public keys must be equal."; + assert!(signatures.len() == messages.len(), ASSERT_MESSAGE); + assert!(signatures.len() == public_keys.len(), ASSERT_MESSAGE); + assert!(public_keys.len() == messages.len(), ASSERT_MESSAGE); + + #[cfg(feature = "alloc")] + use alloc::vec::Vec; + #[cfg(feature = "std")] + use std::vec::Vec; + + use core::iter::once; + + use curve25519_dalek::traits::IsIdentity; + use curve25519_dalek::traits::VartimeMultiscalarMul; + + let batch_size: usize = signatures.len(); + + let Rs: Vec = signatures.iter().map(|sig| sig.R.decompress().unwrap()).collect(); + let ss: Vec = signatures.iter().map(|sig| sig.s).collect(); + let zs: Vec = signatures.iter().map(|_| Scalar::random(csprng)).collect(); + + // Compute z $= ℤ/lℤ, (∑ s[i]z[i] (mod l)) + let B_coefficient: Scalar = zs.iter().zip(ss.iter()).map(|(z,s)| z * s).sum(); + + // Compute H(R || A || M) for each (signature, public_key, message) triplet + let hrams = (0..batch_size).map(|i| { + let mut h: D = D::default(); + h.input(signatures[i].R.as_bytes()); + h.input(public_keys[i].as_bytes()); + h.input(&messages[i]); + Scalar::from_hash(h) + }); + + // Multiple each H(R || A || M) by the random value + let zhrams = hrams.zip(zs.iter()).map(|(hram, z)| hram * z); + + // Decompress the public keys and fail early if any one of them is invalid + let As: Vec = public_keys.iter() + .map(|pubkey| + pubkey.0.decompress() + .ok_or_else(|| SignatureError(InternalError::PointDecompressionError))) + .collect::, _>>()?; + + // Compute (-∑ z[i]s[i] (mod l)) B + ∑ z[i]R[i] + ∑ (z[i] H(R||A||M)[i] (mod l)) A[i] = 0 + if EdwardsPoint::vartime_multiscalar_mul( + once(-B_coefficient) // -∑ z[i]s[i] (mod l) ---------| + .chain(zs.iter().cloned()) // z[i] -----| | + .chain(zhrams), // z[i] H(R||A||M) (mod l) -| | | + once(&constants::ED25519_BASEPOINT_POINT) // B -----|-|-| + .chain(Rs.iter()) // R[i] ---|-| + .chain(As.iter()), // A[i] -| + ).is_identity() { + Ok(()) + } else { + Err(SignatureError(InternalError::VerifyError)) + } +} + #[cfg(feature = "serde")] impl Serialize for PublicKey { fn serialize(&self, serializer: S) -> Result where S: Serializer { @@ -1224,8 +1338,10 @@ mod test { use std::fs::File; use std::string::String; use std::vec::Vec; + use rand::thread_rng; use rand::ChaChaRng; use rand::SeedableRng; + use rand::ThreadRng; use hex::FromHex; use sha2::Sha512; use super::*; @@ -1396,6 +1512,32 @@ mod test { "Verification of a signature on a different message passed!"); } + #[test] + fn verify_batch_seven_signatures() { + let messages: [&[u8]; 7] = [ + b"Watch closely everyone, I'm going to show you how to kill a god.", + b"I'm not a cryptographer I just encrypt a lot.", + b"Still not a cryptographer.", + b"This is a test of the tsunami alert system. This is only a test.", + b"Fuck dumbin' it down, spit ice, skip jewellery: Molotov cocktails on me like accessories.", + b"Hey, I never cared about your bucks, so if I run up with a mask on, probably got a gas can too.", + b"And I'm not here to fill 'er up. Nope, we came to riot, here to incite, we don't want any of your stuff.", ]; + let mut csprng: ThreadRng = thread_rng(); + let mut keypairs: Vec = Vec::new(); + let mut signatures: Vec = Vec::new(); + + for i in 0..messages.len() { + let keypair: Keypair = Keypair::generate::(&mut csprng); + signatures.push(keypair.sign::(&messages[i])); + keypairs.push(keypair); + } + let public_keys: Vec = keypairs.iter().map(|key| key.public).collect(); + + let result = verify_batch::(&messages, &signatures[..], &public_keys[..], &mut csprng); + + assert!(result.is_ok()); + } + #[test] fn public_key_from_bytes() { // Make another function so that we can test the ? operator. From d896886691c4c63f22c959ebfe630fd9b9604541 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 20 Jul 2018 20:20:40 +0000 Subject: [PATCH 144/351] Overwrite secret key material with zeroes on drop. --- src/ed25519.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/ed25519.rs b/src/ed25519.rs index 919ecb4..e25003d 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -173,6 +173,13 @@ impl Debug for SecretKey { } } +/// Overwrite secret key material with null bytes when it goes out of scope. +impl Drop for SecretKey { + fn drop(&mut self) { + self.0 = [0u8; SECRET_KEY_LENGTH]; + } +} + impl SecretKey { /// Expand this `SecretKey` into an `ExpandedSecretKey`. pub fn expand(&self) -> ExpandedSecretKey where D: Digest + Default { @@ -375,6 +382,14 @@ pub struct ExpandedSecretKey { pub (crate) nonce: [u8; 32], } +/// Overwrite secret key material with null bytes when it goes out of scope. +impl Drop for ExpandedSecretKey { + fn drop(&mut self) { + self.key = Scalar::zero(); + self.nonce = [0u8; 32]; + } +} + #[cfg(feature = "sha2")] impl<'a> From<&'a SecretKey> for ExpandedSecretKey { /// Construct an `ExpandedSecretKey` from a `SecretKey`. From 6513d4980acbb4a25322d18927a4a734111279f9 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 20 Jul 2018 22:28:39 +0000 Subject: [PATCH 145/351] Implement Drop for secret key material using clear_on_drop. --- Cargo.toml | 5 ++++- src/ed25519.rs | 33 ++++++++++++++++++++++++++++----- src/lib.rs | 1 + 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e37ea8d..dccd33d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,9 @@ optional = true version = "^0.1.1" default-features = false +[dependencies.clear_on_drop] +version = "0.2" + [dev-dependencies] hex = "^0.3" sha2 = "^0.7" @@ -56,7 +59,7 @@ harness = false default = ["std", "u64_backend"] # We don't add "rand/std" here because it would enable a bunch of Fuchsia dependencies. std = ["curve25519-dalek/std"] -nightly = ["curve25519-dalek/nightly", "rand/nightly"] +nightly = ["curve25519-dalek/nightly", "rand/nightly", "clear_on_drop/nightly"] asm = ["sha2/asm"] yolocrypto = ["curve25519-dalek/yolocrypto"] u64_backend = ["curve25519-dalek/u64_backend"] diff --git a/src/ed25519.rs b/src/ed25519.rs index e25003d..aaec874 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -10,6 +10,7 @@ //! A Rust implementation of ed25519 EdDSA key generation, signing, and //! verification. +use core::default::Default; use core::fmt::{Debug}; use rand::CryptoRng; @@ -27,6 +28,8 @@ use serde::de::Visitor; #[cfg(feature = "sha2")] use sha2::Sha512; +use clear_on_drop::clear::Clear; + use digest::Digest; use generic_array::typenum::U64; @@ -165,6 +168,7 @@ impl<'d> Deserialize<'d> for Signature { /// An EdDSA secret key. #[repr(C)] +#[derive(Default)] pub struct SecretKey(pub (crate) [u8; SECRET_KEY_LENGTH]); impl Debug for SecretKey { @@ -176,7 +180,7 @@ impl Debug for SecretKey { /// Overwrite secret key material with null bytes when it goes out of scope. impl Drop for SecretKey { fn drop(&mut self) { - self.0 = [0u8; SECRET_KEY_LENGTH]; + self.0.clear(); } } @@ -377,6 +381,7 @@ impl<'d> Deserialize<'d> for SecretKey { // better-designed, Schnorr-based signature scheme, see Trevor Perrin's work on // "generalised EdDSA" and "VXEdDSA". #[repr(C)] +#[derive(Default)] pub struct ExpandedSecretKey { pub (crate) key: Scalar, pub (crate) nonce: [u8; 32], @@ -385,8 +390,8 @@ pub struct ExpandedSecretKey { /// Overwrite secret key material with null bytes when it goes out of scope. impl Drop for ExpandedSecretKey { fn drop(&mut self) { - self.key = Scalar::zero(); - self.nonce = [0u8; 32]; + self.key.clear(); + self.nonce.clear(); } } @@ -698,7 +703,7 @@ impl<'d> Deserialize<'d> for ExpandedSecretKey { } /// An ed25519 public key. -#[derive(Copy, Clone, Eq, PartialEq)] +#[derive(Copy, Clone, Default, Eq, PartialEq)] #[repr(C)] pub struct PublicKey(pub (crate) CompressedEdwardsY); @@ -909,7 +914,7 @@ impl<'d> Deserialize<'d> for PublicKey { } /// An ed25519 keypair. -#[derive(Debug)] +#[derive(Debug, Default)] #[repr(C)] pub struct Keypair { /// The secret half of this keypair. @@ -1431,6 +1436,24 @@ mod test { 175, 002, 026, 104, 247, 007, 081, 026, ])))) } + #[test] + fn keypair_clear_on_drop() { + let mut keypair: Keypair = Keypair::from_bytes(&[15u8; KEYPAIR_LENGTH][..]).unwrap(); + + keypair.clear(); + + fn as_bytes(x: &T) -> &[u8] { + use core::mem; + use core::slice; + + unsafe { + slice::from_raw_parts(x as *const T as *const u8, mem::size_of_val(x)) + } + } + + assert!(!as_bytes(&keypair).contains(&0x15)); + } + #[cfg(all(test, feature = "serde"))] use bincode::{serialize, deserialize, Infinite}; diff --git a/src/lib.rs b/src/lib.rs index b74999a..2e599bc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -264,6 +264,7 @@ extern crate generic_array; extern crate digest; extern crate failure; extern crate rand; +extern crate clear_on_drop; #[cfg(any(feature = "std", test))] #[macro_use] From 81a3d3298b4cc16eabb7dc965d7b3f3a5e8e87c3 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 26 Jul 2018 20:15:13 +0000 Subject: [PATCH 146/351] Leave a comment explaining why we derive Default. --- src/ed25519.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index aaec874..8f62f8e 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -168,7 +168,7 @@ impl<'d> Deserialize<'d> for Signature { /// An EdDSA secret key. #[repr(C)] -#[derive(Default)] +#[derive(Default)] // we derive Default in order to use the clear() method in Drop pub struct SecretKey(pub (crate) [u8; SECRET_KEY_LENGTH]); impl Debug for SecretKey { @@ -381,7 +381,7 @@ impl<'d> Deserialize<'d> for SecretKey { // better-designed, Schnorr-based signature scheme, see Trevor Perrin's work on // "generalised EdDSA" and "VXEdDSA". #[repr(C)] -#[derive(Default)] +#[derive(Default)] // we derive Default in order to use the clear() method in Drop pub struct ExpandedSecretKey { pub (crate) key: Scalar, pub (crate) nonce: [u8; 32], @@ -914,7 +914,7 @@ impl<'d> Deserialize<'d> for PublicKey { } /// An ed25519 keypair. -#[derive(Debug, Default)] +#[derive(Debug, Default)] // we derive Default in order to use the clear() method in Drop #[repr(C)] pub struct Keypair { /// The secret half of this keypair. From 050d2a01e5e0ed543b61eb0358179868740025d0 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 27 Jul 2018 03:38:34 +0000 Subject: [PATCH 147/351] Bump curve25519-dalek dependency to 0.19. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index dccd33d..107c46e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ exclude = [ ".gitignore", "TESTVECTORS", "res/*" ] travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master"} [dependencies.curve25519-dalek] -version = "0.18" +version = "0.19" default-features = false [dependencies.rand] From a92a5b73a1027fa2d5053bbf436efeba4b7504f0 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 27 Jul 2018 03:47:14 +0000 Subject: [PATCH 148/351] It's 2018 and nothing's gotten any better outside. --- LICENSE | 2 +- src/ed25519.rs | 2 +- src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/LICENSE b/LICENSE index 20dcc41..0d9a49e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2017 Isis Agora Lovecruft. All rights reserved. +Copyright (c) 2017-2018 Isis Agora Lovecruft. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff --git a/src/ed25519.rs b/src/ed25519.rs index 919ecb4..f1f0d0b 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of ed25519-dalek. -// Copyright (c) 2017 Isis Lovecruft +// Copyright (c) 2017-2018 Isis Lovecruft // See LICENSE for licensing information. // // Authors: diff --git a/src/lib.rs b/src/lib.rs index b74999a..401c4ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of ed25519-dalek. -// Copyright (c) 2017 Isis Lovecruft +// Copyright (c) 2017-2018 Isis Lovecruft // See LICENSE for licensing information. // // Authors: From f60987dee58acb785eb87a690bb13bd6fbc90cbf Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 17 Jul 2018 14:17:54 -0700 Subject: [PATCH 149/351] Try optional_multiscalar_mul --- Cargo.toml | 2 +- src/ed25519.rs | 55 +++++++++++++++++++++++++------------------------- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 245f3ac..c306f93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ exclude = [ ".gitignore", "TESTVECTORS", "res/*" ] travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master"} [dependencies.curve25519-dalek] -version = "0.18" +version = "0.19" default-features = false [dependencies.rand] diff --git a/src/ed25519.rs b/src/ed25519.rs index 9073f7a..8760a3f 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -914,12 +914,15 @@ impl PublicKey { /// ``` #[cfg(any(feature = "alloc", feature = "std"))] #[allow(non_snake_case)] -pub fn verify_batch(messages: &[&[u8]], - signatures: &[Signature], - public_keys: &[PublicKey], - csprng: &mut C) -> Result<(), SignatureError> - where D: Digest + Default, - C: Rng + CryptoRng, +pub fn verify_batch( + messages: &[&[u8]], + signatures: &[Signature], + public_keys: &[PublicKey], + csprng: &mut C, +) -> Result<(), SignatureError> +where + D: Digest + Default, + C: Rng + CryptoRng, { const ASSERT_MESSAGE: &'static [u8] = b"The number of messages, signatures, and public keys must be equal."; assert!(signatures.len() == messages.len(), ASSERT_MESSAGE); @@ -936,17 +939,18 @@ pub fn verify_batch(messages: &[&[u8]], use curve25519_dalek::traits::IsIdentity; use curve25519_dalek::traits::VartimeMultiscalarMul; - let batch_size: usize = signatures.len(); - - let Rs: Vec = signatures.iter().map(|sig| sig.R.decompress().unwrap()).collect(); - let ss: Vec = signatures.iter().map(|sig| sig.s).collect(); let zs: Vec = signatures.iter().map(|_| Scalar::random(csprng)).collect(); // Compute z $= ℤ/lℤ, (∑ s[i]z[i] (mod l)) - let B_coefficient: Scalar = zs.iter().zip(ss.iter()).map(|(z,s)| z * s).sum(); + let B_coefficient: Scalar = signatures + .iter() + .map(|sig| sig.s) + .zip(zs.iter()) + .map(|(s, z)| z * s) + .sum(); // Compute H(R || A || M) for each (signature, public_key, message) triplet - let hrams = (0..batch_size).map(|i| { + let hrams = (0..signatures.len()).map(|i| { let mut h: D = D::default(); h.input(signatures[i].R.as_bytes()); h.input(public_keys[i].as_bytes()); @@ -954,25 +958,20 @@ pub fn verify_batch(messages: &[&[u8]], Scalar::from_hash(h) }); - // Multiple each H(R || A || M) by the random value + // Multiply each H(R || A || M) by the random value let zhrams = hrams.zip(zs.iter()).map(|(hram, z)| hram * z); - // Decompress the public keys and fail early if any one of them is invalid - let As: Vec = public_keys.iter() - .map(|pubkey| - pubkey.0.decompress() - .ok_or_else(|| SignatureError(InternalError::PointDecompressionError))) - .collect::, _>>()?; + let Rs = signatures.iter().map(|sig| sig.R.decompress()); + let As = public_keys.iter().map(|pk| pk.0.decompress()); + let B = once(Some(constants::ED25519_BASEPOINT_POINT)); - // Compute (-∑ z[i]s[i] (mod l)) B + ∑ z[i]R[i] + ∑ (z[i] H(R||A||M)[i] (mod l)) A[i] = 0 - if EdwardsPoint::vartime_multiscalar_mul( - once(-B_coefficient) // -∑ z[i]s[i] (mod l) ---------| - .chain(zs.iter().cloned()) // z[i] -----| | - .chain(zhrams), // z[i] H(R||A||M) (mod l) -| | | - once(&constants::ED25519_BASEPOINT_POINT) // B -----|-|-| - .chain(Rs.iter()) // R[i] ---|-| - .chain(As.iter()), // A[i] -| - ).is_identity() { + // Compute (-∑ z[i]s[i] (mod l)) B + ∑ z[i]R[i] + ∑ (z[i]H(R||A||M)[i] (mod l)) A[i] = 0 + let id = EdwardsPoint::optional_multiscalar_mul( + once(-B_coefficient).chain(zs.iter().cloned()).chain(zhrams), + B.chain(Rs).chain(As), + ).ok_or_else(|| SignatureError(InternalError::VerifyError))?; + + if id.is_identity() { Ok(()) } else { Err(SignatureError(InternalError::VerifyError)) From 4c838decd87517a367bf8034ac45b709729b8074 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 26 Jul 2018 21:09:52 -0700 Subject: [PATCH 150/351] Use 128-bit scalars --- Cargo.toml | 1 + src/ed25519.rs | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c306f93..304c4ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ default-features = false [dependencies.rand] version = "0.5" default-features = false +features = ["i128_support"] [dependencies.digest] version = "^0.7" diff --git a/src/ed25519.rs b/src/ed25519.rs index 8760a3f..9a2d2a9 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -935,13 +935,18 @@ where use std::vec::Vec; use core::iter::once; + use rand::thread_rng; use curve25519_dalek::traits::IsIdentity; use curve25519_dalek::traits::VartimeMultiscalarMul; - let zs: Vec = signatures.iter().map(|_| Scalar::random(csprng)).collect(); + // Select a random 128-bit scalar for each signature. + let zs: Vec = signatures + .iter() + .map(|_| Scalar::from(thread_rng().gen::())) + .collect(); - // Compute z $= ℤ/lℤ, (∑ s[i]z[i] (mod l)) + // Compute the basepoint coefficient, ∑ s[i]z[i] (mod l) let B_coefficient: Scalar = signatures .iter() .map(|sig| sig.s) From 3ad616a23cfed8f5554eb49738c7de901fc7793a Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 27 Jul 2018 17:30:57 +0000 Subject: [PATCH 151/351] Remove unused csprng parameter from verify_batch() function. --- src/ed25519.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 0e056ee..d08a7ca 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -928,21 +928,16 @@ impl PublicKey { /// let signatures: Vec = keypairs.iter().map(|key| key.sign::(&msg)).collect(); /// let public_keys: Vec = keypairs.iter().map(|key| key.public).collect(); /// -/// let result = verify_batch::(&messages[..], &signatures[..], &public_keys[..], &mut csprng); +/// let result = verify_batch::(&messages[..], &signatures[..], &public_keys[..]); /// assert!(result.is_ok()); /// # } /// ``` #[cfg(any(feature = "alloc", feature = "std"))] #[allow(non_snake_case)] -pub fn verify_batch( - messages: &[&[u8]], - signatures: &[Signature], - public_keys: &[PublicKey], - csprng: &mut C, -) -> Result<(), SignatureError> -where - D: Digest + Default, - C: Rng + CryptoRng, +pub fn verify_batch(messages: &[&[u8]], + signatures: &[Signature], + public_keys: &[PublicKey]) -> Result<(), SignatureError> + where D: Digest + Default { const ASSERT_MESSAGE: &'static [u8] = b"The number of messages, signatures, and public keys must be equal."; assert!(signatures.len() == messages.len(), ASSERT_MESSAGE); @@ -1557,7 +1552,7 @@ mod test { } let public_keys: Vec = keypairs.iter().map(|key| key.public).collect(); - let result = verify_batch::(&messages, &signatures[..], &public_keys[..], &mut csprng); + let result = verify_batch::(&messages, &signatures[..], &public_keys[..]); assert!(result.is_ok()); } From 468acd8f1f2c11f96e655e8b38a425a00c211c8f Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 27 Jul 2018 18:28:06 +0000 Subject: [PATCH 152/351] Fix batch benchmarks to use new function signature. --- benches/ed25519_benchmarks.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benches/ed25519_benchmarks.rs b/benches/ed25519_benchmarks.rs index 8347ea5..4b556b2 100644 --- a/benches/ed25519_benchmarks.rs +++ b/benches/ed25519_benchmarks.rs @@ -71,7 +71,7 @@ mod ed25519_benches { let signatures: Vec = keypairs.iter().map(|key| key.sign::(&msg)).collect(); let public_keys: Vec = keypairs.iter().map(|key| key.public).collect(); - b.iter(|| verify_batch::(&messages[..], &signatures[..], &public_keys[..], &mut csprng)); + b.iter(|| verify_batch::(&messages[..], &signatures[..], &public_keys[..])); }, &BATCH_SIZES, ); From 1b702a3fe1fd9cc95914b8bb0f66c3b8dade4e93 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 27 Jul 2018 18:28:17 +0000 Subject: [PATCH 153/351] Add more batch sizes to benchmarks. --- benches/ed25519_benchmarks.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benches/ed25519_benchmarks.rs b/benches/ed25519_benchmarks.rs index 4b556b2..d4f8f53 100644 --- a/benches/ed25519_benchmarks.rs +++ b/benches/ed25519_benchmarks.rs @@ -59,7 +59,7 @@ mod ed25519_benches { } fn verify_batch_signatures(c: &mut Criterion) { - static BATCH_SIZES: [u8; 6] = [4, 8, 16, 32, 64, 96]; + static BATCH_SIZES: [u8; 6] = [4, 8, 16, 32, 64, 96, 128, 256]; c.bench_function_over_inputs( "Ed25519 batch signature verification", From c700a2b5e46b5f6c2c82b3a3f16c70d72d6c9a6d Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 27 Jul 2018 19:17:03 +0000 Subject: [PATCH 154/351] This is what happens when you have separate machines for benchmarks and committing code. --- benches/ed25519_benchmarks.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benches/ed25519_benchmarks.rs b/benches/ed25519_benchmarks.rs index d4f8f53..5db1361 100644 --- a/benches/ed25519_benchmarks.rs +++ b/benches/ed25519_benchmarks.rs @@ -59,7 +59,7 @@ mod ed25519_benches { } fn verify_batch_signatures(c: &mut Criterion) { - static BATCH_SIZES: [u8; 6] = [4, 8, 16, 32, 64, 96, 128, 256]; + static BATCH_SIZES: [usize; 8] = [4, 8, 16, 32, 64, 96, 128, 256]; c.bench_function_over_inputs( "Ed25519 batch signature verification", From f67d9551000733c00292f5a39cec9e56840b3b0a Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 27 Jul 2018 21:34:02 +0000 Subject: [PATCH 155/351] Add README section for batch performance. --- README.md | 34 +- res/batch-violin-benchmark.svg | 4251 ++++++++++++++++++++++++++++++++ 2 files changed, 4282 insertions(+), 3 deletions(-) create mode 100644 res/batch-violin-benchmark.svg diff --git a/README.md b/README.md index 76ed1e0..dd018a9 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Documentation is available [here](https://docs.rs/ed25519-dalek). # Benchmarks -On an Intel i9-7900X running at 3.30 GHz, without TurboBoost, this code achieves +On an Intel Skylake i9-7900X running at 3.30 GHz, without TurboBoost, this code achieves the following performance benchmarks: ∃!isisⒶmistakenot:(master *=)~/code/rust/ed25519-dalek ∴ cargo bench @@ -24,8 +24,8 @@ the following performance benchmarks: By enabling the avx2 backend (on machines with compatible microarchitectures), the performance for signature verification is greatly improved: - ∃!isisⒶmistakenot:(master *=)~/code/rust/ed25519-dalek ∴ export RUSTFLAGS="-C target_cpu=native" - ∃!isisⒶmistakenot:(master *=)~/code/rust/ed25519-dalek ∴ cargo bench --no-default-features --features "std avx2_backend" + ∃!isisⒶmistakenot:(master *=)~/code/rust/ed25519-dalek ∴ export RUSTFLAGS=-Ctarget_cpu=native + ∃!isisⒶmistakenot:(master *=)~/code/rust/ed25519-dalek ∴ cargo bench --features=avx2_backend Compiling ed25519-dalek v0.7.0 (file:///home/isis/code/rust/ed25519-dalek) Finished release [optimized] target(s) in 4.28s Running target/release/deps/ed25519_benchmarks-e4866664de39c84d @@ -55,6 +55,34 @@ Additionally, if you're using a CSPRNG from the `rand` crate, the `nightly` feature will enable `u128`/`i128` features there, resulting in potentially faster performance. +If your protocol or application is able to batch signatures for verification, +the `verify_batch()` function has greatly improved performance. On the +aforementioned Intel Skylake i9-7900X, verifying a batch of 96 signatures takes +1.7673ms. That's 18.4094us, or roughly 60750 cycles, per signature verification, +more than double the speed of batch verification given in the original paper +(this is likely not a fair comparison as that was a Nehalem machine). +The numbers after the `/` in the test name refer to the size of the batch: + + ∃!isisⒶmistakenot:(master *=)~/code/rust/ed25519-dalek ∴ export RUSTFLAGS=-Ctarget_cpu=native + ∃!isisⒶmistakenot:(master *=)~/code/rust/ed25519-dalek ∴ cargo bench --features=avx2_backend batch + Compiling ed25519-dalek v0.8.0 (file:///home/isis/code/rust/ed25519-dalek) + Finished release [optimized] target(s) in 34.16s + Running target/release/deps/ed25519_benchmarks-cf0daf7d68fc71b6 + Ed25519 batch signature verification/4 time: [105.20 us 106.04 us 106.99 us] + Ed25519 batch signature verification/8 time: [178.66 us 179.01 us 179.39 us] + Ed25519 batch signature verification/16 time: [325.65 us 326.67 us 327.90 us] + Ed25519 batch signature verification/32 time: [617.96 us 620.74 us 624.12 us] + Ed25519 batch signature verification/64 time: [1.1862 ms 1.1900 ms 1.1943 ms] + Ed25519 batch signature verification/96 time: [1.7611 ms 1.7673 ms 1.7742 ms] + Ed25519 batch signature verification/128 time: [2.3320 ms 2.3376 ms 2.3446 ms] + Ed25519 batch signature verification/256 time: [5.0124 ms 5.0290 ms 5.0491 ms] + +As you can see, there's an optimal batch size for each machine, so you'll likely +want to your the benchmarks on your target CPU to discover the best size. For +this machine, around 100 signatures per batch is the optimum: + +![](https://github.com/dalek-cryptography/ed25519-dalek/blob/master/res/batch-voilin-benchmark.svg) + Additionally, thanks to Rust, this implementation has both type and memory safety. It's also easily readable by a much larger set of people than those who can read qhasm, making it more readily and more easily auditable. We're of diff --git a/res/batch-violin-benchmark.svg b/res/batch-violin-benchmark.svg new file mode 100644 index 0000000..418fa1d --- /dev/null +++ b/res/batch-violin-benchmark.svg @@ -0,0 +1,4251 @@ + + + +Gnuplot +Produced by GNUPLOT 5.0 patchlevel 6 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ed25519 batch signature verification/256 + + + + + Ed25519 batch signature verification/128 + + + + + Ed25519 batch signature verification/96 + + + + + Ed25519 batch signature verification/64 + + + + + Ed25519 batch signature verification/32 + + + + + Ed25519 batch signature verification/16 + + + + + Ed25519 batch signature verification/8 + + + + + Ed25519 batch signature verification/4 + + + + + + + + + + + + + 0 + + + + + + + + + + + + + 1 + + + + + + + + + + + + + 2 + + + + + + + + + + + + + 3 + + + + + + + + + + + + + 4 + + + + + + + + + + + + + 5 + + + + + + + + + + + + + 6 + + + + + + + + + Input + + + + + Average time (ms) + + + + + Ed25519 batch signature verification: Violin plot + + + PDF + + + PDF + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + gnuplot_plot_2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + gnuplot_plot_3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + gnuplot_plot_4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + gnuplot_plot_5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + gnuplot_plot_6 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + gnuplot_plot_7 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + gnuplot_plot_8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 591aff7025ec3b52018fcbfd9def6fd825d7da33 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 27 Jul 2018 21:35:36 +0000 Subject: [PATCH 156/351] Bump ed25519-dalek version to 0.8.0. --- Cargo.toml | 2 +- README.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 811b3a5..370b141 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "0.7.0" +version = "0.8.0" authors = ["Isis Lovecruft "] readme = "README.md" license = "BSD-3-Clause" diff --git a/README.md b/README.md index dd018a9..29fa307 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,7 @@ To install, add the following to your project's `Cargo.toml`: ```toml [dependencies.ed25519-dalek] -version = "^0.7" +version = "^0.8" ``` Then, in your library or executable source, add: @@ -146,7 +146,7 @@ enabled by default, instead do: ```toml [dependencies.ed25519-dalek] -version = "^0.7" +version = "^0.8" features = ["nightly"] ``` @@ -163,7 +163,7 @@ To enable [serde](https://serde.rs) support, build `ed25519-dalek` with: ```toml [dependencies.ed25519-dalek] -version = "^0.7" +version = "^0.8" features = ["serde"] ``` From 9263d01351eb32c35313b88fcd644003f49e384c Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 27 Jul 2018 21:45:32 +0000 Subject: [PATCH 157/351] Remove outdated TODO section from the README. --- README.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/README.md b/README.md index 29fa307..a6fa65a 100644 --- a/README.md +++ b/README.md @@ -175,13 +175,3 @@ likely want to compile with If you're building for a machine with avx2 instructions, there's also the experimental `avx2_backend`. To use it, compile with `RUSTFLAGS="-C target_cpu=native" cargo build --no-default-features --features="avx2_backend"` - -# TODO - - * Batch signature verification, maybe? - * We can probably make this go even faster if we implement SHA512, - rather than using the rust-crypto implementation whose API requires - that we allocate memory and bzero it before mutating to store the - digest. - * Incorporate ed25519-dalek into Brian Smith's - [crypto-bench](https://github.com/briansmith/crypto-bench). From 5a759ab9fa1671f921777db57394d59be09ce71b Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 27 Jul 2018 22:00:05 +0000 Subject: [PATCH 158/351] Fix typo in image URL in README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a6fa65a..ca22a13 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ As you can see, there's an optimal batch size for each machine, so you'll likely want to your the benchmarks on your target CPU to discover the best size. For this machine, around 100 signatures per batch is the optimum: -![](https://github.com/dalek-cryptography/ed25519-dalek/blob/master/res/batch-voilin-benchmark.svg) +![](https://github.com/dalek-cryptography/ed25519-dalek/blob/master/res/batch-violin-benchmark.svg) Additionally, thanks to Rust, this implementation has both type and memory safety. It's also easily readable by a much larger set of people than those who From 0ea12f922e6ccdcddac4ecbbaa0cc292fa668d0e Mon Sep 17 00:00:00 2001 From: sun Date: Fri, 24 Aug 2018 11:27:15 +0800 Subject: [PATCH 159/351] fix doc code --- src/ed25519.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index d08a7ca..87f0914 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -1221,7 +1221,7 @@ impl Keypair { /// # let prehashed: Sha512 = Sha512::default(); /// # prehashed.input(message); /// # - /// let context: &[u8] = "Ed25519DalekSignPrehashedDoctest"; + /// let context: &[u8] = b"Ed25519DalekSignPrehashedDoctest"; /// /// let sig: Signature = keypair.sign_prehashed(prehashed, Some(context)); /// # } @@ -1285,7 +1285,7 @@ impl Keypair { /// let prehashed: Sha512 = Sha512::default(); /// prehashed.input(message); /// - /// let context: &[u8] = "Ed25519DalekSignPrehashedDoctest"; + /// let context: &[u8] = b"Ed25519DalekSignPrehashedDoctest"; /// /// let sig: Signature = keypair.sign_prehashed(prehashed, Some(context)); /// From 49ebfa13de0fb976b2c22b16c4a9bf8f32554926 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 12 Sep 2018 19:53:20 +0000 Subject: [PATCH 160/351] Implement From for PublicKey. * CLOSES https://github.com/dalek-cryptography/ed25519-dalek/issues/39 --- src/ed25519.rs | 45 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index d08a7ca..b2148ad 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -777,22 +777,36 @@ impl PublicKey { /// Derive this public key from its corresponding `SecretKey`. #[allow(unused_assignments)] pub fn from_secret(secret_key: &SecretKey) -> PublicKey - where D: Digest + Default { - + where D: Digest + Default + { let mut h: D = D::default(); let mut hash: [u8; 64] = [0u8; 64]; let mut digest: [u8; 32] = [0u8; 32]; - let pk: [u8; 32]; h.input(secret_key.as_bytes()); hash.copy_from_slice(h.fixed_result().as_slice()); digest.copy_from_slice(&hash[..32]); - digest[0] &= 248; - digest[31] &= 127; - digest[31] |= 64; - pk = (&Scalar::from_bits(digest) * &constants::ED25519_BASEPOINT_TABLE).compress().to_bytes(); + PublicKey::mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(&mut digest) + } + + /// Derive this public key from its corresponding `ExpandedSecretKey`. + pub fn from_expanded_secret(expanded_secret_key: &ExpandedSecretKey) -> PublicKey { + let mut bits: [u8; 32] = expanded_secret_key.key.to_bytes(); + + PublicKey::mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(&mut bits) + } + + /// Internal utility function for mangling the bits of a (formerly + /// mathematically well-defined) "scalar" and multiplying it to produce a + /// public key. + fn mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(bits: &mut [u8; 32]) -> PublicKey { + bits[0] &= 248; + bits[31] &= 127; + bits[31] |= 64; + + let pk = (&Scalar::from_bits(*bits) * &constants::ED25519_BASEPOINT_TABLE).compress().to_bytes(); PublicKey(CompressedEdwardsY(pk)) } @@ -885,6 +899,12 @@ impl PublicKey { } } +impl From for PublicKey { + fn from(source: ExpandedSecretKey) -> PublicKey { + PublicKey::from_expanded_secret(&source) + } +} + /// Verify a batch of `signatures` on `messages` with their respective `public_keys`. /// /// # Inputs @@ -1595,6 +1615,17 @@ mod test { assert!(!as_bytes(&keypair).contains(&0x15)); } + #[test] + fn pubkey_from_secret_and_expanded_secret() { + let mut csprng = thread_rng(); + let secret: SecretKey = SecretKey::generate::<_>(&mut csprng); + let expanded_secret: ExpandedSecretKey = ExpandedSecretKey::from_secret_key::(&secret); + let public_from_secret: PublicKey = PublicKey::from_secret::(&secret); + let public_from_expanded_secret: PublicKey = PublicKey::from_expanded_secret(&expanded_secret); + + assert!(public_from_secret == public_from_expanded_secret); + } + #[cfg(all(test, feature = "serde"))] use bincode::{serialize, deserialize, Infinite}; From 1e8b9f962b94de1829a4530ae6230a3f0786f823 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Wed, 26 Sep 2018 11:37:18 -0700 Subject: [PATCH 161/351] Remove unused features --- src/lib.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f815da8..1d9bc1f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -254,8 +254,6 @@ //! ``` #![no_std] -#![cfg_attr(feature = "nightly", feature(rand))] -#![cfg_attr(feature = "bench", feature(test))] #![allow(unused_features)] #![deny(missing_docs)] // refuse to compile if documentation is missing @@ -276,9 +274,6 @@ extern crate sha2; #[cfg(test)] extern crate hex; -#[cfg(all(test, feature = "bench"))] -extern crate test; - #[cfg(feature = "serde")] extern crate serde; From 7f823087990f7e4cf61d06e8ae7b57deeaf3c6fe Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 26 Sep 2018 18:46:17 +0000 Subject: [PATCH 162/351] Bump curve25519-dalek dependency to 0.20. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 370b141..84de61a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ exclude = [ ".gitignore", "TESTVECTORS", "res/*" ] travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master"} [dependencies.curve25519-dalek] -version = "0.19" +version = "0.20" default-features = false [dependencies.rand] From 93b73783aac045d1a139404559df137fcba302b0 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 26 Sep 2018 18:46:58 +0000 Subject: [PATCH 163/351] Bump ed25519-dalek version to 0.8.1. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 84de61a..ae2bd8f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "0.8.0" +version = "0.8.1" authors = ["Isis Lovecruft "] readme = "README.md" license = "BSD-3-Clause" From b97fa08900c2ac01f3555ffe58eb184f80c2bf78 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 6 Nov 2018 01:12:58 +0000 Subject: [PATCH 164/351] Update curve25519-dalek dependency to 1.0.0-pre.0. --- Cargo.toml | 13 +++---------- src/ed25519.rs | 48 +++++++++++++++++++++++++----------------------- src/lib.rs | 2 -- 3 files changed, 28 insertions(+), 35 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ae2bd8f..e0ba956 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ exclude = [ ".gitignore", "TESTVECTORS", "res/*" ] travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master"} [dependencies.curve25519-dalek] -version = "0.20" +version = "1.0.0-pre.0" default-features = false [dependencies.rand] @@ -24,19 +24,12 @@ version = "0.5" default-features = false features = ["i128_support"] -[dependencies.digest] -version = "^0.7" - -[dependencies.generic-array] -# same version that digest depends on -version = "0.9" - [dependencies.serde] version = "^1.0" optional = true [dependencies.sha2] -version = "^0.7" +version = "^0.8" optional = true [dependencies.failure] @@ -48,7 +41,7 @@ version = "0.2" [dev-dependencies] hex = "^0.3" -sha2 = "^0.7" +sha2 = "^0.8" bincode = "^0.9" criterion = "0.2" diff --git a/src/ed25519.rs b/src/ed25519.rs index 95898f4..0654eb1 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -30,9 +30,8 @@ use sha2::Sha512; use clear_on_drop::clear::Clear; -use digest::Digest; - -use generic_array::typenum::U64; +use curve25519_dalek::digest::Digest; +use curve25519_dalek::digest::generic_array::typenum::U64; use curve25519_dalek::constants; use curve25519_dalek::edwards::CompressedEdwardsY; @@ -186,7 +185,9 @@ impl Drop for SecretKey { impl SecretKey { /// Expand this `SecretKey` into an `ExpandedSecretKey`. - pub fn expand(&self) -> ExpandedSecretKey where D: Digest + Default { + pub fn expand(&self) -> ExpandedSecretKey + where D: Digest + Default + { ExpandedSecretKey::from_secret_key::(&self) } @@ -556,7 +557,7 @@ impl ExpandedSecretKey { let mut upper: [u8; 32] = [0u8; 32]; h.input(secret_key.as_bytes()); - hash.copy_from_slice(h.fixed_result().as_slice()); + hash.copy_from_slice(h.result().as_slice()); lower.copy_from_slice(&hash[00..32]); upper.copy_from_slice(&hash[32..64]); @@ -620,7 +621,7 @@ impl ExpandedSecretKey { context: Option<&'static [u8]>) -> Signature where D: Digest + Default { - let mut h: D = D::default(); + let mut h: D; let mut prehash: [u8; 64] = [0u8; 64]; let R: CompressedEdwardsY; let r: Scalar; @@ -634,7 +635,7 @@ impl ExpandedSecretKey { let ctx_len: u8 = ctx.len() as u8; // Get the result of the pre-hashed message. - prehash.copy_from_slice(prehashed_message.fixed_result().as_slice()); + prehash.copy_from_slice(prehashed_message.result().as_slice()); // This is the dumbest, ten-years-late, non-admission of fucking up the // domain separation I have ever seen. Why am I still required to put @@ -648,24 +649,25 @@ impl ExpandedSecretKey { // // This is a really fucking stupid bandaid, and the damned scheme is // still bleeding from malleability, for fuck's sake. - h.input(b"SigEd25519 no Ed25519 collisions"); - h.input(&[1]); // Ed25519ph - h.input(&[ctx_len]); - h.input(ctx); - h.input(&self.nonce); - h.input(&prehash); + h = D::default() + .chain(b"SigEd25519 no Ed25519 collisions") + .chain(&[1]) // Ed25519ph + .chain(&[ctx_len]) + .chain(ctx) + .chain(&self.nonce) + .chain(&prehash[..]); r = Scalar::from_hash(h); R = (&r * &constants::ED25519_BASEPOINT_TABLE).compress(); - h = D::default(); - h.input(b"SigEd25519 no Ed25519 collisions"); - h.input(&[1]); // Ed25519ph - h.input(&[ctx_len]); - h.input(ctx); - h.input(R.as_bytes()); - h.input(public_key.as_bytes()); - h.input(&prehash); + h = D::default() + .chain(b"SigEd25519 no Ed25519 collisions") + .chain(&[1]) // Ed25519ph + .chain(&[ctx_len]) + .chain(ctx) + .chain(R.as_bytes()) + .chain(public_key.as_bytes()) + .chain(&prehash[..]); k = Scalar::from_hash(h); s = &(&k * &self.key) + &r; @@ -784,7 +786,7 @@ impl PublicKey { let mut digest: [u8; 32] = [0u8; 32]; h.input(secret_key.as_bytes()); - hash.copy_from_slice(h.fixed_result().as_slice()); + hash.copy_from_slice(h.result().as_slice()); digest.copy_from_slice(&hash[..32]); @@ -886,7 +888,7 @@ impl PublicKey { h.input(ctx); h.input(signature.R.as_bytes()); h.input(self.as_bytes()); - h.input(prehashed_message.fixed_result().as_slice()); + h.input(prehashed_message.result().as_slice()); k = Scalar::from_hash(h); R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(-A), &signature.s); diff --git a/src/lib.rs b/src/lib.rs index 1d9bc1f..488ea5c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -258,8 +258,6 @@ #![deny(missing_docs)] // refuse to compile if documentation is missing extern crate curve25519_dalek; -extern crate generic_array; -extern crate digest; extern crate failure; extern crate rand; extern crate clear_on_drop; From 99c64cc403f9bce90dc6b60c63393a3fca2f5481 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 6 Nov 2018 01:56:13 +0000 Subject: [PATCH 165/351] Bump ed25519-dalek version to 1.0.0-pre.0. --- Cargo.toml | 2 +- README.md | 18 +++--------------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e0ba956..9e3e300 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "0.8.1" +version = "1.0.0-pre.0" authors = ["Isis Lovecruft "] readme = "README.md" license = "BSD-3-Clause" diff --git a/README.md b/README.md index ca22a13..8ebe578 100644 --- a/README.md +++ b/README.md @@ -89,18 +89,6 @@ can read qhasm, making it more readily and more easily auditable. We're of the opinion that, ultimately, these features—combined with speed—are more valuable than simply cycle counts alone. -# Warnings - -ed25519-dalek and -[our elliptic curve library](https://github.com/dalek-cryptography/curve25519-dalek) -(which this code uses) have received *one* formal cryptographic and security -review. Neither have yet received what we would consider *sufficient* peer -review by other qualified cryptographers to be considered in any way, shape, -or form, safe. - -**USE AT YOUR OWN RISK.** - - ### A Note on Signature Malleability The signatures produced by this library are malleable, as discussed in @@ -130,7 +118,7 @@ To install, add the following to your project's `Cargo.toml`: ```toml [dependencies.ed25519-dalek] -version = "^0.8" +version = "1" ``` Then, in your library or executable source, add: @@ -146,7 +134,7 @@ enabled by default, instead do: ```toml [dependencies.ed25519-dalek] -version = "^0.8" +version = "1" features = ["nightly"] ``` @@ -163,7 +151,7 @@ To enable [serde](https://serde.rs) support, build `ed25519-dalek` with: ```toml [dependencies.ed25519-dalek] -version = "^0.8" +version = "1" features = ["serde"] ``` From 617f3186e2c3f95a16e02e80834aab11fafacb42 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Wed, 24 Oct 2018 00:00:37 -0600 Subject: [PATCH 166/351] Expose CI failures --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4c8d19e..732228a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,14 +7,14 @@ rust: env: - TEST_COMMAND=test FEATURES='' - - TEST_COMMAND=test FEATURES=--features="serde" + - TEST_COMMAND=test FEATURES='--features=serde' matrix: include: - rust: nightly - env: TEST_COMMAND=build FEATURES="--no-default-features --features=u32_backend" + env: TEST_COMMAND=build FEATURES='--no-default-features --features=u32_backend' - rust: nightly - env: TEST_COMMAND=test FEATURES=--features="nightly" + env: TEST_COMMAND=test FEATURES='--features=nightly' script: - cargo $TEST_COMMAND $FEATURES From 82948f0dd6fb966249f1035200b4acf863251cf1 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Tue, 23 Oct 2018 22:57:25 -0600 Subject: [PATCH 167/351] Fix serde doc tests And let latest rustfmt reorder imports. --- src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 488ea5c..e01e2fd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -198,11 +198,11 @@ //! # use ed25519_dalek::{Keypair, Signature, PublicKey}; //! use bincode::{serialize, Infinite}; //! # let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]); -//! # let keypair: Keypair = Keypair::generate::(&mut csprng); +//! # let keypair: Keypair = Keypair::generate::(&mut csprng); //! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); //! # let signature: Signature = keypair.sign::(message); //! # let public_key: PublicKey = keypair.public; -//! # let verified: bool = public_key.verify::(message, &signature); +//! # let verified: bool = public_key.verify::(message, &signature).is_ok(); //! //! let encoded_public_key: Vec = serialize(&public_key, Infinite).unwrap(); //! let encoded_signature: Vec = serialize(&signature, Infinite).unwrap(); @@ -232,11 +232,11 @@ //! use bincode::{deserialize}; //! //! # let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]); -//! # let keypair: Keypair = Keypair::generate::(&mut csprng); +//! # let keypair: Keypair = Keypair::generate::(&mut csprng); //! let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); //! # let signature: Signature = keypair.sign::(message); //! # let public_key: PublicKey = keypair.public; -//! # let verified: bool = public_key.verify::(message, &signature); +//! # let verified: bool = public_key.verify::(message, &signature).is_ok(); //! # let encoded_public_key: Vec = serialize(&public_key, Infinite).unwrap(); //! # let encoded_signature: Vec = serialize(&signature, Infinite).unwrap(); //! let decoded_public_key: PublicKey = deserialize(&encoded_public_key).unwrap(); @@ -245,7 +245,7 @@ //! # assert_eq!(public_key, decoded_public_key); //! # assert_eq!(signature, decoded_signature); //! # -//! let verified: bool = decoded_public_key.verify::(&message, &decoded_signature); +//! let verified: bool = decoded_public_key.verify::(&message, &decoded_signature).is_ok(); //! //! assert!(verified); //! # } @@ -257,10 +257,10 @@ #![allow(unused_features)] #![deny(missing_docs)] // refuse to compile if documentation is missing +extern crate clear_on_drop; extern crate curve25519_dalek; extern crate failure; extern crate rand; -extern crate clear_on_drop; #[cfg(any(feature = "std", test))] #[macro_use] From a3cfc7e294ddff09954029cd3efc80816c3a4d2b Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Tue, 23 Oct 2018 23:45:29 -0600 Subject: [PATCH 168/351] Add AsRef instances for PublicKey and SecretKey This just makes it a little easier to migrate to this library from alternatives such as 'ring'. --- src/ed25519.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/ed25519.rs b/src/ed25519.rs index 0654eb1..1a79a11 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -183,6 +183,12 @@ impl Drop for SecretKey { } } +impl AsRef<[u8]> for SecretKey { + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + impl SecretKey { /// Expand this `SecretKey` into an `ExpandedSecretKey`. pub fn expand(&self) -> ExpandedSecretKey @@ -715,6 +721,12 @@ impl Debug for PublicKey { } } +impl AsRef<[u8]> for PublicKey { + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + impl PublicKey { /// Convert this public key to a byte array. #[inline] From 680f68be3137ed24d268a73b2978cf97fd734df5 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Tue, 23 Oct 2018 23:14:29 -0600 Subject: [PATCH 169/351] Add tests for generic_array serialized size generic_array v0.12 no longer serializes GenericArray as a Vec, which reduces the serialized size of PublicKey, Signature, and SecretKey by 8 bytes. Now that generic_array has been upgraded, these tests simply ensure the serialization size doesn't change in the future. --- src/ed25519.rs | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 0654eb1..a3b7454 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -1629,7 +1629,10 @@ mod test { } #[cfg(all(test, feature = "serde"))] - use bincode::{serialize, deserialize, Infinite}; + use bincode::{serialize, serialized_size, deserialize, Infinite}; + + #[cfg(all(test, feature = "serde"))] + use std::mem::size_of; #[cfg(all(test, feature = "serde"))] #[test] @@ -1660,4 +1663,29 @@ mod test { assert_eq!(SECRET_KEY.0[i], decoded_secret_key.0[i]); } } + + #[cfg(all(test, feature = "serde"))] + #[test] + fn serialize_public_key_size() { + assert_eq!( + serialized_size(&PUBLIC_KEY) as usize, + size_of::() + ); + } + + #[cfg(all(test, feature = "serde"))] + #[test] + fn serialize_signature_size() { + let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES).unwrap(); + assert_eq!(serialized_size(&signature) as usize, size_of::()); + } + + #[cfg(all(test, feature = "serde"))] + #[test] + fn serialize_secret_key_size() { + assert_eq!( + serialized_size(&SECRET_KEY) as usize, + size_of::() + ); + } } From 42b5d6ada905d949e66d93985fcdd2a2800e3887 Mon Sep 17 00:00:00 2001 From: Colt Frederickson Date: Wed, 17 Oct 2018 10:26:44 -0600 Subject: [PATCH 170/351] Rand 0.6 version bump --- Cargo.toml | 6 ++++-- src/ed25519.rs | 13 +++++++------ src/lib.rs | 28 +++++++++++++++++++++------- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9e3e300..09748c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,11 +16,12 @@ exclude = [ ".gitignore", "TESTVECTORS", "res/*" ] travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master"} [dependencies.curve25519-dalek] -version = "1.0.0-pre.0" +git = "https://github.com/IronCoreLabs/curve25519-dalek.git" +branch = "rand-0.6" default-features = false [dependencies.rand] -version = "0.5" +version = "0.6.0" default-features = false features = ["i128_support"] @@ -44,6 +45,7 @@ hex = "^0.3" sha2 = "^0.8" bincode = "^0.9" criterion = "0.2" +rand_chacha = "0.1.0" [[bench]] name = "ed25519_benchmarks" diff --git a/src/ed25519.rs b/src/ed25519.rs index 0654eb1..6c119a7 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -281,13 +281,14 @@ impl SecretKey { /// /// ``` /// # extern crate rand; + /// # extern crate rand_chacha; /// # extern crate sha2; /// # extern crate ed25519_dalek; /// # /// # fn main() { /// # /// # use rand::Rng; - /// # use rand::ChaChaRng; + /// # use rand_chacha::ChaChaRng; /// # use rand::SeedableRng; /// # use sha2::Sha512; /// # use ed25519_dalek::PublicKey; @@ -307,7 +308,7 @@ impl SecretKey { /// /// # Input /// - /// A CSPRNG with a `fill_bytes()` method, e.g. `rand::ChaChaRng` + /// A CSPRNG with a `fill_bytes()` method, e.g. `rand_chacha::ChaChaRng` pub fn generate(csprng: &mut T) -> SecretKey where T: CryptoRng + Rng, { @@ -939,7 +940,7 @@ impl From for PublicKey { /// use ed25519_dalek::PublicKey; /// use ed25519_dalek::Signature; /// use rand::thread_rng; -/// use rand::ThreadRng; +/// use rand::rngs::ThreadRng; /// use sha2::Sha512; /// /// # fn main() { @@ -1135,7 +1136,7 @@ impl Keypair { /// /// # Input /// - /// A CSPRNG with a `fill_bytes()` method, e.g. `rand::ChaChaRng`. + /// A CSPRNG with a `fill_bytes()` method, e.g. `rand_chacha::ChaChaRng`. /// /// The caller must also supply a hash function which implements the /// `Digest` and `Default` traits, and which returns 512 bits of output. @@ -1380,9 +1381,9 @@ mod test { use std::string::String; use std::vec::Vec; use rand::thread_rng; - use rand::ChaChaRng; + use rand_chacha::ChaChaRng; use rand::SeedableRng; - use rand::ThreadRng; + use rand::rngs::ThreadRng; use hex::FromHex; use sha2::Sha512; use super::*; diff --git a/src/lib.rs b/src/lib.rs index 488ea5c..a37b964 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,11 +44,12 @@ //! //! ``` //! # extern crate rand; +//! # extern crate rand_chacha; //! # extern crate sha2; //! # extern crate ed25519_dalek; //! # fn main() { //! # use rand::Rng; -//! # use rand::ChaChaRng; +//! # use rand_chacha::ChaChaRng; //! # use rand::SeedableRng; //! # use sha2::Sha512; //! # use ed25519_dalek::Keypair; @@ -67,9 +68,10 @@ //! # extern crate rand; //! # extern crate sha2; //! # extern crate ed25519_dalek; +//! # extern crate rand_chacha; //! # fn main() { //! # use rand::Rng; -//! # use rand::ChaChaRng; +//! # use rand_chacha::ChaChaRng; //! # use rand::SeedableRng; //! # use sha2::Sha512; //! # use ed25519_dalek::Keypair; @@ -89,9 +91,10 @@ //! # extern crate rand; //! # extern crate sha2; //! # extern crate ed25519_dalek; +//! # extern crate rand_chacha; //! # fn main() { //! # use rand::Rng; -//! # use rand::ChaChaRng; +//! # use rand_chacha::ChaChaRng; //! # use rand::SeedableRng; //! # use sha2::Sha512; //! # use ed25519_dalek::Keypair; @@ -119,8 +122,10 @@ //! # extern crate rand; //! # extern crate sha2; //! # extern crate ed25519_dalek; +//! # extern crate rand_chacha; //! # fn main() { -//! # use rand::{Rng, ChaChaRng, SeedableRng}; +//! # use rand::{Rng, SeedableRng}; +//! # use rand_chacha::ChaChaRng; //! # use sha2::Sha512; //! # use ed25519_dalek::{Keypair, Signature, PublicKey}; //! use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; @@ -142,8 +147,10 @@ //! ``` //! # extern crate rand; //! # extern crate sha2; +//! # extern crate rand_chacha; //! # extern crate ed25519_dalek; -//! # use rand::{Rng, ChaChaRng, SeedableRng}; +//! # use rand::{Rng, SeedableRng}; +//! # use rand_chacha::ChaChaRng; //! # use sha2::Sha512; //! # use ed25519_dalek::{Keypair, Signature, PublicKey, SecretKey, SignatureError}; //! # use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; @@ -186,6 +193,7 @@ //! # extern crate rand; //! # extern crate sha2; //! # extern crate ed25519_dalek; +//! # extern crate rand_chacha; //! # #[cfg(feature = "serde")] //! extern crate serde; //! # #[cfg(feature = "serde")] @@ -193,7 +201,8 @@ //! //! # #[cfg(feature = "serde")] //! # fn main() { -//! # use rand::{Rng, ChaChaRng, SeedableRng}; +//! # use rand::{Rng, SeedableRng}; +//! # use rand_chacha::ChaChaRng; //! # use sha2::Sha512; //! # use ed25519_dalek::{Keypair, Signature, PublicKey}; //! use bincode::{serialize, Infinite}; @@ -218,6 +227,7 @@ //! # extern crate rand; //! # extern crate sha2; //! # extern crate ed25519_dalek; +//! # extern crate rand_chacha; //! # #[cfg(feature = "serde")] //! # extern crate serde; //! # #[cfg(feature = "serde")] @@ -225,7 +235,8 @@ //! # //! # #[cfg(feature = "serde")] //! # fn main() { -//! # use rand::{Rng, ChaChaRng, SeedableRng}; +//! # use rand::{Rng, SeedableRng}; +//! # use rand_chacha::ChaChaRng; //! # use sha2::Sha512; //! # use ed25519_dalek::{Keypair, Signature, PublicKey}; //! # use bincode::{serialize, Infinite}; @@ -272,6 +283,9 @@ extern crate sha2; #[cfg(test)] extern crate hex; +#[cfg(test)] +extern crate rand_chacha; + #[cfg(feature = "serde")] extern crate serde; From 1132665ac2e2fa8bcb9ee9e47d4cb131d979ec8f Mon Sep 17 00:00:00 2001 From: Colt Frederickson Date: Mon, 19 Nov 2018 10:13:15 -0700 Subject: [PATCH 171/351] Update to 1.0.0-pre1 curve25519 --- Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 09748c0..98ae864 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,8 +16,7 @@ exclude = [ ".gitignore", "TESTVECTORS", "res/*" ] travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master"} [dependencies.curve25519-dalek] -git = "https://github.com/IronCoreLabs/curve25519-dalek.git" -branch = "rand-0.6" +version = "1.0.0-pre.1" default-features = false [dependencies.rand] From 26e017df7c7227ba61522aa6ef8f7a4e3126b7a0 Mon Sep 17 00:00:00 2001 From: isis agora lovecruft Date: Tue, 18 Dec 2018 01:09:00 +0000 Subject: [PATCH 172/351] Revert "Add AsRef instances for PublicKey and SecretKey" --- src/ed25519.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 1a79a11..0654eb1 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -183,12 +183,6 @@ impl Drop for SecretKey { } } -impl AsRef<[u8]> for SecretKey { - fn as_ref(&self) -> &[u8] { - self.as_bytes() - } -} - impl SecretKey { /// Expand this `SecretKey` into an `ExpandedSecretKey`. pub fn expand(&self) -> ExpandedSecretKey @@ -721,12 +715,6 @@ impl Debug for PublicKey { } } -impl AsRef<[u8]> for PublicKey { - fn as_ref(&self) -> &[u8] { - self.as_bytes() - } -} - impl PublicKey { /// Convert this public key to a byte array. #[inline] From 0708974aaaf480d3ffbcc3a7445bea872bbbe75c Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 22 Dec 2018 12:10:03 +0000 Subject: [PATCH 173/351] =?UTF-8?q?Bump=20curve25519-dalek=20dependency=20?= =?UTF-8?q?to=20version=201.0.=20=F0=9F=8E=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 98ae864..b5158ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ exclude = [ ".gitignore", "TESTVECTORS", "res/*" ] travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master"} [dependencies.curve25519-dalek] -version = "1.0.0-pre.1" +version = "1" default-features = false [dependencies.rand] From b9f078af16e9216cbf3bcc7de216000b88335e55 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 22 Dec 2018 12:14:45 +0000 Subject: [PATCH 174/351] Remove default-features=false from rand dependency. cf. https://github.com/rust-random/rand/issues/645 --- Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b5158ba..572cad4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,8 +20,7 @@ version = "1" default-features = false [dependencies.rand] -version = "0.6.0" -default-features = false +version = "0.6" features = ["i128_support"] [dependencies.serde] From 80ae5d06832aee8a25f1842e0571256bc094e8b8 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 22 Dec 2018 12:20:59 +0000 Subject: [PATCH 175/351] Cleanup RNG usage after merging #57. --- Cargo.toml | 1 - benches/ed25519_benchmarks.rs | 2 +- src/ed25519.rs | 33 ++++++++++++----------- src/lib.rs | 51 +++++++++++++---------------------- 4 files changed, 37 insertions(+), 50 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 572cad4..776b7ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,7 +43,6 @@ hex = "^0.3" sha2 = "^0.8" bincode = "^0.9" criterion = "0.2" -rand_chacha = "0.1.0" [[bench]] name = "ed25519_benchmarks" diff --git a/benches/ed25519_benchmarks.rs b/benches/ed25519_benchmarks.rs index 5db1361..79575c9 100644 --- a/benches/ed25519_benchmarks.rs +++ b/benches/ed25519_benchmarks.rs @@ -23,7 +23,7 @@ mod ed25519_benches { use ed25519_dalek::Signature; use ed25519_dalek::verify_batch; use rand::thread_rng; - use rand::ThreadRng; + use rand::rngs::ThreadRng; use sha2::Sha512; fn sign(c: &mut Criterion) { diff --git a/src/ed25519.rs b/src/ed25519.rs index 41acac6..1803e47 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -267,7 +267,7 @@ impl SecretKey { /// # fn main() { /// # /// use rand::Rng; - /// use rand::OsRng; + /// use rand::rngs::OsRng; /// use sha2::Sha512; /// use ed25519_dalek::PublicKey; /// use ed25519_dalek::SecretKey; @@ -287,21 +287,19 @@ impl SecretKey { /// /// ``` /// # extern crate rand; - /// # extern crate rand_chacha; /// # extern crate sha2; /// # extern crate ed25519_dalek; /// # /// # fn main() { /// # /// # use rand::Rng; - /// # use rand_chacha::ChaChaRng; - /// # use rand::SeedableRng; + /// # use rand::thread_rng; /// # use sha2::Sha512; /// # use ed25519_dalek::PublicKey; /// # use ed25519_dalek::SecretKey; /// # use ed25519_dalek::Signature; /// # - /// # let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]); + /// # let mut csprng = thread_rng(); /// # let secret_key: SecretKey = SecretKey::generate(&mut csprng); /// /// let public_key: PublicKey = PublicKey::from_secret::(&secret_key); @@ -417,7 +415,8 @@ impl<'a> From<&'a SecretKey> for ExpandedSecretKey { /// # #[cfg(all(feature = "std", feature = "sha2"))] /// # fn main() { /// # - /// use rand::{Rng, OsRng}; + /// use rand::Rng; + /// use rand::rngs::OsRng; /// use sha2::Sha512; /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; /// @@ -453,7 +452,8 @@ impl ExpandedSecretKey { /// # #[cfg(all(feature = "sha2", feature = "std"))] /// # fn main() { /// # - /// use rand::{Rng, OsRng}; + /// use rand::Rng; + /// use rand::rngs::OsRng; /// use sha2::Sha512; /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; /// @@ -494,7 +494,8 @@ impl ExpandedSecretKey { /// # #[cfg(all(feature = "sha2", feature = "std"))] /// # fn do_test() -> Result { /// # - /// use rand::{Rng, OsRng}; + /// use rand::Rng; + /// use rand::rngs::OsRng; /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; /// use ed25519_dalek::SignatureError; /// @@ -544,7 +545,8 @@ impl ExpandedSecretKey { /// # #[cfg(all(feature = "std", feature = "sha2"))] /// # fn main() { /// # - /// use rand::{Rng, OsRng}; + /// use rand::Rng; + /// use rand::rngs::OsRng; /// use sha2::Sha512; /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; /// @@ -927,7 +929,8 @@ impl From for PublicKey { /// * `messages` is a slice of byte slices, one per signed message. /// * `signatures` is a slice of `Signature`s. /// * `public_keys` is a slice of `PublicKey`s. -/// * `csprng` is an implementation of `Rng + CryptoRng`, such as `rand::ThreadRng`. +/// * `csprng` is an implementation of `Rng + CryptoRng`, such as +/// `rand::rngs::ThreadRng`. /// /// # Panics /// @@ -1393,8 +1396,6 @@ mod test { use std::string::String; use std::vec::Vec; use rand::thread_rng; - use rand_chacha::ChaChaRng; - use rand::SeedableRng; use rand::rngs::ThreadRng; use hex::FromHex; use sha2::Sha512; @@ -1428,7 +1429,7 @@ mod test { #[test] fn sign_verify() { // TestSignVerify - let mut csprng: ChaChaRng; + let mut csprng: ThreadRng; let keypair: Keypair; let good_sig: Signature; let bad_sig: Signature; @@ -1436,7 +1437,7 @@ mod test { let good: &[u8] = "test message".as_bytes(); let bad: &[u8] = "wrong message".as_bytes(); - csprng = ChaChaRng::from_seed([0u8; 32]); + csprng = thread_rng(); keypair = Keypair::generate::(&mut csprng); good_sig = keypair.sign::(&good); bad_sig = keypair.sign::(&bad); @@ -1530,7 +1531,7 @@ mod test { #[test] fn ed25519ph_sign_verify() { - let mut csprng: ChaChaRng; + let mut csprng: ThreadRng; let keypair: Keypair; let good_sig: Signature; let bad_sig: Signature; @@ -1553,7 +1554,7 @@ mod test { let context: &[u8] = b"testing testing 1 2 3"; - csprng = ChaChaRng::from_seed([0u8; 32]); + csprng = thread_rng(); keypair = Keypair::generate::(&mut csprng); good_sig = keypair.sign_prehashed::(prehashed_good1, Some(context)); bad_sig = keypair.sign_prehashed::(prehashed_bad1, Some(context)); diff --git a/src/lib.rs b/src/lib.rs index d6a023b..ff8ed03 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,7 +33,7 @@ //! use ed25519_dalek::Signature; //! //! let mut csprng: OsRng = OsRng::new().unwrap(); -//! let keypair: Keypair = Keypair::generate::(&mut csprng); +//! let keypair: Keypair = Keypair::generate::(&mut csprng); // The `_` can be the type of `csprng` //! # } //! # //! # #[cfg(any(not(feature = "std"), not(feature = "sha2")))] @@ -44,17 +44,15 @@ //! //! ``` //! # extern crate rand; -//! # extern crate rand_chacha; //! # extern crate sha2; //! # extern crate ed25519_dalek; //! # fn main() { //! # use rand::Rng; -//! # use rand_chacha::ChaChaRng; -//! # use rand::SeedableRng; +//! # use rand::thread_rng; //! # use sha2::Sha512; //! # use ed25519_dalek::Keypair; //! # use ed25519_dalek::Signature; -//! # let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]); +//! # let mut csprng = thread_rng(); //! # let keypair: Keypair = Keypair::generate::(&mut csprng); //! let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); //! let signature: Signature = keypair.sign::(message); @@ -68,15 +66,13 @@ //! # extern crate rand; //! # extern crate sha2; //! # extern crate ed25519_dalek; -//! # extern crate rand_chacha; //! # fn main() { //! # use rand::Rng; -//! # use rand_chacha::ChaChaRng; -//! # use rand::SeedableRng; +//! # use rand::thread_rng; //! # use sha2::Sha512; //! # use ed25519_dalek::Keypair; //! # use ed25519_dalek::Signature; -//! # let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]); +//! # let mut csprng = thread_rng(); //! # let keypair: Keypair = Keypair::generate::(&mut csprng); //! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); //! # let signature: Signature = keypair.sign::(message); @@ -91,16 +87,14 @@ //! # extern crate rand; //! # extern crate sha2; //! # extern crate ed25519_dalek; -//! # extern crate rand_chacha; //! # fn main() { //! # use rand::Rng; -//! # use rand_chacha::ChaChaRng; -//! # use rand::SeedableRng; +//! # use rand::thread_rng; //! # use sha2::Sha512; //! # use ed25519_dalek::Keypair; //! # use ed25519_dalek::Signature; //! use ed25519_dalek::PublicKey; -//! # let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]); +//! # let mut csprng = thread_rng(); //! # let keypair: Keypair = Keypair::generate::(&mut csprng); //! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); //! # let signature: Signature = keypair.sign::(message); @@ -122,14 +116,13 @@ //! # extern crate rand; //! # extern crate sha2; //! # extern crate ed25519_dalek; -//! # extern crate rand_chacha; //! # fn main() { -//! # use rand::{Rng, SeedableRng}; -//! # use rand_chacha::ChaChaRng; +//! # use rand::Rng; +//! # use rand::thread_rng; //! # use sha2::Sha512; //! # use ed25519_dalek::{Keypair, Signature, PublicKey}; //! use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; -//! # let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]); +//! # let mut csprng = thread_rng(); //! # let keypair: Keypair = Keypair::generate::(&mut csprng); //! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); //! # let signature: Signature = keypair.sign::(message); @@ -147,15 +140,14 @@ //! ``` //! # extern crate rand; //! # extern crate sha2; -//! # extern crate rand_chacha; //! # extern crate ed25519_dalek; -//! # use rand::{Rng, SeedableRng}; -//! # use rand_chacha::ChaChaRng; +//! # use rand::Rng; +//! # use rand::thread_rng; //! # use sha2::Sha512; //! # use ed25519_dalek::{Keypair, Signature, PublicKey, SecretKey, SignatureError}; //! # use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; //! # fn do_test() -> Result<(SecretKey, PublicKey, Keypair, Signature), SignatureError> { -//! # let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]); +//! # let mut csprng = thread_rng(); //! # let keypair_orig: Keypair = Keypair::generate::(&mut csprng); //! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); //! # let signature_orig: Signature = keypair_orig.sign::(message); @@ -193,7 +185,6 @@ //! # extern crate rand; //! # extern crate sha2; //! # extern crate ed25519_dalek; -//! # extern crate rand_chacha; //! # #[cfg(feature = "serde")] //! extern crate serde; //! # #[cfg(feature = "serde")] @@ -201,12 +192,12 @@ //! //! # #[cfg(feature = "serde")] //! # fn main() { -//! # use rand::{Rng, SeedableRng}; -//! # use rand_chacha::ChaChaRng; +//! # use rand::Rng; +//! # use rand::thread_rng; //! # use sha2::Sha512; //! # use ed25519_dalek::{Keypair, Signature, PublicKey}; //! use bincode::{serialize, Infinite}; -//! # let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]); +//! # let mut csprng = thread_rng(); //! # let keypair: Keypair = Keypair::generate::(&mut csprng); //! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); //! # let signature: Signature = keypair.sign::(message); @@ -227,7 +218,6 @@ //! # extern crate rand; //! # extern crate sha2; //! # extern crate ed25519_dalek; -//! # extern crate rand_chacha; //! # #[cfg(feature = "serde")] //! # extern crate serde; //! # #[cfg(feature = "serde")] @@ -235,14 +225,14 @@ //! # //! # #[cfg(feature = "serde")] //! # fn main() { -//! # use rand::{Rng, SeedableRng}; -//! # use rand_chacha::ChaChaRng; +//! # use rand::Rng; +//! # use rand::thread_rng; //! # use sha2::Sha512; //! # use ed25519_dalek::{Keypair, Signature, PublicKey}; //! # use bincode::{serialize, Infinite}; //! use bincode::{deserialize}; //! -//! # let mut csprng: ChaChaRng = ChaChaRng::from_seed([0u8; 32]); +//! # let mut csprng = thread_rng(); //! # let keypair: Keypair = Keypair::generate::(&mut csprng); //! let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); //! # let signature: Signature = keypair.sign::(message); @@ -283,9 +273,6 @@ extern crate sha2; #[cfg(test)] extern crate hex; -#[cfg(test)] -extern crate rand_chacha; - #[cfg(feature = "serde")] extern crate serde; From d052e63da86156f955f4d9d900ae85c43726c3a4 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 22 Dec 2018 12:21:59 +0000 Subject: [PATCH 176/351] Enabling std feature can now enable rand/std. Previously it pulled in a bunch of fuschia dependencies regardless of target system. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 776b7ed..5b04835 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,7 +51,7 @@ harness = false [features] default = ["std", "u64_backend"] # We don't add "rand/std" here because it would enable a bunch of Fuchsia dependencies. -std = ["curve25519-dalek/std"] +std = ["curve25519-dalek/std", "rand/std"] alloc = ["curve25519-dalek/alloc"] nightly = ["curve25519-dalek/nightly", "rand/nightly", "clear_on_drop/nightly"] asm = ["sha2/asm"] From a9e5410f695b1b3da9a2e86b9312d2d07920af16 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 22 Dec 2018 12:57:04 +0000 Subject: [PATCH 177/351] Fix serialised size assumptions from #48. Unfortunately the serialised size is likely never going to be the same as the type's size in memory, as most serialisation formats define additional headers for parsing safety reasons, such as buffer lengths and type information. --- src/ed25519.rs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 1803e47..8343f39 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -1645,9 +1645,6 @@ mod test { #[cfg(all(test, feature = "serde"))] use bincode::{serialize, serialized_size, deserialize, Infinite}; - #[cfg(all(test, feature = "serde"))] - use std::mem::size_of; - #[cfg(all(test, feature = "serde"))] #[test] fn serialize_deserialize_signature() { @@ -1681,25 +1678,19 @@ mod test { #[cfg(all(test, feature = "serde"))] #[test] fn serialize_public_key_size() { - assert_eq!( - serialized_size(&PUBLIC_KEY) as usize, - size_of::() - ); + assert_eq!(serialized_size(&PUBLIC_KEY) as usize, 40); // These sizes are specific to bincode==1.0.1 } #[cfg(all(test, feature = "serde"))] #[test] fn serialize_signature_size() { let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES).unwrap(); - assert_eq!(serialized_size(&signature) as usize, size_of::()); + assert_eq!(serialized_size(&signature) as usize, 72); // These sizes are specific to bincode==1.0.1 } #[cfg(all(test, feature = "serde"))] #[test] fn serialize_secret_key_size() { - assert_eq!( - serialized_size(&SECRET_KEY) as usize, - size_of::() - ); + assert_eq!(serialized_size(&SECRET_KEY) as usize, 40); // These sizes are specific to bincode==1.0.1 } } From 3d697bf27af293464e6e32028631d1e8937ad40d Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 22 Dec 2018 13:11:56 +0000 Subject: [PATCH 178/351] Fix doctests which relied on the sha2 feature being enabled. --- src/ed25519.rs | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 8343f39..8367022 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -491,6 +491,8 @@ impl ExpandedSecretKey { /// # extern crate sha2; /// # extern crate ed25519_dalek; /// # + /// # use ed25519_dalek::{ExpandedSecretKey, SignatureError}; + /// # /// # #[cfg(all(feature = "sha2", feature = "std"))] /// # fn do_test() -> Result { /// # @@ -1200,17 +1202,17 @@ impl Keypair { /// use ed25519_dalek::Keypair; /// use ed25519_dalek::Signature; /// use rand::thread_rng; - /// use rand::ThreadRng; + /// use sha2::Digest; /// use sha2::Sha512; /// /// # #[cfg(all(feature = "std", feature = "sha2"))] /// # fn main() { - /// let mut csprng: ThreadRng = thread_rng(); + /// let mut csprng = thread_rng(); /// let keypair: Keypair = Keypair::generate::(&mut csprng); /// let message: &[u8] = b"All I want is to pet all of the dogs."; /// /// // Create a hash digest object which we'll feed the message into: - /// let prehashed: Sha512 = Sha512::default(); + /// let mut prehashed: Sha512 = Sha512::default(); /// /// prehashed.input(message); /// # } @@ -1248,15 +1250,15 @@ impl Keypair { /// # use ed25519_dalek::Keypair; /// # use ed25519_dalek::Signature; /// # use rand::thread_rng; - /// # use rand::ThreadRng; + /// # use sha2::Digest; /// # use sha2::Sha512; /// # /// # #[cfg(all(feature = "std", feature = "sha2"))] /// # fn main() { - /// # let mut csprng: ThreadRng = thread_rng(); + /// # let mut csprng = thread_rng(); /// # let keypair: Keypair = Keypair::generate::(&mut csprng); /// # let message: &[u8] = b"All I want is to pet all of the dogs."; - /// # let prehashed: Sha512 = Sha512::default(); + /// # let mut prehashed: Sha512 = Sha512::default(); /// # prehashed.input(message); /// # /// let context: &[u8] = b"Ed25519DalekSignPrehashedDoctest"; @@ -1311,16 +1313,16 @@ impl Keypair { /// use ed25519_dalek::Keypair; /// use ed25519_dalek::Signature; /// use rand::thread_rng; - /// use rand::ThreadRng; + /// use sha2::Digest; /// use sha2::Sha512; /// /// # #[cfg(all(feature = "std", feature = "sha2"))] /// # fn main() { - /// let mut csprng: ThreadRng = thread_rng(); + /// let mut csprng = thread_rng(); /// let keypair: Keypair = Keypair::generate::(&mut csprng); /// let message: &[u8] = b"All I want is to pet all of the dogs."; /// - /// let prehashed: Sha512 = Sha512::default(); + /// let mut prehashed: Sha512 = Sha512::default(); /// prehashed.input(message); /// /// let context: &[u8] = b"Ed25519DalekSignPrehashedDoctest"; @@ -1328,12 +1330,12 @@ impl Keypair { /// let sig: Signature = keypair.sign_prehashed(prehashed, Some(context)); /// /// // The sha2::Sha512 struct doesn't implement Copy, so we'll have to create a new one: - /// let prehashed_again: Sha512 = Sha512::default(); + /// let mut prehashed_again: Sha512 = Sha512::default(); /// prehashed_again.input(message); /// - /// let valid: bool = keypair.public.verify_prehashed(prehashed_again, context, sig); + /// let verified = keypair.public.verify_prehashed(prehashed_again, Some(context), &sig); /// - /// assert!(valid); + /// assert!(verified.is_ok()); /// # } /// # /// # #[cfg(any(not(feature = "sha2"), not(feature = "std")))] From 1459e726887b220379eea36f6437261c4c1f8fc0 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 22 Dec 2018 13:20:44 +0000 Subject: [PATCH 179/351] Only test serde feature on stable to save CI resources. --- .travis.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 732228a..fa73140 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,14 +7,18 @@ rust: env: - TEST_COMMAND=test FEATURES='' - - TEST_COMMAND=test FEATURES='--features=serde' matrix: include: + # We use the 64-bit optimised curve backend by default, so also test with the 32-bit backend: - rust: nightly env: TEST_COMMAND=build FEATURES='--no-default-features --features=u32_backend' + # Test any nightly gated features on nightly: - rust: nightly env: TEST_COMMAND=test FEATURES='--features=nightly' + # Test serde support on stable, assuming that if it works there it'll work everywhere: + - rust: stable + env: TEST_COMMAND=test FEATURE='--features=serde' script: - cargo $TEST_COMMAND $FEATURES From aee0043a927c96f2f5d529dc5cebf8f1c0ab98fc Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 22 Dec 2018 13:23:41 +0000 Subject: [PATCH 180/351] Also exercise the test suite with the sha2 feature enabled in CI. --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index fa73140..7a43c0d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,6 +19,9 @@ matrix: # Test serde support on stable, assuming that if it works there it'll work everywhere: - rust: stable env: TEST_COMMAND=test FEATURE='--features=serde' + # Test with the optional sha2 feature enabled: + - rust: stable + env: TEST_COMMAND=test FEATURE='--features=sha2' script: - cargo $TEST_COMMAND $FEATURES From 4f53a4826d01d2c54a68f09becd8301ae148e188 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 22 Dec 2018 13:27:44 +0000 Subject: [PATCH 181/351] Add comment in .travis.yml about testing no_std. --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7a43c0d..82c3918 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,8 @@ env: matrix: include: - # We use the 64-bit optimised curve backend by default, so also test with the 32-bit backend: + # We use the 64-bit optimised curve backend by default, so also test with + # the 32-bit backend (this also exercises testing with `no_std`): - rust: nightly env: TEST_COMMAND=build FEATURES='--no-default-features --features=u32_backend' # Test any nightly gated features on nightly: From 8dbaf9a8d249a24a5225a1247195d4135669f608 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 20 Dec 2018 15:21:20 +0000 Subject: [PATCH 182/351] Move PublicKey point decompression into initialisation. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This caches the public key internally so that we effectively get a free speedup on key reuse in regular signature verification, similar to that in batch verification. (However, this also "speeds up"¹ batch verifications.) ¹ Less of a speed up than moving the computation elsewhere, but the speed up on reuse still also applies to key reuse for batch verification. --- src/ed25519.rs | 56 ++++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 8367022..d8e7766 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -718,11 +718,14 @@ impl<'d> Deserialize<'d> for ExpandedSecretKey { /// An ed25519 public key. #[derive(Copy, Clone, Default, Eq, PartialEq)] #[repr(C)] -pub struct PublicKey(pub (crate) CompressedEdwardsY); +pub struct PublicKey( + pub (crate) CompressedEdwardsY, + pub (crate) EdwardsPoint, +); impl Debug for PublicKey { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "PublicKey( CompressedEdwardsY( {:?} ))", self.0) + write!(f, "PublicKey({:?}), {:?})", self.0, self.1) } } @@ -790,7 +793,10 @@ impl PublicKey { let mut bits: [u8; 32] = [0u8; 32]; bits.copy_from_slice(&bytes[..32]); - Ok(PublicKey(CompressedEdwardsY(bits))) + let compressed = CompressedEdwardsY(bits); + let point = compressed.decompress().ok_or(SignatureError(InternalError::PointDecompressionError))?; + + Ok(PublicKey(compressed, point)) } /// Derive this public key from its corresponding `SecretKey`. @@ -825,9 +831,10 @@ impl PublicKey { bits[31] &= 127; bits[31] |= 64; - let pk = (&Scalar::from_bits(*bits) * &constants::ED25519_BASEPOINT_TABLE).compress().to_bytes(); + let point = &Scalar::from_bits(*bits) * &constants::ED25519_BASEPOINT_TABLE; + let compressed = point.compress(); - PublicKey(CompressedEdwardsY(pk)) + PublicKey(compressed, point) } /// Verify a signature on a message with this keypair's public key. @@ -842,18 +849,14 @@ impl PublicKey { let mut h: D = D::default(); let R: EdwardsPoint; let k: Scalar; - - let A: EdwardsPoint = match self.0.decompress() { - Some(x) => x, - None => return Err(SignatureError(InternalError::PointDecompressionError)), - }; + let minus_A: EdwardsPoint = -self.1; h.input(signature.R.as_bytes()); h.input(self.as_bytes()); h.input(&message); k = Scalar::from_hash(h); - R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(-A), &signature.s); + R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); if R.compress() == signature.R { Ok(()) @@ -894,10 +897,7 @@ impl PublicKey { let ctx: &[u8] = context.unwrap_or(b""); debug_assert!(ctx.len() <= 255, "The context must not be longer than 255 octets."); - let A: EdwardsPoint = match self.0.decompress() { - Some(x) => x, - None => return Err(SignatureError(InternalError::PointDecompressionError)), - }; + let minus_A: EdwardsPoint = -self.1; h.input(b"SigEd25519 no Ed25519 collisions"); h.input(&[1]); // Ed25519ph @@ -908,7 +908,7 @@ impl PublicKey { h.input(prehashed_message.result().as_slice()); k = Scalar::from_hash(h); - R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(-A), &signature.s); + R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); if R.compress() == signature.R { Ok(()) @@ -1022,7 +1022,7 @@ pub fn verify_batch(messages: &[&[u8]], let zhrams = hrams.zip(zs.iter()).map(|(hram, z)| hram * z); let Rs = signatures.iter().map(|sig| sig.R.decompress()); - let As = public_keys.iter().map(|pk| pk.0.decompress()); + let As = public_keys.iter().map(|pk| Some(pk.1)); let B = once(Some(constants::ED25519_BASEPOINT_POINT)); // Compute (-∑ z[i]s[i] (mod l)) B + ∑ z[i]R[i] + ∑ (z[i]H(R||A||M)[i] (mod l)) A[i] = 0 @@ -1404,11 +1404,11 @@ mod test { use super::*; #[cfg(all(test, feature = "serde"))] - static PUBLIC_KEY: PublicKey = PublicKey(CompressedEdwardsY([ + static PUBLIC_KEY_BYTES: [u8; PUBLIC_KEY_LENGTH] = [ 130, 039, 155, 015, 062, 076, 188, 063, 124, 122, 026, 251, 233, 253, 225, 220, 014, 041, 166, 120, 108, 035, 254, 077, - 160, 083, 172, 058, 219, 042, 086, 120, ])); + 160, 083, 172, 058, 219, 042, 086, 120, ]; #[cfg(all(test, feature = "serde"))] static SECRET_KEY: SecretKey = SecretKey([ @@ -1612,12 +1612,17 @@ mod test { 215, 090, 152, 001, 130, 177, 010, 183, 213, 075, 254, 211, 201, 100, 007, 058, 014, 225, 114, 243, 218, 166, 035, 037, - 175, 002, 026, 104, 247, 007, 081, 026, ])))) + 175, 002, 026, 104, 247, 007, 081, 026, ]), + CompressedEdwardsY([ + 215, 090, 152, 001, 130, 177, 010, 183, + 213, 075, 254, 211, 201, 100, 007, 058, + 014, 225, 114, 243, 218, 166, 035, 037, + 175, 002, 026, 104, 247, 007, 081, 026, ]).decompress().unwrap()))) } #[test] fn keypair_clear_on_drop() { - let mut keypair: Keypair = Keypair::from_bytes(&[15u8; KEYPAIR_LENGTH][..]).unwrap(); + let mut keypair: Keypair = Keypair::from_bytes(&[1u8; KEYPAIR_LENGTH][..]).unwrap(); keypair.clear(); @@ -1660,10 +1665,12 @@ mod test { #[cfg(all(test, feature = "serde"))] #[test] fn serialize_deserialize_public_key() { - let encoded_public_key: Vec = serialize(&PUBLIC_KEY, Infinite).unwrap(); + let public_key: PublicKey = PublicKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap(); + let encoded_public_key: Vec = serialize(&public_key, Infinite).unwrap(); let decoded_public_key: PublicKey = deserialize(&encoded_public_key).unwrap(); - assert_eq!(PUBLIC_KEY, decoded_public_key); + assert_eq!(&PUBLIC_KEY_BYTES[..], &encoded_public_key[encoded_public_key.len() - 32..]); + assert_eq!(public_key, decoded_public_key); } #[cfg(all(test, feature = "serde"))] @@ -1680,7 +1687,8 @@ mod test { #[cfg(all(test, feature = "serde"))] #[test] fn serialize_public_key_size() { - assert_eq!(serialized_size(&PUBLIC_KEY) as usize, 40); // These sizes are specific to bincode==1.0.1 + let public_key: PublicKey = PublicKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap(); + assert_eq!(serialized_size(&public_key) as usize, 40); // These sizes are specific to bincode==1.0.1 } #[cfg(all(test, feature = "serde"))] From 7877a7fa00c526d6a42b984c769ffc7263a1ee83 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 22 Dec 2018 14:40:42 +0000 Subject: [PATCH 183/351] WARNING: Remove #[repr(C)] from all types. --- src/ed25519.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index d8e7766..4d2fabd 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -71,7 +71,6 @@ pub const EXPANDED_SECRET_KEY_LENGTH: usize = EXPANDED_SECRET_KEY_KEY_LENGTH + E /// been signed. #[allow(non_snake_case)] #[derive(Copy, Eq, PartialEq)] -#[repr(C)] pub struct Signature { /// `R` is an `EdwardsPoint`, formed by using an hash function with /// 512-bits output to produce the digest of: @@ -166,7 +165,6 @@ impl<'d> Deserialize<'d> for Signature { } /// An EdDSA secret key. -#[repr(C)] #[derive(Default)] // we derive Default in order to use the clear() method in Drop pub struct SecretKey(pub (crate) [u8; SECRET_KEY_LENGTH]); @@ -386,7 +384,6 @@ impl<'d> Deserialize<'d> for SecretKey { // same signature scheme, and which both fail in exactly the same way. For a // better-designed, Schnorr-based signature scheme, see Trevor Perrin's work on // "generalised EdDSA" and "VXEdDSA". -#[repr(C)] #[derive(Default)] // we derive Default in order to use the clear() method in Drop pub struct ExpandedSecretKey { pub (crate) key: Scalar, @@ -717,7 +714,6 @@ impl<'d> Deserialize<'d> for ExpandedSecretKey { /// An ed25519 public key. #[derive(Copy, Clone, Default, Eq, PartialEq)] -#[repr(C)] pub struct PublicKey( pub (crate) CompressedEdwardsY, pub (crate) EdwardsPoint, @@ -1068,7 +1064,6 @@ impl<'d> Deserialize<'d> for PublicKey { /// An ed25519 keypair. #[derive(Debug, Default)] // we derive Default in order to use the clear() method in Drop -#[repr(C)] pub struct Keypair { /// The secret half of this keypair. pub secret: SecretKey, From d81d43e3ae957e4c707560d7aaf9f7326a96eaaa Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 29 Dec 2018 22:56:16 +0000 Subject: [PATCH 184/351] Hardcode use of sha2::Sha512 in most cases. This implements https://github.com/dalek-cryptography/ed25519-dalek/issues/64 You can still choose the "prehash" algorithm, as long as it has 64 bytes of output. Otherwise, everything is hardcoded to use sha2::Sha512. To use a different implementation you'll need a [patch.crates-io] section in cargo config. --- Cargo.toml | 5 +- src/ed25519.rs | 332 ++++++++++++++++++++++--------------------------- src/lib.rs | 80 +++++------- 3 files changed, 179 insertions(+), 238 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5b04835..5d5cc94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ optional = true [dependencies.sha2] version = "^0.8" -optional = true +default-features = false [dependencies.failure] version = "^0.1.1" @@ -40,7 +40,6 @@ version = "0.2" [dev-dependencies] hex = "^0.3" -sha2 = "^0.8" bincode = "^0.9" criterion = "0.2" @@ -51,7 +50,7 @@ harness = false [features] default = ["std", "u64_backend"] # We don't add "rand/std" here because it would enable a bunch of Fuchsia dependencies. -std = ["curve25519-dalek/std", "rand/std"] +std = ["curve25519-dalek/std", "rand/std", "sha2/std"] alloc = ["curve25519-dalek/alloc"] nightly = ["curve25519-dalek/nightly", "rand/nightly", "clear_on_drop/nightly"] asm = ["sha2/asm"] diff --git a/src/ed25519.rs b/src/ed25519.rs index 4d2fabd..fe207df 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -7,8 +7,7 @@ // Authors: // - Isis Agora Lovecruft -//! A Rust implementation of ed25519 EdDSA key generation, signing, and -//! verification. +//! A Rust implementation of ed25519 key generation, signing, and verification. use core::default::Default; use core::fmt::{Debug}; @@ -25,12 +24,11 @@ use serde::de::Error as SerdeError; #[cfg(feature = "serde")] use serde::de::Visitor; -#[cfg(feature = "sha2")] -use sha2::Sha512; +pub use sha2::Sha512; use clear_on_drop::clear::Clear; -use curve25519_dalek::digest::Digest; +pub use curve25519_dalek::digest::Digest; use curve25519_dalek::digest::generic_array::typenum::U64; use curve25519_dalek::constants; @@ -188,13 +186,6 @@ impl AsRef<[u8]> for SecretKey { } impl SecretKey { - /// Expand this `SecretKey` into an `ExpandedSecretKey`. - pub fn expand(&self) -> ExpandedSecretKey - where D: Digest + Default - { - ExpandedSecretKey::from_secret_key::(&self) - } - /// Convert this secret key to a byte array. #[inline] pub fn to_bytes(&self) -> [u8; SECRET_KEY_LENGTH] { @@ -279,20 +270,16 @@ impl SecretKey { /// # fn main() { } /// ``` /// - /// Afterwards, you can generate the corresponding public—provided you also - /// supply a hash function which implements the `Digest` and `Default` - /// traits, and which returns 512 bits of output—via: + /// Afterwards, you can generate the corresponding public: /// /// ``` /// # extern crate rand; - /// # extern crate sha2; /// # extern crate ed25519_dalek; /// # /// # fn main() { /// # /// # use rand::Rng; /// # use rand::thread_rng; - /// # use sha2::Sha512; /// # use ed25519_dalek::PublicKey; /// # use ed25519_dalek::SecretKey; /// # use ed25519_dalek::Signature; @@ -300,17 +287,13 @@ impl SecretKey { /// # let mut csprng = thread_rng(); /// # let secret_key: SecretKey = SecretKey::generate(&mut csprng); /// - /// let public_key: PublicKey = PublicKey::from_secret::(&secret_key); + /// let public_key: PublicKey = (&secret_key).into(); /// # } /// ``` /// - /// The standard hash function used for most ed25519 libraries is SHA-512, - /// which is available with `use sha2::Sha512` as in the example above. - /// Other suitable hash functions include Keccak-512 and Blake2b-512. - /// /// # Input /// - /// A CSPRNG with a `fill_bytes()` method, e.g. `rand_chacha::ChaChaRng` + /// A CSPRNG with a `fill_bytes()` method, e.g. `rand::OsRng` pub fn generate(csprng: &mut T) -> SecretKey where T: CryptoRng + Rng, { @@ -398,7 +381,6 @@ impl Drop for ExpandedSecretKey { } } -#[cfg(feature = "sha2")] impl<'a> From<&'a SecretKey> for ExpandedSecretKey { /// Construct an `ExpandedSecretKey` from a `SecretKey`. /// @@ -409,24 +391,35 @@ impl<'a> From<&'a SecretKey> for ExpandedSecretKey { /// # extern crate sha2; /// # extern crate ed25519_dalek; /// # - /// # #[cfg(all(feature = "std", feature = "sha2"))] /// # fn main() { /// # /// use rand::Rng; - /// use rand::rngs::OsRng; + /// use rand::thread_rng; /// use sha2::Sha512; /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; /// - /// let mut csprng: OsRng = OsRng::new().unwrap(); + /// let mut csprng = thread_rng(); /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key); /// # } - /// # - /// # #[cfg(any(not(feature = "std"), not(feature = "sha2")))] - /// # fn main() {} /// ``` fn from(secret_key: &'a SecretKey) -> ExpandedSecretKey { - ExpandedSecretKey::from_secret_key::(&secret_key) + let mut h: Sha512 = Sha512::default(); + let mut hash: [u8; 64] = [0u8; 64]; + let mut lower: [u8; 32] = [0u8; 32]; + let mut upper: [u8; 32] = [0u8; 32]; + + h.input(secret_key.as_bytes()); + hash.copy_from_slice(h.result().as_slice()); + + lower.copy_from_slice(&hash[00..32]); + upper.copy_from_slice(&hash[32..64]); + + lower[0] &= 248; + lower[31] &= 63; + lower[31] |= 64; + + ExpandedSecretKey{ key: Scalar::from_bits(lower), nonce: upper, } } } @@ -532,56 +525,10 @@ impl ExpandedSecretKey { nonce: upper }) } - /// Construct an `ExpandedSecretKey` from a `SecretKey`, using hash function `D`. - /// - /// # Examples - /// - /// ``` - /// # extern crate rand; - /// # extern crate sha2; - /// # extern crate ed25519_dalek; - /// # - /// # #[cfg(all(feature = "std", feature = "sha2"))] - /// # fn main() { - /// # - /// use rand::Rng; - /// use rand::rngs::OsRng; - /// use sha2::Sha512; - /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; - /// - /// let mut csprng: OsRng = OsRng::new().unwrap(); - /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); - /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from_secret_key::(&secret_key); - /// # } - /// # - /// # #[cfg(any(not(feature = "sha2"), not(feature = "std")))] - /// # fn main() { } - /// ``` - pub fn from_secret_key(secret_key: &SecretKey) -> ExpandedSecretKey - where D: Digest + Default { - let mut h: D = D::default(); - let mut hash: [u8; 64] = [0u8; 64]; - let mut lower: [u8; 32] = [0u8; 32]; - let mut upper: [u8; 32] = [0u8; 32]; - - h.input(secret_key.as_bytes()); - hash.copy_from_slice(h.result().as_slice()); - - lower.copy_from_slice(&hash[00..32]); - upper.copy_from_slice(&hash[32..64]); - - lower[0] &= 248; - lower[31] &= 63; - lower[31] |= 64; - - ExpandedSecretKey{ key: Scalar::from_bits(lower), nonce: upper, } - } - /// Sign a message with this `ExpandedSecretKey`. #[allow(non_snake_case)] - pub fn sign(&self, message: &[u8], public_key: &PublicKey) -> Signature - where D: Digest + Default { - let mut h: D = D::default(); + pub fn sign(&self, message: &[u8], public_key: &PublicKey) -> Signature { + let mut h: Sha512 = Sha512::new(); let R: CompressedEdwardsY; let r: Scalar; let s: Scalar; @@ -593,7 +540,7 @@ impl ExpandedSecretKey { r = Scalar::from_hash(h); R = (&r * &constants::ED25519_BASEPOINT_TABLE).compress(); - h = D::default(); + h = Sha512::new(); h.input(R.as_bytes()); h.input(public_key.as_bytes()); h.input(&message); @@ -623,13 +570,16 @@ impl ExpandedSecretKey { /// /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 #[allow(non_snake_case)] - pub fn sign_prehashed(&self, - prehashed_message: D, - public_key: &PublicKey, - context: Option<&'static [u8]>) -> Signature - where D: Digest + Default + pub fn sign_prehashed( + &self, + prehashed_message: D, + public_key: &PublicKey, + context: Option<&'static [u8]>, + ) -> Signature + where + D: Digest, { - let mut h: D; + let mut h: Sha512; let mut prehash: [u8; 64] = [0u8; 64]; let R: CompressedEdwardsY; let r: Scalar; @@ -657,7 +607,7 @@ impl ExpandedSecretKey { // // This is a really fucking stupid bandaid, and the damned scheme is // still bleeding from malleability, for fuck's sake. - h = D::default() + h = Sha512::new() .chain(b"SigEd25519 no Ed25519 collisions") .chain(&[1]) // Ed25519ph .chain(&[ctx_len]) @@ -668,7 +618,7 @@ impl ExpandedSecretKey { r = Scalar::from_hash(h); R = (&r * &constants::ED25519_BASEPOINT_TABLE).compress(); - h = D::default() + h = Sha512::new() .chain(b"SigEd25519 no Ed25519 collisions") .chain(&[1]) // Ed25519ph .chain(&[ctx_len]) @@ -794,13 +744,12 @@ impl PublicKey { Ok(PublicKey(compressed, point)) } +} +impl<'a> From<&'a SecretKey> for PublicKey { /// Derive this public key from its corresponding `SecretKey`. - #[allow(unused_assignments)] - pub fn from_secret(secret_key: &SecretKey) -> PublicKey - where D: Digest + Default - { - let mut h: D = D::default(); + fn from(secret_key: &SecretKey) -> PublicKey { + let mut h: Sha512 = Sha512::new(); let mut hash: [u8; 64] = [0u8; 64]; let mut digest: [u8; 32] = [0u8; 32]; @@ -811,14 +760,18 @@ impl PublicKey { PublicKey::mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(&mut digest) } +} +impl<'a> From<&'a ExpandedSecretKey> for PublicKey { /// Derive this public key from its corresponding `ExpandedSecretKey`. - pub fn from_expanded_secret(expanded_secret_key: &ExpandedSecretKey) -> PublicKey { + fn from(expanded_secret_key: &ExpandedSecretKey) -> PublicKey { let mut bits: [u8; 32] = expanded_secret_key.key.to_bytes(); PublicKey::mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(&mut bits) } +} +impl PublicKey { /// Internal utility function for mangling the bits of a (formerly /// mathematically well-defined) "scalar" and multiplying it to produce a /// public key. @@ -839,10 +792,13 @@ impl PublicKey { /// /// Returns `Ok(())` if the signature is valid, and `Err` otherwise. #[allow(non_snake_case)] - pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> - where D: Digest + Default + pub fn verify( + &self, + message: &[u8], + signature: &Signature + ) -> Result<(), SignatureError> { - let mut h: D = D::default(); + let mut h: Sha512 = Sha512::new(); let R: EdwardsPoint; let k: Scalar; let minus_A: EdwardsPoint = -self.1; @@ -880,13 +836,16 @@ impl PublicKey { /// /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 #[allow(non_snake_case)] - pub fn verify_prehashed(&self, - prehashed_message: D, - context: Option<&[u8]>, - signature: &Signature) -> Result<(), SignatureError> - where D: Digest + Default + pub fn verify_prehashed( + &self, + prehashed_message: D, + context: Option<&[u8]>, + signature: &Signature, + ) -> Result<(), SignatureError> + where + D: Digest, { - let mut h: D = D::default(); + let mut h: Sha512 = Sha512::default(); let R: EdwardsPoint; let k: Scalar; @@ -914,12 +873,6 @@ impl PublicKey { } } -impl From for PublicKey { - fn from(source: ExpandedSecretKey) -> PublicKey { - PublicKey::from_expanded_secret(&source) - } -} - /// Verify a batch of `signatures` on `messages` with their respective `public_keys`. /// /// # Inputs @@ -946,7 +899,6 @@ impl From for PublicKey { /// ``` /// extern crate ed25519_dalek; /// extern crate rand; -/// extern crate sha2; /// /// use ed25519_dalek::verify_batch; /// use ed25519_dalek::Keypair; @@ -954,26 +906,26 @@ impl From for PublicKey { /// use ed25519_dalek::Signature; /// use rand::thread_rng; /// use rand::rngs::ThreadRng; -/// use sha2::Sha512; /// /// # fn main() { /// let mut csprng: ThreadRng = thread_rng(); -/// let keypairs: Vec = (0..64).map(|_| Keypair::generate::(&mut csprng)).collect(); +/// let keypairs: Vec = (0..64).map(|_| Keypair::generate(&mut csprng)).collect(); /// let msg: &[u8] = b"They're good dogs Brant"; /// let messages: Vec<&[u8]> = (0..64).map(|_| msg).collect(); -/// let signatures: Vec = keypairs.iter().map(|key| key.sign::(&msg)).collect(); +/// let signatures: Vec = keypairs.iter().map(|key| key.sign(&msg)).collect(); /// let public_keys: Vec = keypairs.iter().map(|key| key.public).collect(); /// -/// let result = verify_batch::(&messages[..], &signatures[..], &public_keys[..]); +/// let result = verify_batch(&messages[..], &signatures[..], &public_keys[..]); /// assert!(result.is_ok()); /// # } /// ``` #[cfg(any(feature = "alloc", feature = "std"))] #[allow(non_snake_case)] -pub fn verify_batch(messages: &[&[u8]], - signatures: &[Signature], - public_keys: &[PublicKey]) -> Result<(), SignatureError> - where D: Digest + Default +pub fn verify_batch( + messages: &[&[u8]], + signatures: &[Signature], + public_keys: &[PublicKey], +) -> Result<(), SignatureError> { const ASSERT_MESSAGE: &'static [u8] = b"The number of messages, signatures, and public keys must be equal."; assert!(signatures.len() == messages.len(), ASSERT_MESSAGE); @@ -1007,7 +959,7 @@ pub fn verify_batch(messages: &[&[u8]], // Compute H(R || A || M) for each (signature, public_key, message) triplet let hrams = (0..signatures.len()).map(|i| { - let mut h: D = D::default(); + let mut h: Sha512 = Sha512::default(); h.input(signatures[i].R.as_bytes()); h.input(public_keys[i].as_bytes()); h.input(&messages[i]); @@ -1125,24 +1077,22 @@ impl Keypair { /// /// ``` /// extern crate rand; - /// extern crate sha2; /// extern crate ed25519_dalek; /// - /// # #[cfg(all(feature = "std", feature = "sha2"))] + /// # #[cfg(feature = "std")] /// # fn main() { /// /// use rand::Rng; /// use rand::OsRng; - /// use sha2::Sha512; /// use ed25519_dalek::Keypair; /// use ed25519_dalek::Signature; /// /// let mut csprng: OsRng = OsRng::new().unwrap(); - /// let keypair: Keypair = Keypair::generate::(&mut csprng); + /// let keypair: Keypair = Keypair::generate(&mut csprng); /// /// # } /// # - /// # #[cfg(any(not(feature = "sha2"), not(feature = "std")))] + /// # #[cfg(not(feature = "std"))] /// # fn main() { } /// ``` /// @@ -1155,20 +1105,21 @@ impl Keypair { /// The standard hash function used for most ed25519 libraries is SHA-512, /// which is available with `use sha2::Sha512` as in the example above. /// Other suitable hash functions include Keccak-512 and Blake2b-512. - pub fn generate(csprng: &mut R) -> Keypair - where D: Digest + Default, - R: CryptoRng + Rng, + pub fn generate(csprng: &mut R) -> Keypair + where R: CryptoRng + Rng, { let sk: SecretKey = SecretKey::generate(csprng); - let pk: PublicKey = PublicKey::from_secret::(&sk); + let pk: PublicKey = (&sk).into(); Keypair{ public: pk, secret: sk } } /// Sign a message with this keypair's secret key. - pub fn sign(&self, message: &[u8]) -> Signature - where D: Digest + Default { - self.secret.expand::().sign::(&message, &self.public) + pub fn sign(&self, message: &[u8]) -> Signature + { + let expanded: ExpandedSecretKey = (&self.secret).into(); + + expanded.sign(&message, &self.public) } /// Sign a `prehashed_message` with this `Keypair` using the @@ -1192,27 +1143,26 @@ impl Keypair { /// ``` /// extern crate ed25519_dalek; /// extern crate rand; - /// extern crate sha2; /// + /// use ed25519_dalek::Digest; /// use ed25519_dalek::Keypair; + /// use ed25519_dalek::Sha512; /// use ed25519_dalek::Signature; /// use rand::thread_rng; - /// use sha2::Digest; - /// use sha2::Sha512; /// - /// # #[cfg(all(feature = "std", feature = "sha2"))] + /// # #[cfg(feature = "std")] /// # fn main() { /// let mut csprng = thread_rng(); - /// let keypair: Keypair = Keypair::generate::(&mut csprng); + /// let keypair: Keypair = Keypair::generate(&mut csprng); /// let message: &[u8] = b"All I want is to pet all of the dogs."; /// /// // Create a hash digest object which we'll feed the message into: - /// let mut prehashed: Sha512 = Sha512::default(); + /// let mut prehashed: Sha512 = Sha512::new(); /// /// prehashed.input(message); /// # } /// # - /// # #[cfg(any(not(feature = "sha2"), not(feature = "std")))] + /// # #[cfg(not(feature = "std"))] /// # fn main() { } /// ``` /// @@ -1240,20 +1190,19 @@ impl Keypair { /// ``` /// # extern crate ed25519_dalek; /// # extern crate rand; - /// # extern crate sha2; /// # + /// # use ed25519_dalek::Digest; /// # use ed25519_dalek::Keypair; /// # use ed25519_dalek::Signature; + /// # use ed25519_dalek::Sha512; /// # use rand::thread_rng; - /// # use sha2::Digest; - /// # use sha2::Sha512; /// # - /// # #[cfg(all(feature = "std", feature = "sha2"))] + /// # #[cfg(feature = "std")] /// # fn main() { /// # let mut csprng = thread_rng(); - /// # let keypair: Keypair = Keypair::generate::(&mut csprng); + /// # let keypair: Keypair = Keypair::generate(&mut csprng); /// # let message: &[u8] = b"All I want is to pet all of the dogs."; - /// # let mut prehashed: Sha512 = Sha512::default(); + /// # let mut prehashed: Sha512 = Sha512::new(); /// # prehashed.input(message); /// # /// let context: &[u8] = b"Ed25519DalekSignPrehashedDoctest"; @@ -1261,24 +1210,33 @@ impl Keypair { /// let sig: Signature = keypair.sign_prehashed(prehashed, Some(context)); /// # } /// # - /// # #[cfg(any(not(feature = "sha2"), not(feature = "std")))] + /// # #[cfg(not(feature = "std"))] /// # fn main() { } /// ``` /// /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 /// [terrible_idea]: https://github.com/isislovecruft/scripts/blob/master/gpgkey2bc.py - pub fn sign_prehashed(&self, - prehashed_message: D, - context: Option<&'static [u8]>) -> Signature - where D: Digest + Default + pub fn sign_prehashed( + &self, + prehashed_message: D, + context: Option<&'static [u8]> + ) -> Signature + where + D: Digest, { - self.secret.expand::().sign_prehashed::(prehashed_message, &self.public, context) + let expanded: ExpandedSecretKey = (&self.secret).into(); // xxx thanks i hate this + + expanded.sign_prehashed(prehashed_message, &self.public, context) } /// Verify a signature on a message with this keypair's public key. - pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> - where D: Digest + Default { - self.public.verify::(message, signature) + pub fn verify( + &self, + message: &[u8], + signature: &Signature + ) -> Result<(), SignatureError> + { + self.public.verify(message, signature) } /// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm. @@ -1303,18 +1261,17 @@ impl Keypair { /// ``` /// extern crate ed25519_dalek; /// extern crate rand; - /// extern crate sha2; /// + /// use ed25519_dalek::Digest; /// use ed25519_dalek::Keypair; /// use ed25519_dalek::Signature; + /// use ed25519_dalek::Sha512; /// use rand::thread_rng; - /// use sha2::Digest; - /// use sha2::Sha512; /// - /// # #[cfg(all(feature = "std", feature = "sha2"))] + /// # #[cfg(feature = "std")] /// # fn main() { /// let mut csprng = thread_rng(); - /// let keypair: Keypair = Keypair::generate::(&mut csprng); + /// let keypair: Keypair = Keypair::generate(&mut csprng); /// let message: &[u8] = b"All I want is to pet all of the dogs."; /// /// let mut prehashed: Sha512 = Sha512::default(); @@ -1333,18 +1290,21 @@ impl Keypair { /// assert!(verified.is_ok()); /// # } /// # - /// # #[cfg(any(not(feature = "sha2"), not(feature = "std")))] + /// # #[cfg(not(feature = "std"))] /// # fn main() { } /// ``` /// /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 - pub fn verify_prehashed(&self, - prehashed_message: D, - context: Option<&[u8]>, - signature: &Signature) -> Result<(), SignatureError> - where D: Digest + Default + pub fn verify_prehashed( + &self, + prehashed_message: D, + context: Option<&[u8]>, + signature: &Signature + ) -> Result<(), SignatureError> + where + D: Digest, { - self.public.verify_prehashed::(prehashed_message, context, signature) + self.public.verify_prehashed(prehashed_message, context, signature) } } @@ -1435,15 +1395,15 @@ mod test { let bad: &[u8] = "wrong message".as_bytes(); csprng = thread_rng(); - keypair = Keypair::generate::(&mut csprng); - good_sig = keypair.sign::(&good); - bad_sig = keypair.sign::(&bad); + keypair = Keypair::generate(&mut csprng); + good_sig = keypair.sign(&good); + bad_sig = keypair.sign(&bad); - assert!(keypair.verify::(&good, &good_sig).is_ok(), + assert!(keypair.verify(&good, &good_sig).is_ok(), "Verification of a valid signature failed!"); - assert!(keypair.verify::(&good, &bad_sig).is_err(), + assert!(keypair.verify(&good, &bad_sig).is_err(), "Verification of a signature on a different message passed!"); - assert!(keypair.verify::(&bad, &good_sig).is_err(), + assert!(keypair.verify(&bad, &good_sig).is_err(), "Verification of a signature on a different message passed!"); } @@ -1485,10 +1445,10 @@ mod test { // The signatures in the test vectors also include the message // at the end, but we just want R and S. let sig1: Signature = Signature::from_bytes(&sig_bytes[..64]).unwrap(); - let sig2: Signature = keypair.sign::(&msg_bytes); + let sig2: Signature = keypair.sign(&msg_bytes); assert!(sig1 == sig2, "Signature bytes not equal on line {}", lineno); - assert!(keypair.verify::(&msg_bytes, &sig2).is_ok(), + assert!(keypair.verify(&msg_bytes, &sig2).is_ok(), "Signature verification failed on line {}", lineno); } } @@ -1552,15 +1512,15 @@ mod test { let context: &[u8] = b"testing testing 1 2 3"; csprng = thread_rng(); - keypair = Keypair::generate::(&mut csprng); - good_sig = keypair.sign_prehashed::(prehashed_good1, Some(context)); - bad_sig = keypair.sign_prehashed::(prehashed_bad1, Some(context)); + keypair = Keypair::generate(&mut csprng); + good_sig = keypair.sign_prehashed(prehashed_good1, Some(context)); + bad_sig = keypair.sign_prehashed(prehashed_bad1, Some(context)); - assert!(keypair.verify_prehashed::(prehashed_good2, Some(context), &good_sig).is_ok(), + assert!(keypair.verify_prehashed(prehashed_good2, Some(context), &good_sig).is_ok(), "Verification of a valid signature failed!"); - assert!(keypair.verify_prehashed::(prehashed_good3, Some(context), &bad_sig).is_err(), + assert!(keypair.verify_prehashed(prehashed_good3, Some(context), &bad_sig).is_err(), "Verification of a signature on a different message passed!"); - assert!(keypair.verify_prehashed::(prehashed_bad2, Some(context), &good_sig).is_err(), + assert!(keypair.verify_prehashed(prehashed_bad2, Some(context), &good_sig).is_err(), "Verification of a signature on a different message passed!"); } @@ -1579,13 +1539,13 @@ mod test { let mut signatures: Vec = Vec::new(); for i in 0..messages.len() { - let keypair: Keypair = Keypair::generate::(&mut csprng); - signatures.push(keypair.sign::(&messages[i])); + let keypair: Keypair = Keypair::generate(&mut csprng); + signatures.push(keypair.sign(&messages[i])); keypairs.push(keypair); } let public_keys: Vec = keypairs.iter().map(|key| key.public).collect(); - let result = verify_batch::(&messages, &signatures[..], &public_keys[..]); + let result = verify_batch(&messages, &signatures[..], &public_keys[..]); assert!(result.is_ok()); } @@ -1636,10 +1596,10 @@ mod test { #[test] fn pubkey_from_secret_and_expanded_secret() { let mut csprng = thread_rng(); - let secret: SecretKey = SecretKey::generate::<_>(&mut csprng); - let expanded_secret: ExpandedSecretKey = ExpandedSecretKey::from_secret_key::(&secret); - let public_from_secret: PublicKey = PublicKey::from_secret::(&secret); - let public_from_expanded_secret: PublicKey = PublicKey::from_expanded_secret(&expanded_secret); + let secret: SecretKey = SecretKey::generate(&mut csprng); + let expanded_secret: ExpandedSecretKey = (&secret).into(); + let public_from_secret: PublicKey = (&secret).into(); // XXX eww + let public_from_expanded_secret: PublicKey = (&expanded_secret).into(); // XXX eww assert!(public_from_secret == public_from_expanded_secret); } diff --git a/src/lib.rs b/src/lib.rs index ff8ed03..6c92afb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,28 +15,25 @@ //! //! First, we need to generate a `Keypair`, which includes both public and //! secret halves of an asymmetric key. To do so, we need a cryptographically -//! secure pseudorandom number generator (CSPRNG), and a hash function which -//! has 512 bits of output. For this example, we'll use the operating -//! system's builtin PRNG and SHA-512 to generate a keypair: +//! secure pseudorandom number generator (CSPRNG). For this example, we'll use +//! the operating system's builtin PRNG: //! //! ``` //! extern crate rand; -//! extern crate sha2; //! extern crate ed25519_dalek; //! -//! # #[cfg(all(feature = "std", feature = "sha2"))] +//! # #[cfg(feature = "std")] //! # fn main() { //! use rand::Rng; //! use rand::OsRng; -//! use sha2::Sha512; //! use ed25519_dalek::Keypair; //! use ed25519_dalek::Signature; //! //! let mut csprng: OsRng = OsRng::new().unwrap(); -//! let keypair: Keypair = Keypair::generate::(&mut csprng); // The `_` can be the type of `csprng` +//! let keypair: Keypair = Keypair::generate(&mut csprng); //! # } //! # -//! # #[cfg(any(not(feature = "std"), not(feature = "sha2")))] +//! # #[cfg(not(feature = "std"))] //! # fn main() { } //! ``` //! @@ -44,18 +41,16 @@ //! //! ``` //! # extern crate rand; -//! # extern crate sha2; //! # extern crate ed25519_dalek; //! # fn main() { //! # use rand::Rng; //! # use rand::thread_rng; -//! # use sha2::Sha512; //! # use ed25519_dalek::Keypair; //! # use ed25519_dalek::Signature; //! # let mut csprng = thread_rng(); -//! # let keypair: Keypair = Keypair::generate::(&mut csprng); -//! let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); -//! let signature: Signature = keypair.sign::(message); +//! # let keypair: Keypair = Keypair::generate(&mut csprng); +//! let message: &[u8] = b"This is a test of the tsunami alert system."; +//! let signature: Signature = keypair.sign(message); //! # } //! ``` //! @@ -64,19 +59,17 @@ //! //! ``` //! # extern crate rand; -//! # extern crate sha2; //! # extern crate ed25519_dalek; //! # fn main() { //! # use rand::Rng; //! # use rand::thread_rng; -//! # use sha2::Sha512; //! # use ed25519_dalek::Keypair; //! # use ed25519_dalek::Signature; //! # let mut csprng = thread_rng(); -//! # let keypair: Keypair = Keypair::generate::(&mut csprng); -//! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); -//! # let signature: Signature = keypair.sign::(message); -//! assert!(keypair.verify::(message, &signature).is_ok()); +//! # let keypair: Keypair = Keypair::generate(&mut csprng); +//! # let message: &[u8] = b"This is a test of the tsunami alert system."; +//! # let signature: Signature = keypair.sign(message); +//! assert!(keypair.verify(message, &signature).is_ok()); //! # } //! ``` //! @@ -85,22 +78,20 @@ //! //! ``` //! # extern crate rand; -//! # extern crate sha2; //! # extern crate ed25519_dalek; //! # fn main() { //! # use rand::Rng; //! # use rand::thread_rng; -//! # use sha2::Sha512; //! # use ed25519_dalek::Keypair; //! # use ed25519_dalek::Signature; //! use ed25519_dalek::PublicKey; //! # let mut csprng = thread_rng(); -//! # let keypair: Keypair = Keypair::generate::(&mut csprng); -//! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); -//! # let signature: Signature = keypair.sign::(message); +//! # let keypair: Keypair = Keypair::generate(&mut csprng); +//! # let message: &[u8] = b"This is a test of the tsunami alert system."; +//! # let signature: Signature = keypair.sign(message); //! //! let public_key: PublicKey = keypair.public; -//! assert!(public_key.verify::(message, &signature).is_ok()); +//! assert!(public_key.verify(message, &signature).is_ok()); //! # } //! ``` //! @@ -114,18 +105,16 @@ //! //! ``` //! # extern crate rand; -//! # extern crate sha2; //! # extern crate ed25519_dalek; //! # fn main() { //! # use rand::Rng; //! # use rand::thread_rng; -//! # use sha2::Sha512; //! # use ed25519_dalek::{Keypair, Signature, PublicKey}; //! use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; //! # let mut csprng = thread_rng(); -//! # let keypair: Keypair = Keypair::generate::(&mut csprng); -//! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); -//! # let signature: Signature = keypair.sign::(message); +//! # let keypair: Keypair = Keypair::generate(&mut csprng); +//! # let message: &[u8] = b"This is a test of the tsunami alert system."; +//! # let signature: Signature = keypair.sign(message); //! # let public_key: PublicKey = keypair.public; //! //! let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = public_key.to_bytes(); @@ -139,18 +128,16 @@ //! //! ``` //! # extern crate rand; -//! # extern crate sha2; //! # extern crate ed25519_dalek; //! # use rand::Rng; //! # use rand::thread_rng; -//! # use sha2::Sha512; //! # use ed25519_dalek::{Keypair, Signature, PublicKey, SecretKey, SignatureError}; //! # use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; //! # fn do_test() -> Result<(SecretKey, PublicKey, Keypair, Signature), SignatureError> { //! # let mut csprng = thread_rng(); -//! # let keypair_orig: Keypair = Keypair::generate::(&mut csprng); -//! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); -//! # let signature_orig: Signature = keypair_orig.sign::(message); +//! # let keypair_orig: Keypair = Keypair::generate(&mut csprng); +//! # let message: &[u8] = b"This is a test of the tsunami alert system."; +//! # let signature_orig: Signature = keypair_orig.sign(message); //! # let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = keypair_orig.public.to_bytes(); //! # let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = keypair_orig.secret.to_bytes(); //! # let keypair_bytes: [u8; KEYPAIR_LENGTH] = keypair_orig.to_bytes(); @@ -183,7 +170,6 @@ //! //! ``` //! # extern crate rand; -//! # extern crate sha2; //! # extern crate ed25519_dalek; //! # #[cfg(feature = "serde")] //! extern crate serde; @@ -194,15 +180,14 @@ //! # fn main() { //! # use rand::Rng; //! # use rand::thread_rng; -//! # use sha2::Sha512; //! # use ed25519_dalek::{Keypair, Signature, PublicKey}; //! use bincode::{serialize, Infinite}; //! # let mut csprng = thread_rng(); -//! # let keypair: Keypair = Keypair::generate::(&mut csprng); -//! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); -//! # let signature: Signature = keypair.sign::(message); +//! # let keypair: Keypair = Keypair::generate(&mut csprng); +//! # let message: &[u8] = b"This is a test of the tsunami alert system."; +//! # let signature: Signature = keypair.sign(message); //! # let public_key: PublicKey = keypair.public; -//! # let verified: bool = public_key.verify::(message, &signature).is_ok(); +//! # let verified: bool = public_key.verify(message, &signature).is_ok(); //! //! let encoded_public_key: Vec = serialize(&public_key, Infinite).unwrap(); //! let encoded_signature: Vec = serialize(&signature, Infinite).unwrap(); @@ -216,7 +201,6 @@ //! //! ``` //! # extern crate rand; -//! # extern crate sha2; //! # extern crate ed25519_dalek; //! # #[cfg(feature = "serde")] //! # extern crate serde; @@ -227,17 +211,16 @@ //! # fn main() { //! # use rand::Rng; //! # use rand::thread_rng; -//! # use sha2::Sha512; //! # use ed25519_dalek::{Keypair, Signature, PublicKey}; //! # use bincode::{serialize, Infinite}; //! use bincode::{deserialize}; //! //! # let mut csprng = thread_rng(); -//! # let keypair: Keypair = Keypair::generate::(&mut csprng); -//! let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); -//! # let signature: Signature = keypair.sign::(message); +//! # let keypair: Keypair = Keypair::generate(&mut csprng); +//! let message: &[u8] = b"This is a test of the tsunami alert system."; +//! # let signature: Signature = keypair.sign(message); //! # let public_key: PublicKey = keypair.public; -//! # let verified: bool = public_key.verify::(message, &signature).is_ok(); +//! # let verified: bool = public_key.verify(message, &signature).is_ok(); //! # let encoded_public_key: Vec = serialize(&public_key, Infinite).unwrap(); //! # let encoded_signature: Vec = serialize(&signature, Infinite).unwrap(); //! let decoded_public_key: PublicKey = deserialize(&encoded_public_key).unwrap(); @@ -246,7 +229,7 @@ //! # assert_eq!(public_key, decoded_public_key); //! # assert_eq!(signature, decoded_signature); //! # -//! let verified: bool = decoded_public_key.verify::(&message, &decoded_signature).is_ok(); +//! let verified: bool = decoded_public_key.verify(&message, &decoded_signature).is_ok(); //! //! assert!(verified); //! # } @@ -267,7 +250,6 @@ extern crate rand; #[macro_use] extern crate std; -#[cfg(any(test, feature = "sha2"))] extern crate sha2; #[cfg(test)] From 486f23f1ad75ebbf917c980faead84fcaf08faf9 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 30 Dec 2018 02:25:19 +0000 Subject: [PATCH 185/351] Fix some inconsistent terminology in docstrings. --- src/ed25519.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index fe207df..9f881be 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -39,25 +39,25 @@ use curve25519_dalek::scalar::Scalar; use errors::SignatureError; use errors::InternalError; -/// The length of a curve25519 EdDSA `Signature`, in bytes. +/// The length of a ed25519 `Signature`, in bytes. pub const SIGNATURE_LENGTH: usize = 64; -/// The length of a curve25519 EdDSA `SecretKey`, in bytes. +/// The length of a ed25519 `SecretKey`, in bytes. pub const SECRET_KEY_LENGTH: usize = 32; -/// The length of an ed25519 EdDSA `PublicKey`, in bytes. +/// The length of an ed25519 `PublicKey`, in bytes. pub const PUBLIC_KEY_LENGTH: usize = 32; -/// The length of an ed25519 EdDSA `Keypair`, in bytes. +/// The length of an ed25519 `Keypair`, in bytes. pub const KEYPAIR_LENGTH: usize = SECRET_KEY_LENGTH + PUBLIC_KEY_LENGTH; -/// The length of the "key" portion of an "expanded" curve25519 EdDSA secret key, in bytes. +/// The length of the "key" portion of an "expanded" ed25519 secret key, in bytes. const EXPANDED_SECRET_KEY_KEY_LENGTH: usize = 32; -/// The length of the "nonce" portion of an "expanded" curve25519 EdDSA secret key, in bytes. +/// The length of the "nonce" portion of an "expanded" ed25519 secret key, in bytes. const EXPANDED_SECRET_KEY_NONCE_LENGTH: usize = 32; -/// The length of an "expanded" curve25519 EdDSA key, `ExpandedSecretKey`, in bytes. +/// The length of an "expanded" ed25519 key, `ExpandedSecretKey`, in bytes. pub const EXPANDED_SECRET_KEY_LENGTH: usize = EXPANDED_SECRET_KEY_KEY_LENGTH + EXPANDED_SECRET_KEY_NONCE_LENGTH; /// An EdDSA signature. From 4ee77b915ee42eaa47fa19619f8a2cf0b160c86b Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 30 Dec 2018 02:26:05 +0000 Subject: [PATCH 186/351] Avoid using deprecated import path for rand::rngs::OsRng. --- src/ed25519.rs | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 9f881be..d6d9553 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -1083,7 +1083,7 @@ impl Keypair { /// # fn main() { /// /// use rand::Rng; - /// use rand::OsRng; + /// use rand::rngs::OsRng; /// use ed25519_dalek::Keypair; /// use ed25519_dalek::Signature; /// diff --git a/src/lib.rs b/src/lib.rs index 6c92afb..90510d4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,7 +25,7 @@ //! # #[cfg(feature = "std")] //! # fn main() { //! use rand::Rng; -//! use rand::OsRng; +//! use rand::rngs::OsRng; //! use ed25519_dalek::Keypair; //! use ed25519_dalek::Signature; //! From e88da5ea85959b3477e4f8d83a04cd50af55e8c7 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 30 Dec 2018 01:59:24 +0000 Subject: [PATCH 187/351] Move integration tests to their own directory. --- src/ed25519.rs | 296 +---------------------------------------------- tests/ed25519.rs | 294 ++++++++++++++++++++++++++++++++++++++++++++++ tests/mod.rs | 17 +++ 3 files changed, 313 insertions(+), 294 deletions(-) create mode 100644 tests/ed25519.rs create mode 100644 tests/mod.rs diff --git a/src/ed25519.rs b/src/ed25519.rs index d6d9553..8622fcb 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -1347,234 +1347,8 @@ impl<'d> Deserialize<'d> for Keypair { #[cfg(test)] mod test { - use std::io::BufReader; - use std::io::BufRead; - use std::fs::File; - use std::string::String; - use std::vec::Vec; - use rand::thread_rng; - use rand::rngs::ThreadRng; - use hex::FromHex; - use sha2::Sha512; use super::*; - #[cfg(all(test, feature = "serde"))] - static PUBLIC_KEY_BYTES: [u8; PUBLIC_KEY_LENGTH] = [ - 130, 039, 155, 015, 062, 076, 188, 063, - 124, 122, 026, 251, 233, 253, 225, 220, - 014, 041, 166, 120, 108, 035, 254, 077, - 160, 083, 172, 058, 219, 042, 086, 120, ]; - - #[cfg(all(test, feature = "serde"))] - static SECRET_KEY: SecretKey = SecretKey([ - 062, 070, 027, 163, 092, 182, 011, 003, - 077, 234, 098, 004, 011, 127, 079, 228, - 243, 187, 150, 073, 201, 137, 076, 022, - 085, 251, 152, 002, 241, 042, 072, 054, ]); - - /// Signature with the above keypair of a blank message. - #[cfg(all(test, feature = "serde"))] - static SIGNATURE_BYTES: [u8; SIGNATURE_LENGTH] = [ - 010, 126, 151, 143, 157, 064, 047, 001, - 196, 140, 179, 058, 226, 152, 018, 102, - 160, 123, 080, 016, 210, 086, 196, 028, - 053, 231, 012, 157, 169, 019, 158, 063, - 045, 154, 238, 007, 053, 185, 227, 229, - 079, 108, 213, 080, 124, 252, 084, 167, - 216, 085, 134, 144, 129, 149, 041, 081, - 063, 120, 126, 100, 092, 059, 050, 011, ]; - - #[test] - fn sign_verify() { // TestSignVerify - let mut csprng: ThreadRng; - let keypair: Keypair; - let good_sig: Signature; - let bad_sig: Signature; - - let good: &[u8] = "test message".as_bytes(); - let bad: &[u8] = "wrong message".as_bytes(); - - csprng = thread_rng(); - keypair = Keypair::generate(&mut csprng); - good_sig = keypair.sign(&good); - bad_sig = keypair.sign(&bad); - - assert!(keypair.verify(&good, &good_sig).is_ok(), - "Verification of a valid signature failed!"); - assert!(keypair.verify(&good, &bad_sig).is_err(), - "Verification of a signature on a different message passed!"); - assert!(keypair.verify(&bad, &good_sig).is_err(), - "Verification of a signature on a different message passed!"); - } - - // TESTVECTORS is taken from sign.input.gz in agl's ed25519 Golang - // package. It is a selection of test cases from - // http://ed25519.cr.yp.to/python/sign.input - #[cfg(test)] - #[cfg(not(release))] - #[test] - fn golden() { // TestGolden - let mut line: String; - let mut lineno: usize = 0; - - let f = File::open("TESTVECTORS"); - if f.is_err() { - println!("This test is only available when the code has been cloned \ - from the git repository, since the TESTVECTORS file is large \ - and is therefore not included within the distributed crate."); - panic!(); - } - let file = BufReader::new(f.unwrap()); - - for l in file.lines() { - lineno += 1; - line = l.unwrap(); - - let parts: Vec<&str> = line.split(':').collect(); - assert_eq!(parts.len(), 5, "wrong number of fields in line {}", lineno); - - let sec_bytes: Vec = FromHex::from_hex(&parts[0]).unwrap(); - let pub_bytes: Vec = FromHex::from_hex(&parts[1]).unwrap(); - let msg_bytes: Vec = FromHex::from_hex(&parts[2]).unwrap(); - let sig_bytes: Vec = FromHex::from_hex(&parts[3]).unwrap(); - - let secret: SecretKey = SecretKey::from_bytes(&sec_bytes[..SECRET_KEY_LENGTH]).unwrap(); - let public: PublicKey = PublicKey::from_bytes(&pub_bytes[..PUBLIC_KEY_LENGTH]).unwrap(); - let keypair: Keypair = Keypair{ secret: secret, public: public }; - - // The signatures in the test vectors also include the message - // at the end, but we just want R and S. - let sig1: Signature = Signature::from_bytes(&sig_bytes[..64]).unwrap(); - let sig2: Signature = keypair.sign(&msg_bytes); - - assert!(sig1 == sig2, "Signature bytes not equal on line {}", lineno); - assert!(keypair.verify(&msg_bytes, &sig2).is_ok(), - "Signature verification failed on line {}", lineno); - } - } - - // From https://tools.ietf.org/html/rfc8032#section-7.3 - #[test] - fn ed25519ph_rf8032_test_vector() { - let secret_key: &[u8] = b"833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42"; - let public_key: &[u8] = b"ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf"; - let message: &[u8] = b"616263"; - let signature: &[u8] = b"98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406"; - - let sec_bytes: Vec = FromHex::from_hex(secret_key).unwrap(); - let pub_bytes: Vec = FromHex::from_hex(public_key).unwrap(); - let msg_bytes: Vec = FromHex::from_hex(message).unwrap(); - let sig_bytes: Vec = FromHex::from_hex(signature).unwrap(); - - let secret: SecretKey = SecretKey::from_bytes(&sec_bytes[..SECRET_KEY_LENGTH]).unwrap(); - let public: PublicKey = PublicKey::from_bytes(&pub_bytes[..PUBLIC_KEY_LENGTH]).unwrap(); - let keypair: Keypair = Keypair{ secret: secret, public: public }; - let sig1: Signature = Signature::from_bytes(&sig_bytes[..]).unwrap(); - - let mut prehash_for_signing: Sha512 = Sha512::default(); - let mut prehash_for_verifying: Sha512 = Sha512::default(); - - prehash_for_signing.input(&msg_bytes[..]); - prehash_for_verifying.input(&msg_bytes[..]); - - let sig2: Signature = keypair.sign_prehashed(prehash_for_signing, None); - - assert!(sig1 == sig2, - "Original signature from test vectors doesn't equal signature produced:\ - \noriginal:\n{:?}\nproduced:\n{:?}", sig1, sig2); - assert!(keypair.verify_prehashed(prehash_for_verifying, None, &sig2).is_ok(), - "Could not verify ed25519ph signature!"); - } - - #[test] - fn ed25519ph_sign_verify() { - let mut csprng: ThreadRng; - let keypair: Keypair; - let good_sig: Signature; - let bad_sig: Signature; - - let good: &[u8] = b"test message"; - let bad: &[u8] = b"wrong message"; - - // ugh… there's no `impl Copy for Sha512`… i hope we can all agree these are the same hashes - let mut prehashed_good1: Sha512 = Sha512::default(); - prehashed_good1.input(good); - let mut prehashed_good2: Sha512 = Sha512::default(); - prehashed_good2.input(good); - let mut prehashed_good3: Sha512 = Sha512::default(); - prehashed_good3.input(good); - - let mut prehashed_bad1: Sha512 = Sha512::default(); - prehashed_bad1.input(bad); - let mut prehashed_bad2: Sha512 = Sha512::default(); - prehashed_bad2.input(bad); - - let context: &[u8] = b"testing testing 1 2 3"; - - csprng = thread_rng(); - keypair = Keypair::generate(&mut csprng); - good_sig = keypair.sign_prehashed(prehashed_good1, Some(context)); - bad_sig = keypair.sign_prehashed(prehashed_bad1, Some(context)); - - assert!(keypair.verify_prehashed(prehashed_good2, Some(context), &good_sig).is_ok(), - "Verification of a valid signature failed!"); - assert!(keypair.verify_prehashed(prehashed_good3, Some(context), &bad_sig).is_err(), - "Verification of a signature on a different message passed!"); - assert!(keypair.verify_prehashed(prehashed_bad2, Some(context), &good_sig).is_err(), - "Verification of a signature on a different message passed!"); - } - - #[test] - fn verify_batch_seven_signatures() { - let messages: [&[u8]; 7] = [ - b"Watch closely everyone, I'm going to show you how to kill a god.", - b"I'm not a cryptographer I just encrypt a lot.", - b"Still not a cryptographer.", - b"This is a test of the tsunami alert system. This is only a test.", - b"Fuck dumbin' it down, spit ice, skip jewellery: Molotov cocktails on me like accessories.", - b"Hey, I never cared about your bucks, so if I run up with a mask on, probably got a gas can too.", - b"And I'm not here to fill 'er up. Nope, we came to riot, here to incite, we don't want any of your stuff.", ]; - let mut csprng: ThreadRng = thread_rng(); - let mut keypairs: Vec = Vec::new(); - let mut signatures: Vec = Vec::new(); - - for i in 0..messages.len() { - let keypair: Keypair = Keypair::generate(&mut csprng); - signatures.push(keypair.sign(&messages[i])); - keypairs.push(keypair); - } - let public_keys: Vec = keypairs.iter().map(|key| key.public).collect(); - - let result = verify_batch(&messages, &signatures[..], &public_keys[..]); - - assert!(result.is_ok()); - } - - #[test] - fn public_key_from_bytes() { - // Make another function so that we can test the ? operator. - fn do_the_test() -> Result { - let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = [ - 215, 090, 152, 001, 130, 177, 010, 183, - 213, 075, 254, 211, 201, 100, 007, 058, - 014, 225, 114, 243, 218, 166, 035, 037, - 175, 002, 026, 104, 247, 007, 081, 026, ]; - let public_key = PublicKey::from_bytes(&public_key_bytes)?; - - Ok(public_key) - } - assert_eq!(do_the_test(), Ok(PublicKey(CompressedEdwardsY([ - 215, 090, 152, 001, 130, 177, 010, 183, - 213, 075, 254, 211, 201, 100, 007, 058, - 014, 225, 114, 243, 218, 166, 035, 037, - 175, 002, 026, 104, 247, 007, 081, 026, ]), - CompressedEdwardsY([ - 215, 090, 152, 001, 130, 177, 010, 183, - 213, 075, 254, 211, 201, 100, 007, 058, - 014, 225, 114, 243, 218, 166, 035, 037, - 175, 002, 026, 104, 247, 007, 081, 026, ]).decompress().unwrap()))) - } - #[test] fn keypair_clear_on_drop() { let mut keypair: Keypair = Keypair::from_bytes(&[1u8; KEYPAIR_LENGTH][..]).unwrap(); @@ -1582,8 +1356,8 @@ mod test { keypair.clear(); fn as_bytes(x: &T) -> &[u8] { - use core::mem; - use core::slice; + use std::mem; + use std::slice; unsafe { slice::from_raw_parts(x as *const T as *const u8, mem::size_of_val(x)) @@ -1592,70 +1366,4 @@ mod test { assert!(!as_bytes(&keypair).contains(&0x15)); } - - #[test] - fn pubkey_from_secret_and_expanded_secret() { - let mut csprng = thread_rng(); - let secret: SecretKey = SecretKey::generate(&mut csprng); - let expanded_secret: ExpandedSecretKey = (&secret).into(); - let public_from_secret: PublicKey = (&secret).into(); // XXX eww - let public_from_expanded_secret: PublicKey = (&expanded_secret).into(); // XXX eww - - assert!(public_from_secret == public_from_expanded_secret); - } - - #[cfg(all(test, feature = "serde"))] - use bincode::{serialize, serialized_size, deserialize, Infinite}; - - #[cfg(all(test, feature = "serde"))] - #[test] - fn serialize_deserialize_signature() { - let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES).unwrap(); - let encoded_signature: Vec = serialize(&signature, Infinite).unwrap(); - let decoded_signature: Signature = deserialize(&encoded_signature).unwrap(); - - assert_eq!(signature, decoded_signature); - } - - #[cfg(all(test, feature = "serde"))] - #[test] - fn serialize_deserialize_public_key() { - let public_key: PublicKey = PublicKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap(); - let encoded_public_key: Vec = serialize(&public_key, Infinite).unwrap(); - let decoded_public_key: PublicKey = deserialize(&encoded_public_key).unwrap(); - - assert_eq!(&PUBLIC_KEY_BYTES[..], &encoded_public_key[encoded_public_key.len() - 32..]); - assert_eq!(public_key, decoded_public_key); - } - - #[cfg(all(test, feature = "serde"))] - #[test] - fn serialize_deserialize_secret_key() { - let encoded_secret_key: Vec = serialize(&SECRET_KEY, Infinite).unwrap(); - let decoded_secret_key: SecretKey = deserialize(&encoded_secret_key).unwrap(); - - for i in 0..32 { - assert_eq!(SECRET_KEY.0[i], decoded_secret_key.0[i]); - } - } - - #[cfg(all(test, feature = "serde"))] - #[test] - fn serialize_public_key_size() { - let public_key: PublicKey = PublicKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap(); - assert_eq!(serialized_size(&public_key) as usize, 40); // These sizes are specific to bincode==1.0.1 - } - - #[cfg(all(test, feature = "serde"))] - #[test] - fn serialize_signature_size() { - let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES).unwrap(); - assert_eq!(serialized_size(&signature) as usize, 72); // These sizes are specific to bincode==1.0.1 - } - - #[cfg(all(test, feature = "serde"))] - #[test] - fn serialize_secret_key_size() { - assert_eq!(serialized_size(&SECRET_KEY) as usize, 40); // These sizes are specific to bincode==1.0.1 - } } diff --git a/tests/ed25519.rs b/tests/ed25519.rs new file mode 100644 index 0000000..f0a63c7 --- /dev/null +++ b/tests/ed25519.rs @@ -0,0 +1,294 @@ +// -*- mode: rust; -*- +// +// This file is part of ed25519-dalek. +// Copyright (c) 2017-2018 isis lovecruft +// See LICENSE for licensing information. +// +// Authors: +// - isis agora lovecruft + +//! Integration tests for ed25519-dalek. + +extern crate clear_on_drop; +extern crate ed25519_dalek; +extern crate hex; +extern crate rand; +extern crate sha2; + +use std::io::BufReader; +use std::io::BufRead; +use std::fs::File; +use std::string::String; +use std::vec::Vec; + +use ed25519_dalek::*; + +use hex::FromHex; + +use rand::thread_rng; +use rand::rngs::ThreadRng; + +use sha2::Sha512; + +#[cfg(test)] +mod integrations { + use super::*; + + #[cfg(all(test, feature = "serde"))] + static PUBLIC_KEY_BYTES: [u8; PUBLIC_KEY_LENGTH] = [ + 130, 039, 155, 015, 062, 076, 188, 063, + 124, 122, 026, 251, 233, 253, 225, 220, + 014, 041, 166, 120, 108, 035, 254, 077, + 160, 083, 172, 058, 219, 042, 086, 120, ]; + + #[cfg(all(test, feature = "serde"))] + static SECRET_KEY: SecretKey = SecretKey([ + 062, 070, 027, 163, 092, 182, 011, 003, + 077, 234, 098, 004, 011, 127, 079, 228, + 243, 187, 150, 073, 201, 137, 076, 022, + 085, 251, 152, 002, 241, 042, 072, 054, ]); + + /// Signature with the above keypair of a blank message. + #[cfg(all(test, feature = "serde"))] + static SIGNATURE_BYTES: [u8; SIGNATURE_LENGTH] = [ + 010, 126, 151, 143, 157, 064, 047, 001, + 196, 140, 179, 058, 226, 152, 018, 102, + 160, 123, 080, 016, 210, 086, 196, 028, + 053, 231, 012, 157, 169, 019, 158, 063, + 045, 154, 238, 007, 053, 185, 227, 229, + 079, 108, 213, 080, 124, 252, 084, 167, + 216, 085, 134, 144, 129, 149, 041, 081, + 063, 120, 126, 100, 092, 059, 050, 011, ]; + + #[test] + fn sign_verify() { // TestSignVerify + let mut csprng: ThreadRng; + let keypair: Keypair; + let good_sig: Signature; + let bad_sig: Signature; + + let good: &[u8] = "test message".as_bytes(); + let bad: &[u8] = "wrong message".as_bytes(); + + csprng = thread_rng(); + keypair = Keypair::generate(&mut csprng); + good_sig = keypair.sign(&good); + bad_sig = keypair.sign(&bad); + + assert!(keypair.verify(&good, &good_sig).is_ok(), + "Verification of a valid signature failed!"); + assert!(keypair.verify(&good, &bad_sig).is_err(), + "Verification of a signature on a different message passed!"); + assert!(keypair.verify(&bad, &good_sig).is_err(), + "Verification of a signature on a different message passed!"); + } + + // TESTVECTORS is taken from sign.input.gz in agl's ed25519 Golang + // package. It is a selection of test cases from + // http://ed25519.cr.yp.to/python/sign.input + #[cfg(test)] + #[cfg(not(release))] + #[test] + fn golden() { // TestGolden + let mut line: String; + let mut lineno: usize = 0; + + let f = File::open("TESTVECTORS"); + if f.is_err() { + println!("This test is only available when the code has been cloned \ + from the git repository, since the TESTVECTORS file is large \ + and is therefore not included within the distributed crate."); + panic!(); + } + let file = BufReader::new(f.unwrap()); + + for l in file.lines() { + lineno += 1; + line = l.unwrap(); + + let parts: Vec<&str> = line.split(':').collect(); + assert_eq!(parts.len(), 5, "wrong number of fields in line {}", lineno); + + let sec_bytes: Vec = FromHex::from_hex(&parts[0]).unwrap(); + let pub_bytes: Vec = FromHex::from_hex(&parts[1]).unwrap(); + let msg_bytes: Vec = FromHex::from_hex(&parts[2]).unwrap(); + let sig_bytes: Vec = FromHex::from_hex(&parts[3]).unwrap(); + + let secret: SecretKey = SecretKey::from_bytes(&sec_bytes[..SECRET_KEY_LENGTH]).unwrap(); + let public: PublicKey = PublicKey::from_bytes(&pub_bytes[..PUBLIC_KEY_LENGTH]).unwrap(); + let keypair: Keypair = Keypair{ secret: secret, public: public }; + + // The signatures in the test vectors also include the message + // at the end, but we just want R and S. + let sig1: Signature = Signature::from_bytes(&sig_bytes[..64]).unwrap(); + let sig2: Signature = keypair.sign(&msg_bytes); + + assert!(sig1 == sig2, "Signature bytes not equal on line {}", lineno); + assert!(keypair.verify(&msg_bytes, &sig2).is_ok(), + "Signature verification failed on line {}", lineno); + } + } + + // From https://tools.ietf.org/html/rfc8032#section-7.3 + #[test] + fn ed25519ph_rf8032_test_vector() { + let secret_key: &[u8] = b"833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42"; + let public_key: &[u8] = b"ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf"; + let message: &[u8] = b"616263"; + let signature: &[u8] = b"98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406"; + + let sec_bytes: Vec = FromHex::from_hex(secret_key).unwrap(); + let pub_bytes: Vec = FromHex::from_hex(public_key).unwrap(); + let msg_bytes: Vec = FromHex::from_hex(message).unwrap(); + let sig_bytes: Vec = FromHex::from_hex(signature).unwrap(); + + let secret: SecretKey = SecretKey::from_bytes(&sec_bytes[..SECRET_KEY_LENGTH]).unwrap(); + let public: PublicKey = PublicKey::from_bytes(&pub_bytes[..PUBLIC_KEY_LENGTH]).unwrap(); + let keypair: Keypair = Keypair{ secret: secret, public: public }; + let sig1: Signature = Signature::from_bytes(&sig_bytes[..]).unwrap(); + + let mut prehash_for_signing: Sha512 = Sha512::default(); + let mut prehash_for_verifying: Sha512 = Sha512::default(); + + prehash_for_signing.input(&msg_bytes[..]); + prehash_for_verifying.input(&msg_bytes[..]); + + let sig2: Signature = keypair.sign_prehashed(prehash_for_signing, None); + + assert!(sig1 == sig2, + "Original signature from test vectors doesn't equal signature produced:\ + \noriginal:\n{:?}\nproduced:\n{:?}", sig1, sig2); + assert!(keypair.verify_prehashed(prehash_for_verifying, None, &sig2).is_ok(), + "Could not verify ed25519ph signature!"); + } + + #[test] + fn ed25519ph_sign_verify() { + let mut csprng: ThreadRng; + let keypair: Keypair; + let good_sig: Signature; + let bad_sig: Signature; + + let good: &[u8] = b"test message"; + let bad: &[u8] = b"wrong message"; + + // ugh… there's no `impl Copy for Sha512`… i hope we can all agree these are the same hashes + let mut prehashed_good1: Sha512 = Sha512::default(); + prehashed_good1.input(good); + let mut prehashed_good2: Sha512 = Sha512::default(); + prehashed_good2.input(good); + let mut prehashed_good3: Sha512 = Sha512::default(); + prehashed_good3.input(good); + + let mut prehashed_bad1: Sha512 = Sha512::default(); + prehashed_bad1.input(bad); + let mut prehashed_bad2: Sha512 = Sha512::default(); + prehashed_bad2.input(bad); + + let context: &[u8] = b"testing testing 1 2 3"; + + csprng = thread_rng(); + keypair = Keypair::generate(&mut csprng); + good_sig = keypair.sign_prehashed(prehashed_good1, Some(context)); + bad_sig = keypair.sign_prehashed(prehashed_bad1, Some(context)); + + assert!(keypair.verify_prehashed(prehashed_good2, Some(context), &good_sig).is_ok(), + "Verification of a valid signature failed!"); + assert!(keypair.verify_prehashed(prehashed_good3, Some(context), &bad_sig).is_err(), + "Verification of a signature on a different message passed!"); + assert!(keypair.verify_prehashed(prehashed_bad2, Some(context), &good_sig).is_err(), + "Verification of a signature on a different message passed!"); + } + + #[test] + fn verify_batch_seven_signatures() { + let messages: [&[u8]; 7] = [ + b"Watch closely everyone, I'm going to show you how to kill a god.", + b"I'm not a cryptographer I just encrypt a lot.", + b"Still not a cryptographer.", + b"This is a test of the tsunami alert system. This is only a test.", + b"Fuck dumbin' it down, spit ice, skip jewellery: Molotov cocktails on me like accessories.", + b"Hey, I never cared about your bucks, so if I run up with a mask on, probably got a gas can too.", + b"And I'm not here to fill 'er up. Nope, we came to riot, here to incite, we don't want any of your stuff.", ]; + let mut csprng: ThreadRng = thread_rng(); + let mut keypairs: Vec = Vec::new(); + let mut signatures: Vec = Vec::new(); + + for i in 0..messages.len() { + let keypair: Keypair = Keypair::generate(&mut csprng); + signatures.push(keypair.sign(&messages[i])); + keypairs.push(keypair); + } + let public_keys: Vec = keypairs.iter().map(|key| key.public).collect(); + + let result = verify_batch(&messages, &signatures[..], &public_keys[..]); + + assert!(result.is_ok()); + } + + #[test] + fn pubkey_from_secret_and_expanded_secret() { + let mut csprng = thread_rng(); + let secret: SecretKey = SecretKey::generate(&mut csprng); + let expanded_secret: ExpandedSecretKey = (&secret).into(); + let public_from_secret: PublicKey = (&secret).into(); // XXX eww + let public_from_expanded_secret: PublicKey = (&expanded_secret).into(); // XXX eww + + assert!(public_from_secret == public_from_expanded_secret); + } + + #[cfg(all(test, feature = "serde"))] + use bincode::{serialize, serialized_size, deserialize, Infinite}; + + #[cfg(all(test, feature = "serde"))] + #[test] + fn serialize_deserialize_signature() { + let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES).unwrap(); + let encoded_signature: Vec = serialize(&signature, Infinite).unwrap(); + let decoded_signature: Signature = deserialize(&encoded_signature).unwrap(); + + assert_eq!(signature, decoded_signature); + } + + #[cfg(all(test, feature = "serde"))] + #[test] + fn serialize_deserialize_public_key() { + let public_key: PublicKey = PublicKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap(); + let encoded_public_key: Vec = serialize(&public_key, Infinite).unwrap(); + let decoded_public_key: PublicKey = deserialize(&encoded_public_key).unwrap(); + + assert_eq!(&PUBLIC_KEY_BYTES[..], &encoded_public_key[encoded_public_key.len() - 32..]); + assert_eq!(public_key, decoded_public_key); + } + + #[cfg(all(test, feature = "serde"))] + #[test] + fn serialize_deserialize_secret_key() { + let encoded_secret_key: Vec = serialize(&SECRET_KEY, Infinite).unwrap(); + let decoded_secret_key: SecretKey = deserialize(&encoded_secret_key).unwrap(); + + for i in 0..32 { + assert_eq!(SECRET_KEY.0[i], decoded_secret_key.0[i]); + } + } + + #[cfg(all(test, feature = "serde"))] + #[test] + fn serialize_public_key_size() { + let public_key: PublicKey = PublicKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap(); + assert_eq!(serialized_size(&public_key) as usize, 40); // These sizes are specific to bincode==1.0.1 + } + + #[cfg(all(test, feature = "serde"))] + #[test] + fn serialize_signature_size() { + let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES).unwrap(); + assert_eq!(serialized_size(&signature) as usize, 72); // These sizes are specific to bincode==1.0.1 + } + + #[cfg(all(test, feature = "serde"))] + #[test] + fn serialize_secret_key_size() { + assert_eq!(serialized_size(&SECRET_KEY) as usize, 40); // These sizes are specific to bincode==1.0.1 + } +} diff --git a/tests/mod.rs b/tests/mod.rs new file mode 100644 index 0000000..8b3a9bb --- /dev/null +++ b/tests/mod.rs @@ -0,0 +1,17 @@ +// -*- mode: rust; -*- +// +// This file is part of ed25519-dalek. +// Copyright (c) 2017-2018 isis lovecruft +// See LICENSE for licensing information. +// +// Authors: +// - isis agora lovecruft + +//! Integration tests for ed25519-dalek. + +extern crate ed25519_dalek; +extern crate hex; +extern crate rand; +extern crate sha2; + +mod ed25519; From eb8ab9f06bd536ffdb67c9f17896a97d3ecccf7e Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 30 Dec 2018 02:17:56 +0000 Subject: [PATCH 188/351] Organise integration tests into modules. --- tests/ed25519.rs | 140 +++++++++++++++++++++++------------------------ 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/tests/ed25519.rs b/tests/ed25519.rs index f0a63c7..ff89b90 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -9,18 +9,14 @@ //! Integration tests for ed25519-dalek. +#[cfg(all(test, feature = "serde"))] +extern crate bincode; extern crate clear_on_drop; extern crate ed25519_dalek; extern crate hex; extern crate rand; extern crate sha2; -use std::io::BufReader; -use std::io::BufRead; -use std::fs::File; -use std::string::String; -use std::vec::Vec; - use ed25519_dalek::*; use hex::FromHex; @@ -31,65 +27,18 @@ use rand::rngs::ThreadRng; use sha2::Sha512; #[cfg(test)] -mod integrations { +mod vectors { + use std::io::BufReader; + use std::io::BufRead; + use std::fs::File; + use super::*; - #[cfg(all(test, feature = "serde"))] - static PUBLIC_KEY_BYTES: [u8; PUBLIC_KEY_LENGTH] = [ - 130, 039, 155, 015, 062, 076, 188, 063, - 124, 122, 026, 251, 233, 253, 225, 220, - 014, 041, 166, 120, 108, 035, 254, 077, - 160, 083, 172, 058, 219, 042, 086, 120, ]; - - #[cfg(all(test, feature = "serde"))] - static SECRET_KEY: SecretKey = SecretKey([ - 062, 070, 027, 163, 092, 182, 011, 003, - 077, 234, 098, 004, 011, 127, 079, 228, - 243, 187, 150, 073, 201, 137, 076, 022, - 085, 251, 152, 002, 241, 042, 072, 054, ]); - - /// Signature with the above keypair of a blank message. - #[cfg(all(test, feature = "serde"))] - static SIGNATURE_BYTES: [u8; SIGNATURE_LENGTH] = [ - 010, 126, 151, 143, 157, 064, 047, 001, - 196, 140, 179, 058, 226, 152, 018, 102, - 160, 123, 080, 016, 210, 086, 196, 028, - 053, 231, 012, 157, 169, 019, 158, 063, - 045, 154, 238, 007, 053, 185, 227, 229, - 079, 108, 213, 080, 124, 252, 084, 167, - 216, 085, 134, 144, 129, 149, 041, 081, - 063, 120, 126, 100, 092, 059, 050, 011, ]; - - #[test] - fn sign_verify() { // TestSignVerify - let mut csprng: ThreadRng; - let keypair: Keypair; - let good_sig: Signature; - let bad_sig: Signature; - - let good: &[u8] = "test message".as_bytes(); - let bad: &[u8] = "wrong message".as_bytes(); - - csprng = thread_rng(); - keypair = Keypair::generate(&mut csprng); - good_sig = keypair.sign(&good); - bad_sig = keypair.sign(&bad); - - assert!(keypair.verify(&good, &good_sig).is_ok(), - "Verification of a valid signature failed!"); - assert!(keypair.verify(&good, &bad_sig).is_err(), - "Verification of a signature on a different message passed!"); - assert!(keypair.verify(&bad, &good_sig).is_err(), - "Verification of a signature on a different message passed!"); - } - // TESTVECTORS is taken from sign.input.gz in agl's ed25519 Golang // package. It is a selection of test cases from // http://ed25519.cr.yp.to/python/sign.input - #[cfg(test)] - #[cfg(not(release))] #[test] - fn golden() { // TestGolden + fn against_reference_implementation() { // TestGolden let mut line: String; let mut lineno: usize = 0; @@ -161,6 +110,34 @@ mod integrations { assert!(keypair.verify_prehashed(prehash_for_verifying, None, &sig2).is_ok(), "Could not verify ed25519ph signature!"); } +} + +#[cfg(test)] +mod integrations { + use super::*; + + #[test] + fn sign_verify() { // TestSignVerify + let mut csprng: ThreadRng; + let keypair: Keypair; + let good_sig: Signature; + let bad_sig: Signature; + + let good: &[u8] = "test message".as_bytes(); + let bad: &[u8] = "wrong message".as_bytes(); + + csprng = thread_rng(); + keypair = Keypair::generate(&mut csprng); + good_sig = keypair.sign(&good); + bad_sig = keypair.sign(&bad); + + assert!(keypair.verify(&good, &good_sig).is_ok(), + "Verification of a valid signature failed!"); + assert!(keypair.verify(&good, &bad_sig).is_err(), + "Verification of a signature on a different message passed!"); + assert!(keypair.verify(&bad, &good_sig).is_err(), + "Verification of a signature on a different message passed!"); + } #[test] fn ed25519ph_sign_verify() { @@ -236,11 +213,37 @@ mod integrations { assert!(public_from_secret == public_from_expanded_secret); } +} - #[cfg(all(test, feature = "serde"))] - use bincode::{serialize, serialized_size, deserialize, Infinite}; +#[cfg(all(test, feature = "serde"))] +mod serialisation { + use super::*; + + use self::bincode::{serialize, serialized_size, deserialize, Infinite}; + + static PUBLIC_KEY_BYTES: [u8; PUBLIC_KEY_LENGTH] = [ + 130, 039, 155, 015, 062, 076, 188, 063, + 124, 122, 026, 251, 233, 253, 225, 220, + 014, 041, 166, 120, 108, 035, 254, 077, + 160, 083, 172, 058, 219, 042, 086, 120, ]; + + static SECRET_KEY_BYTES: [u8; SECRET_KEY_LENGTH] = [ + 062, 070, 027, 163, 092, 182, 011, 003, + 077, 234, 098, 004, 011, 127, 079, 228, + 243, 187, 150, 073, 201, 137, 076, 022, + 085, 251, 152, 002, 241, 042, 072, 054, ]; + + /// Signature with the above keypair of a blank message. + static SIGNATURE_BYTES: [u8; SIGNATURE_LENGTH] = [ + 010, 126, 151, 143, 157, 064, 047, 001, + 196, 140, 179, 058, 226, 152, 018, 102, + 160, 123, 080, 016, 210, 086, 196, 028, + 053, 231, 012, 157, 169, 019, 158, 063, + 045, 154, 238, 007, 053, 185, 227, 229, + 079, 108, 213, 080, 124, 252, 084, 167, + 216, 085, 134, 144, 129, 149, 041, 081, + 063, 120, 126, 100, 092, 059, 050, 011, ]; - #[cfg(all(test, feature = "serde"))] #[test] fn serialize_deserialize_signature() { let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES).unwrap(); @@ -250,7 +253,6 @@ mod integrations { assert_eq!(signature, decoded_signature); } - #[cfg(all(test, feature = "serde"))] #[test] fn serialize_deserialize_public_key() { let public_key: PublicKey = PublicKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap(); @@ -261,34 +263,32 @@ mod integrations { assert_eq!(public_key, decoded_public_key); } - #[cfg(all(test, feature = "serde"))] #[test] fn serialize_deserialize_secret_key() { - let encoded_secret_key: Vec = serialize(&SECRET_KEY, Infinite).unwrap(); + let secret_key: SecretKey = SecretKey::from_bytes(&SECRET_KEY_BYTES).unwrap(); + let encoded_secret_key: Vec = serialize(&secret_key, Infinite).unwrap(); let decoded_secret_key: SecretKey = deserialize(&encoded_secret_key).unwrap(); for i in 0..32 { - assert_eq!(SECRET_KEY.0[i], decoded_secret_key.0[i]); + assert_eq!(SECRET_KEY_BYTES[i], decoded_secret_key.as_bytes()[i]); } } - #[cfg(all(test, feature = "serde"))] #[test] fn serialize_public_key_size() { let public_key: PublicKey = PublicKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap(); assert_eq!(serialized_size(&public_key) as usize, 40); // These sizes are specific to bincode==1.0.1 } - #[cfg(all(test, feature = "serde"))] #[test] fn serialize_signature_size() { let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES).unwrap(); assert_eq!(serialized_size(&signature) as usize, 72); // These sizes are specific to bincode==1.0.1 } - #[cfg(all(test, feature = "serde"))] #[test] fn serialize_secret_key_size() { - assert_eq!(serialized_size(&SECRET_KEY) as usize, 40); // These sizes are specific to bincode==1.0.1 + let secret_key: SecretKey = SecretKey::from_bytes(&SECRET_KEY_BYTES).unwrap(); + assert_eq!(serialized_size(&secret_key) as usize, 40); // These sizes are specific to bincode==1.0.1 } } From 80e72db67765509be7f74464868c6b4b4a11b5c0 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 30 Dec 2018 02:36:49 +0000 Subject: [PATCH 189/351] Create new module for constants. --- src/constants.rs | 31 +++++++++++++++++++++++++++++++ src/ed25519.rs | 27 ++++----------------------- src/lib.rs | 1 + 3 files changed, 36 insertions(+), 23 deletions(-) create mode 100644 src/constants.rs diff --git a/src/constants.rs b/src/constants.rs new file mode 100644 index 0000000..783ffb2 --- /dev/null +++ b/src/constants.rs @@ -0,0 +1,31 @@ +// -*- mode: rust; -*- +// +// This file is part of ed25519-dalek. +// Copyright (c) 2017-2018 isis lovecruft +// See LICENSE for licensing information. +// +// Authors: +// - isis agora lovecruft + +//! Common constants such as buffer sizes for keypairs and signatures. + +/// The length of a ed25519 `Signature`, in bytes. +pub const SIGNATURE_LENGTH: usize = 64; + +/// The length of a ed25519 `SecretKey`, in bytes. +pub const SECRET_KEY_LENGTH: usize = 32; + +/// The length of an ed25519 `PublicKey`, in bytes. +pub const PUBLIC_KEY_LENGTH: usize = 32; + +/// The length of an ed25519 `Keypair`, in bytes. +pub const KEYPAIR_LENGTH: usize = SECRET_KEY_LENGTH + PUBLIC_KEY_LENGTH; + +/// The length of the "key" portion of an "expanded" ed25519 secret key, in bytes. +const EXPANDED_SECRET_KEY_KEY_LENGTH: usize = 32; + +/// The length of the "nonce" portion of an "expanded" ed25519 secret key, in bytes. +const EXPANDED_SECRET_KEY_NONCE_LENGTH: usize = 32; + +/// The length of an "expanded" ed25519 key, `ExpandedSecretKey`, in bytes. +pub const EXPANDED_SECRET_KEY_LENGTH: usize = EXPANDED_SECRET_KEY_KEY_LENGTH + EXPANDED_SECRET_KEY_NONCE_LENGTH; diff --git a/src/ed25519.rs b/src/ed25519.rs index 8622fcb..a4d82fc 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -1,11 +1,11 @@ // -*- mode: rust; -*- // // This file is part of ed25519-dalek. -// Copyright (c) 2017-2018 Isis Lovecruft +// Copyright (c) 2017-2018 isis lovecruft // See LICENSE for licensing information. // // Authors: -// - Isis Agora Lovecruft +// - isis agora lovecruft //! A Rust implementation of ed25519 key generation, signing, and verification. @@ -36,30 +36,11 @@ use curve25519_dalek::edwards::CompressedEdwardsY; use curve25519_dalek::edwards::EdwardsPoint; use curve25519_dalek::scalar::Scalar; +pub use constants::*; + use errors::SignatureError; use errors::InternalError; -/// The length of a ed25519 `Signature`, in bytes. -pub const SIGNATURE_LENGTH: usize = 64; - -/// The length of a ed25519 `SecretKey`, in bytes. -pub const SECRET_KEY_LENGTH: usize = 32; - -/// The length of an ed25519 `PublicKey`, in bytes. -pub const PUBLIC_KEY_LENGTH: usize = 32; - -/// The length of an ed25519 `Keypair`, in bytes. -pub const KEYPAIR_LENGTH: usize = SECRET_KEY_LENGTH + PUBLIC_KEY_LENGTH; - -/// The length of the "key" portion of an "expanded" ed25519 secret key, in bytes. -const EXPANDED_SECRET_KEY_KEY_LENGTH: usize = 32; - -/// The length of the "nonce" portion of an "expanded" ed25519 secret key, in bytes. -const EXPANDED_SECRET_KEY_NONCE_LENGTH: usize = 32; - -/// The length of an "expanded" ed25519 key, `ExpandedSecretKey`, in bytes. -pub const EXPANDED_SECRET_KEY_LENGTH: usize = EXPANDED_SECRET_KEY_KEY_LENGTH + EXPANDED_SECRET_KEY_NONCE_LENGTH; - /// An EdDSA signature. /// /// # Note diff --git a/src/lib.rs b/src/lib.rs index 90510d4..72df455 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -261,6 +261,7 @@ extern crate serde; #[cfg(all(test, feature = "serde"))] extern crate bincode; +mod constants; mod ed25519; pub mod errors; From d748a41894758bf7a4a44b42c26c6530613381c3 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 30 Dec 2018 02:43:01 +0000 Subject: [PATCH 190/351] Create new module for Signature type. --- src/ed25519.rs | 109 ++------------------------------------- src/lib.rs | 1 + src/signature.rs | 129 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 106 deletions(-) create mode 100644 src/signature.rs diff --git a/src/ed25519.rs b/src/ed25519.rs index a4d82fc..9f000c7 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -10,7 +10,7 @@ //! A Rust implementation of ed25519 key generation, signing, and verification. use core::default::Default; -use core::fmt::{Debug}; +use core::fmt::Debug; use rand::CryptoRng; use rand::Rng; @@ -37,111 +37,8 @@ use curve25519_dalek::edwards::EdwardsPoint; use curve25519_dalek::scalar::Scalar; pub use constants::*; - -use errors::SignatureError; -use errors::InternalError; - -/// An EdDSA signature. -/// -/// # Note -/// -/// These signatures, unlike the ed25519 signature reference implementation, are -/// "detached"—that is, they do **not** include a copy of the message which has -/// been signed. -#[allow(non_snake_case)] -#[derive(Copy, Eq, PartialEq)] -pub struct Signature { - /// `R` is an `EdwardsPoint`, formed by using an hash function with - /// 512-bits output to produce the digest of: - /// - /// - the nonce half of the `ExpandedSecretKey`, and - /// - the message to be signed. - /// - /// This digest is then interpreted as a `Scalar` and reduced into an - /// element in ℤ/lℤ. The scalar is then multiplied by the distinguished - /// basepoint to produce `R`, and `EdwardsPoint`. - pub (crate) R: CompressedEdwardsY, - - /// `s` is a `Scalar`, formed by using an hash function with 512-bits output - /// to produce the digest of: - /// - /// - the `r` portion of this `Signature`, - /// - the `PublicKey` which should be used to verify this `Signature`, and - /// - the message to be signed. - /// - /// This digest is then interpreted as a `Scalar` and reduced into an - /// element in ℤ/lℤ. - pub (crate) s: Scalar, -} - -impl Clone for Signature { - fn clone(&self) -> Self { *self } -} - -impl Debug for Signature { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "Signature( R: {:?}, s: {:?} )", &self.R, &self.s) - } -} - -impl Signature { - /// Convert this `Signature` to a byte array. - #[inline] - pub fn to_bytes(&self) -> [u8; SIGNATURE_LENGTH] { - let mut signature_bytes: [u8; SIGNATURE_LENGTH] = [0u8; SIGNATURE_LENGTH]; - - signature_bytes[..32].copy_from_slice(&self.R.as_bytes()[..]); - signature_bytes[32..].copy_from_slice(&self.s.as_bytes()[..]); - signature_bytes - } - - /// Construct a `Signature` from a slice of bytes. - #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != SIGNATURE_LENGTH { - return Err(SignatureError(InternalError::BytesLengthError{ - name: "Signature", length: SIGNATURE_LENGTH })); - } - let mut lower: [u8; 32] = [0u8; 32]; - let mut upper: [u8; 32] = [0u8; 32]; - - lower.copy_from_slice(&bytes[..32]); - upper.copy_from_slice(&bytes[32..]); - - if upper[31] & 224 != 0 { - return Err(SignatureError(InternalError::ScalarFormatError)); - } - - Ok(Signature{ R: CompressedEdwardsY(lower), s: Scalar::from_bits(upper) }) - } -} - -#[cfg(feature = "serde")] -impl Serialize for Signature { - fn serialize(&self, serializer: S) -> Result where S: Serializer { - serializer.serialize_bytes(&self.to_bytes()[..]) - } -} - -#[cfg(feature = "serde")] -impl<'d> Deserialize<'d> for Signature { - fn deserialize(deserializer: D) -> Result where D: Deserializer<'d> { - struct SignatureVisitor; - - impl<'d> Visitor<'d> for SignatureVisitor { - type Value = Signature; - - fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - formatter.write_str("An ed25519 signature as 64 bytes, as specified in RFC8032.") - } - - fn visit_bytes(self, bytes: &[u8]) -> Result where E: SerdeError{ - Signature::from_bytes(bytes).or(Err(SerdeError::invalid_length(bytes.len(), &self))) - } - } - deserializer.deserialize_bytes(SignatureVisitor) - } -} +pub use errors::*; +pub use signature::*; /// An EdDSA secret key. #[derive(Default)] // we derive Default in order to use the clear() method in Drop diff --git a/src/lib.rs b/src/lib.rs index 72df455..521b34f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -263,6 +263,7 @@ extern crate bincode; mod constants; mod ed25519; +mod signature; pub mod errors; diff --git a/src/signature.rs b/src/signature.rs new file mode 100644 index 0000000..f2f2316 --- /dev/null +++ b/src/signature.rs @@ -0,0 +1,129 @@ +// -*- mode: rust; -*- +// +// This file is part of ed25519-dalek. +// Copyright (c) 2017-2018 isis lovecruft +// See LICENSE for licensing information. +// +// Authors: +// - isis agora lovecruft + +//! An ed25519 signature. + +use core::fmt::Debug; + +use curve25519_dalek::edwards::CompressedEdwardsY; +use curve25519_dalek::scalar::Scalar; + +#[cfg(feature = "serde")] +use serde::{Serialize, Deserialize}; +#[cfg(feature = "serde")] +use serde::{Serializer, Deserializer}; +#[cfg(feature = "serde")] +use serde::de::Error as SerdeError; +#[cfg(feature = "serde")] +use serde::de::Visitor; + +use constants::*; +use errors::*; + +/// An ed25519 signature. +/// +/// # Note +/// +/// These signatures, unlike the ed25519 signature reference implementation, are +/// "detached"—that is, they do **not** include a copy of the message which has +/// been signed. +#[allow(non_snake_case)] +#[derive(Copy, Eq, PartialEq)] +pub struct Signature { + /// `R` is an `EdwardsPoint`, formed by using an hash function with + /// 512-bits output to produce the digest of: + /// + /// - the nonce half of the `ExpandedSecretKey`, and + /// - the message to be signed. + /// + /// This digest is then interpreted as a `Scalar` and reduced into an + /// element in ℤ/lℤ. The scalar is then multiplied by the distinguished + /// basepoint to produce `R`, and `EdwardsPoint`. + pub (crate) R: CompressedEdwardsY, + + /// `s` is a `Scalar`, formed by using an hash function with 512-bits output + /// to produce the digest of: + /// + /// - the `r` portion of this `Signature`, + /// - the `PublicKey` which should be used to verify this `Signature`, and + /// - the message to be signed. + /// + /// This digest is then interpreted as a `Scalar` and reduced into an + /// element in ℤ/lℤ. + pub (crate) s: Scalar, +} + +impl Clone for Signature { + fn clone(&self) -> Self { *self } +} + +impl Debug for Signature { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "Signature( R: {:?}, s: {:?} )", &self.R, &self.s) + } +} + +impl Signature { + /// Convert this `Signature` to a byte array. + #[inline] + pub fn to_bytes(&self) -> [u8; SIGNATURE_LENGTH] { + let mut signature_bytes: [u8; SIGNATURE_LENGTH] = [0u8; SIGNATURE_LENGTH]; + + signature_bytes[..32].copy_from_slice(&self.R.as_bytes()[..]); + signature_bytes[32..].copy_from_slice(&self.s.as_bytes()[..]); + signature_bytes + } + + /// Construct a `Signature` from a slice of bytes. + #[inline] + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != SIGNATURE_LENGTH { + return Err(SignatureError(InternalError::BytesLengthError{ + name: "Signature", length: SIGNATURE_LENGTH })); + } + let mut lower: [u8; 32] = [0u8; 32]; + let mut upper: [u8; 32] = [0u8; 32]; + + lower.copy_from_slice(&bytes[..32]); + upper.copy_from_slice(&bytes[32..]); + + if upper[31] & 224 != 0 { + return Err(SignatureError(InternalError::ScalarFormatError)); + } + + Ok(Signature{ R: CompressedEdwardsY(lower), s: Scalar::from_bits(upper) }) + } +} + +#[cfg(feature = "serde")] +impl Serialize for Signature { + fn serialize(&self, serializer: S) -> Result where S: Serializer { + serializer.serialize_bytes(&self.to_bytes()[..]) + } +} + +#[cfg(feature = "serde")] +impl<'d> Deserialize<'d> for Signature { + fn deserialize(deserializer: D) -> Result where D: Deserializer<'d> { + struct SignatureVisitor; + + impl<'d> Visitor<'d> for SignatureVisitor { + type Value = Signature; + + fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + formatter.write_str("An ed25519 signature as 64 bytes, as specified in RFC8032.") + } + + fn visit_bytes(self, bytes: &[u8]) -> Result where E: SerdeError{ + Signature::from_bytes(bytes).or(Err(SerdeError::invalid_length(bytes.len(), &self))) + } + } + deserializer.deserialize_bytes(SignatureVisitor) + } +} From ce857a50e79a2587db0ca981bce1192d720f5705 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 30 Dec 2018 03:11:28 +0000 Subject: [PATCH 191/351] Remove unnecessary #![allow(unused_features)] lint. --- src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 521b34f..dff7e27 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -238,7 +238,6 @@ //! ``` #![no_std] -#![allow(unused_features)] #![deny(missing_docs)] // refuse to compile if documentation is missing extern crate clear_on_drop; From 1cf581d67d0b33b731316c930b7b33890faf9d59 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 30 Dec 2018 03:13:13 +0000 Subject: [PATCH 192/351] Add lints (and fix warnings) for Rust 2018 code. --- src/ed25519.rs | 18 +++++++++--------- src/errors.rs | 6 +++--- src/lib.rs | 12 ++++-------- src/signature.rs | 8 ++++---- 4 files changed, 20 insertions(+), 24 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 9f000c7..6938374 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -36,16 +36,16 @@ use curve25519_dalek::edwards::CompressedEdwardsY; use curve25519_dalek::edwards::EdwardsPoint; use curve25519_dalek::scalar::Scalar; -pub use constants::*; -pub use errors::*; -pub use signature::*; +pub use crate::constants::*; +pub use crate::errors::*; +pub use crate::signature::*; /// An EdDSA secret key. #[derive(Default)] // we derive Default in order to use the clear() method in Drop pub struct SecretKey(pub (crate) [u8; SECRET_KEY_LENGTH]); impl Debug for SecretKey { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { write!(f, "SecretKey: {:?}", &self.0[..]) } } @@ -198,7 +198,7 @@ impl<'d> Deserialize<'d> for SecretKey { impl<'d> Visitor<'d> for SecretKeyVisitor { type Value = SecretKey; - fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { formatter.write_str("An ed25519 secret key as 32 bytes, as specified in RFC8032.") } @@ -528,7 +528,7 @@ impl<'d> Deserialize<'d> for ExpandedSecretKey { impl<'d> Visitor<'d> for ExpandedSecretKeyVisitor { type Value = ExpandedSecretKey; - fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { formatter.write_str("An ed25519 expanded secret key as 64 bytes, as specified in RFC8032.") } @@ -548,7 +548,7 @@ pub struct PublicKey( ); impl Debug for PublicKey { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { write!(f, "PublicKey({:?}), {:?})", self.0, self.1) } } @@ -880,7 +880,7 @@ impl<'d> Deserialize<'d> for PublicKey { impl<'d> Visitor<'d> for PublicKeyVisitor { type Value = PublicKey; - fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { formatter.write_str("An ed25519 public key as a 32-byte compressed point, as specified in RFC8032") } @@ -1202,7 +1202,7 @@ impl<'d> Deserialize<'d> for Keypair { impl<'d> Visitor<'d> for KeypairVisitor { type Value = Keypair; - fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { formatter.write_str("An ed25519 keypair, 64 bytes in total where the secret key is \ the first 32 bytes and is in unexpanded form, and the second \ 32 bytes is a compressed point for a public key.") diff --git a/src/errors.rs b/src/errors.rs index bf568a6..f531849 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -33,7 +33,7 @@ pub (crate) enum InternalError { } impl Display for InternalError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { InternalError::PointDecompressionError => write!(f, "Cannot decompress Edwards point"), @@ -67,13 +67,13 @@ impl ::failure::Fail for InternalError {} pub struct SignatureError(pub (crate) InternalError); impl Display for SignatureError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } impl ::failure::Fail for SignatureError { - fn cause(&self) -> Option<&::failure::Fail> { + fn cause(&self) -> Option<&dyn (::failure::Fail)> { Some(&self.0) } } diff --git a/src/lib.rs b/src/lib.rs index dff7e27..462ae40 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -238,6 +238,9 @@ //! ``` #![no_std] +#![warn(future_incompatible)] +#![warn(rust_2018_compatibility)] +#![warn(rust_2018_idioms)] #![deny(missing_docs)] // refuse to compile if documentation is missing extern crate clear_on_drop; @@ -251,15 +254,9 @@ extern crate std; extern crate sha2; -#[cfg(test)] -extern crate hex; - #[cfg(feature = "serde")] extern crate serde; -#[cfg(all(test, feature = "serde"))] -extern crate bincode; - mod constants; mod ed25519; mod signature; @@ -267,5 +264,4 @@ mod signature; pub mod errors; // Export everything public in ed25519. -pub use ed25519::*; -pub use errors::*; +pub use crate::ed25519::*; diff --git a/src/signature.rs b/src/signature.rs index f2f2316..d93b572 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -23,8 +23,8 @@ use serde::de::Error as SerdeError; #[cfg(feature = "serde")] use serde::de::Visitor; -use constants::*; -use errors::*; +use crate::constants::*; +use crate::errors::*; /// An ed25519 signature. /// @@ -64,7 +64,7 @@ impl Clone for Signature { } impl Debug for Signature { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { write!(f, "Signature( R: {:?}, s: {:?} )", &self.R, &self.s) } } @@ -116,7 +116,7 @@ impl<'d> Deserialize<'d> for Signature { impl<'d> Visitor<'d> for SignatureVisitor { type Value = Signature; - fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { formatter.write_str("An ed25519 signature as 64 bytes, as specified in RFC8032.") } From f6ec28c077e73e8b16989483025984636eb3c38c Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 30 Dec 2018 03:40:32 +0000 Subject: [PATCH 193/351] Create module for secret key types. --- src/ed25519.rs | 505 +-------------------------------------------- src/lib.rs | 1 + src/secret.rs | 540 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 544 insertions(+), 502 deletions(-) create mode 100644 src/secret.rs diff --git a/src/ed25519.rs b/src/ed25519.rs index 6938374..e992c17 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -26,8 +26,6 @@ use serde::de::Visitor; pub use sha2::Sha512; -use clear_on_drop::clear::Clear; - pub use curve25519_dalek::digest::Digest; use curve25519_dalek::digest::generic_array::typenum::U64; @@ -38,508 +36,9 @@ use curve25519_dalek::scalar::Scalar; pub use crate::constants::*; pub use crate::errors::*; +pub use crate::secret::*; pub use crate::signature::*; -/// An EdDSA secret key. -#[derive(Default)] // we derive Default in order to use the clear() method in Drop -pub struct SecretKey(pub (crate) [u8; SECRET_KEY_LENGTH]); - -impl Debug for SecretKey { - fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - write!(f, "SecretKey: {:?}", &self.0[..]) - } -} - -/// Overwrite secret key material with null bytes when it goes out of scope. -impl Drop for SecretKey { - fn drop(&mut self) { - self.0.clear(); - } -} - -impl AsRef<[u8]> for SecretKey { - fn as_ref(&self) -> &[u8] { - self.as_bytes() - } -} - -impl SecretKey { - /// Convert this secret key to a byte array. - #[inline] - pub fn to_bytes(&self) -> [u8; SECRET_KEY_LENGTH] { - self.0 - } - - /// View this secret key as a byte array. - #[inline] - pub fn as_bytes<'a>(&'a self) -> &'a [u8; SECRET_KEY_LENGTH] { - &self.0 - } - - /// Construct a `SecretKey` from a slice of bytes. - /// - /// # Example - /// - /// ``` - /// # extern crate ed25519_dalek; - /// # - /// use ed25519_dalek::SecretKey; - /// use ed25519_dalek::SECRET_KEY_LENGTH; - /// use ed25519_dalek::SignatureError; - /// - /// # fn doctest() -> Result { - /// let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = [ - /// 157, 097, 177, 157, 239, 253, 090, 096, - /// 186, 132, 074, 244, 146, 236, 044, 196, - /// 068, 073, 197, 105, 123, 050, 105, 025, - /// 112, 059, 172, 003, 028, 174, 127, 096, ]; - /// - /// let secret_key: SecretKey = SecretKey::from_bytes(&secret_key_bytes)?; - /// # - /// # Ok(secret_key) - /// # } - /// # - /// # fn main() { - /// # let result = doctest(); - /// # assert!(result.is_ok()); - /// # } - /// ``` - /// - /// # Returns - /// - /// A `Result` whose okay value is an EdDSA `SecretKey` or whose error value - /// is an `SignatureError` wrapping the internal error that occurred. - #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != SECRET_KEY_LENGTH { - return Err(SignatureError(InternalError::BytesLengthError{ - name: "SecretKey", length: SECRET_KEY_LENGTH })); - } - let mut bits: [u8; 32] = [0u8; 32]; - bits.copy_from_slice(&bytes[..32]); - - Ok(SecretKey(bits)) - } - - /// Generate a `SecretKey` from a `csprng`. - /// - /// # Example - /// - /// ``` - /// extern crate rand; - /// extern crate sha2; - /// extern crate ed25519_dalek; - /// - /// # #[cfg(feature = "std")] - /// # fn main() { - /// # - /// use rand::Rng; - /// use rand::rngs::OsRng; - /// use sha2::Sha512; - /// use ed25519_dalek::PublicKey; - /// use ed25519_dalek::SecretKey; - /// use ed25519_dalek::Signature; - /// - /// let mut csprng: OsRng = OsRng::new().unwrap(); - /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); - /// # } - /// # - /// # #[cfg(not(feature = "std"))] - /// # fn main() { } - /// ``` - /// - /// Afterwards, you can generate the corresponding public: - /// - /// ``` - /// # extern crate rand; - /// # extern crate ed25519_dalek; - /// # - /// # fn main() { - /// # - /// # use rand::Rng; - /// # use rand::thread_rng; - /// # use ed25519_dalek::PublicKey; - /// # use ed25519_dalek::SecretKey; - /// # use ed25519_dalek::Signature; - /// # - /// # let mut csprng = thread_rng(); - /// # let secret_key: SecretKey = SecretKey::generate(&mut csprng); - /// - /// let public_key: PublicKey = (&secret_key).into(); - /// # } - /// ``` - /// - /// # Input - /// - /// A CSPRNG with a `fill_bytes()` method, e.g. `rand::OsRng` - pub fn generate(csprng: &mut T) -> SecretKey - where T: CryptoRng + Rng, - { - let mut sk: SecretKey = SecretKey([0u8; 32]); - - csprng.fill_bytes(&mut sk.0); - - sk - } -} - -#[cfg(feature = "serde")] -impl Serialize for SecretKey { - fn serialize(&self, serializer: S) -> Result where S: Serializer { - serializer.serialize_bytes(self.as_bytes()) - } -} - -#[cfg(feature = "serde")] -impl<'d> Deserialize<'d> for SecretKey { - fn deserialize(deserializer: D) -> Result where D: Deserializer<'d> { - struct SecretKeyVisitor; - - impl<'d> Visitor<'d> for SecretKeyVisitor { - type Value = SecretKey; - - fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - formatter.write_str("An ed25519 secret key as 32 bytes, as specified in RFC8032.") - } - - fn visit_bytes(self, bytes: &[u8]) -> Result where E: SerdeError { - SecretKey::from_bytes(bytes).or(Err(SerdeError::invalid_length(bytes.len(), &self))) - } - } - deserializer.deserialize_bytes(SecretKeyVisitor) - } -} - -/// An "expanded" secret key. -/// -/// This is produced by using an hash function with 512-bits output to digest a -/// `SecretKey`. The output digest is then split in half, the lower half being -/// the actual `key` used to sign messages, after twiddling with some bits.¹ The -/// upper half is used a sort of half-baked, ill-designed² pseudo-domain-separation -/// "nonce"-like thing, which is used during signature production by -/// concatenating it with the message to be signed before the message is hashed. -// -// ¹ This results in a slight bias towards non-uniformity at one spectrum of -// the range of valid keys. Oh well: not my idea; not my problem. -// -// ² It is the author's view (specifically, isis agora lovecruft, in the event -// you'd like to complain about me, again) that this is "ill-designed" because -// this doesn't actually provide true hash domain separation, in that in many -// real-world applications a user wishes to have one key which is used in -// several contexts (such as within tor, which does does domain separation -// manually by pre-concatenating static strings to messages to achieve more -// robust domain separation). In other real-world applications, such as -// bitcoind, a user might wish to have one master keypair from which others are -// derived (à la BIP32) and different domain separators between keys derived at -// different levels (and similarly for tree-based key derivation constructions, -// such as hash-based signatures). Leaving the domain separation to -// application designers, who thus far have produced incompatible, -// slightly-differing, ad hoc domain separation (at least those application -// designers who knew enough cryptographic theory to do so!), is therefore a -// bad design choice on the part of the cryptographer designing primitives -// which should be simple and as foolproof as possible to use for -// non-cryptographers. Further, later in the ed25519 signature scheme, as -// specified in RFC8032, the public key is added into *another* hash digest -// (along with the message, again); it is unclear to this author why there's -// not only one but two poorly-thought-out attempts at domain separation in the -// same signature scheme, and which both fail in exactly the same way. For a -// better-designed, Schnorr-based signature scheme, see Trevor Perrin's work on -// "generalised EdDSA" and "VXEdDSA". -#[derive(Default)] // we derive Default in order to use the clear() method in Drop -pub struct ExpandedSecretKey { - pub (crate) key: Scalar, - pub (crate) nonce: [u8; 32], -} - -/// Overwrite secret key material with null bytes when it goes out of scope. -impl Drop for ExpandedSecretKey { - fn drop(&mut self) { - self.key.clear(); - self.nonce.clear(); - } -} - -impl<'a> From<&'a SecretKey> for ExpandedSecretKey { - /// Construct an `ExpandedSecretKey` from a `SecretKey`. - /// - /// # Examples - /// - /// ``` - /// # extern crate rand; - /// # extern crate sha2; - /// # extern crate ed25519_dalek; - /// # - /// # fn main() { - /// # - /// use rand::Rng; - /// use rand::thread_rng; - /// use sha2::Sha512; - /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; - /// - /// let mut csprng = thread_rng(); - /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); - /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key); - /// # } - /// ``` - fn from(secret_key: &'a SecretKey) -> ExpandedSecretKey { - let mut h: Sha512 = Sha512::default(); - let mut hash: [u8; 64] = [0u8; 64]; - let mut lower: [u8; 32] = [0u8; 32]; - let mut upper: [u8; 32] = [0u8; 32]; - - h.input(secret_key.as_bytes()); - hash.copy_from_slice(h.result().as_slice()); - - lower.copy_from_slice(&hash[00..32]); - upper.copy_from_slice(&hash[32..64]); - - lower[0] &= 248; - lower[31] &= 63; - lower[31] |= 64; - - ExpandedSecretKey{ key: Scalar::from_bits(lower), nonce: upper, } - } -} - -impl ExpandedSecretKey { - /// Convert this `ExpandedSecretKey` into an array of 64 bytes. - /// - /// # Returns - /// - /// An array of 64 bytes. The first 32 bytes represent the "expanded" - /// secret key, and the last 32 bytes represent the "domain-separation" - /// "nonce". - /// - /// # Examples - /// - /// ``` - /// # extern crate rand; - /// # extern crate sha2; - /// # extern crate ed25519_dalek; - /// # - /// # #[cfg(all(feature = "sha2", feature = "std"))] - /// # fn main() { - /// # - /// use rand::Rng; - /// use rand::rngs::OsRng; - /// use sha2::Sha512; - /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; - /// - /// let mut csprng: OsRng = OsRng::new().unwrap(); - /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); - /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key); - /// let expanded_secret_key_bytes: [u8; 64] = expanded_secret_key.to_bytes(); - /// - /// assert!(&expanded_secret_key_bytes[..] != &[0u8; 64][..]); - /// # } - /// # - /// # #[cfg(any(not(feature = "sha2"), not(feature = "std")))] - /// # fn main() { } - /// ``` - #[inline] - pub fn to_bytes(&self) -> [u8; EXPANDED_SECRET_KEY_LENGTH] { - let mut bytes: [u8; 64] = [0u8; 64]; - - bytes[..32].copy_from_slice(self.key.as_bytes()); - bytes[32..].copy_from_slice(&self.nonce[..]); - bytes - } - - /// Construct an `ExpandedSecretKey` from a slice of bytes. - /// - /// # Returns - /// - /// A `Result` whose okay value is an EdDSA `ExpandedSecretKey` or whose - /// error value is an `SignatureError` describing the error that occurred. - /// - /// # Examples - /// - /// ``` - /// # extern crate rand; - /// # extern crate sha2; - /// # extern crate ed25519_dalek; - /// # - /// # use ed25519_dalek::{ExpandedSecretKey, SignatureError}; - /// # - /// # #[cfg(all(feature = "sha2", feature = "std"))] - /// # fn do_test() -> Result { - /// # - /// use rand::Rng; - /// use rand::rngs::OsRng; - /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; - /// use ed25519_dalek::SignatureError; - /// - /// let mut csprng: OsRng = OsRng::new().unwrap(); - /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); - /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key); - /// let bytes: [u8; 64] = expanded_secret_key.to_bytes(); - /// let expanded_secret_key_again = ExpandedSecretKey::from_bytes(&bytes)?; - /// # - /// # Ok(expanded_secret_key_again) - /// # } - /// # - /// # #[cfg(all(feature = "sha2", feature = "std"))] - /// # fn main() { - /// # let result = do_test(); - /// # assert!(result.is_ok()); - /// # } - /// # - /// # #[cfg(any(not(feature = "sha2"), not(feature = "std")))] - /// # fn main() { } - /// ``` - #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != EXPANDED_SECRET_KEY_LENGTH { - return Err(SignatureError(InternalError::BytesLengthError{ - name: "ExpandedSecretKey", length: EXPANDED_SECRET_KEY_LENGTH })); - } - let mut lower: [u8; 32] = [0u8; 32]; - let mut upper: [u8; 32] = [0u8; 32]; - - lower.copy_from_slice(&bytes[00..32]); - upper.copy_from_slice(&bytes[32..64]); - - Ok(ExpandedSecretKey{ key: Scalar::from_bits(lower), - nonce: upper }) - } - - /// Sign a message with this `ExpandedSecretKey`. - #[allow(non_snake_case)] - pub fn sign(&self, message: &[u8], public_key: &PublicKey) -> Signature { - let mut h: Sha512 = Sha512::new(); - let R: CompressedEdwardsY; - let r: Scalar; - let s: Scalar; - let k: Scalar; - - h.input(&self.nonce); - h.input(&message); - - r = Scalar::from_hash(h); - R = (&r * &constants::ED25519_BASEPOINT_TABLE).compress(); - - h = Sha512::new(); - h.input(R.as_bytes()); - h.input(public_key.as_bytes()); - h.input(&message); - - k = Scalar::from_hash(h); - s = &(&k * &self.key) + &r; - - Signature{ R, s } - } - - /// Sign a `prehashed_message` with this `ExpandedSecretKey` using the - /// Ed25519ph algorithm defined in [RFC8032 §5.1][rfc8032]. - /// - /// # Inputs - /// - /// * `prehashed_message` is an instantiated hash digest with 512-bits of - /// output which has had the message to be signed previously fed into its - /// state. - /// * `public_key` is a [`PublicKey`] which corresponds to this secret key. - /// * `context` is an optional context string, up to 255 bytes inclusive, - /// which may be used to provide additional domain separation. If not - /// set, this will default to an empty string. - /// - /// # Returns - /// - /// An Ed25519ph [`Signature`] on the `prehashed_message`. - /// - /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 - #[allow(non_snake_case)] - pub fn sign_prehashed( - &self, - prehashed_message: D, - public_key: &PublicKey, - context: Option<&'static [u8]>, - ) -> Signature - where - D: Digest, - { - let mut h: Sha512; - let mut prehash: [u8; 64] = [0u8; 64]; - let R: CompressedEdwardsY; - let r: Scalar; - let s: Scalar; - let k: Scalar; - - let ctx: &[u8] = context.unwrap_or(b""); // By default, the context is an empty string. - - debug_assert!(ctx.len() <= 255, "The context must not be longer than 255 octets."); - - let ctx_len: u8 = ctx.len() as u8; - - // Get the result of the pre-hashed message. - prehash.copy_from_slice(prehashed_message.result().as_slice()); - - // This is the dumbest, ten-years-late, non-admission of fucking up the - // domain separation I have ever seen. Why am I still required to put - // the upper half "prefix" of the hashed "secret key" in here? Why - // can't the user just supply their own nonce and decide for themselves - // whether or not they want a deterministic signature scheme? Why does - // the message go into what's ostensibly the signature domain separation - // hash? Why wasn't there always a way to provide a context string? - // - // ... - // - // This is a really fucking stupid bandaid, and the damned scheme is - // still bleeding from malleability, for fuck's sake. - h = Sha512::new() - .chain(b"SigEd25519 no Ed25519 collisions") - .chain(&[1]) // Ed25519ph - .chain(&[ctx_len]) - .chain(ctx) - .chain(&self.nonce) - .chain(&prehash[..]); - - r = Scalar::from_hash(h); - R = (&r * &constants::ED25519_BASEPOINT_TABLE).compress(); - - h = Sha512::new() - .chain(b"SigEd25519 no Ed25519 collisions") - .chain(&[1]) // Ed25519ph - .chain(&[ctx_len]) - .chain(ctx) - .chain(R.as_bytes()) - .chain(public_key.as_bytes()) - .chain(&prehash[..]); - - k = Scalar::from_hash(h); - s = &(&k * &self.key) + &r; - - Signature{ R, s } - } - -} - -#[cfg(feature = "serde")] -impl Serialize for ExpandedSecretKey { - fn serialize(&self, serializer: S) -> Result where S: Serializer { - serializer.serialize_bytes(&self.to_bytes()[..]) - } -} - -#[cfg(feature = "serde")] -impl<'d> Deserialize<'d> for ExpandedSecretKey { - fn deserialize(deserializer: D) -> Result where D: Deserializer<'d> { - struct ExpandedSecretKeyVisitor; - - impl<'d> Visitor<'d> for ExpandedSecretKeyVisitor { - type Value = ExpandedSecretKey; - - fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - formatter.write_str("An ed25519 expanded secret key as 64 bytes, as specified in RFC8032.") - } - - fn visit_bytes(self, bytes: &[u8]) -> Result where E: SerdeError { - ExpandedSecretKey::from_bytes(bytes).or(Err(SerdeError::invalid_length(bytes.len(), &self))) - } - } - deserializer.deserialize_bytes(ExpandedSecretKeyVisitor) - } -} - /// An ed25519 public key. #[derive(Copy, Clone, Default, Eq, PartialEq)] pub struct PublicKey( @@ -1227,6 +726,8 @@ impl<'d> Deserialize<'d> for Keypair { mod test { use super::*; + use clear_on_drop::clear::Clear; + #[test] fn keypair_clear_on_drop() { let mut keypair: Keypair = Keypair::from_bytes(&[1u8; KEYPAIR_LENGTH][..]).unwrap(); diff --git a/src/lib.rs b/src/lib.rs index 462ae40..b12a0f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -259,6 +259,7 @@ extern crate serde; mod constants; mod ed25519; +mod secret; mod signature; pub mod errors; diff --git a/src/secret.rs b/src/secret.rs new file mode 100644 index 0000000..e953baa --- /dev/null +++ b/src/secret.rs @@ -0,0 +1,540 @@ +// -*- mode: rust; -*- +// +// This file is part of ed25519-dalek. +// Copyright (c) 2017-2018 isis lovecruft +// See LICENSE for licensing information. +// +// Authors: +// - isis agora lovecruft + +//! ed25519 secret key types. + +use core::fmt::Debug; + +use clear_on_drop::clear::Clear; + +use curve25519_dalek::constants; +use curve25519_dalek::digest::Digest; +use curve25519_dalek::digest::generic_array::typenum::U64; +use curve25519_dalek::edwards::CompressedEdwardsY; +use curve25519_dalek::scalar::Scalar; + +use rand::CryptoRng; +use rand::Rng; + +use sha2::Sha512; + +#[cfg(feature = "serde")] +use serde::{Serialize, Deserialize}; +#[cfg(feature = "serde")] +use serde::{Serializer, Deserializer}; +#[cfg(feature = "serde")] +use serde::de::Error as SerdeError; +#[cfg(feature = "serde")] +use serde::de::Visitor; + +use crate::constants::*; +use crate::errors::*; +use crate::signature::*; + +use crate::PublicKey; + +/// An EdDSA secret key. +#[derive(Default)] // we derive Default in order to use the clear() method in Drop +pub struct SecretKey(pub (crate) [u8; SECRET_KEY_LENGTH]); + +impl Debug for SecretKey { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + write!(f, "SecretKey: {:?}", &self.0[..]) + } +} + +/// Overwrite secret key material with null bytes when it goes out of scope. +impl Drop for SecretKey { + fn drop(&mut self) { + self.0.clear(); + } +} + +impl AsRef<[u8]> for SecretKey { + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + +impl SecretKey { + /// Convert this secret key to a byte array. + #[inline] + pub fn to_bytes(&self) -> [u8; SECRET_KEY_LENGTH] { + self.0 + } + + /// View this secret key as a byte array. + #[inline] + pub fn as_bytes<'a>(&'a self) -> &'a [u8; SECRET_KEY_LENGTH] { + &self.0 + } + + /// Construct a `SecretKey` from a slice of bytes. + /// + /// # Example + /// + /// ``` + /// # extern crate ed25519_dalek; + /// # + /// use ed25519_dalek::SecretKey; + /// use ed25519_dalek::SECRET_KEY_LENGTH; + /// use ed25519_dalek::SignatureError; + /// + /// # fn doctest() -> Result { + /// let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = [ + /// 157, 097, 177, 157, 239, 253, 090, 096, + /// 186, 132, 074, 244, 146, 236, 044, 196, + /// 068, 073, 197, 105, 123, 050, 105, 025, + /// 112, 059, 172, 003, 028, 174, 127, 096, ]; + /// + /// let secret_key: SecretKey = SecretKey::from_bytes(&secret_key_bytes)?; + /// # + /// # Ok(secret_key) + /// # } + /// # + /// # fn main() { + /// # let result = doctest(); + /// # assert!(result.is_ok()); + /// # } + /// ``` + /// + /// # Returns + /// + /// A `Result` whose okay value is an EdDSA `SecretKey` or whose error value + /// is an `SignatureError` wrapping the internal error that occurred. + #[inline] + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != SECRET_KEY_LENGTH { + return Err(SignatureError(InternalError::BytesLengthError{ + name: "SecretKey", length: SECRET_KEY_LENGTH })); + } + let mut bits: [u8; 32] = [0u8; 32]; + bits.copy_from_slice(&bytes[..32]); + + Ok(SecretKey(bits)) + } + + /// Generate a `SecretKey` from a `csprng`. + /// + /// # Example + /// + /// ``` + /// extern crate rand; + /// extern crate sha2; + /// extern crate ed25519_dalek; + /// + /// # #[cfg(feature = "std")] + /// # fn main() { + /// # + /// use rand::Rng; + /// use rand::rngs::OsRng; + /// use sha2::Sha512; + /// use ed25519_dalek::PublicKey; + /// use ed25519_dalek::SecretKey; + /// use ed25519_dalek::Signature; + /// + /// let mut csprng: OsRng = OsRng::new().unwrap(); + /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); + /// # } + /// # + /// # #[cfg(not(feature = "std"))] + /// # fn main() { } + /// ``` + /// + /// Afterwards, you can generate the corresponding public: + /// + /// ``` + /// # extern crate rand; + /// # extern crate ed25519_dalek; + /// # + /// # fn main() { + /// # + /// # use rand::Rng; + /// # use rand::thread_rng; + /// # use ed25519_dalek::PublicKey; + /// # use ed25519_dalek::SecretKey; + /// # use ed25519_dalek::Signature; + /// # + /// # let mut csprng = thread_rng(); + /// # let secret_key: SecretKey = SecretKey::generate(&mut csprng); + /// + /// let public_key: PublicKey = (&secret_key).into(); + /// # } + /// ``` + /// + /// # Input + /// + /// A CSPRNG with a `fill_bytes()` method, e.g. `rand::OsRng` + pub fn generate(csprng: &mut T) -> SecretKey + where T: CryptoRng + Rng, + { + let mut sk: SecretKey = SecretKey([0u8; 32]); + + csprng.fill_bytes(&mut sk.0); + + sk + } +} + +#[cfg(feature = "serde")] +impl Serialize for SecretKey { + fn serialize(&self, serializer: S) -> Result where S: Serializer { + serializer.serialize_bytes(self.as_bytes()) + } +} + +#[cfg(feature = "serde")] +impl<'d> Deserialize<'d> for SecretKey { + fn deserialize(deserializer: D) -> Result where D: Deserializer<'d> { + struct SecretKeyVisitor; + + impl<'d> Visitor<'d> for SecretKeyVisitor { + type Value = SecretKey; + + fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + formatter.write_str("An ed25519 secret key as 32 bytes, as specified in RFC8032.") + } + + fn visit_bytes(self, bytes: &[u8]) -> Result where E: SerdeError { + SecretKey::from_bytes(bytes).or(Err(SerdeError::invalid_length(bytes.len(), &self))) + } + } + deserializer.deserialize_bytes(SecretKeyVisitor) + } +} + +/// An "expanded" secret key. +/// +/// This is produced by using an hash function with 512-bits output to digest a +/// `SecretKey`. The output digest is then split in half, the lower half being +/// the actual `key` used to sign messages, after twiddling with some bits.¹ The +/// upper half is used a sort of half-baked, ill-designed² pseudo-domain-separation +/// "nonce"-like thing, which is used during signature production by +/// concatenating it with the message to be signed before the message is hashed. +// +// ¹ This results in a slight bias towards non-uniformity at one spectrum of +// the range of valid keys. Oh well: not my idea; not my problem. +// +// ² It is the author's view (specifically, isis agora lovecruft, in the event +// you'd like to complain about me, again) that this is "ill-designed" because +// this doesn't actually provide true hash domain separation, in that in many +// real-world applications a user wishes to have one key which is used in +// several contexts (such as within tor, which does does domain separation +// manually by pre-concatenating static strings to messages to achieve more +// robust domain separation). In other real-world applications, such as +// bitcoind, a user might wish to have one master keypair from which others are +// derived (à la BIP32) and different domain separators between keys derived at +// different levels (and similarly for tree-based key derivation constructions, +// such as hash-based signatures). Leaving the domain separation to +// application designers, who thus far have produced incompatible, +// slightly-differing, ad hoc domain separation (at least those application +// designers who knew enough cryptographic theory to do so!), is therefore a +// bad design choice on the part of the cryptographer designing primitives +// which should be simple and as foolproof as possible to use for +// non-cryptographers. Further, later in the ed25519 signature scheme, as +// specified in RFC8032, the public key is added into *another* hash digest +// (along with the message, again); it is unclear to this author why there's +// not only one but two poorly-thought-out attempts at domain separation in the +// same signature scheme, and which both fail in exactly the same way. For a +// better-designed, Schnorr-based signature scheme, see Trevor Perrin's work on +// "generalised EdDSA" and "VXEdDSA". +#[derive(Default)] // we derive Default in order to use the clear() method in Drop +pub struct ExpandedSecretKey { + pub (crate) key: Scalar, + pub (crate) nonce: [u8; 32], +} + +/// Overwrite secret key material with null bytes when it goes out of scope. +impl Drop for ExpandedSecretKey { + fn drop(&mut self) { + self.key.clear(); + self.nonce.clear(); + } +} + +impl<'a> From<&'a SecretKey> for ExpandedSecretKey { + /// Construct an `ExpandedSecretKey` from a `SecretKey`. + /// + /// # Examples + /// + /// ``` + /// # extern crate rand; + /// # extern crate sha2; + /// # extern crate ed25519_dalek; + /// # + /// # fn main() { + /// # + /// use rand::Rng; + /// use rand::thread_rng; + /// use sha2::Sha512; + /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; + /// + /// let mut csprng = thread_rng(); + /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); + /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key); + /// # } + /// ``` + fn from(secret_key: &'a SecretKey) -> ExpandedSecretKey { + let mut h: Sha512 = Sha512::default(); + let mut hash: [u8; 64] = [0u8; 64]; + let mut lower: [u8; 32] = [0u8; 32]; + let mut upper: [u8; 32] = [0u8; 32]; + + h.input(secret_key.as_bytes()); + hash.copy_from_slice(h.result().as_slice()); + + lower.copy_from_slice(&hash[00..32]); + upper.copy_from_slice(&hash[32..64]); + + lower[0] &= 248; + lower[31] &= 63; + lower[31] |= 64; + + ExpandedSecretKey{ key: Scalar::from_bits(lower), nonce: upper, } + } +} + +impl ExpandedSecretKey { + /// Convert this `ExpandedSecretKey` into an array of 64 bytes. + /// + /// # Returns + /// + /// An array of 64 bytes. The first 32 bytes represent the "expanded" + /// secret key, and the last 32 bytes represent the "domain-separation" + /// "nonce". + /// + /// # Examples + /// + /// ``` + /// # extern crate rand; + /// # extern crate sha2; + /// # extern crate ed25519_dalek; + /// # + /// # #[cfg(all(feature = "sha2", feature = "std"))] + /// # fn main() { + /// # + /// use rand::Rng; + /// use rand::rngs::OsRng; + /// use sha2::Sha512; + /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; + /// + /// let mut csprng: OsRng = OsRng::new().unwrap(); + /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); + /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key); + /// let expanded_secret_key_bytes: [u8; 64] = expanded_secret_key.to_bytes(); + /// + /// assert!(&expanded_secret_key_bytes[..] != &[0u8; 64][..]); + /// # } + /// # + /// # #[cfg(any(not(feature = "sha2"), not(feature = "std")))] + /// # fn main() { } + /// ``` + #[inline] + pub fn to_bytes(&self) -> [u8; EXPANDED_SECRET_KEY_LENGTH] { + let mut bytes: [u8; 64] = [0u8; 64]; + + bytes[..32].copy_from_slice(self.key.as_bytes()); + bytes[32..].copy_from_slice(&self.nonce[..]); + bytes + } + + /// Construct an `ExpandedSecretKey` from a slice of bytes. + /// + /// # Returns + /// + /// A `Result` whose okay value is an EdDSA `ExpandedSecretKey` or whose + /// error value is an `SignatureError` describing the error that occurred. + /// + /// # Examples + /// + /// ``` + /// # extern crate rand; + /// # extern crate sha2; + /// # extern crate ed25519_dalek; + /// # + /// # use ed25519_dalek::{ExpandedSecretKey, SignatureError}; + /// # + /// # #[cfg(all(feature = "sha2", feature = "std"))] + /// # fn do_test() -> Result { + /// # + /// use rand::Rng; + /// use rand::rngs::OsRng; + /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; + /// use ed25519_dalek::SignatureError; + /// + /// let mut csprng: OsRng = OsRng::new().unwrap(); + /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); + /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key); + /// let bytes: [u8; 64] = expanded_secret_key.to_bytes(); + /// let expanded_secret_key_again = ExpandedSecretKey::from_bytes(&bytes)?; + /// # + /// # Ok(expanded_secret_key_again) + /// # } + /// # + /// # #[cfg(all(feature = "sha2", feature = "std"))] + /// # fn main() { + /// # let result = do_test(); + /// # assert!(result.is_ok()); + /// # } + /// # + /// # #[cfg(any(not(feature = "sha2"), not(feature = "std")))] + /// # fn main() { } + /// ``` + #[inline] + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != EXPANDED_SECRET_KEY_LENGTH { + return Err(SignatureError(InternalError::BytesLengthError{ + name: "ExpandedSecretKey", length: EXPANDED_SECRET_KEY_LENGTH })); + } + let mut lower: [u8; 32] = [0u8; 32]; + let mut upper: [u8; 32] = [0u8; 32]; + + lower.copy_from_slice(&bytes[00..32]); + upper.copy_from_slice(&bytes[32..64]); + + Ok(ExpandedSecretKey{ key: Scalar::from_bits(lower), + nonce: upper }) + } + + /// Sign a message with this `ExpandedSecretKey`. + #[allow(non_snake_case)] + pub fn sign(&self, message: &[u8], public_key: &PublicKey) -> Signature { + let mut h: Sha512 = Sha512::new(); + let R: CompressedEdwardsY; + let r: Scalar; + let s: Scalar; + let k: Scalar; + + h.input(&self.nonce); + h.input(&message); + + r = Scalar::from_hash(h); + R = (&r * &constants::ED25519_BASEPOINT_TABLE).compress(); + + h = Sha512::new(); + h.input(R.as_bytes()); + h.input(public_key.as_bytes()); + h.input(&message); + + k = Scalar::from_hash(h); + s = &(&k * &self.key) + &r; + + Signature{ R, s } + } + + /// Sign a `prehashed_message` with this `ExpandedSecretKey` using the + /// Ed25519ph algorithm defined in [RFC8032 §5.1][rfc8032]. + /// + /// # Inputs + /// + /// * `prehashed_message` is an instantiated hash digest with 512-bits of + /// output which has had the message to be signed previously fed into its + /// state. + /// * `public_key` is a [`PublicKey`] which corresponds to this secret key. + /// * `context` is an optional context string, up to 255 bytes inclusive, + /// which may be used to provide additional domain separation. If not + /// set, this will default to an empty string. + /// + /// # Returns + /// + /// An Ed25519ph [`Signature`] on the `prehashed_message`. + /// + /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 + #[allow(non_snake_case)] + pub fn sign_prehashed( + &self, + prehashed_message: D, + public_key: &PublicKey, + context: Option<&'static [u8]>, + ) -> Signature + where + D: Digest, + { + let mut h: Sha512; + let mut prehash: [u8; 64] = [0u8; 64]; + let R: CompressedEdwardsY; + let r: Scalar; + let s: Scalar; + let k: Scalar; + + let ctx: &[u8] = context.unwrap_or(b""); // By default, the context is an empty string. + + debug_assert!(ctx.len() <= 255, "The context must not be longer than 255 octets."); + + let ctx_len: u8 = ctx.len() as u8; + + // Get the result of the pre-hashed message. + prehash.copy_from_slice(prehashed_message.result().as_slice()); + + // This is the dumbest, ten-years-late, non-admission of fucking up the + // domain separation I have ever seen. Why am I still required to put + // the upper half "prefix" of the hashed "secret key" in here? Why + // can't the user just supply their own nonce and decide for themselves + // whether or not they want a deterministic signature scheme? Why does + // the message go into what's ostensibly the signature domain separation + // hash? Why wasn't there always a way to provide a context string? + // + // ... + // + // This is a really fucking stupid bandaid, and the damned scheme is + // still bleeding from malleability, for fuck's sake. + h = Sha512::new() + .chain(b"SigEd25519 no Ed25519 collisions") + .chain(&[1]) // Ed25519ph + .chain(&[ctx_len]) + .chain(ctx) + .chain(&self.nonce) + .chain(&prehash[..]); + + r = Scalar::from_hash(h); + R = (&r * &constants::ED25519_BASEPOINT_TABLE).compress(); + + h = Sha512::new() + .chain(b"SigEd25519 no Ed25519 collisions") + .chain(&[1]) // Ed25519ph + .chain(&[ctx_len]) + .chain(ctx) + .chain(R.as_bytes()) + .chain(public_key.as_bytes()) + .chain(&prehash[..]); + + k = Scalar::from_hash(h); + s = &(&k * &self.key) + &r; + + Signature{ R, s } + } + +} + +#[cfg(feature = "serde")] +impl Serialize for ExpandedSecretKey { + fn serialize(&self, serializer: S) -> Result where S: Serializer { + serializer.serialize_bytes(&self.to_bytes()[..]) + } +} + +#[cfg(feature = "serde")] +impl<'d> Deserialize<'d> for ExpandedSecretKey { + fn deserialize(deserializer: D) -> Result where D: Deserializer<'d> { + struct ExpandedSecretKeyVisitor; + + impl<'d> Visitor<'d> for ExpandedSecretKeyVisitor { + type Value = ExpandedSecretKey; + + fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + formatter.write_str("An ed25519 expanded secret key as 64 bytes, as specified in RFC8032.") + } + + fn visit_bytes(self, bytes: &[u8]) -> Result where E: SerdeError { + ExpandedSecretKey::from_bytes(bytes).or(Err(SerdeError::invalid_length(bytes.len(), &self))) + } + } + deserializer.deserialize_bytes(ExpandedSecretKeyVisitor) + } +} From 6fea2e1ea0866a118679e3cc051ee273430f2a3e Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 30 Dec 2018 03:41:00 +0000 Subject: [PATCH 194/351] Realphabetise extern crates. --- src/lib.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b12a0f6..806979d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -243,17 +243,15 @@ #![warn(rust_2018_idioms)] #![deny(missing_docs)] // refuse to compile if documentation is missing -extern crate clear_on_drop; -extern crate curve25519_dalek; -extern crate failure; -extern crate rand; - #[cfg(any(feature = "std", test))] #[macro_use] extern crate std; +extern crate clear_on_drop; +extern crate curve25519_dalek; +extern crate failure; +extern crate rand; extern crate sha2; - #[cfg(feature = "serde")] extern crate serde; From e3d7c16aaccc9dd87426f6ebacc469541c1d87a1 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 30 Dec 2018 03:41:13 +0000 Subject: [PATCH 195/351] Make errors module private. --- src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 806979d..db1b291 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -257,10 +257,9 @@ extern crate serde; mod constants; mod ed25519; +mod errors; mod secret; mod signature; -pub mod errors; - // Export everything public in ed25519. pub use crate::ed25519::*; From e6528bd68305c04c90c088a086773cc22a1cb71e Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 30 Dec 2018 03:53:11 +0000 Subject: [PATCH 196/351] Create new module for public key code. --- src/ed25519.rs | 242 +------------------------------------------ src/lib.rs | 1 + src/public.rs | 272 +++++++++++++++++++++++++++++++++++++++++++++++++ src/secret.rs | 3 +- 4 files changed, 275 insertions(+), 243 deletions(-) create mode 100644 src/public.rs diff --git a/src/ed25519.rs b/src/ed25519.rs index e992c17..a6d5f31 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -10,7 +10,6 @@ //! A Rust implementation of ed25519 key generation, signing, and verification. use core::default::Default; -use core::fmt::Debug; use rand::CryptoRng; use rand::Rng; @@ -30,226 +29,15 @@ pub use curve25519_dalek::digest::Digest; use curve25519_dalek::digest::generic_array::typenum::U64; use curve25519_dalek::constants; -use curve25519_dalek::edwards::CompressedEdwardsY; use curve25519_dalek::edwards::EdwardsPoint; use curve25519_dalek::scalar::Scalar; pub use crate::constants::*; pub use crate::errors::*; +pub use crate::public::*; pub use crate::secret::*; pub use crate::signature::*; -/// An ed25519 public key. -#[derive(Copy, Clone, Default, Eq, PartialEq)] -pub struct PublicKey( - pub (crate) CompressedEdwardsY, - pub (crate) EdwardsPoint, -); - -impl Debug for PublicKey { - fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - write!(f, "PublicKey({:?}), {:?})", self.0, self.1) - } -} - -impl AsRef<[u8]> for PublicKey { - fn as_ref(&self) -> &[u8] { - self.as_bytes() - } -} - -impl PublicKey { - /// Convert this public key to a byte array. - #[inline] - pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_LENGTH] { - self.0.to_bytes() - } - - /// View this public key as a byte array. - #[inline] - pub fn as_bytes<'a>(&'a self) -> &'a [u8; PUBLIC_KEY_LENGTH] { - &(self.0).0 - } - - /// Construct a `PublicKey` from a slice of bytes. - /// - /// # Warning - /// - /// The caller is responsible for ensuring that the bytes passed into this - /// method actually represent a `curve25519_dalek::curve::CompressedEdwardsY` - /// and that said compressed point is actually a point on the curve. - /// - /// # Example - /// - /// ``` - /// # extern crate ed25519_dalek; - /// # - /// use ed25519_dalek::PublicKey; - /// use ed25519_dalek::PUBLIC_KEY_LENGTH; - /// use ed25519_dalek::SignatureError; - /// - /// # fn doctest() -> Result { - /// let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = [ - /// 215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, - /// 14, 225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26]; - /// - /// let public_key = PublicKey::from_bytes(&public_key_bytes)?; - /// # - /// # Ok(public_key) - /// # } - /// # - /// # fn main() { - /// # doctest(); - /// # } - /// ``` - /// - /// # Returns - /// - /// A `Result` whose okay value is an EdDSA `PublicKey` or whose error value - /// is an `SignatureError` describing the error that occurred. - #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != PUBLIC_KEY_LENGTH { - return Err(SignatureError(InternalError::BytesLengthError{ - name: "PublicKey", length: PUBLIC_KEY_LENGTH })); - } - let mut bits: [u8; 32] = [0u8; 32]; - bits.copy_from_slice(&bytes[..32]); - - let compressed = CompressedEdwardsY(bits); - let point = compressed.decompress().ok_or(SignatureError(InternalError::PointDecompressionError))?; - - Ok(PublicKey(compressed, point)) - } -} - -impl<'a> From<&'a SecretKey> for PublicKey { - /// Derive this public key from its corresponding `SecretKey`. - fn from(secret_key: &SecretKey) -> PublicKey { - let mut h: Sha512 = Sha512::new(); - let mut hash: [u8; 64] = [0u8; 64]; - let mut digest: [u8; 32] = [0u8; 32]; - - h.input(secret_key.as_bytes()); - hash.copy_from_slice(h.result().as_slice()); - - digest.copy_from_slice(&hash[..32]); - - PublicKey::mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(&mut digest) - } -} - -impl<'a> From<&'a ExpandedSecretKey> for PublicKey { - /// Derive this public key from its corresponding `ExpandedSecretKey`. - fn from(expanded_secret_key: &ExpandedSecretKey) -> PublicKey { - let mut bits: [u8; 32] = expanded_secret_key.key.to_bytes(); - - PublicKey::mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(&mut bits) - } -} - -impl PublicKey { - /// Internal utility function for mangling the bits of a (formerly - /// mathematically well-defined) "scalar" and multiplying it to produce a - /// public key. - fn mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(bits: &mut [u8; 32]) -> PublicKey { - bits[0] &= 248; - bits[31] &= 127; - bits[31] |= 64; - - let point = &Scalar::from_bits(*bits) * &constants::ED25519_BASEPOINT_TABLE; - let compressed = point.compress(); - - PublicKey(compressed, point) - } - - /// Verify a signature on a message with this keypair's public key. - /// - /// # Return - /// - /// Returns `Ok(())` if the signature is valid, and `Err` otherwise. - #[allow(non_snake_case)] - pub fn verify( - &self, - message: &[u8], - signature: &Signature - ) -> Result<(), SignatureError> - { - let mut h: Sha512 = Sha512::new(); - let R: EdwardsPoint; - let k: Scalar; - let minus_A: EdwardsPoint = -self.1; - - h.input(signature.R.as_bytes()); - h.input(self.as_bytes()); - h.input(&message); - - k = Scalar::from_hash(h); - R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); - - if R.compress() == signature.R { - Ok(()) - } else { - Err(SignatureError(InternalError::VerifyError)) - } - } - - /// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm. - /// - /// # Inputs - /// - /// * `prehashed_message` is an instantiated hash digest with 512-bits of - /// output which has had the message to be signed previously fed into its - /// state. - /// * `context` is an optional context string, up to 255 bytes inclusive, - /// which may be used to provide additional domain separation. If not - /// set, this will default to an empty string. - /// * `signature` is a purported Ed25519ph [`Signature`] on the `prehashed_message`. - /// - /// # Returns - /// - /// Returns `true` if the `signature` was a valid signature created by this - /// `Keypair` on the `prehashed_message`. - /// - /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 - #[allow(non_snake_case)] - pub fn verify_prehashed( - &self, - prehashed_message: D, - context: Option<&[u8]>, - signature: &Signature, - ) -> Result<(), SignatureError> - where - D: Digest, - { - let mut h: Sha512 = Sha512::default(); - let R: EdwardsPoint; - let k: Scalar; - - let ctx: &[u8] = context.unwrap_or(b""); - debug_assert!(ctx.len() <= 255, "The context must not be longer than 255 octets."); - - let minus_A: EdwardsPoint = -self.1; - - h.input(b"SigEd25519 no Ed25519 collisions"); - h.input(&[1]); // Ed25519ph - h.input(&[ctx.len() as u8]); - h.input(ctx); - h.input(signature.R.as_bytes()); - h.input(self.as_bytes()); - h.input(prehashed_message.result().as_slice()); - - k = Scalar::from_hash(h); - R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); - - if R.compress() == signature.R { - Ok(()) - } else { - Err(SignatureError(InternalError::VerifyError)) - } - } -} - /// Verify a batch of `signatures` on `messages` with their respective `public_keys`. /// /// # Inputs @@ -363,34 +151,6 @@ pub fn verify_batch( } } -#[cfg(feature = "serde")] -impl Serialize for PublicKey { - fn serialize(&self, serializer: S) -> Result where S: Serializer { - serializer.serialize_bytes(self.as_bytes()) - } -} - -#[cfg(feature = "serde")] -impl<'d> Deserialize<'d> for PublicKey { - fn deserialize(deserializer: D) -> Result where D: Deserializer<'d> { - - struct PublicKeyVisitor; - - impl<'d> Visitor<'d> for PublicKeyVisitor { - type Value = PublicKey; - - fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - formatter.write_str("An ed25519 public key as a 32-byte compressed point, as specified in RFC8032") - } - - fn visit_bytes(self, bytes: &[u8]) -> Result where E: SerdeError { - PublicKey::from_bytes(bytes).or(Err(SerdeError::invalid_length(bytes.len(), &self))) - } - } - deserializer.deserialize_bytes(PublicKeyVisitor) - } -} - /// An ed25519 keypair. #[derive(Debug, Default)] // we derive Default in order to use the clear() method in Drop pub struct Keypair { diff --git a/src/lib.rs b/src/lib.rs index db1b291..de05f8b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -258,6 +258,7 @@ extern crate serde; mod constants; mod ed25519; mod errors; +mod public; mod secret; mod signature; diff --git a/src/public.rs b/src/public.rs new file mode 100644 index 0000000..60e504a --- /dev/null +++ b/src/public.rs @@ -0,0 +1,272 @@ +// -*- mode: rust; -*- +// +// This file is part of ed25519-dalek. +// Copyright (c) 2017-2018 isis lovecruft +// See LICENSE for licensing information. +// +// Authors: +// - isis agora lovecruft + +//! ed25519 public keys. + +use core::fmt::Debug; + +use curve25519_dalek::constants; +use curve25519_dalek::digest::Digest; +use curve25519_dalek::digest::generic_array::typenum::U64; +use curve25519_dalek::edwards::CompressedEdwardsY; +use curve25519_dalek::edwards::EdwardsPoint; +use curve25519_dalek::scalar::Scalar; + +pub use sha2::Sha512; + +#[cfg(feature = "serde")] +use serde::{Serialize, Deserialize}; +#[cfg(feature = "serde")] +use serde::{Serializer, Deserializer}; +#[cfg(feature = "serde")] +use serde::de::Error as SerdeError; +#[cfg(feature = "serde")] +use serde::de::Visitor; + +use crate::constants::*; +use crate::errors::*; +use crate::secret::*; +use crate::signature::*; + +/// An ed25519 public key. +#[derive(Copy, Clone, Default, Eq, PartialEq)] +pub struct PublicKey( + pub (crate) CompressedEdwardsY, + pub (crate) EdwardsPoint, +); + +impl Debug for PublicKey { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + write!(f, "PublicKey({:?}), {:?})", self.0, self.1) + } +} + +impl AsRef<[u8]> for PublicKey { + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + +impl<'a> From<&'a SecretKey> for PublicKey { + /// Derive this public key from its corresponding `SecretKey`. + fn from(secret_key: &SecretKey) -> PublicKey { + let mut h: Sha512 = Sha512::new(); + let mut hash: [u8; 64] = [0u8; 64]; + let mut digest: [u8; 32] = [0u8; 32]; + + h.input(secret_key.as_bytes()); + hash.copy_from_slice(h.result().as_slice()); + + digest.copy_from_slice(&hash[..32]); + + PublicKey::mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(&mut digest) + } +} + +impl<'a> From<&'a ExpandedSecretKey> for PublicKey { + /// Derive this public key from its corresponding `ExpandedSecretKey`. + fn from(expanded_secret_key: &ExpandedSecretKey) -> PublicKey { + let mut bits: [u8; 32] = expanded_secret_key.key.to_bytes(); + + PublicKey::mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(&mut bits) + } +} + +impl PublicKey { + /// Convert this public key to a byte array. + #[inline] + pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_LENGTH] { + self.0.to_bytes() + } + + /// View this public key as a byte array. + #[inline] + pub fn as_bytes<'a>(&'a self) -> &'a [u8; PUBLIC_KEY_LENGTH] { + &(self.0).0 + } + + /// Construct a `PublicKey` from a slice of bytes. + /// + /// # Warning + /// + /// The caller is responsible for ensuring that the bytes passed into this + /// method actually represent a `curve25519_dalek::curve::CompressedEdwardsY` + /// and that said compressed point is actually a point on the curve. + /// + /// # Example + /// + /// ``` + /// # extern crate ed25519_dalek; + /// # + /// use ed25519_dalek::PublicKey; + /// use ed25519_dalek::PUBLIC_KEY_LENGTH; + /// use ed25519_dalek::SignatureError; + /// + /// # fn doctest() -> Result { + /// let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = [ + /// 215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, + /// 14, 225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26]; + /// + /// let public_key = PublicKey::from_bytes(&public_key_bytes)?; + /// # + /// # Ok(public_key) + /// # } + /// # + /// # fn main() { + /// # doctest(); + /// # } + /// ``` + /// + /// # Returns + /// + /// A `Result` whose okay value is an EdDSA `PublicKey` or whose error value + /// is an `SignatureError` describing the error that occurred. + #[inline] + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != PUBLIC_KEY_LENGTH { + return Err(SignatureError(InternalError::BytesLengthError{ + name: "PublicKey", length: PUBLIC_KEY_LENGTH })); + } + let mut bits: [u8; 32] = [0u8; 32]; + bits.copy_from_slice(&bytes[..32]); + + let compressed = CompressedEdwardsY(bits); + let point = compressed.decompress().ok_or(SignatureError(InternalError::PointDecompressionError))?; + + Ok(PublicKey(compressed, point)) + } + + /// Internal utility function for mangling the bits of a (formerly + /// mathematically well-defined) "scalar" and multiplying it to produce a + /// public key. + fn mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(bits: &mut [u8; 32]) -> PublicKey { + bits[0] &= 248; + bits[31] &= 127; + bits[31] |= 64; + + let point = &Scalar::from_bits(*bits) * &constants::ED25519_BASEPOINT_TABLE; + let compressed = point.compress(); + + PublicKey(compressed, point) + } + + /// Verify a signature on a message with this keypair's public key. + /// + /// # Return + /// + /// Returns `Ok(())` if the signature is valid, and `Err` otherwise. + #[allow(non_snake_case)] + pub fn verify( + &self, + message: &[u8], + signature: &Signature + ) -> Result<(), SignatureError> + { + let mut h: Sha512 = Sha512::new(); + let R: EdwardsPoint; + let k: Scalar; + let minus_A: EdwardsPoint = -self.1; + + h.input(signature.R.as_bytes()); + h.input(self.as_bytes()); + h.input(&message); + + k = Scalar::from_hash(h); + R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); + + if R.compress() == signature.R { + Ok(()) + } else { + Err(SignatureError(InternalError::VerifyError)) + } + } + + /// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm. + /// + /// # Inputs + /// + /// * `prehashed_message` is an instantiated hash digest with 512-bits of + /// output which has had the message to be signed previously fed into its + /// state. + /// * `context` is an optional context string, up to 255 bytes inclusive, + /// which may be used to provide additional domain separation. If not + /// set, this will default to an empty string. + /// * `signature` is a purported Ed25519ph [`Signature`] on the `prehashed_message`. + /// + /// # Returns + /// + /// Returns `true` if the `signature` was a valid signature created by this + /// `Keypair` on the `prehashed_message`. + /// + /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 + #[allow(non_snake_case)] + pub fn verify_prehashed( + &self, + prehashed_message: D, + context: Option<&[u8]>, + signature: &Signature, + ) -> Result<(), SignatureError> + where + D: Digest, + { + let mut h: Sha512 = Sha512::default(); + let R: EdwardsPoint; + let k: Scalar; + + let ctx: &[u8] = context.unwrap_or(b""); + debug_assert!(ctx.len() <= 255, "The context must not be longer than 255 octets."); + + let minus_A: EdwardsPoint = -self.1; + + h.input(b"SigEd25519 no Ed25519 collisions"); + h.input(&[1]); // Ed25519ph + h.input(&[ctx.len() as u8]); + h.input(ctx); + h.input(signature.R.as_bytes()); + h.input(self.as_bytes()); + h.input(prehashed_message.result().as_slice()); + + k = Scalar::from_hash(h); + R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); + + if R.compress() == signature.R { + Ok(()) + } else { + Err(SignatureError(InternalError::VerifyError)) + } + } +} + +#[cfg(feature = "serde")] +impl Serialize for PublicKey { + fn serialize(&self, serializer: S) -> Result where S: Serializer { + serializer.serialize_bytes(self.as_bytes()) + } +} + +#[cfg(feature = "serde")] +impl<'d> Deserialize<'d> for PublicKey { + fn deserialize(deserializer: D) -> Result where D: Deserializer<'d> { + + struct PublicKeyVisitor; + + impl<'d> Visitor<'d> for PublicKeyVisitor { + type Value = PublicKey; + + fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + formatter.write_str("An ed25519 public key as a 32-byte compressed point, as specified in RFC8032") + } + + fn visit_bytes(self, bytes: &[u8]) -> Result where E: SerdeError { + PublicKey::from_bytes(bytes).or(Err(SerdeError::invalid_length(bytes.len(), &self))) + } + } + deserializer.deserialize_bytes(PublicKeyVisitor) + } +} diff --git a/src/secret.rs b/src/secret.rs index e953baa..d5c3e67 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -35,10 +35,9 @@ use serde::de::Visitor; use crate::constants::*; use crate::errors::*; +use crate::public::*; use crate::signature::*; -use crate::PublicKey; - /// An EdDSA secret key. #[derive(Default)] // we derive Default in order to use the clear() method in Drop pub struct SecretKey(pub (crate) [u8; SECRET_KEY_LENGTH]); From 144e87bfc809aff581c92de186f5051376eb45ee Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 30 Dec 2018 04:01:39 +0000 Subject: [PATCH 197/351] Remove unnecessary tests/mod.rs file. --- tests/mod.rs | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 tests/mod.rs diff --git a/tests/mod.rs b/tests/mod.rs deleted file mode 100644 index 8b3a9bb..0000000 --- a/tests/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -// -*- mode: rust; -*- -// -// This file is part of ed25519-dalek. -// Copyright (c) 2017-2018 isis lovecruft -// See LICENSE for licensing information. -// -// Authors: -// - isis agora lovecruft - -//! Integration tests for ed25519-dalek. - -extern crate ed25519_dalek; -extern crate hex; -extern crate rand; -extern crate sha2; - -mod ed25519; From a1418635423c0f6bb96f34de8c4d9034265e7d62 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 30 Dec 2018 04:05:20 +0000 Subject: [PATCH 198/351] Run rustfmt on src/signature.rs. --- src/signature.rs | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/src/signature.rs b/src/signature.rs index d93b572..3dbc478 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -14,14 +14,14 @@ use core::fmt::Debug; use curve25519_dalek::edwards::CompressedEdwardsY; use curve25519_dalek::scalar::Scalar; -#[cfg(feature = "serde")] -use serde::{Serialize, Deserialize}; -#[cfg(feature = "serde")] -use serde::{Serializer, Deserializer}; #[cfg(feature = "serde")] use serde::de::Error as SerdeError; #[cfg(feature = "serde")] use serde::de::Visitor; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; +#[cfg(feature = "serde")] +use serde::{Deserializer, Serializer}; use crate::constants::*; use crate::errors::*; @@ -45,7 +45,7 @@ pub struct Signature { /// This digest is then interpreted as a `Scalar` and reduced into an /// element in ℤ/lℤ. The scalar is then multiplied by the distinguished /// basepoint to produce `R`, and `EdwardsPoint`. - pub (crate) R: CompressedEdwardsY, + pub(crate) R: CompressedEdwardsY, /// `s` is a `Scalar`, formed by using an hash function with 512-bits output /// to produce the digest of: @@ -56,11 +56,13 @@ pub struct Signature { /// /// This digest is then interpreted as a `Scalar` and reduced into an /// element in ℤ/lℤ. - pub (crate) s: Scalar, + pub(crate) s: Scalar, } impl Clone for Signature { - fn clone(&self) -> Self { *self } + fn clone(&self) -> Self { + *self + } } impl Debug for Signature { @@ -84,8 +86,10 @@ impl Signature { #[inline] pub fn from_bytes(bytes: &[u8]) -> Result { if bytes.len() != SIGNATURE_LENGTH { - return Err(SignatureError(InternalError::BytesLengthError{ - name: "Signature", length: SIGNATURE_LENGTH })); + return Err(SignatureError(InternalError::BytesLengthError { + name: "Signature", + length: SIGNATURE_LENGTH, + })); } let mut lower: [u8; 32] = [0u8; 32]; let mut upper: [u8; 32] = [0u8; 32]; @@ -97,20 +101,29 @@ impl Signature { return Err(SignatureError(InternalError::ScalarFormatError)); } - Ok(Signature{ R: CompressedEdwardsY(lower), s: Scalar::from_bits(upper) }) + Ok(Signature { + R: CompressedEdwardsY(lower), + s: Scalar::from_bits(upper), + }) } } #[cfg(feature = "serde")] impl Serialize for Signature { - fn serialize(&self, serializer: S) -> Result where S: Serializer { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { serializer.serialize_bytes(&self.to_bytes()[..]) } } #[cfg(feature = "serde")] impl<'d> Deserialize<'d> for Signature { - fn deserialize(deserializer: D) -> Result where D: Deserializer<'d> { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'d>, + { struct SignatureVisitor; impl<'d> Visitor<'d> for SignatureVisitor { @@ -120,7 +133,10 @@ impl<'d> Deserialize<'d> for Signature { formatter.write_str("An ed25519 signature as 64 bytes, as specified in RFC8032.") } - fn visit_bytes(self, bytes: &[u8]) -> Result where E: SerdeError{ + fn visit_bytes(self, bytes: &[u8]) -> Result + where + E: SerdeError, + { Signature::from_bytes(bytes).or(Err(SerdeError::invalid_length(bytes.len(), &self))) } } From d853856c3653abf49d0508557ce79093e9555e8f Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 30 Dec 2018 04:07:50 +0000 Subject: [PATCH 199/351] Run rustfmt on src/public.rs. --- src/public.rs | 59 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/src/public.rs b/src/public.rs index 60e504a..4ff2353 100644 --- a/src/public.rs +++ b/src/public.rs @@ -12,22 +12,22 @@ use core::fmt::Debug; use curve25519_dalek::constants; -use curve25519_dalek::digest::Digest; use curve25519_dalek::digest::generic_array::typenum::U64; +use curve25519_dalek::digest::Digest; use curve25519_dalek::edwards::CompressedEdwardsY; use curve25519_dalek::edwards::EdwardsPoint; use curve25519_dalek::scalar::Scalar; pub use sha2::Sha512; -#[cfg(feature = "serde")] -use serde::{Serialize, Deserialize}; -#[cfg(feature = "serde")] -use serde::{Serializer, Deserializer}; #[cfg(feature = "serde")] use serde::de::Error as SerdeError; #[cfg(feature = "serde")] use serde::de::Visitor; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; +#[cfg(feature = "serde")] +use serde::{Deserializer, Serializer}; use crate::constants::*; use crate::errors::*; @@ -36,10 +36,7 @@ use crate::signature::*; /// An ed25519 public key. #[derive(Copy, Clone, Default, Eq, PartialEq)] -pub struct PublicKey( - pub (crate) CompressedEdwardsY, - pub (crate) EdwardsPoint, -); +pub struct PublicKey(pub(crate) CompressedEdwardsY, pub(crate) EdwardsPoint); impl Debug for PublicKey { fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { @@ -56,8 +53,8 @@ impl AsRef<[u8]> for PublicKey { impl<'a> From<&'a SecretKey> for PublicKey { /// Derive this public key from its corresponding `SecretKey`. fn from(secret_key: &SecretKey) -> PublicKey { - let mut h: Sha512 = Sha512::new(); - let mut hash: [u8; 64] = [0u8; 64]; + let mut h: Sha512 = Sha512::new(); + let mut hash: [u8; 64] = [0u8; 64]; let mut digest: [u8; 32] = [0u8; 32]; h.input(secret_key.as_bytes()); @@ -130,14 +127,18 @@ impl PublicKey { #[inline] pub fn from_bytes(bytes: &[u8]) -> Result { if bytes.len() != PUBLIC_KEY_LENGTH { - return Err(SignatureError(InternalError::BytesLengthError{ - name: "PublicKey", length: PUBLIC_KEY_LENGTH })); + return Err(SignatureError(InternalError::BytesLengthError { + name: "PublicKey", + length: PUBLIC_KEY_LENGTH, + })); } let mut bits: [u8; 32] = [0u8; 32]; bits.copy_from_slice(&bytes[..32]); let compressed = CompressedEdwardsY(bits); - let point = compressed.decompress().ok_or(SignatureError(InternalError::PointDecompressionError))?; + let point = compressed + .decompress() + .ok_or(SignatureError(InternalError::PointDecompressionError))?; Ok(PublicKey(compressed, point)) } @@ -145,8 +146,10 @@ impl PublicKey { /// Internal utility function for mangling the bits of a (formerly /// mathematically well-defined) "scalar" and multiplying it to produce a /// public key. - fn mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(bits: &mut [u8; 32]) -> PublicKey { - bits[0] &= 248; + fn mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key( + bits: &mut [u8; 32], + ) -> PublicKey { + bits[0] &= 248; bits[31] &= 127; bits[31] |= 64; @@ -212,8 +215,8 @@ impl PublicKey { context: Option<&[u8]>, signature: &Signature, ) -> Result<(), SignatureError> - where - D: Digest, + where + D: Digest, { let mut h: Sha512 = Sha512::default(); let R: EdwardsPoint; @@ -245,25 +248,35 @@ impl PublicKey { #[cfg(feature = "serde")] impl Serialize for PublicKey { - fn serialize(&self, serializer: S) -> Result where S: Serializer { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { serializer.serialize_bytes(self.as_bytes()) } } #[cfg(feature = "serde")] impl<'d> Deserialize<'d> for PublicKey { - fn deserialize(deserializer: D) -> Result where D: Deserializer<'d> { - + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'d>, + { struct PublicKeyVisitor; impl<'d> Visitor<'d> for PublicKeyVisitor { type Value = PublicKey; fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - formatter.write_str("An ed25519 public key as a 32-byte compressed point, as specified in RFC8032") + formatter.write_str( + "An ed25519 public key as a 32-byte compressed point, as specified in RFC8032", + ) } - fn visit_bytes(self, bytes: &[u8]) -> Result where E: SerdeError { + fn visit_bytes(self, bytes: &[u8]) -> Result + where + E: SerdeError, + { PublicKey::from_bytes(bytes).or(Err(SerdeError::invalid_length(bytes.len(), &self))) } } From 811793ba2b532791b605bd27f12a20135f8bf0b4 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 30 Dec 2018 04:10:09 +0000 Subject: [PATCH 200/351] Run rustfmt on src/secret.rs. --- src/secret.rs | 83 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 28 deletions(-) diff --git a/src/secret.rs b/src/secret.rs index d5c3e67..4519ab5 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -14,8 +14,8 @@ use core::fmt::Debug; use clear_on_drop::clear::Clear; use curve25519_dalek::constants; -use curve25519_dalek::digest::Digest; use curve25519_dalek::digest::generic_array::typenum::U64; +use curve25519_dalek::digest::Digest; use curve25519_dalek::edwards::CompressedEdwardsY; use curve25519_dalek::scalar::Scalar; @@ -24,14 +24,14 @@ use rand::Rng; use sha2::Sha512; -#[cfg(feature = "serde")] -use serde::{Serialize, Deserialize}; -#[cfg(feature = "serde")] -use serde::{Serializer, Deserializer}; #[cfg(feature = "serde")] use serde::de::Error as SerdeError; #[cfg(feature = "serde")] use serde::de::Visitor; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; +#[cfg(feature = "serde")] +use serde::{Deserializer, Serializer}; use crate::constants::*; use crate::errors::*; @@ -40,7 +40,7 @@ use crate::signature::*; /// An EdDSA secret key. #[derive(Default)] // we derive Default in order to use the clear() method in Drop -pub struct SecretKey(pub (crate) [u8; SECRET_KEY_LENGTH]); +pub struct SecretKey(pub(crate) [u8; SECRET_KEY_LENGTH]); impl Debug for SecretKey { fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { @@ -110,8 +110,10 @@ impl SecretKey { #[inline] pub fn from_bytes(bytes: &[u8]) -> Result { if bytes.len() != SECRET_KEY_LENGTH { - return Err(SignatureError(InternalError::BytesLengthError{ - name: "SecretKey", length: SECRET_KEY_LENGTH })); + return Err(SignatureError(InternalError::BytesLengthError { + name: "SecretKey", + length: SECRET_KEY_LENGTH, + })); } let mut bits: [u8; 32] = [0u8; 32]; bits.copy_from_slice(&bytes[..32]); @@ -171,7 +173,8 @@ impl SecretKey { /// /// A CSPRNG with a `fill_bytes()` method, e.g. `rand::OsRng` pub fn generate(csprng: &mut T) -> SecretKey - where T: CryptoRng + Rng, + where + T: CryptoRng + Rng, { let mut sk: SecretKey = SecretKey([0u8; 32]); @@ -183,14 +186,20 @@ impl SecretKey { #[cfg(feature = "serde")] impl Serialize for SecretKey { - fn serialize(&self, serializer: S) -> Result where S: Serializer { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { serializer.serialize_bytes(self.as_bytes()) } } #[cfg(feature = "serde")] impl<'d> Deserialize<'d> for SecretKey { - fn deserialize(deserializer: D) -> Result where D: Deserializer<'d> { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'d>, + { struct SecretKeyVisitor; impl<'d> Visitor<'d> for SecretKeyVisitor { @@ -200,7 +209,10 @@ impl<'d> Deserialize<'d> for SecretKey { formatter.write_str("An ed25519 secret key as 32 bytes, as specified in RFC8032.") } - fn visit_bytes(self, bytes: &[u8]) -> Result where E: SerdeError { + fn visit_bytes(self, bytes: &[u8]) -> Result + where + E: SerdeError, + { SecretKey::from_bytes(bytes).or(Err(SerdeError::invalid_length(bytes.len(), &self))) } } @@ -245,8 +257,8 @@ impl<'d> Deserialize<'d> for SecretKey { // "generalised EdDSA" and "VXEdDSA". #[derive(Default)] // we derive Default in order to use the clear() method in Drop pub struct ExpandedSecretKey { - pub (crate) key: Scalar, - pub (crate) nonce: [u8; 32], + pub(crate) key: Scalar, + pub(crate) nonce: [u8; 32], } /// Overwrite secret key material with null bytes when it goes out of scope. @@ -388,8 +400,10 @@ impl ExpandedSecretKey { #[inline] pub fn from_bytes(bytes: &[u8]) -> Result { if bytes.len() != EXPANDED_SECRET_KEY_LENGTH { - return Err(SignatureError(InternalError::BytesLengthError{ - name: "ExpandedSecretKey", length: EXPANDED_SECRET_KEY_LENGTH })); + return Err(SignatureError(InternalError::BytesLengthError { + name: "ExpandedSecretKey", + length: EXPANDED_SECRET_KEY_LENGTH, + })); } let mut lower: [u8; 32] = [0u8; 32]; let mut upper: [u8; 32] = [0u8; 32]; @@ -397,8 +411,10 @@ impl ExpandedSecretKey { lower.copy_from_slice(&bytes[00..32]); upper.copy_from_slice(&bytes[32..64]); - Ok(ExpandedSecretKey{ key: Scalar::from_bits(lower), - nonce: upper }) + Ok(ExpandedSecretKey { + key: Scalar::from_bits(lower), + nonce: upper, + }) } /// Sign a message with this `ExpandedSecretKey`. @@ -424,7 +440,7 @@ impl ExpandedSecretKey { k = Scalar::from_hash(h); s = &(&k * &self.key) + &r; - Signature{ R, s } + Signature { R, s } } /// Sign a `prehashed_message` with this `ExpandedSecretKey` using the @@ -452,8 +468,8 @@ impl ExpandedSecretKey { public_key: &PublicKey, context: Option<&'static [u8]>, ) -> Signature - where - D: Digest, + where + D: Digest, { let mut h: Sha512; let mut prehash: [u8; 64] = [0u8; 64]; @@ -506,32 +522,43 @@ impl ExpandedSecretKey { k = Scalar::from_hash(h); s = &(&k * &self.key) + &r; - Signature{ R, s } + Signature { R, s } } - } #[cfg(feature = "serde")] impl Serialize for ExpandedSecretKey { - fn serialize(&self, serializer: S) -> Result where S: Serializer { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { serializer.serialize_bytes(&self.to_bytes()[..]) } } #[cfg(feature = "serde")] impl<'d> Deserialize<'d> for ExpandedSecretKey { - fn deserialize(deserializer: D) -> Result where D: Deserializer<'d> { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'d>, + { struct ExpandedSecretKeyVisitor; impl<'d> Visitor<'d> for ExpandedSecretKeyVisitor { type Value = ExpandedSecretKey; fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - formatter.write_str("An ed25519 expanded secret key as 64 bytes, as specified in RFC8032.") + formatter.write_str( + "An ed25519 expanded secret key as 64 bytes, as specified in RFC8032.", + ) } - fn visit_bytes(self, bytes: &[u8]) -> Result where E: SerdeError { - ExpandedSecretKey::from_bytes(bytes).or(Err(SerdeError::invalid_length(bytes.len(), &self))) + fn visit_bytes(self, bytes: &[u8]) -> Result + where + E: SerdeError, + { + ExpandedSecretKey::from_bytes(bytes) + .or(Err(SerdeError::invalid_length(bytes.len(), &self))) } } deserializer.deserialize_bytes(ExpandedSecretKeyVisitor) From ad49d31bbc3c6a3603eabb9b7bbe6ff219e8e508 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 30 Dec 2018 04:10:59 +0000 Subject: [PATCH 201/351] Run rustfmt on src/errors.rs. --- src/errors.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index f531849..30f821f 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -19,7 +19,7 @@ use core::fmt::Display; /// Internal errors. Most application-level developers will likely not /// need to pay any attention to these. #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] -pub (crate) enum InternalError { +pub(crate) enum InternalError { PointDecompressionError, ScalarFormatError, /// An error in the length of bytes handed to a constructor. @@ -27,7 +27,10 @@ pub (crate) enum InternalError { /// To use this, pass a string specifying the `name` of the type which is /// returning the error, and the `length` in bytes which its constructor /// expects. - BytesLengthError{ name: &'static str, length: usize }, + BytesLengthError { + name: &'static str, + length: usize, + }, /// The verification equation wasn't satisfied VerifyError, } @@ -64,7 +67,7 @@ impl ::failure::Fail for InternalError {} /// /// * Failure of a signature to satisfy the verification equation. #[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)] -pub struct SignatureError(pub (crate) InternalError); +pub struct SignatureError(pub(crate) InternalError); impl Display for SignatureError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { From fa726cfdccf9014d3e825ac68bd4d225383d01d7 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 30 Dec 2018 04:12:33 +0000 Subject: [PATCH 202/351] Run rustfmt on src/lib.rs. --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index de05f8b..70e8ef0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -251,9 +251,9 @@ extern crate clear_on_drop; extern crate curve25519_dalek; extern crate failure; extern crate rand; -extern crate sha2; #[cfg(feature = "serde")] extern crate serde; +extern crate sha2; mod constants; mod ed25519; From 8b30d4084ac3323ae1e7ef510a8f4fdcfe2358b1 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 30 Dec 2018 04:16:03 +0000 Subject: [PATCH 203/351] Run rustfmt on src/ed25519.rs. --- src/ed25519.rs | 56 ++++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index a6d5f31..5708108 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -14,19 +14,19 @@ use core::default::Default; use rand::CryptoRng; use rand::Rng; -#[cfg(feature = "serde")] -use serde::{Serialize, Deserialize}; -#[cfg(feature = "serde")] -use serde::{Serializer, Deserializer}; #[cfg(feature = "serde")] use serde::de::Error as SerdeError; #[cfg(feature = "serde")] use serde::de::Visitor; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; +#[cfg(feature = "serde")] +use serde::{Deserializer, Serializer}; pub use sha2::Sha512; -pub use curve25519_dalek::digest::Digest; use curve25519_dalek::digest::generic_array::typenum::U64; +pub use curve25519_dalek::digest::Digest; use curve25519_dalek::constants; use curve25519_dalek::edwards::EdwardsPoint; @@ -199,8 +199,10 @@ impl Keypair { /// is an `SignatureError` describing the error that occurred. pub fn from_bytes<'a>(bytes: &'a [u8]) -> Result { if bytes.len() != KEYPAIR_LENGTH { - return Err(SignatureError(InternalError::BytesLengthError{ - name: "Keypair", length: KEYPAIR_LENGTH})); + return Err(SignatureError(InternalError::BytesLengthError { + name: "Keypair", + length: KEYPAIR_LENGTH, + })); } let secret = SecretKey::from_bytes(&bytes[..SECRET_KEY_LENGTH])?; let public = PublicKey::from_bytes(&bytes[SECRET_KEY_LENGTH..])?; @@ -243,7 +245,8 @@ impl Keypair { /// which is available with `use sha2::Sha512` as in the example above. /// Other suitable hash functions include Keccak-512 and Blake2b-512. pub fn generate(csprng: &mut R) -> Keypair - where R: CryptoRng + Rng, + where + R: CryptoRng + Rng, { let sk: SecretKey = SecretKey::generate(csprng); let pk: PublicKey = (&sk).into(); @@ -252,8 +255,7 @@ impl Keypair { } /// Sign a message with this keypair's secret key. - pub fn sign(&self, message: &[u8]) -> Signature - { + pub fn sign(&self, message: &[u8]) -> Signature { let expanded: ExpandedSecretKey = (&self.secret).into(); expanded.sign(&message, &self.public) @@ -356,12 +358,12 @@ impl Keypair { pub fn sign_prehashed( &self, prehashed_message: D, - context: Option<&'static [u8]> + context: Option<&'static [u8]>, ) -> Signature - where - D: Digest, + where + D: Digest, { - let expanded: ExpandedSecretKey = (&self.secret).into(); // xxx thanks i hate this + let expanded: ExpandedSecretKey = (&self.secret).into(); // xxx thanks i hate this expanded.sign_prehashed(prehashed_message, &self.public, context) } @@ -436,10 +438,10 @@ impl Keypair { &self, prehashed_message: D, context: Option<&[u8]>, - signature: &Signature + signature: &Signature, ) -> Result<(), SignatureError> - where - D: Digest, + where + D: Digest, { self.public.verify_prehashed(prehashed_message, context, signature) } @@ -447,15 +449,20 @@ impl Keypair { #[cfg(feature = "serde")] impl Serialize for Keypair { - fn serialize(&self, serializer: S) -> Result where S: Serializer { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { serializer.serialize_bytes(&self.to_bytes()[..]) } } #[cfg(feature = "serde")] impl<'d> Deserialize<'d> for Keypair { - fn deserialize(deserializer: D) -> Result where D: Deserializer<'d> { - + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'d>, + { struct KeypairVisitor; impl<'d> Visitor<'d> for KeypairVisitor { @@ -467,7 +474,10 @@ impl<'d> Deserialize<'d> for Keypair { 32 bytes is a compressed point for a public key.") } - fn visit_bytes(self, bytes: &[u8]) -> Result where E: SerdeError { + fn visit_bytes(self, bytes: &[u8]) -> Result + where + E: SerdeError, + { let secret_key = SecretKey::from_bytes(&bytes[..SECRET_KEY_LENGTH]); let public_key = PublicKey::from_bytes(&bytes[SECRET_KEY_LENGTH..]); @@ -498,9 +508,7 @@ mod test { use std::mem; use std::slice; - unsafe { - slice::from_raw_parts(x as *const T as *const u8, mem::size_of_val(x)) - } + unsafe { slice::from_raw_parts(x as *const T as *const u8, mem::size_of_val(x)) } } assert!(!as_bytes(&keypair).contains(&0x15)); From 82cdcb9cc934fbbbff1ab1b27dfb29382b276059 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 30 Dec 2018 04:26:22 +0000 Subject: [PATCH 204/351] Fix benchmarks after merging #64. --- benches/ed25519_benchmarks.rs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/benches/ed25519_benchmarks.rs b/benches/ed25519_benchmarks.rs index 79575c9..c628bd7 100644 --- a/benches/ed25519_benchmarks.rs +++ b/benches/ed25519_benchmarks.rs @@ -11,7 +11,6 @@ extern crate criterion; extern crate ed25519_dalek; extern crate rand; -extern crate sha2; use criterion::Criterion; @@ -24,37 +23,36 @@ mod ed25519_benches { use ed25519_dalek::verify_batch; use rand::thread_rng; use rand::rngs::ThreadRng; - use sha2::Sha512; fn sign(c: &mut Criterion) { let mut csprng: ThreadRng = thread_rng(); - let keypair: Keypair = Keypair::generate::(&mut csprng); + let keypair: Keypair = Keypair::generate(&mut csprng); let msg: &[u8] = b""; c.bench_function("Ed25519 signing", move |b| { - b.iter(| | keypair.sign::(msg)) + b.iter(| | keypair.sign(msg)) }); } fn sign_expanded_key(c: &mut Criterion) { let mut csprng: ThreadRng = thread_rng(); - let keypair: Keypair = Keypair::generate::(&mut csprng); - let expanded: ExpandedSecretKey = keypair.secret.expand::(); + let keypair: Keypair = Keypair::generate(&mut csprng); + let expanded: ExpandedSecretKey = (&keypair.secret).into(); let msg: &[u8] = b""; c.bench_function("Ed25519 signing with an expanded secret key", move |b| { - b.iter(| | expanded.sign::(msg, &keypair.public)) + b.iter(| | expanded.sign(msg, &keypair.public)) }); } fn verify(c: &mut Criterion) { let mut csprng: ThreadRng = thread_rng(); - let keypair: Keypair = Keypair::generate::(&mut csprng); + let keypair: Keypair = Keypair::generate(&mut csprng); let msg: &[u8] = b""; - let sig: Signature = keypair.sign::(msg); + let sig: Signature = keypair.sign(msg); c.bench_function("Ed25519 signature verification", move |b| { - b.iter(| | keypair.verify::(msg, &sig)) + b.iter(| | keypair.verify(msg, &sig)) }); } @@ -65,13 +63,13 @@ mod ed25519_benches { "Ed25519 batch signature verification", |b, &&size| { let mut csprng: ThreadRng = thread_rng(); - let keypairs: Vec = (0..size).map(|_| Keypair::generate::(&mut csprng)).collect(); + let keypairs: Vec = (0..size).map(|_| Keypair::generate(&mut csprng)).collect(); let msg: &[u8] = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; let messages: Vec<&[u8]> = (0..size).map(|_| msg).collect(); - let signatures: Vec = keypairs.iter().map(|key| key.sign::(&msg)).collect(); + let signatures: Vec = keypairs.iter().map(|key| key.sign(&msg)).collect(); let public_keys: Vec = keypairs.iter().map(|key| key.public).collect(); - b.iter(|| verify_batch::(&messages[..], &signatures[..], &public_keys[..])); + b.iter(|| verify_batch(&messages[..], &signatures[..], &public_keys[..])); }, &BATCH_SIZES, ); @@ -81,7 +79,7 @@ mod ed25519_benches { let mut csprng: ThreadRng = thread_rng(); c.bench_function("Ed25519 keypair generation", move |b| { - b.iter(| | Keypair::generate::(&mut csprng)) + b.iter(| | Keypair::generate(&mut csprng)) }); } From 42b571eb24d77877ea933d7d87dff6f08650df57 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 30 Dec 2018 04:26:56 +0000 Subject: [PATCH 205/351] Remove unused clear_on_drop import from tests. --- tests/ed25519.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/ed25519.rs b/tests/ed25519.rs index ff89b90..c7e358a 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -11,7 +11,6 @@ #[cfg(all(test, feature = "serde"))] extern crate bincode; -extern crate clear_on_drop; extern crate ed25519_dalek; extern crate hex; extern crate rand; From 0ddf39e44371a961bfe4669832deef23c5a3c412 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 30 Dec 2018 04:27:33 +0000 Subject: [PATCH 206/351] Revise some module descriptions. --- src/ed25519.rs | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 5708108..6b7e3cc 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -7,7 +7,7 @@ // Authors: // - isis agora lovecruft -//! A Rust implementation of ed25519 key generation, signing, and verification. +//! ed25519 keypairs and batch verification. use core::default::Default; diff --git a/src/lib.rs b/src/lib.rs index 70e8ef0..d6ab121 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,7 @@ // Authors: // - Isis Agora Lovecruft -//! ed25519 signatures and verification +//! A Rust implementation of ed25519 key generation, signing, and verification. //! //! # Example //! From 762eb0c4703c0a954961f0aeb31974b60f807e22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Sat, 5 Jan 2019 15:58:59 +0300 Subject: [PATCH 207/351] use rand_core --- Cargo.toml | 15 ++++++--- src/ed25519.rs | 86 +++++++++++++++++++++++++------------------------- src/lib.rs | 6 +++- 3 files changed, 58 insertions(+), 49 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9e3e300..89b9fea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,10 +19,13 @@ travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master" version = "1.0.0-pre.0" default-features = false -[dependencies.rand] -version = "0.5" +[dependencies.rand_core] +version = "0.3" default-features = false -features = ["i128_support"] + +[dependencies.rand] +version = "0.6" +optional = true [dependencies.serde] version = "^1.0" @@ -44,6 +47,8 @@ hex = "^0.3" sha2 = "^0.8" bincode = "^0.9" criterion = "0.2" +rand_os = "0.1.0" +rand_chacha = "0.1.0" [[bench]] name = "ed25519_benchmarks" @@ -52,9 +57,9 @@ harness = false [features] default = ["std", "u64_backend"] # We don't add "rand/std" here because it would enable a bunch of Fuchsia dependencies. -std = ["curve25519-dalek/std"] +std = ["curve25519-dalek/std", "rand"] alloc = ["curve25519-dalek/alloc"] -nightly = ["curve25519-dalek/nightly", "rand/nightly", "clear_on_drop/nightly"] +nightly = ["curve25519-dalek/nightly", "clear_on_drop/nightly"] asm = ["sha2/asm"] yolocrypto = ["curve25519-dalek/yolocrypto"] u64_backend = ["curve25519-dalek/u64_backend"] diff --git a/src/ed25519.rs b/src/ed25519.rs index 0654eb1..829908d 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -13,8 +13,7 @@ use core::default::Default; use core::fmt::{Debug}; -use rand::CryptoRng; -use rand::Rng; +use rand_core::{CryptoRng, RngCore}; #[cfg(feature = "serde")] use serde::{Serialize, Deserialize}; @@ -253,15 +252,15 @@ impl SecretKey { /// # Example /// /// ``` - /// extern crate rand; + /// extern crate rand_os; /// extern crate sha2; /// extern crate ed25519_dalek; /// /// # #[cfg(feature = "std")] /// # fn main() { /// # - /// use rand::Rng; - /// use rand::OsRng; + /// use rand_os::OsRng; + /// use sha2::Sha512; /// use ed25519_dalek::PublicKey; /// use ed25519_dalek::SecretKey; @@ -280,15 +279,15 @@ impl SecretKey { /// traits, and which returns 512 bits of output—via: /// /// ``` - /// # extern crate rand; + /// # extern crate rand_chacha; + /// # extern crate rand_core; /// # extern crate sha2; /// # extern crate ed25519_dalek; /// # /// # fn main() { /// # - /// # use rand::Rng; - /// # use rand::ChaChaRng; - /// # use rand::SeedableRng; + /// # use rand_core::SeedableRng; + /// # use rand_chacha::ChaChaRng; /// # use sha2::Sha512; /// # use ed25519_dalek::PublicKey; /// # use ed25519_dalek::SecretKey; @@ -309,7 +308,7 @@ impl SecretKey { /// /// A CSPRNG with a `fill_bytes()` method, e.g. `rand::ChaChaRng` pub fn generate(csprng: &mut T) -> SecretKey - where T: CryptoRng + Rng, + where T: CryptoRng + RngCore, { let mut sk: SecretKey = SecretKey([0u8; 32]); @@ -403,14 +402,14 @@ impl<'a> From<&'a SecretKey> for ExpandedSecretKey { /// # Examples /// /// ``` - /// # extern crate rand; + /// # extern crate rand_os; /// # extern crate sha2; /// # extern crate ed25519_dalek; /// # /// # #[cfg(all(feature = "std", feature = "sha2"))] /// # fn main() { /// # - /// use rand::{Rng, OsRng}; + /// use rand_os::OsRng; /// use sha2::Sha512; /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; /// @@ -439,14 +438,14 @@ impl ExpandedSecretKey { /// # Examples /// /// ``` - /// # extern crate rand; + /// # extern crate rand_os; /// # extern crate sha2; /// # extern crate ed25519_dalek; /// # /// # #[cfg(all(feature = "sha2", feature = "std"))] /// # fn main() { /// # - /// use rand::{Rng, OsRng}; + /// use rand_os::OsRng; /// use sha2::Sha512; /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; /// @@ -480,16 +479,17 @@ impl ExpandedSecretKey { /// # Examples /// /// ``` - /// # extern crate rand; + /// # extern crate rand_os; /// # extern crate sha2; /// # extern crate ed25519_dalek; /// # + /// use rand_os::OsRng; + /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; + /// use ed25519_dalek::SignatureError; + /// # /// # #[cfg(all(feature = "sha2", feature = "std"))] /// # fn do_test() -> Result { /// # - /// use rand::{Rng, OsRng}; - /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; - /// use ed25519_dalek::SignatureError; /// /// let mut csprng: OsRng = OsRng::new().unwrap(); /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); @@ -530,14 +530,14 @@ impl ExpandedSecretKey { /// # Examples /// /// ``` - /// # extern crate rand; + /// # extern crate rand_os; /// # extern crate sha2; /// # extern crate ed25519_dalek; /// # /// # #[cfg(all(feature = "std", feature = "sha2"))] /// # fn main() { /// # - /// use rand::{Rng, OsRng}; + /// use rand_os::OsRng; /// use sha2::Sha512; /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; /// @@ -914,7 +914,7 @@ impl From for PublicKey { /// * `messages` is a slice of byte slices, one per signed message. /// * `signatures` is a slice of `Signature`s. /// * `public_keys` is a slice of `PublicKey`s. -/// * `csprng` is an implementation of `Rng + CryptoRng`, such as `rand::ThreadRng`. +/// * `csprng` is an implementation of `Rng + CryptoRng`, such as `rand::rngs::ThreadRng`. /// /// # Panics /// @@ -939,7 +939,7 @@ impl From for PublicKey { /// use ed25519_dalek::PublicKey; /// use ed25519_dalek::Signature; /// use rand::thread_rng; -/// use rand::ThreadRng; +/// use rand::rngs::ThreadRng; /// use sha2::Sha512; /// /// # fn main() { @@ -972,7 +972,7 @@ pub fn verify_batch(messages: &[&[u8]], use std::vec::Vec; use core::iter::once; - use rand::thread_rng; + use rand::{Rng, thread_rng}; use curve25519_dalek::traits::IsIdentity; use curve25519_dalek::traits::VartimeMultiscalarMul; @@ -1111,15 +1111,14 @@ impl Keypair { /// # Example /// /// ``` - /// extern crate rand; + /// extern crate rand_os; /// extern crate sha2; /// extern crate ed25519_dalek; /// /// # #[cfg(all(feature = "std", feature = "sha2"))] /// # fn main() { /// - /// use rand::Rng; - /// use rand::OsRng; + /// use rand_os::OsRng; /// use sha2::Sha512; /// use ed25519_dalek::Keypair; /// use ed25519_dalek::Signature; @@ -1135,7 +1134,7 @@ impl Keypair { /// /// # Input /// - /// A CSPRNG with a `fill_bytes()` method, e.g. `rand::ChaChaRng`. + /// A CSPRNG with a `fill_bytes()` method, e.g. `rand_chacha::ChaChaRng`. /// /// The caller must also supply a hash function which implements the /// `Digest` and `Default` traits, and which returns 512 bits of output. @@ -1144,7 +1143,7 @@ impl Keypair { /// Other suitable hash functions include Keccak-512 and Blake2b-512. pub fn generate(csprng: &mut R) -> Keypair where D: Digest + Default, - R: CryptoRng + Rng, + R: CryptoRng + RngCore, { let sk: SecretKey = SecretKey::generate(csprng); let pk: PublicKey = PublicKey::from_secret::(&sk); @@ -1184,8 +1183,8 @@ impl Keypair { /// use ed25519_dalek::Keypair; /// use ed25519_dalek::Signature; /// use rand::thread_rng; - /// use rand::ThreadRng; - /// use sha2::Sha512; + /// use rand::rngs::ThreadRng; + /// use sha2::{Sha512, Digest}; /// /// # #[cfg(all(feature = "std", feature = "sha2"))] /// # fn main() { @@ -1194,7 +1193,7 @@ impl Keypair { /// let message: &[u8] = b"All I want is to pet all of the dogs."; /// /// // Create a hash digest object which we'll feed the message into: - /// let prehashed: Sha512 = Sha512::default(); + /// let mut prehashed: Sha512 = Sha512::default(); /// /// prehashed.input(message); /// # } @@ -1232,15 +1231,15 @@ impl Keypair { /// # use ed25519_dalek::Keypair; /// # use ed25519_dalek::Signature; /// # use rand::thread_rng; - /// # use rand::ThreadRng; - /// # use sha2::Sha512; + /// # use rand::rngs::ThreadRng; + /// # use sha2::{Sha512, Digest}; /// # /// # #[cfg(all(feature = "std", feature = "sha2"))] /// # fn main() { /// # let mut csprng: ThreadRng = thread_rng(); /// # let keypair: Keypair = Keypair::generate::(&mut csprng); /// # let message: &[u8] = b"All I want is to pet all of the dogs."; - /// # let prehashed: Sha512 = Sha512::default(); + /// # let mut prehashed: Sha512 = Sha512::default(); /// # prehashed.input(message); /// # /// let context: &[u8] = b"Ed25519DalekSignPrehashedDoctest"; @@ -1294,9 +1293,10 @@ impl Keypair { /// /// use ed25519_dalek::Keypair; /// use ed25519_dalek::Signature; + /// use ed25519_dalek::SignatureError; /// use rand::thread_rng; - /// use rand::ThreadRng; - /// use sha2::Sha512; + /// use rand::rngs::ThreadRng; + /// use sha2::{Sha512, Digest}; /// /// # #[cfg(all(feature = "std", feature = "sha2"))] /// # fn main() { @@ -1304,7 +1304,7 @@ impl Keypair { /// let keypair: Keypair = Keypair::generate::(&mut csprng); /// let message: &[u8] = b"All I want is to pet all of the dogs."; /// - /// let prehashed: Sha512 = Sha512::default(); + /// let mut prehashed: Sha512 = Sha512::new(); /// prehashed.input(message); /// /// let context: &[u8] = b"Ed25519DalekSignPrehashedDoctest"; @@ -1312,12 +1312,12 @@ impl Keypair { /// let sig: Signature = keypair.sign_prehashed(prehashed, Some(context)); /// /// // The sha2::Sha512 struct doesn't implement Copy, so we'll have to create a new one: - /// let prehashed_again: Sha512 = Sha512::default(); + /// let mut prehashed_again: Sha512 = Sha512::default(); /// prehashed_again.input(message); /// - /// let valid: bool = keypair.public.verify_prehashed(prehashed_again, context, sig); + /// let res: Result<(), SignatureError> = keypair.public.verify_prehashed(prehashed_again, Some(context), &sig); /// - /// assert!(valid); + /// assert!(res.is_ok()); /// # } /// # /// # #[cfg(any(not(feature = "sha2"), not(feature = "std")))] @@ -1380,9 +1380,9 @@ mod test { use std::string::String; use std::vec::Vec; use rand::thread_rng; - use rand::ChaChaRng; - use rand::SeedableRng; - use rand::ThreadRng; + use rand::rngs::ThreadRng; + use rand_chacha::ChaChaRng; + use rand_core::SeedableRng; use hex::FromHex; use sha2::Sha512; use super::*; diff --git a/src/lib.rs b/src/lib.rs index 488ea5c..f113a47 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -259,8 +259,12 @@ extern crate curve25519_dalek; extern crate failure; -extern crate rand; +extern crate rand_core; extern crate clear_on_drop; +#[cfg(any(feature = "std", test))] +extern crate rand; +#[cfg(test)] +extern crate rand_chacha; #[cfg(any(feature = "std", test))] #[macro_use] From 35b4dfd24d52bf750e4bb4315d43d4601fa5f8b9 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 17 Jan 2019 22:39:48 +0000 Subject: [PATCH 208/351] Bump rand dependency version to 0.6. --- Cargo.toml | 2 +- src/ed25519.rs | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9e3e300..3c626e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ version = "1.0.0-pre.0" default-features = false [dependencies.rand] -version = "0.5" +version = "0.6" default-features = false features = ["i128_support"] diff --git a/src/ed25519.rs b/src/ed25519.rs index 0654eb1..b754bb4 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -939,11 +939,10 @@ impl From for PublicKey { /// use ed25519_dalek::PublicKey; /// use ed25519_dalek::Signature; /// use rand::thread_rng; -/// use rand::ThreadRng; /// use sha2::Sha512; /// /// # fn main() { -/// let mut csprng: ThreadRng = thread_rng(); +/// let mut csprng = thread_rng(); /// let keypairs: Vec = (0..64).map(|_| Keypair::generate::(&mut csprng)).collect(); /// let msg: &[u8] = b"They're good dogs Brant"; /// let messages: Vec<&[u8]> = (0..64).map(|_| msg).collect(); @@ -1382,7 +1381,6 @@ mod test { use rand::thread_rng; use rand::ChaChaRng; use rand::SeedableRng; - use rand::ThreadRng; use hex::FromHex; use sha2::Sha512; use super::*; @@ -1563,7 +1561,7 @@ mod test { b"Fuck dumbin' it down, spit ice, skip jewellery: Molotov cocktails on me like accessories.", b"Hey, I never cared about your bucks, so if I run up with a mask on, probably got a gas can too.", b"And I'm not here to fill 'er up. Nope, we came to riot, here to incite, we don't want any of your stuff.", ]; - let mut csprng: ThreadRng = thread_rng(); + let mut csprng = thread_rng(); let mut keypairs: Vec = Vec::new(); let mut signatures: Vec = Vec::new(); From f851aaf464cff9a215306c6191618539a3eab9e7 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 17 Jan 2019 22:41:18 +0000 Subject: [PATCH 209/351] Bump ed25519-dalek version to 0.9. --- Cargo.toml | 2 +- README.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3c626e7..9b8273e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "1.0.0-pre.0" +version = "0.9.0" authors = ["Isis Lovecruft "] readme = "README.md" license = "BSD-3-Clause" diff --git a/README.md b/README.md index 8ebe578..fd883ea 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,7 @@ To install, add the following to your project's `Cargo.toml`: ```toml [dependencies.ed25519-dalek] -version = "1" +version = "0.9" ``` Then, in your library or executable source, add: @@ -134,7 +134,7 @@ enabled by default, instead do: ```toml [dependencies.ed25519-dalek] -version = "1" +version = "0.9" features = ["nightly"] ``` @@ -151,7 +151,7 @@ To enable [serde](https://serde.rs) support, build `ed25519-dalek` with: ```toml [dependencies.ed25519-dalek] -version = "1" +version = "0.9" features = ["serde"] ``` From 84abd4855a64097e45f4c7e33cc4fbe2ff8b6ad7 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 18 Jan 2019 04:45:23 +0000 Subject: [PATCH 210/351] Update curve25519-dalek dependency to 1.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 9b8273e..41181e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ exclude = [ ".gitignore", "TESTVECTORS", "res/*" ] travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master"} [dependencies.curve25519-dalek] -version = "1.0.0-pre.0" +version = "1" default-features = false [dependencies.rand] From d447efbd593de90dff5cff0886b5f8c9c1da9d9c Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 18 Jan 2019 04:47:54 +0000 Subject: [PATCH 211/351] Bump ed25519-dalek to 0.9.1. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 41181e1..4ed5f7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "0.9.0" +version = "0.9.1" authors = ["Isis Lovecruft "] readme = "README.md" license = "BSD-3-Clause" From 6d1d3ff5ea8f40b0d19e88c9c5c3e67870b05618 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 17 Jan 2019 20:39:23 +0000 Subject: [PATCH 212/351] Remove outdated comment about Fuchsia dependencies from Cargo.toml. --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 5d5cc94..c64aa9c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,6 @@ harness = false [features] default = ["std", "u64_backend"] -# We don't add "rand/std" here because it would enable a bunch of Fuchsia dependencies. std = ["curve25519-dalek/std", "rand/std", "sha2/std"] alloc = ["curve25519-dalek/alloc"] nightly = ["curve25519-dalek/nightly", "rand/nightly", "clear_on_drop/nightly"] From 131fc2b07f7e5197a14ba3ab938f574def5205f2 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 17 Jan 2019 20:52:32 +0000 Subject: [PATCH 213/351] Bump ed25519-dalek version to 1.0.0-pre.1. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c64aa9c..bba552a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "1.0.0-pre.0" +version = "1.0.0-pre.1" authors = ["Isis Lovecruft "] readme = "README.md" license = "BSD-3-Clause" From ae8764fbeff32ac0fef41850864761226b670cdd Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 17 Jan 2019 23:28:13 +0000 Subject: [PATCH 214/351] Update copyright year to 2019 and destroy capitalism. --- Cargo.toml | 2 +- LICENSE | 2 +- benches/ed25519_benchmarks.rs | 4 ++-- src/constants.rs | 2 +- src/ed25519.rs | 2 +- src/errors.rs | 4 ++-- src/lib.rs | 4 ++-- src/public.rs | 2 +- src/secret.rs | 2 +- src/signature.rs | 2 +- tests/ed25519.rs | 2 +- 11 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bba552a..fbf67f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ed25519-dalek" version = "1.0.0-pre.1" -authors = ["Isis Lovecruft "] +authors = ["isis lovecruft "] readme = "README.md" license = "BSD-3-Clause" repository = "https://github.com/dalek-cryptography/ed25519-dalek" diff --git a/LICENSE b/LICENSE index 0d9a49e..acf8498 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2017-2018 Isis Agora Lovecruft. All rights reserved. +Copyright (c) 2017-2019 isis agora lovecruft. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff --git a/benches/ed25519_benchmarks.rs b/benches/ed25519_benchmarks.rs index c628bd7..52cb597 100644 --- a/benches/ed25519_benchmarks.rs +++ b/benches/ed25519_benchmarks.rs @@ -1,11 +1,11 @@ // -*- mode: rust; -*- // // This file is part of ed25519-dalek. -// Copyright (c) 2018 Isis Lovecruft +// Copyright (c) 2018-2019 isis lovecruft // See LICENSE for licensing information. // // Authors: -// - Isis Agora Lovecruft +// - isis agora lovecruft #[macro_use] extern crate criterion; diff --git a/src/constants.rs b/src/constants.rs index 783ffb2..f8ccb84 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of ed25519-dalek. -// Copyright (c) 2017-2018 isis lovecruft +// Copyright (c) 2017-2019 isis lovecruft // See LICENSE for licensing information. // // Authors: diff --git a/src/ed25519.rs b/src/ed25519.rs index 6b7e3cc..2d144ce 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of ed25519-dalek. -// Copyright (c) 2017-2018 isis lovecruft +// Copyright (c) 2017-2019 isis lovecruft // See LICENSE for licensing information. // // Authors: diff --git a/src/errors.rs b/src/errors.rs index 30f821f..6597f73 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,11 +1,11 @@ // -*- mode: rust; -*- // // This file is part of ed25519-dalek. -// Copyright (c) 2017 Isis Lovecruft +// Copyright (c) 2017-2019 isis lovecruft // See LICENSE for licensing information. // // Authors: -// - Isis Agora Lovecruft +// - isis agora lovecruft //! Errors which may occur when parsing keys and/or signatures to or from wire formats. diff --git a/src/lib.rs b/src/lib.rs index d6ab121..faf6873 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,11 @@ // -*- mode: rust; -*- // // This file is part of ed25519-dalek. -// Copyright (c) 2017-2018 Isis Lovecruft +// Copyright (c) 2017-2019 isis lovecruft // See LICENSE for licensing information. // // Authors: -// - Isis Agora Lovecruft +// - isis agora lovecruft //! A Rust implementation of ed25519 key generation, signing, and verification. //! diff --git a/src/public.rs b/src/public.rs index 4ff2353..ae3bfa3 100644 --- a/src/public.rs +++ b/src/public.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of ed25519-dalek. -// Copyright (c) 2017-2018 isis lovecruft +// Copyright (c) 2017-2019 isis lovecruft // See LICENSE for licensing information. // // Authors: diff --git a/src/secret.rs b/src/secret.rs index 4519ab5..3bfeb7c 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of ed25519-dalek. -// Copyright (c) 2017-2018 isis lovecruft +// Copyright (c) 2017-2019 isis lovecruft // See LICENSE for licensing information. // // Authors: diff --git a/src/signature.rs b/src/signature.rs index 3dbc478..d5079fd 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of ed25519-dalek. -// Copyright (c) 2017-2018 isis lovecruft +// Copyright (c) 2017-2019 isis lovecruft // See LICENSE for licensing information. // // Authors: diff --git a/tests/ed25519.rs b/tests/ed25519.rs index c7e358a..d849e41 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of ed25519-dalek. -// Copyright (c) 2017-2018 isis lovecruft +// Copyright (c) 2017-2019 isis lovecruft // See LICENSE for licensing information. // // Authors: From e527280d2e5781e070da269893cc514392312fdd Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 17 Jan 2019 23:34:29 +0000 Subject: [PATCH 215/351] Remove TravisCI test for the sha2 feature which was removed. --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 82c3918..f7f92d7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,9 +20,6 @@ matrix: # Test serde support on stable, assuming that if it works there it'll work everywhere: - rust: stable env: TEST_COMMAND=test FEATURE='--features=serde' - # Test with the optional sha2 feature enabled: - - rust: stable - env: TEST_COMMAND=test FEATURE='--features=sha2' script: - cargo $TEST_COMMAND $FEATURES From 1dfe00b79d5f4b8ec29cd4b581cd72913c28fd8f Mon Sep 17 00:00:00 2001 From: Nicolas Stalder Date: Sun, 27 Jan 2019 03:06:18 +0100 Subject: [PATCH 216/351] Fix rand dependency, deal with unusedness warnings --- Cargo.toml | 1 + src/ed25519.rs | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index fbf67f8..8298c07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ default-features = false [dependencies.rand] version = "0.6" features = ["i128_support"] +default-features = false [dependencies.serde] version = "^1.0" diff --git a/src/ed25519.rs b/src/ed25519.rs index 2d144ce..7262bee 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -9,6 +9,7 @@ //! ed25519 keypairs and batch verification. +#[allow(unused_imports)] use core::default::Default; use rand::CryptoRng; @@ -28,8 +29,11 @@ pub use sha2::Sha512; use curve25519_dalek::digest::generic_array::typenum::U64; pub use curve25519_dalek::digest::Digest; +#[cfg(any(feature = "alloc", feature = "std"))] use curve25519_dalek::constants; +#[cfg(any(feature = "alloc", feature = "std"))] use curve25519_dalek::edwards::EdwardsPoint; +#[cfg(any(feature = "alloc", feature = "std"))] use curve25519_dalek::scalar::Scalar; pub use crate::constants::*; @@ -96,7 +100,7 @@ pub fn verify_batch( assert!(signatures.len() == messages.len(), ASSERT_MESSAGE); assert!(signatures.len() == public_keys.len(), ASSERT_MESSAGE); assert!(public_keys.len() == messages.len(), ASSERT_MESSAGE); - + #[cfg(feature = "alloc")] use alloc::vec::Vec; #[cfg(feature = "std")] From 1edc2965adaf88babf57f28fdc2a4bf2590e2fd5 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 12 Mar 2019 20:52:34 +0000 Subject: [PATCH 217/351] Fix typo in README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8ebe578..9cb233c 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ The numbers after the `/` in the test name refer to the size of the batch: Ed25519 batch signature verification/256 time: [5.0124 ms 5.0290 ms 5.0491 ms] As you can see, there's an optimal batch size for each machine, so you'll likely -want to your the benchmarks on your target CPU to discover the best size. For +want to test the benchmarks on your target CPU to discover the best size. For this machine, around 100 signatures per batch is the optimum: ![](https://github.com/dalek-cryptography/ed25519-dalek/blob/master/res/batch-violin-benchmark.svg) From 43baaaf27940f49f5394d2f27311bf1d887fb6bc Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 12 Mar 2019 21:12:43 +0000 Subject: [PATCH 218/351] Also test the `alloc` features on Travis. --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index f7f92d7..7451b70 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,9 @@ matrix: # the 32-bit backend (this also exercises testing with `no_std`): - rust: nightly env: TEST_COMMAND=build FEATURES='--no-default-features --features=u32_backend' + # Also test the `alloc` feature with `no_std`: + - rust: nightly + env: TEST_COMMAND=build FEATURES='--no-default-features --features="u64_backend alloc"' # Test any nightly gated features on nightly: - rust: nightly env: TEST_COMMAND=test FEATURES='--features=nightly' From d31df0aaa8791e291bcd223e2605fb6e5ba775dd Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 2 Apr 2019 01:46:23 +0000 Subject: [PATCH 219/351] Remove sha2 dep; limit rand depends; fixes after PR#68 merge. * ADD new "batch" feature for feature-gating ed25519 batch verification; off by default. The "batch" feature is the only thing which depends on all of the `rand` crate, since it requires the functionality of `rand::thread_rng()`. Without batch verification, the rest of ed25519-dalek only depends on `rand_os` and `rand_core`. --- Cargo.toml | 12 +++++--- src/ed25519.rs | 39 +++++++++++++------------- src/lib.rs | 72 +++++++++++++++++++++++++++--------------------- src/secret.rs | 28 ++++++++----------- tests/ed25519.rs | 18 ++++++------ 5 files changed, 89 insertions(+), 80 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7dc63a7..4f46a48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,10 @@ features = ["i128_support"] default-features = false optional = true +[dependencies.rand_os] +version = "0.1" +optional = true + [dependencies.serde] version = "^1.0" optional = true @@ -48,8 +52,7 @@ version = "0.2" hex = "^0.3" bincode = "^0.9" criterion = "0.2" -rand_os = "0.1.0" -rand_chacha = "0.1.0" +rand_os = "0.1" [[bench]] name = "ed25519_benchmarks" @@ -57,9 +60,10 @@ harness = false [features] default = ["std", "u64_backend"] -std = ["curve25519-dalek/std", "rand", "sha2/std"] -alloc = ["curve25519-dalek/alloc"] +std = ["curve25519-dalek/std", "rand_os", "sha2/std"] +alloc = ["curve25519-dalek/alloc", "rand_os"] nightly = ["curve25519-dalek/nightly", "clear_on_drop/nightly"] +batch = ["rand"] asm = ["sha2/asm"] yolocrypto = ["curve25519-dalek/yolocrypto"] u64_backend = ["curve25519-dalek/u64_backend"] diff --git a/src/ed25519.rs b/src/ed25519.rs index f483bd1..1b6334a 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -28,11 +28,11 @@ pub use sha2::Sha512; use curve25519_dalek::digest::generic_array::typenum::U64; pub use curve25519_dalek::digest::Digest; -#[cfg(any(feature = "alloc", feature = "std"))] +#[cfg(all(feature = "batch", any(feature = "alloc", feature = "std")))] use curve25519_dalek::constants; -#[cfg(any(feature = "alloc", feature = "std"))] +#[cfg(all(feature = "batch", any(feature = "alloc", feature = "std")))] use curve25519_dalek::edwards::EdwardsPoint; -#[cfg(any(feature = "alloc", feature = "std"))] +#[cfg(all(feature = "batch", any(feature = "alloc", feature = "std")))] use curve25519_dalek::scalar::Scalar; pub use crate::constants::*; @@ -48,7 +48,7 @@ pub use crate::signature::*; /// * `messages` is a slice of byte slices, one per signed message. /// * `signatures` is a slice of `Signature`s. /// * `public_keys` is a slice of `PublicKey`s. -/// * `csprng` is an implementation of `Rng + CryptoRng`, such as `rand::rngs::ThreadRng`. +/// * `csprng` is an implementation of `Rng + CryptoRng`. /// /// # Panics /// @@ -65,17 +65,16 @@ pub use crate::signature::*; /// /// ``` /// extern crate ed25519_dalek; -/// extern crate rand; +/// extern crate rand_os; /// /// use ed25519_dalek::verify_batch; /// use ed25519_dalek::Keypair; /// use ed25519_dalek::PublicKey; /// use ed25519_dalek::Signature; -/// use rand::thread_rng; -/// use rand::rngs::ThreadRng; +/// use rand_os::OsRng; /// /// # fn main() { -/// let mut csprng: ThreadRng = thread_rng(); +/// let mut csprng: OsRng = OsRng::new().unwrap(); /// let keypairs: Vec = (0..64).map(|_| Keypair::generate(&mut csprng)).collect(); /// let msg: &[u8] = b"They're good dogs Brant"; /// let messages: Vec<&[u8]> = (0..64).map(|_| msg).collect(); @@ -86,7 +85,7 @@ pub use crate::signature::*; /// assert!(result.is_ok()); /// # } /// ``` -#[cfg(any(feature = "alloc", feature = "std"))] +#[cfg(all(feature = "batch", any(feature = "alloc", feature = "std")))] #[allow(non_snake_case)] pub fn verify_batch( messages: &[&[u8]], @@ -217,12 +216,14 @@ impl Keypair { /// # Example /// /// ``` + /// extern crate rand_core; /// extern crate rand_os; /// extern crate ed25519_dalek; /// /// # #[cfg(feature = "std")] /// # fn main() { /// + /// use rand_core::{CryptoRng, RngCore}; /// use rand_os::OsRng; /// use ed25519_dalek::Keypair; /// use ed25519_dalek::Signature; @@ -238,7 +239,7 @@ impl Keypair { /// /// # Input /// - /// A CSPRNG with a `fill_bytes()` method, e.g. `rand_chacha::ChaChaRng`. + /// A CSPRNG with a `fill_bytes()` method, e.g. `rand_os::OsRng`. /// /// The caller must also supply a hash function which implements the /// `Digest` and `Default` traits, and which returns 512 bits of output. @@ -282,17 +283,17 @@ impl Keypair { /// /// ``` /// extern crate ed25519_dalek; - /// extern crate rand; + /// extern crate rand_os; /// /// use ed25519_dalek::Digest; /// use ed25519_dalek::Keypair; /// use ed25519_dalek::Sha512; /// use ed25519_dalek::Signature; - /// use rand::thread_rng; + /// use rand_os::OsRng; /// /// # #[cfg(feature = "std")] /// # fn main() { - /// let mut csprng = thread_rng(); + /// let mut csprng = OsRng::new().unwrap(); /// let keypair: Keypair = Keypair::generate(&mut csprng); /// let message: &[u8] = b"All I want is to pet all of the dogs."; /// @@ -329,17 +330,17 @@ impl Keypair { /// /// ``` /// # extern crate ed25519_dalek; - /// # extern crate rand; + /// # extern crate rand_os; /// # /// # use ed25519_dalek::Digest; /// # use ed25519_dalek::Keypair; /// # use ed25519_dalek::Signature; /// # use ed25519_dalek::Sha512; - /// # use rand::thread_rng; + /// # use rand_os::OsRng; /// # /// # #[cfg(feature = "std")] /// # fn main() { - /// # let mut csprng = thread_rng(); + /// # let mut csprng: OsRng = OsRng::new().unwrap(); /// # let keypair: Keypair = Keypair::generate(&mut csprng); /// # let message: &[u8] = b"All I want is to pet all of the dogs."; /// # let mut prehashed: Sha512 = Sha512::new(); @@ -400,17 +401,17 @@ impl Keypair { /// /// ``` /// extern crate ed25519_dalek; - /// extern crate rand; + /// extern crate rand_os; /// /// use ed25519_dalek::Digest; /// use ed25519_dalek::Keypair; /// use ed25519_dalek::Signature; /// use ed25519_dalek::Sha512; - /// use rand::thread_rng; + /// use rand_os::OsRng; /// /// # #[cfg(feature = "std")] /// # fn main() { - /// let mut csprng = thread_rng(); + /// let mut csprng: OsRng = OsRng::new().unwrap(); /// let keypair: Keypair = Keypair::generate(&mut csprng); /// let message: &[u8] = b"All I want is to pet all of the dogs."; /// diff --git a/src/lib.rs b/src/lib.rs index 9b72ac8..1007b6c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,12 +19,13 @@ //! the operating system's builtin PRNG: //! //! ``` +//! extern crate rand_core; //! extern crate rand_os; //! extern crate ed25519_dalek; //! //! # #[cfg(feature = "std")] //! # fn main() { -//! use rand::Rng; +//! use rand_core::RngCore; //! use rand_os::OsRng; //! use ed25519_dalek::Keypair; //! use ed25519_dalek::Signature; @@ -40,14 +41,15 @@ //! We can now use this `keypair` to sign a message: //! //! ``` -//! # extern crate rand; +//! # extern crate rand_core; +//! # extern crate rand_os; //! # extern crate ed25519_dalek; //! # fn main() { -//! # use rand::Rng; -//! # use rand::thread_rng; +//! # use rand_core::RngCore; +//! # use rand_os::OsRng; //! # use ed25519_dalek::Keypair; //! # use ed25519_dalek::Signature; -//! # let mut csprng = thread_rng(); +//! # let mut csprng = OsRng::new().unwrap(); //! # let keypair: Keypair = Keypair::generate(&mut csprng); //! let message: &[u8] = b"This is a test of the tsunami alert system."; //! let signature: Signature = keypair.sign(message); @@ -58,14 +60,15 @@ //! that `message`: //! //! ``` -//! # extern crate rand; +//! # extern crate rand_core; +//! # extern crate rand_os; //! # extern crate ed25519_dalek; //! # fn main() { -//! # use rand::Rng; -//! # use rand::thread_rng; +//! # use rand_core::RngCore; +//! # use rand_os::OsRng; //! # use ed25519_dalek::Keypair; //! # use ed25519_dalek::Signature; -//! # let mut csprng = thread_rng(); +//! # let mut csprng = OsRng::new().unwrap(); //! # let keypair: Keypair = Keypair::generate(&mut csprng); //! # let message: &[u8] = b"This is a test of the tsunami alert system."; //! # let signature: Signature = keypair.sign(message); @@ -77,15 +80,16 @@ //! verify this signature: //! //! ``` -//! # extern crate rand; +//! # extern crate rand_core; +//! # extern crate rand_os; //! # extern crate ed25519_dalek; //! # fn main() { -//! # use rand::Rng; -//! # use rand::thread_rng; +//! # use rand_core::RngCore; +//! # use rand_os::OsRng; //! # use ed25519_dalek::Keypair; //! # use ed25519_dalek::Signature; //! use ed25519_dalek::PublicKey; -//! # let mut csprng = thread_rng(); +//! # let mut csprng = OsRng::new().unwrap(); //! # let keypair: Keypair = Keypair::generate(&mut csprng); //! # let message: &[u8] = b"This is a test of the tsunami alert system."; //! # let signature: Signature = keypair.sign(message); @@ -104,14 +108,15 @@ //! verify your signatures!) //! //! ``` -//! # extern crate rand; +//! # extern crate rand_core; +//! # extern crate rand_os; //! # extern crate ed25519_dalek; //! # fn main() { -//! # use rand::Rng; -//! # use rand::thread_rng; +//! # use rand_core::RngCore; +//! # use rand_os::OsRng; //! # use ed25519_dalek::{Keypair, Signature, PublicKey}; //! use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; -//! # let mut csprng = thread_rng(); +//! # let mut csprng = OsRng::new().unwrap(); //! # let keypair: Keypair = Keypair::generate(&mut csprng); //! # let message: &[u8] = b"This is a test of the tsunami alert system."; //! # let signature: Signature = keypair.sign(message); @@ -127,14 +132,15 @@ //! And similarly, decoded from bytes with `::from_bytes()`: //! //! ``` -//! # extern crate rand; +//! # extern crate rand_core; +//! # extern crate rand_os; //! # extern crate ed25519_dalek; -//! # use rand::Rng; -//! # use rand::thread_rng; +//! # use rand_core::RngCore; +//! # use rand_os::OsRng; //! # use ed25519_dalek::{Keypair, Signature, PublicKey, SecretKey, SignatureError}; //! # use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; //! # fn do_test() -> Result<(SecretKey, PublicKey, Keypair, Signature), SignatureError> { -//! # let mut csprng = thread_rng(); +//! # let mut csprng = OsRng::new().unwrap(); //! # let keypair_orig: Keypair = Keypair::generate(&mut csprng); //! # let message: &[u8] = b"This is a test of the tsunami alert system."; //! # let signature_orig: Signature = keypair_orig.sign(message); @@ -169,7 +175,8 @@ //! For example, using [bincode](https://github.com/TyOverby/bincode): //! //! ``` -//! # extern crate rand; +//! # extern crate rand_core; +//! # extern crate rand_os; //! # extern crate ed25519_dalek; //! # #[cfg(feature = "serde")] //! extern crate serde; @@ -178,11 +185,11 @@ //! //! # #[cfg(feature = "serde")] //! # fn main() { -//! # use rand::Rng; -//! # use rand::thread_rng; +//! # use rand_core::RngCore; +//! # use rand_os::OsRng; //! # use ed25519_dalek::{Keypair, Signature, PublicKey}; //! use bincode::{serialize, Infinite}; -//! # let mut csprng = thread_rng(); +//! # let mut csprng = OsRng::new().unwrap(); //! # let keypair: Keypair = Keypair::generate(&mut csprng); //! # let message: &[u8] = b"This is a test of the tsunami alert system."; //! # let signature: Signature = keypair.sign(message); @@ -200,7 +207,8 @@ //! recipient may deserialise them and verify: //! //! ``` -//! # extern crate rand; +//! # extern crate rand_core; +//! # extern crate rand_os; //! # extern crate ed25519_dalek; //! # #[cfg(feature = "serde")] //! # extern crate serde; @@ -209,13 +217,13 @@ //! # //! # #[cfg(feature = "serde")] //! # fn main() { -//! # use rand::Rng; -//! # use rand::thread_rng; +//! # use rand_core::RngCore; +//! # use rand_os::OsRng; //! # use ed25519_dalek::{Keypair, Signature, PublicKey}; //! # use bincode::{serialize, Infinite}; //! use bincode::{deserialize}; //! -//! # let mut csprng = thread_rng(); +//! # let mut csprng = OsRng::new().unwrap(); //! # let keypair: Keypair = Keypair::generate(&mut csprng); //! let message: &[u8] = b"This is a test of the tsunami alert system."; //! # let signature: Signature = keypair.sign(message); @@ -250,10 +258,10 @@ extern crate std; extern crate clear_on_drop; extern crate curve25519_dalek; extern crate failure; -#[cfg(any(feature = "std", test))] +#[cfg(all(feature = "batch", any(feature = "std", feature = "alloc", test)))] extern crate rand; -#[cfg(test)] -extern crate rand_chacha; +#[cfg(any(feature = "std", test))] +extern crate rand_os; extern crate rand_core; #[cfg(feature = "serde")] extern crate serde; diff --git a/src/secret.rs b/src/secret.rs index 3e1a5d8..b3c0e0a 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -126,14 +126,12 @@ impl SecretKey { /// /// ``` /// extern crate rand_os; - /// extern crate sha2; /// extern crate ed25519_dalek; /// /// # #[cfg(feature = "std")] /// # fn main() { /// # /// use rand_os::OsRng; - /// use sha2::Sha512; /// use ed25519_dalek::PublicKey; /// use ed25519_dalek::SecretKey; /// use ed25519_dalek::Signature; @@ -149,18 +147,17 @@ impl SecretKey { /// Afterwards, you can generate the corresponding public: /// /// ``` - /// # extern crate rand; + /// # extern crate rand_os; /// # extern crate ed25519_dalek; /// # /// # fn main() { /// # - /// # use rand::Rng; - /// # use rand::thread_rng; + /// # use rand_os::OsRng; /// # use ed25519_dalek::PublicKey; /// # use ed25519_dalek::SecretKey; /// # use ed25519_dalek::Signature; /// # - /// # let mut csprng = thread_rng(); + /// # let mut csprng = OsRng::new().unwrap(); /// # let secret_key: SecretKey = SecretKey::generate(&mut csprng); /// /// let public_key: PublicKey = (&secret_key).into(); @@ -172,7 +169,7 @@ impl SecretKey { /// A CSPRNG with a `fill_bytes()` method, e.g. `rand::OsRng` pub fn generate(csprng: &mut T) -> SecretKey where - T: CryptoRng + Rng, + T: CryptoRng + RngCore, { let mut sk: SecretKey = SecretKey([0u8; 32]); @@ -273,18 +270,18 @@ impl<'a> From<&'a SecretKey> for ExpandedSecretKey { /// # Examples /// /// ``` - /// # extern crate rand; + /// # extern crate rand_core; + /// # extern crate rand_os; /// # extern crate sha2; /// # extern crate ed25519_dalek; /// # /// # fn main() { /// # - /// use rand::Rng; - /// use rand::thread_rng; - /// use sha2::Sha512; + /// use rand_core::RngCore; + /// use rand_os::OsRng; /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; /// - /// let mut csprng = thread_rng(); + /// let mut csprng = OsRng::new().unwrap(); /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key); /// # } @@ -329,7 +326,6 @@ impl ExpandedSecretKey { /// # fn main() { /// # /// use rand_os::OsRng; - /// use sha2::Sha512; /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; /// /// let mut csprng: OsRng = OsRng::new().unwrap(); @@ -340,7 +336,7 @@ impl ExpandedSecretKey { /// assert!(&expanded_secret_key_bytes[..] != &[0u8; 64][..]); /// # } /// # - /// # #[cfg(any(not(feature = "sha2"), not(feature = "std")))] + /// # #[cfg(not(feature = "std"))] /// # fn main() { } /// ``` #[inline] @@ -384,13 +380,13 @@ impl ExpandedSecretKey { /// # Ok(expanded_secret_key_again) /// # } /// # - /// # #[cfg(all(feature = "sha2", feature = "std"))] + /// # #[cfg(feature = "std")] /// # fn main() { /// # let result = do_test(); /// # assert!(result.is_ok()); /// # } /// # - /// # #[cfg(any(not(feature = "sha2"), not(feature = "std")))] + /// # #[cfg(not(feature = "std"))] /// # fn main() { } /// ``` #[inline] diff --git a/tests/ed25519.rs b/tests/ed25519.rs index d849e41..555b952 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -13,15 +13,14 @@ extern crate bincode; extern crate ed25519_dalek; extern crate hex; -extern crate rand; +extern crate rand_os; extern crate sha2; use ed25519_dalek::*; use hex::FromHex; -use rand::thread_rng; -use rand::rngs::ThreadRng; +use rand_os::OsRng; use sha2::Sha512; @@ -117,7 +116,6 @@ mod integrations { #[test] fn sign_verify() { // TestSignVerify - let mut csprng: ThreadRng; let keypair: Keypair; let good_sig: Signature; let bad_sig: Signature; @@ -125,7 +123,8 @@ mod integrations { let good: &[u8] = "test message".as_bytes(); let bad: &[u8] = "wrong message".as_bytes(); - csprng = thread_rng(); + let mut csprng: OsRng = OsRng::new().unwrap(); + keypair = Keypair::generate(&mut csprng); good_sig = keypair.sign(&good); bad_sig = keypair.sign(&bad); @@ -140,7 +139,6 @@ mod integrations { #[test] fn ed25519ph_sign_verify() { - let mut csprng: ThreadRng; let keypair: Keypair; let good_sig: Signature; let bad_sig: Signature; @@ -148,6 +146,8 @@ mod integrations { let good: &[u8] = b"test message"; let bad: &[u8] = b"wrong message"; + let mut csprng: OsRng = OsRng::new().unwrap(); + // ugh… there's no `impl Copy for Sha512`… i hope we can all agree these are the same hashes let mut prehashed_good1: Sha512 = Sha512::default(); prehashed_good1.input(good); @@ -163,7 +163,6 @@ mod integrations { let context: &[u8] = b"testing testing 1 2 3"; - csprng = thread_rng(); keypair = Keypair::generate(&mut csprng); good_sig = keypair.sign_prehashed(prehashed_good1, Some(context)); bad_sig = keypair.sign_prehashed(prehashed_bad1, Some(context)); @@ -176,6 +175,7 @@ mod integrations { "Verification of a signature on a different message passed!"); } + #[cfg(feature = "batch")] #[test] fn verify_batch_seven_signatures() { let messages: [&[u8]; 7] = [ @@ -186,7 +186,7 @@ mod integrations { b"Fuck dumbin' it down, spit ice, skip jewellery: Molotov cocktails on me like accessories.", b"Hey, I never cared about your bucks, so if I run up with a mask on, probably got a gas can too.", b"And I'm not here to fill 'er up. Nope, we came to riot, here to incite, we don't want any of your stuff.", ]; - let mut csprng: ThreadRng = thread_rng(); + let mut csprng: OsRng = OsRng::new().unwrap(); let mut keypairs: Vec = Vec::new(); let mut signatures: Vec = Vec::new(); @@ -204,7 +204,7 @@ mod integrations { #[test] fn pubkey_from_secret_and_expanded_secret() { - let mut csprng = thread_rng(); + let mut csprng = OsRng::new().unwrap(); let secret: SecretKey = SecretKey::generate(&mut csprng); let expanded_secret: ExpandedSecretKey = (&secret).into(); let public_from_secret: PublicKey = (&secret).into(); // XXX eww From 1c9f484d97c1fdcbb807ea0f91313b1880a7ba4b Mon Sep 17 00:00:00 2001 From: Arnaud Castellanos Galea Date: Mon, 30 Sep 2019 16:42:52 +0800 Subject: [PATCH 220/351] Drop the static lifetime for context in sign_prehashed --- src/ed25519.rs | 2 +- src/secret.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 2d144ce..17a6d5a 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -358,7 +358,7 @@ impl Keypair { pub fn sign_prehashed( &self, prehashed_message: D, - context: Option<&'static [u8]>, + context: Option<&[u8]>, ) -> Signature where D: Digest, diff --git a/src/secret.rs b/src/secret.rs index 3bfeb7c..4c8c2ce 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -462,11 +462,11 @@ impl ExpandedSecretKey { /// /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 #[allow(non_snake_case)] - pub fn sign_prehashed( + pub fn sign_prehashed<'a, D>( &self, prehashed_message: D, public_key: &PublicKey, - context: Option<&'static [u8]>, + context: Option<&'a [u8]>, ) -> Signature where D: Digest, From dc4b77b55196f0921ea0106084acd7615ca24792 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 3 Oct 2019 23:14:49 +0000 Subject: [PATCH 221/351] Fix bad import and feature specification in benchmarks. --- Cargo.toml | 4 ++++ benches/ed25519_benchmarks.rs | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4f46a48..a814efe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,11 +52,15 @@ version = "0.2" hex = "^0.3" bincode = "^0.9" criterion = "0.2" +rand = "0.6" rand_os = "0.1" [[bench]] name = "ed25519_benchmarks" harness = false +# This doesn't seem to work with criterion, cf. https://github.com/bheisler/criterion.rs/issues/344 +# For now, we have to bench by doing `cargo bench --features="batch"`. +# required-features = ["batch"] [features] default = ["std", "u64_backend"] diff --git a/benches/ed25519_benchmarks.rs b/benches/ed25519_benchmarks.rs index 52cb597..e07eb61 100644 --- a/benches/ed25519_benchmarks.rs +++ b/benches/ed25519_benchmarks.rs @@ -22,7 +22,7 @@ mod ed25519_benches { use ed25519_dalek::Signature; use ed25519_dalek::verify_batch; use rand::thread_rng; - use rand::rngs::ThreadRng; + use rand::prelude::ThreadRng; fn sign(c: &mut Criterion) { let mut csprng: ThreadRng = thread_rng(); From aa49b4cd8d61ef8d7fb5527f3c4642cd32393715 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 4 Oct 2019 01:19:23 +0000 Subject: [PATCH 222/351] Fix two failing doctests. --- src/secret.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/secret.rs b/src/secret.rs index b3c0e0a..5393e4d 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -322,7 +322,7 @@ impl ExpandedSecretKey { /// # extern crate sha2; /// # extern crate ed25519_dalek; /// # - /// # #[cfg(all(feature = "sha2", feature = "std"))] + /// # #[cfg(feature = "std")] /// # fn main() { /// # /// use rand_os::OsRng; @@ -364,7 +364,7 @@ impl ExpandedSecretKey { /// # /// # use ed25519_dalek::{ExpandedSecretKey, SignatureError}; /// # - /// # #[cfg(all(feature = "sha2", feature = "std"))] + /// # #[cfg(feature = "std")] /// # fn do_test() -> Result { /// # /// use rand_os::OsRng; From 52ee8010221089376698713b6d7b1a1721b80e80 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 4 Oct 2019 01:23:59 +0000 Subject: [PATCH 223/351] Fix Travis CI builds after change in features syntax parsing. cf. https://travis-ci.org/isislovecruft/ed25519-dalek/jobs/593331585#L194 --- .travis.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7451b70..72f94de 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,26 +6,26 @@ rust: - nightly env: - - TEST_COMMAND=test FEATURES='' + - TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='' matrix: include: # We use the 64-bit optimised curve backend by default, so also test with # the 32-bit backend (this also exercises testing with `no_std`): - rust: nightly - env: TEST_COMMAND=build FEATURES='--no-default-features --features=u32_backend' - # Also test the `alloc` feature with `no_std`: + env: TEST_COMMAND=build EXTRA_FLAGS='--no-default-features' FEATURES='u32_backend alloc' + # Also test the batch feature: - rust: nightly - env: TEST_COMMAND=build FEATURES='--no-default-features --features="u64_backend alloc"' + env: TEST_COMMAND=build EXTRA_FLAGS='--no-default-features' FEATURES='u64_backend alloc batch' # Test any nightly gated features on nightly: - rust: nightly - env: TEST_COMMAND=test FEATURES='--features=nightly' + env: TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='nightly' # Test serde support on stable, assuming that if it works there it'll work everywhere: - rust: stable - env: TEST_COMMAND=test FEATURE='--features=serde' + env: TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='serde' script: - - cargo $TEST_COMMAND $FEATURES + - cargo $TEST_COMMAND --features="$FEATURES" $EXTRA_FLAGS notifications: slack: From 1342e2a3a4ea916b798a91582d657f3c8b9ca90f Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 4 Oct 2019 02:05:20 +0000 Subject: [PATCH 224/351] Fix no_std+alloc builds. --- src/ed25519.rs | 2 +- src/lib.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 1b6334a..a76b091 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -93,7 +93,7 @@ pub fn verify_batch( public_keys: &[PublicKey], ) -> Result<(), SignatureError> { - const ASSERT_MESSAGE: &'static [u8] = b"The number of messages, signatures, and public keys must be equal."; + const ASSERT_MESSAGE: &'static str = "The number of messages, signatures, and public keys must be equal."; assert!(signatures.len() == messages.len(), ASSERT_MESSAGE); assert!(signatures.len() == public_keys.len(), ASSERT_MESSAGE); assert!(public_keys.len() == messages.len(), ASSERT_MESSAGE); diff --git a/src/lib.rs b/src/lib.rs index 1007b6c..fcd52af 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -255,6 +255,8 @@ #[macro_use] extern crate std; +#[cfg(all(feature = "alloc", not(feature = "std")))] +extern crate alloc; extern crate clear_on_drop; extern crate curve25519_dalek; extern crate failure; From 46811866cc3342fa145d121ae14de34f0a716570 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 4 Oct 2019 02:16:06 +0000 Subject: [PATCH 225/351] Bump ed25519-dalek version to 1.0.0-pre.2. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a814efe..453d72e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "1.0.0-pre.1" +version = "1.0.0-pre.2" authors = ["isis lovecruft "] readme = "README.md" license = "BSD-3-Clause" From 7dd99afb67a552274f1eb180edbc149083543a7e Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 4 Oct 2019 02:43:38 +0000 Subject: [PATCH 226/351] Remove most of the rand_os crate, which is only used for testing. --- Cargo.toml | 2 +- src/lib.rs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 453d72e..b385b0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,7 +65,7 @@ harness = false [features] default = ["std", "u64_backend"] std = ["curve25519-dalek/std", "rand_os", "sha2/std"] -alloc = ["curve25519-dalek/alloc", "rand_os"] +alloc = ["curve25519-dalek/alloc"] nightly = ["curve25519-dalek/nightly", "clear_on_drop/nightly"] batch = ["rand"] asm = ["sha2/asm"] diff --git a/src/lib.rs b/src/lib.rs index fcd52af..13784fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -262,8 +262,6 @@ extern crate curve25519_dalek; extern crate failure; #[cfg(all(feature = "batch", any(feature = "std", feature = "alloc", test)))] extern crate rand; -#[cfg(any(feature = "std", test))] -extern crate rand_os; extern crate rand_core; #[cfg(feature = "serde")] extern crate serde; From 28eed1cba0acdd0e9804324118cae95c08eaa9d8 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 27 Sep 2019 00:37:30 +0000 Subject: [PATCH 227/351] Add PublicKey::verify_strict() and Keypair::verify_strict() methods. --- benches/ed25519_benchmarks.rs | 12 +++++ src/ed25519.rs | 72 +++++++++++++++++++++++++ src/public.rs | 99 +++++++++++++++++++++++++++++++++++ 3 files changed, 183 insertions(+) diff --git a/benches/ed25519_benchmarks.rs b/benches/ed25519_benchmarks.rs index e07eb61..0af2812 100644 --- a/benches/ed25519_benchmarks.rs +++ b/benches/ed25519_benchmarks.rs @@ -56,6 +56,17 @@ mod ed25519_benches { }); } + fn verify_strict(c: &mut Criterion) { + let mut csprng: ThreadRng = thread_rng(); + let keypair: Keypair = Keypair::generate(&mut csprng); + let msg: &[u8] = b""; + let sig: Signature = keypair.sign(msg); + + c.bench_function("Ed25519 strict signature verification", move |b| { + b.iter(| | keypair.verify_strict(msg, &sig)) + }); + } + fn verify_batch_signatures(c: &mut Criterion) { static BATCH_SIZES: [usize; 8] = [4, 8, 16, 32, 64, 96, 128, 256]; @@ -90,6 +101,7 @@ mod ed25519_benches { sign, sign_expanded_key, verify, + verify_strict, verify_batch_signatures, key_generation, } diff --git a/src/ed25519.rs b/src/ed25519.rs index a76b091..43da922 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -447,6 +447,78 @@ impl Keypair { { self.public.verify_prehashed(prehashed_message, context, signature) } + + /// Strictly verify a signature on a message with this keypair's public key. + /// + /// # On The (Multiple) Sources of Malleability in Ed25519 Signatures + /// + /// This version of verification is technically non-RFC8032 compliant. The + /// following explains why. + /// + /// 1. Scalar Malleability + /// + /// The authors of the RFC explicitly stated that verification of an ed25519 + /// signature must fail if the scalar `s` is not properly reduced mod \ell: + /// + /// > To verify a signature on a message M using public key A, with F + /// > being 0 for Ed25519ctx, 1 for Ed25519ph, and if Ed25519ctx or + /// > Ed25519ph is being used, C being the context, first split the + /// > signature into two 32-octet halves. Decode the first half as a + /// > point R, and the second half as an integer S, in the range + /// > 0 <= s < L. Decode the public key A as point A'. If any of the + /// > decodings fail (including S being out of range), the signature is + /// > invalid.) + /// + /// All `verify_*()` functions within ed25519-dalek perform this check. + /// + /// 2. Point malleability + /// + /// The authors of the RFC added in a malleability check to step #3 in + /// §5.1.7, for small torsion components in the `R` value of the signature, + /// *which is not strictly required*, as they state: + /// + /// > Check the group equation [8][S]B = [8]R + [8][k]A'. It's + /// > sufficient, but not required, to instead check [S]B = R + [k]A'. + /// + /// # History of Malleability Checks + /// + /// As originally defined (cf. the "Malleability" section in the README of + /// this repo), ed25519 signatures didn't consider *any* form of + /// malleability to be an issue. Later the scalar malleability was + /// considered important. Still later, particularly with interests in + /// cryptocurrency design and in unique identities (e.g. for Signal users, + /// Tor onion services, etc.), the group element malleability became a + /// concern. + /// + /// However, libraries had already been created to conform to the original + /// definition. One well-used library in particular even implemented the + /// group element malleability check, *but only for batch verification*! + /// Which meant that even using the same library, a single signature could + /// verify fine individually, but suddenly, when verifying it with a bunch + /// of other signatures, the whole batch would fail! + /// + /// # "Strict" Verification + /// + /// This method performs *both* of the above signature malleability checks. + /// + /// It must be done as a separate method because one doesn't simply get to + /// change the definition of a cryptographic primitive ten years + /// after-the-fact with zero consideration for backwards compatibility in + /// hardware and protocols which have it already have the older definition + /// baked in. + /// + /// # Return + /// + /// Returns `Ok(())` if the signature is valid, and `Err` otherwise. + #[allow(non_snake_case)] + pub fn verify_strict( + &self, + message: &[u8], + signature: &Signature, + ) -> Result<(), SignatureError> + { + self.public.verify_strict(message, signature) + } } #[cfg(feature = "serde")] diff --git a/src/public.rs b/src/public.rs index ae3bfa3..f25c88f 100644 --- a/src/public.rs +++ b/src/public.rs @@ -244,6 +244,105 @@ impl PublicKey { Err(SignatureError(InternalError::VerifyError)) } } + + /// Strictly verify a signature on a message with this keypair's public key. + /// + /// # On The (Multiple) Sources of Malleability in Ed25519 Signatures + /// + /// This version of verification is technically non-RFC8032 compliant. The + /// following explains why. + /// + /// 1. Scalar Malleability + /// + /// The authors of the RFC explicitly stated that verification of an ed25519 + /// signature must fail if the scalar `s` is not properly reduced mod \ell: + /// + /// > To verify a signature on a message M using public key A, with F + /// > being 0 for Ed25519ctx, 1 for Ed25519ph, and if Ed25519ctx or + /// > Ed25519ph is being used, C being the context, first split the + /// > signature into two 32-octet halves. Decode the first half as a + /// > point R, and the second half as an integer S, in the range + /// > 0 <= s < L. Decode the public key A as point A'. If any of the + /// > decodings fail (including S being out of range), the signature is + /// > invalid.) + /// + /// All `verify_*()` functions within ed25519-dalek perform this check. + /// + /// 2. Point malleability + /// + /// The authors of the RFC added in a malleability check to step #3 in + /// §5.1.7, for small torsion components in the `R` value of the signature, + /// *which is not strictly required*, as they state: + /// + /// > Check the group equation [8][S]B = [8]R + [8][k]A'. It's + /// > sufficient, but not required, to instead check [S]B = R + [k]A'. + /// + /// # History of Malleability Checks + /// + /// As originally defined (cf. the "Malleability" section in the README of + /// this repo), ed25519 signatures didn't consider *any* form of + /// malleability to be an issue. Later the scalar malleability was + /// considered important. Still later, particularly with interests in + /// cryptocurrency design and in unique identities (e.g. for Signal users, + /// Tor onion services, etc.), the group element malleability became a + /// concern. + /// + /// However, libraries had already been created to conform to the original + /// definition. One well-used library in particular even implemented the + /// group element malleability check, *but only for batch verification*! + /// Which meant that even using the same library, a single signature could + /// verify fine individually, but suddenly, when verifying it with a bunch + /// of other signatures, the whole batch would fail! + /// + /// # "Strict" Verification + /// + /// This method performs *both* of the above signature malleability checks. + /// + /// It must be done as a separate method because one doesn't simply get to + /// change the definition of a cryptographic primitive ten years + /// after-the-fact with zero consideration for backwards compatibility in + /// hardware and protocols which have it already have the older definition + /// baked in. + /// + /// # Return + /// + /// Returns `Ok(())` if the signature is valid, and `Err` otherwise. + #[allow(non_snake_case)] + pub fn verify_strict( + &self, + message: &[u8], + signature: &Signature, + ) -> Result<(), SignatureError> + { + let mut h: Sha512 = Sha512::new(); + let R: EdwardsPoint; + let k: Scalar; + let minus_A: EdwardsPoint = -self.1; + let signature_R: EdwardsPoint; + + match signature.R.decompress() { + None => return Err(SignatureError(InternalError::VerifyError)), + Some(x) => signature_R = x, + } + + // Logical OR is fine here as we're not trying to be constant time. + if signature_R.is_small_order() || self.1.is_small_order() { + return Err(SignatureError(InternalError::VerifyError)); + } + + h.input(signature.R.as_bytes()); + h.input(self.as_bytes()); + h.input(&message); + + k = Scalar::from_hash(h); + R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); + + if R == signature_R { + Ok(()) + } else { + Err(SignatureError(InternalError::VerifyError)) + } + } } #[cfg(feature = "serde")] From ce2260afab60c6ef1cda5c7571aef1f69019c7d9 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 3 Oct 2019 23:42:16 +0000 Subject: [PATCH 228/351] Implement stricter scalar malleability checking for signatures. Previously, we were checking that the highest 3 bits were unset, which still leaves 2^253 - 2^252 + 27742317777372353535851937790883648493 potential scalars for the `s` component of a signature which are not strictly mod \ell. This change fixes that. Note: This change makes ed25519-dalek incompatible with ed25519-donna in that some signatures produced by donna will be verifiable by donna but NOT VERIFIABLE by dalek. On the other hand, libsodium exports a -DED25519_COMPAT feature, which when enabled, means it is compatible with dalek with the `legacy_compatibility` feature disabled. Otherwise, libsodium's behaviour is identical to the behaviour enabled by default in this patch. --- Cargo.toml | 2 ++ src/signature.rs | 34 +++++++++++++++++++++++++++++++--- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 453d72e..28411f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,6 +69,8 @@ alloc = ["curve25519-dalek/alloc", "rand_os"] nightly = ["curve25519-dalek/nightly", "clear_on_drop/nightly"] batch = ["rand"] asm = ["sha2/asm"] +# This features turns off stricter checking for scalar malleability in signatures +legacy_compatibility = [] yolocrypto = ["curve25519-dalek/yolocrypto"] u64_backend = ["curve25519-dalek/u64_backend"] u32_backend = ["curve25519-dalek/u32_backend"] diff --git a/src/signature.rs b/src/signature.rs index d5079fd..653155d 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -71,6 +71,31 @@ impl Debug for Signature { } } +#[cfg(feature = "legacy_compatibility")] +#[inline(always)] +fn check_scalar(bytes: [u8; 32]) -> Result { + // The highest 3 bits must not be set. No other checking for the + // remaining 2^253 - 2^252 + 27742317777372353535851937790883648493 + // potential non-reduced scalars is performed. + // + // This is compatible with ed25519-donna and libsodium when + // -DED25519_COMPAT is NOT specified. + if bytes[31] & 224 != 0 { + return Err(SignatureError(InternalError::ScalarFormatError)); + } + + Ok(Scalar::from_bits(bytes)) +} + +#[cfg(not(feature = "legacy_compatibility"))] +#[inline(always)] +fn check_scalar(bytes: [u8; 32]) -> Result { + match Scalar::from_canonical_bytes(bytes) { + None => return Err(SignatureError(InternalError::ScalarFormatError)), + Some(x) => return Ok(x), + }; +} + impl Signature { /// Convert this `Signature` to a byte array. #[inline] @@ -97,13 +122,16 @@ impl Signature { lower.copy_from_slice(&bytes[..32]); upper.copy_from_slice(&bytes[32..]); - if upper[31] & 224 != 0 { - return Err(SignatureError(InternalError::ScalarFormatError)); + let s: Scalar; + + match check_scalar(upper) { + Ok(x) => s = x, + Err(x) => return Err(x), } Ok(Signature { R: CompressedEdwardsY(lower), - s: Scalar::from_bits(upper), + s: s, }) } } From 2d5fe86f3062ec5edf36a73d5b4799d9756ce3ce Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 7 Oct 2019 19:03:15 +0000 Subject: [PATCH 229/351] Document anti-malleability features/functionality. --- README.md | 42 ++++++++++++++++++++++++++++++++++++++--- src/signature.rs | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9cb233c..6941314 100644 --- a/README.md +++ b/README.md @@ -108,9 +108,45 @@ after the fact, breaking compatibility with every other implementation. In short, if malleable signatures are bad for your protocol, don't use them. Consider using a curve25519-based Verifiable Random Function (VRF), such as [Trevor Perrin's VXEdDSA](https://www.whispersystems.org/docs/specifications/xeddsa/), -instead. We -[plan](https://github.com/dalek-cryptography/curve25519-dalek/issues/9) to -eventually support VXEdDSA in curve25519-dalek. +instead. + +#### The `legacy_compatibility` Feature + +By default, this library performs a stricter check for malleability in the +scalar component of a signature, upon signature deserialisation. This stricter +check, that `s < \ell` where `\ell` is the order of the basepoint, is +[mandated by RFC8032](https://tools.ietf.org/html/rfc8032#section-5.1.7). +However, that RFC was standardised a decade after the original paper, which, as +described above, (usually, falsely) stated that malleability was inconsequential. + +Because of this, most ed25519 implementations only perform a limited, hackier +check that the most significant three bits of the scalar are unset. If you need +compatibility with legacy implementations, including: + +* ed25519-donna +* Golang's /x/crypto ed25519 +* libsodium (only when built with `-DED25519_COMPAT`) +* NaCl's "ref" implementation +* probably a bunch of others + +then enable `ed25519-dalek`'s `legacy_compatibility` feature. Please note and +be forewarned that doing so allows for signature malleability, meaning that +there may be two different and "valid" signatures with the same key for the same +message, which is obviously incredibly dangerous in a number of contexts, +including—but not limited to—identification protocols and cryptocurrency +transactions. + +#### The `verify_strict()` Function + +The scalar component of a signature is not the only source of signature +malleability, however. Both the public key used for signature verification and +the group element component of the signature are malleable, as they may contain +a small torsion component as a consquence of the curve25519 group not being of +prime order, but having a small cofactor of 8. + +If you wish to also eliminate this source of signature malleability, please +review the +[documentation for the `verify_strict()` function](https://doc.dalek.rs/ed25519_dalek/struct.PublicKey.html#method.verify_strict). # Installation diff --git a/src/signature.rs b/src/signature.rs index 653155d..8bcbbe3 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -108,6 +108,55 @@ impl Signature { } /// Construct a `Signature` from a slice of bytes. + /// + /// # Scalar Malleability Checking + /// + /// As originally specified in the ed25519 paper (cf. the "Malleability" + /// section of the README in this repo), no checks whatsoever were performed + /// for signature malleability. + /// + /// Later, a semi-functional, hacky check was added to most libraries to + /// "ensure" that the scalar portion, `s`, of the signature was reduced `mod + /// \ell`, the order of the basepoint: + /// + /// ```ignore + /// if signature.s[31] & 224 != 0 { + /// return Err(); + /// } + /// ``` + /// + /// This bit-twiddling ensures that the most significant three bits of the + /// scalar are not set: + /// + /// ```python,ignore + /// >>> 0b00010000 & 224 + /// 0 + /// >>> 0b00100000 & 224 + /// 32 + /// >>> 0b01000000 & 224 + /// 64 + /// >>> 0b10000000 & 224 + /// 128 + /// ``` + /// + /// However, this check is hacky and insufficient to check that the scalar is + /// fully reduced `mod \ell = 2^252 + 27742317777372353535851937790883648493` as + /// it leaves us with a guanteed bound of 253 bits. This means that there are + /// `2^253 - 2^252 + 2774231777737235353585193779088364849311` remaining scalars + /// which could cause malleabilllity. + /// + /// RFC8032 [states](https://tools.ietf.org/html/rfc8032#section-5.1.7): + /// + /// > To verify a signature on a message M using public key A, [...] + /// > first split the signature into two 32-octet halves. Decode the first + /// > half as a point R, and the second half as an integer S, in the range + /// > 0 <= s < L. Decode the public key A as point A'. If any of the + /// > decodings fail (including S being out of range), the signature is + /// > invalid. + /// + /// However, by the time this was standardised, most libraries in use were + /// only checking the most significant three bits. (See also the + /// documentation for `PublicKey.verify_strict`.) #[inline] pub fn from_bytes(bytes: &[u8]) -> Result { if bytes.len() != SIGNATURE_LENGTH { From a065bee381d7bfe63d3eda2ed66c6cd40ce85695 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 7 Oct 2019 23:01:10 +0000 Subject: [PATCH 230/351] Enable Rust 2018. --- Cargo.toml | 1 + src/lib.rs | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b385b0d..d4cf4e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "ed25519-dalek" version = "1.0.0-pre.2" +edition = "2018" authors = ["isis lovecruft "] readme = "README.md" license = "BSD-3-Clause" diff --git a/src/lib.rs b/src/lib.rs index 13784fb..1b0476a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -247,8 +247,6 @@ #![no_std] #![warn(future_incompatible)] -#![warn(rust_2018_compatibility)] -#![warn(rust_2018_idioms)] #![deny(missing_docs)] // refuse to compile if documentation is missing #[cfg(any(feature = "std", test))] From deca36d07421df7b8cc5e1124bac5d4f777c6a01 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 11 Oct 2019 21:33:55 +0000 Subject: [PATCH 231/351] Add an optimisation to succeed fast for scalars whose 4th MSB is unset. This is only done during signature verification. --- src/signature.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/signature.rs b/src/signature.rs index 8bcbbe3..59da225 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -90,6 +90,18 @@ fn check_scalar(bytes: [u8; 32]) -> Result { #[cfg(not(feature = "legacy_compatibility"))] #[inline(always)] fn check_scalar(bytes: [u8; 32]) -> Result { + // Since this is only used in signature deserialisation (i.e. upon + // verification), we can do a "succeed fast" trick by checking that the most + // significant 4 bits are unset. If they are unset, we can succeed fast + // because we are guaranteed that the scalar is fully reduced. However, if + // the 4th most significant bit is set, we must do the full reduction check, + // as the order of the basepoint is roughly a 2^(252.5) bit number. + // + // This succeed-fast trick should succeed for roughly half of all scalars. + if bytes[31] & 240 == 0 { + return Ok(Scalar::from_bits(bytes)) + } + match Scalar::from_canonical_bytes(bytes) { None => return Err(SignatureError(InternalError::ScalarFormatError)), Some(x) => return Ok(x), From f1d4c4a732eff4ff195970ddb81146dcf2b8a773 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 16 Oct 2019 20:57:31 +0000 Subject: [PATCH 232/351] Remove panics from batch verification API in lieu of better error handling. --- src/ed25519.rs | 18 +++++++++--------- src/errors.rs | 10 ++++++++++ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 9352595..f1da97b 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -50,11 +50,6 @@ pub use crate::signature::*; /// * `public_keys` is a slice of `PublicKey`s. /// * `csprng` is an implementation of `Rng + CryptoRng`. /// -/// # Panics -/// -/// This function will panic if the `messages, `signatures`, and `public_keys` -/// slices are not equal length. -/// /// # Returns /// /// * A `Result` whose `Ok` value is an emtpy tuple and whose `Err` value is a @@ -93,10 +88,15 @@ pub fn verify_batch( public_keys: &[PublicKey], ) -> Result<(), SignatureError> { - const ASSERT_MESSAGE: &'static str = "The number of messages, signatures, and public keys must be equal."; - assert!(signatures.len() == messages.len(), ASSERT_MESSAGE); - assert!(signatures.len() == public_keys.len(), ASSERT_MESSAGE); - assert!(public_keys.len() == messages.len(), ASSERT_MESSAGE); + if signatures.len() != messages.len() || + signatures.len() != public_keys.len() || + public_keys.len() != messages.len() { + return Err(SignatureError(InternalError::ArrayLengthError{ + name_a: "signatures", length_a: signatures.len(), + name_b: "messages", length_b: messages.len(), + name_c: "public_keys", length_c: public_keys.len(), + })); + } #[cfg(feature = "alloc")] use alloc::vec::Vec; diff --git a/src/errors.rs b/src/errors.rs index 6597f73..6e88124 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -33,6 +33,11 @@ pub(crate) enum InternalError { }, /// The verification equation wasn't satisfied VerifyError, + /// Two arrays did not match in size, making the called signature + /// verification method impossible. + ArrayLengthError{ name_a: &'static str, length_a: usize, + name_b: &'static str, length_b: usize, + name_c: &'static str, length_c: usize, }, } impl Display for InternalError { @@ -46,6 +51,11 @@ impl Display for InternalError { => write!(f, "{} must be {} bytes in length", n, l), InternalError::VerifyError => write!(f, "Verification equation was not satisfied"), + InternalError::ArrayLengthError{ name_a: na, length_a: la, + name_b: nb, length_b: lb, + name_c: nc, length_c: lc, } + => write!(f, "Arrays must be the same length: {} has length {}, + {} has length {}, {} has length {}.", na, la, nb, lb, nc, lc), } } } From c3f4c7a67ed666a69d4787134a74e0f179914485 Mon Sep 17 00:00:00 2001 From: Michael Lodder Date: Mon, 21 Oct 2019 09:03:08 -0600 Subject: [PATCH 233/351] Update to latest rand Signed-off-by: Michael Lodder --- Cargo.toml | 24 +++++------------ src/ed25519.rs | 34 +++++++++++------------- src/lib.rs | 69 ++++++++++++++++++------------------------------ src/secret.rs | 34 +++++++++++------------- tests/ed25519.rs | 13 +++++---- 5 files changed, 70 insertions(+), 104 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 453d72e..95161a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,20 +19,11 @@ travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master" version = "1" default-features = false -[dependencies.rand_core] -version = "0.3" -default-features = false - [dependencies.rand] -version = "0.6" -features = ["i128_support"] +version = "0.7" default-features = false optional = true -[dependencies.rand_os] -version = "0.1" -optional = true - [dependencies.serde] version = "^1.0" optional = true @@ -49,11 +40,10 @@ default-features = false version = "0.2" [dev-dependencies] -hex = "^0.3" +hex = "^0.4" bincode = "^0.9" -criterion = "0.2" -rand = "0.6" -rand_os = "0.1" +criterion = "0.3" +rand = "0.7" [[bench]] name = "ed25519_benchmarks" @@ -64,9 +54,9 @@ harness = false [features] default = ["std", "u64_backend"] -std = ["curve25519-dalek/std", "rand_os", "sha2/std"] -alloc = ["curve25519-dalek/alloc", "rand_os"] -nightly = ["curve25519-dalek/nightly", "clear_on_drop/nightly"] +std = ["curve25519-dalek/std", "sha2/std", "rand/std"] +alloc = ["curve25519-dalek/alloc", "rand/alloc"] +nightly = ["curve25519-dalek/nightly", "clear_on_drop/nightly", "rand/nightly"] batch = ["rand"] asm = ["sha2/asm"] yolocrypto = ["curve25519-dalek/yolocrypto"] diff --git a/src/ed25519.rs b/src/ed25519.rs index a76b091..f8e2cbc 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -12,7 +12,7 @@ #[allow(unused_imports)] use core::default::Default; -use rand_core::{CryptoRng, RngCore}; +use rand::{CryptoRng, RngCore}; #[cfg(feature = "serde")] use serde::de::Error as SerdeError; @@ -65,16 +65,16 @@ pub use crate::signature::*; /// /// ``` /// extern crate ed25519_dalek; -/// extern crate rand_os; +/// extern crate rand; /// /// use ed25519_dalek::verify_batch; /// use ed25519_dalek::Keypair; /// use ed25519_dalek::PublicKey; /// use ed25519_dalek::Signature; -/// use rand_os::OsRng; +/// use rand::rngs::OsRng; /// /// # fn main() { -/// let mut csprng: OsRng = OsRng::new().unwrap(); +/// let mut csprng = OsRng{}; /// let keypairs: Vec = (0..64).map(|_| Keypair::generate(&mut csprng)).collect(); /// let msg: &[u8] = b"They're good dogs Brant"; /// let messages: Vec<&[u8]> = (0..64).map(|_| msg).collect(); @@ -216,19 +216,17 @@ impl Keypair { /// # Example /// /// ``` - /// extern crate rand_core; - /// extern crate rand_os; + /// extern crate rand; /// extern crate ed25519_dalek; /// /// # #[cfg(feature = "std")] /// # fn main() { /// - /// use rand_core::{CryptoRng, RngCore}; - /// use rand_os::OsRng; + /// use rand::rngs::OsRng; /// use ed25519_dalek::Keypair; /// use ed25519_dalek::Signature; /// - /// let mut csprng: OsRng = OsRng::new().unwrap(); + /// let mut csprng = OsRng{}; /// let keypair: Keypair = Keypair::generate(&mut csprng); /// /// # } @@ -283,17 +281,17 @@ impl Keypair { /// /// ``` /// extern crate ed25519_dalek; - /// extern crate rand_os; + /// extern crate rand; /// /// use ed25519_dalek::Digest; /// use ed25519_dalek::Keypair; /// use ed25519_dalek::Sha512; /// use ed25519_dalek::Signature; - /// use rand_os::OsRng; + /// use rand::rngs::OsRng; /// /// # #[cfg(feature = "std")] /// # fn main() { - /// let mut csprng = OsRng::new().unwrap(); + /// let mut csprng = OsRng{}; /// let keypair: Keypair = Keypair::generate(&mut csprng); /// let message: &[u8] = b"All I want is to pet all of the dogs."; /// @@ -330,17 +328,17 @@ impl Keypair { /// /// ``` /// # extern crate ed25519_dalek; - /// # extern crate rand_os; + /// # extern crate rand; /// # /// # use ed25519_dalek::Digest; /// # use ed25519_dalek::Keypair; /// # use ed25519_dalek::Signature; /// # use ed25519_dalek::Sha512; - /// # use rand_os::OsRng; + /// # use rand::rngs::OsRng; /// # /// # #[cfg(feature = "std")] /// # fn main() { - /// # let mut csprng: OsRng = OsRng::new().unwrap(); + /// # let mut csprng = OsRng{}; /// # let keypair: Keypair = Keypair::generate(&mut csprng); /// # let message: &[u8] = b"All I want is to pet all of the dogs."; /// # let mut prehashed: Sha512 = Sha512::new(); @@ -401,17 +399,17 @@ impl Keypair { /// /// ``` /// extern crate ed25519_dalek; - /// extern crate rand_os; + /// extern crate rand; /// /// use ed25519_dalek::Digest; /// use ed25519_dalek::Keypair; /// use ed25519_dalek::Signature; /// use ed25519_dalek::Sha512; - /// use rand_os::OsRng; + /// use rand::rngs::OsRng; /// /// # #[cfg(feature = "std")] /// # fn main() { - /// let mut csprng: OsRng = OsRng::new().unwrap(); + /// let mut csprng = OsRng{}; /// let keypair: Keypair = Keypair::generate(&mut csprng); /// let message: &[u8] = b"All I want is to pet all of the dogs."; /// diff --git a/src/lib.rs b/src/lib.rs index fcd52af..2974124 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,18 +19,16 @@ //! the operating system's builtin PRNG: //! //! ``` -//! extern crate rand_core; -//! extern crate rand_os; +//! extern crate rand; //! extern crate ed25519_dalek; //! //! # #[cfg(feature = "std")] //! # fn main() { -//! use rand_core::RngCore; -//! use rand_os::OsRng; +//! use rand::rngs::OsRng; //! use ed25519_dalek::Keypair; //! use ed25519_dalek::Signature; //! -//! let mut csprng: OsRng = OsRng::new().unwrap(); +//! let mut csprng = OsRng{}; //! let keypair: Keypair = Keypair::generate(&mut csprng); //! # } //! # @@ -41,15 +39,13 @@ //! We can now use this `keypair` to sign a message: //! //! ``` -//! # extern crate rand_core; -//! # extern crate rand_os; +//! # extern crate rand; //! # extern crate ed25519_dalek; //! # fn main() { -//! # use rand_core::RngCore; -//! # use rand_os::OsRng; +//! # use rand::rngs::OsRng; //! # use ed25519_dalek::Keypair; //! # use ed25519_dalek::Signature; -//! # let mut csprng = OsRng::new().unwrap(); +//! # let mut csprng = OsRng{}; //! # let keypair: Keypair = Keypair::generate(&mut csprng); //! let message: &[u8] = b"This is a test of the tsunami alert system."; //! let signature: Signature = keypair.sign(message); @@ -60,15 +56,13 @@ //! that `message`: //! //! ``` -//! # extern crate rand_core; -//! # extern crate rand_os; +//! # extern crate rand; //! # extern crate ed25519_dalek; //! # fn main() { -//! # use rand_core::RngCore; -//! # use rand_os::OsRng; +//! # use rand::rngs::OsRng; //! # use ed25519_dalek::Keypair; //! # use ed25519_dalek::Signature; -//! # let mut csprng = OsRng::new().unwrap(); +//! # let mut csprng = OsRng{}; //! # let keypair: Keypair = Keypair::generate(&mut csprng); //! # let message: &[u8] = b"This is a test of the tsunami alert system."; //! # let signature: Signature = keypair.sign(message); @@ -80,16 +74,14 @@ //! verify this signature: //! //! ``` -//! # extern crate rand_core; -//! # extern crate rand_os; +//! # extern crate rand; //! # extern crate ed25519_dalek; //! # fn main() { -//! # use rand_core::RngCore; -//! # use rand_os::OsRng; +//! # use rand::rngs::OsRng; //! # use ed25519_dalek::Keypair; //! # use ed25519_dalek::Signature; //! use ed25519_dalek::PublicKey; -//! # let mut csprng = OsRng::new().unwrap(); +//! # let mut csprng = OsRng{}; //! # let keypair: Keypair = Keypair::generate(&mut csprng); //! # let message: &[u8] = b"This is a test of the tsunami alert system."; //! # let signature: Signature = keypair.sign(message); @@ -108,15 +100,13 @@ //! verify your signatures!) //! //! ``` -//! # extern crate rand_core; -//! # extern crate rand_os; +//! # extern crate rand; //! # extern crate ed25519_dalek; //! # fn main() { -//! # use rand_core::RngCore; -//! # use rand_os::OsRng; +//! # use rand::rngs::OsRng; //! # use ed25519_dalek::{Keypair, Signature, PublicKey}; //! use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; -//! # let mut csprng = OsRng::new().unwrap(); +//! # let mut csprng = OsRng{}; //! # let keypair: Keypair = Keypair::generate(&mut csprng); //! # let message: &[u8] = b"This is a test of the tsunami alert system."; //! # let signature: Signature = keypair.sign(message); @@ -132,15 +122,13 @@ //! And similarly, decoded from bytes with `::from_bytes()`: //! //! ``` -//! # extern crate rand_core; -//! # extern crate rand_os; +//! # extern crate rand; //! # extern crate ed25519_dalek; -//! # use rand_core::RngCore; -//! # use rand_os::OsRng; +//! # use rand::rngs::OsRng; //! # use ed25519_dalek::{Keypair, Signature, PublicKey, SecretKey, SignatureError}; //! # use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; //! # fn do_test() -> Result<(SecretKey, PublicKey, Keypair, Signature), SignatureError> { -//! # let mut csprng = OsRng::new().unwrap(); +//! # let mut csprng = OsRng{}; //! # let keypair_orig: Keypair = Keypair::generate(&mut csprng); //! # let message: &[u8] = b"This is a test of the tsunami alert system."; //! # let signature_orig: Signature = keypair_orig.sign(message); @@ -175,8 +163,7 @@ //! For example, using [bincode](https://github.com/TyOverby/bincode): //! //! ``` -//! # extern crate rand_core; -//! # extern crate rand_os; +//! # extern crate rand; //! # extern crate ed25519_dalek; //! # #[cfg(feature = "serde")] //! extern crate serde; @@ -185,11 +172,10 @@ //! //! # #[cfg(feature = "serde")] //! # fn main() { -//! # use rand_core::RngCore; -//! # use rand_os::OsRng; +//! # use rand::rngs::OsRng; //! # use ed25519_dalek::{Keypair, Signature, PublicKey}; //! use bincode::{serialize, Infinite}; -//! # let mut csprng = OsRng::new().unwrap(); +//! # let mut csprng = OsRng{}; //! # let keypair: Keypair = Keypair::generate(&mut csprng); //! # let message: &[u8] = b"This is a test of the tsunami alert system."; //! # let signature: Signature = keypair.sign(message); @@ -207,8 +193,7 @@ //! recipient may deserialise them and verify: //! //! ``` -//! # extern crate rand_core; -//! # extern crate rand_os; +//! # extern crate rand; //! # extern crate ed25519_dalek; //! # #[cfg(feature = "serde")] //! # extern crate serde; @@ -217,13 +202,12 @@ //! # //! # #[cfg(feature = "serde")] //! # fn main() { -//! # use rand_core::RngCore; -//! # use rand_os::OsRng; +//! # use rand::rngs::OsRng; //! # use ed25519_dalek::{Keypair, Signature, PublicKey}; //! # use bincode::{serialize, Infinite}; //! use bincode::{deserialize}; //! -//! # let mut csprng = OsRng::new().unwrap(); +//! # let mut csprng = OsRng{}; //! # let keypair: Keypair = Keypair::generate(&mut csprng); //! let message: &[u8] = b"This is a test of the tsunami alert system."; //! # let signature: Signature = keypair.sign(message); @@ -260,11 +244,8 @@ extern crate alloc; extern crate clear_on_drop; extern crate curve25519_dalek; extern crate failure; -#[cfg(all(feature = "batch", any(feature = "std", feature = "alloc", test)))] +#[cfg(any(feature = "batch", feature = "std", feature = "alloc", test))] extern crate rand; -#[cfg(any(feature = "std", test))] -extern crate rand_os; -extern crate rand_core; #[cfg(feature = "serde")] extern crate serde; extern crate sha2; diff --git a/src/secret.rs b/src/secret.rs index 5393e4d..8dd96d1 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -19,7 +19,7 @@ use curve25519_dalek::digest::Digest; use curve25519_dalek::edwards::CompressedEdwardsY; use curve25519_dalek::scalar::Scalar; -use rand_core::{CryptoRng, RngCore}; +use rand::{CryptoRng, RngCore}; use sha2::Sha512; @@ -125,18 +125,18 @@ impl SecretKey { /// # Example /// /// ``` - /// extern crate rand_os; + /// extern crate rand; /// extern crate ed25519_dalek; /// /// # #[cfg(feature = "std")] /// # fn main() { /// # - /// use rand_os::OsRng; + /// use rand::rngs::OsRng; /// use ed25519_dalek::PublicKey; /// use ed25519_dalek::SecretKey; /// use ed25519_dalek::Signature; /// - /// let mut csprng: OsRng = OsRng::new().unwrap(); + /// let mut csprng = OsRng{}; /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); /// # } /// # @@ -147,17 +147,17 @@ impl SecretKey { /// Afterwards, you can generate the corresponding public: /// /// ``` - /// # extern crate rand_os; + /// # extern crate rand; /// # extern crate ed25519_dalek; /// # /// # fn main() { /// # - /// # use rand_os::OsRng; + /// # use rand::rngs::OsRng; /// # use ed25519_dalek::PublicKey; /// # use ed25519_dalek::SecretKey; /// # use ed25519_dalek::Signature; /// # - /// # let mut csprng = OsRng::new().unwrap(); + /// # let mut csprng = OsRng{}; /// # let secret_key: SecretKey = SecretKey::generate(&mut csprng); /// /// let public_key: PublicKey = (&secret_key).into(); @@ -270,18 +270,16 @@ impl<'a> From<&'a SecretKey> for ExpandedSecretKey { /// # Examples /// /// ``` - /// # extern crate rand_core; - /// # extern crate rand_os; + /// # extern crate rand; /// # extern crate sha2; /// # extern crate ed25519_dalek; /// # /// # fn main() { /// # - /// use rand_core::RngCore; - /// use rand_os::OsRng; + /// use rand::rngs::OsRng; /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; /// - /// let mut csprng = OsRng::new().unwrap(); + /// let mut csprng = OsRng{}; /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key); /// # } @@ -318,17 +316,17 @@ impl ExpandedSecretKey { /// # Examples /// /// ``` - /// # extern crate rand_os; + /// # extern crate rand; /// # extern crate sha2; /// # extern crate ed25519_dalek; /// # /// # #[cfg(feature = "std")] /// # fn main() { /// # - /// use rand_os::OsRng; + /// use rand::rngs::OsRng; /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; /// - /// let mut csprng: OsRng = OsRng::new().unwrap(); + /// let mut csprng = OsRng{}; /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key); /// let expanded_secret_key_bytes: [u8; 64] = expanded_secret_key.to_bytes(); @@ -358,7 +356,7 @@ impl ExpandedSecretKey { /// # Examples /// /// ``` - /// # extern crate rand_os; + /// # extern crate rand; /// # extern crate sha2; /// # extern crate ed25519_dalek; /// # @@ -367,11 +365,11 @@ impl ExpandedSecretKey { /// # #[cfg(feature = "std")] /// # fn do_test() -> Result { /// # - /// use rand_os::OsRng; + /// use rand::rngs::OsRng; /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; /// use ed25519_dalek::SignatureError; /// - /// let mut csprng: OsRng = OsRng::new().unwrap(); + /// let mut csprng = OsRng{}; /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key); /// let bytes: [u8; 64] = expanded_secret_key.to_bytes(); diff --git a/tests/ed25519.rs b/tests/ed25519.rs index 555b952..88a24df 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -13,15 +13,13 @@ extern crate bincode; extern crate ed25519_dalek; extern crate hex; -extern crate rand_os; extern crate sha2; +extern crate rand; use ed25519_dalek::*; use hex::FromHex; -use rand_os::OsRng; - use sha2::Sha512; #[cfg(test)] @@ -113,6 +111,7 @@ mod vectors { #[cfg(test)] mod integrations { use super::*; + use rand::rngs::OsRng; #[test] fn sign_verify() { // TestSignVerify @@ -123,7 +122,7 @@ mod integrations { let good: &[u8] = "test message".as_bytes(); let bad: &[u8] = "wrong message".as_bytes(); - let mut csprng: OsRng = OsRng::new().unwrap(); + let mut csprng = OsRng{}; keypair = Keypair::generate(&mut csprng); good_sig = keypair.sign(&good); @@ -146,7 +145,7 @@ mod integrations { let good: &[u8] = b"test message"; let bad: &[u8] = b"wrong message"; - let mut csprng: OsRng = OsRng::new().unwrap(); + let mut csprng = OsRng{}; // ugh… there's no `impl Copy for Sha512`… i hope we can all agree these are the same hashes let mut prehashed_good1: Sha512 = Sha512::default(); @@ -186,7 +185,7 @@ mod integrations { b"Fuck dumbin' it down, spit ice, skip jewellery: Molotov cocktails on me like accessories.", b"Hey, I never cared about your bucks, so if I run up with a mask on, probably got a gas can too.", b"And I'm not here to fill 'er up. Nope, we came to riot, here to incite, we don't want any of your stuff.", ]; - let mut csprng: OsRng = OsRng::new().unwrap(); + let mut csprng = OsRng{}; let mut keypairs: Vec = Vec::new(); let mut signatures: Vec = Vec::new(); @@ -204,7 +203,7 @@ mod integrations { #[test] fn pubkey_from_secret_and_expanded_secret() { - let mut csprng = OsRng::new().unwrap(); + let mut csprng = OsRng{}; let secret: SecretKey = SecretKey::generate(&mut csprng); let expanded_secret: ExpandedSecretKey = (&secret).into(); let public_from_secret: PublicKey = (&secret).into(); // XXX eww From ecb6fd8ec4fb68d9430db41a551fc2a1529c9d90 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 25 Oct 2019 22:18:42 +0000 Subject: [PATCH 234/351] Cleanup dependencies in Cargo.toml. --- Cargo.toml | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 09bcc67..7c582b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,29 +16,13 @@ exclude = [ ".gitignore", "TESTVECTORS", "res/*" ] [badges] travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master"} -[dependencies.curve25519-dalek] -version = "1" -default-features = false - -[dependencies.rand] -version = "0.7" -default-features = false -optional = true - -[dependencies.serde] -version = "^1.0" -optional = true - -[dependencies.sha2] -version = "^0.8" -default-features = false - -[dependencies.failure] -version = "^0.1.1" -default-features = false - -[dependencies.clear_on_drop] -version = "0.2" +[dependencies] +clear_on_drop = { version = "0.2" } +curve25519-dalek = { version = "1", default-features = false } +failure = { version = "0.1", default-features = false } +rand = { version = "0.7", default-features = false, optional = true } +serde = { version = "1.0", optional = true } +sha2 = { version = "0.8", default-features = false } [dev-dependencies] hex = "^0.4" From 81f906ca30ebe3ff95f4b64612c702b1abdf34eb Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 26 Oct 2019 04:16:33 +0000 Subject: [PATCH 235/351] Replace failure dependency with impls of std::error::Error. --- Cargo.toml | 1 - src/errors.rs | 11 ++++++++--- src/lib.rs | 1 - 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7c582b1..27838cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,6 @@ travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master" [dependencies] clear_on_drop = { version = "0.2" } curve25519-dalek = { version = "1", default-features = false } -failure = { version = "0.1", default-features = false } rand = { version = "0.7", default-features = false, optional = true } serde = { version = "1.0", optional = true } sha2 = { version = "0.8", default-features = false } diff --git a/src/errors.rs b/src/errors.rs index 6597f73..ba59180 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -16,6 +16,9 @@ use core::fmt; use core::fmt::Display; +#[cfg(feature = "std")] +use std::error::Error; + /// Internal errors. Most application-level developers will likely not /// need to pay any attention to these. #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] @@ -50,7 +53,8 @@ impl Display for InternalError { } } -impl ::failure::Fail for InternalError {} +#[cfg(feature = "std")] +impl Error for InternalError { } /// Errors which may occur while processing signatures and keypairs. /// @@ -75,8 +79,9 @@ impl Display for SignatureError { } } -impl ::failure::Fail for SignatureError { - fn cause(&self) -> Option<&dyn (::failure::Fail)> { +#[cfg(feature = "std")] +impl Error for SignatureError { + fn source(&self) -> Option<&(dyn Error + 'static)> { Some(&self.0) } } diff --git a/src/lib.rs b/src/lib.rs index e40f4d5..92f1d45 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -241,7 +241,6 @@ extern crate std; extern crate alloc; extern crate clear_on_drop; extern crate curve25519_dalek; -extern crate failure; #[cfg(any(feature = "batch", feature = "std", feature = "alloc", test))] extern crate rand; #[cfg(feature = "serde")] From 15d0a6596f6c419ae57f324900b53c6bd5b4c224 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 14 Nov 2019 22:01:34 +0000 Subject: [PATCH 236/351] Document batch verification on docs.rs and fix false autolinking. --- Cargo.toml | 5 +++++ src/ed25519.rs | 4 ++-- src/public.rs | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 27838cc..09b8ef2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,11 @@ exclude = [ ".gitignore", "TESTVECTORS", "res/*" ] [badges] travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master"} +[package.metadata.docs.rs] +# Disabled for now since this is borked; tracking https://github.com/rust-lang/docs.rs/issues/302 +# rustdoc-args = ["--html-in-header", ".cargo/registry/src/github.com-1ecc6299db9ec823/curve25519-dalek-0.13.2/rustdoc-include-katex-header.html"] +features = ["nightly", "batch"] + [dependencies] clear_on_drop = { version = "0.2" } curve25519-dalek = { version = "1", default-features = false } diff --git a/src/ed25519.rs b/src/ed25519.rs index 8bddb67..831cc1d 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -475,8 +475,8 @@ impl Keypair { /// §5.1.7, for small torsion components in the `R` value of the signature, /// *which is not strictly required*, as they state: /// - /// > Check the group equation [8][S]B = [8]R + [8][k]A'. It's - /// > sufficient, but not required, to instead check [S]B = R + [k]A'. + /// > Check the group equation \[8\]\[S\]B = \[8\]R + \[8\]\[k\]A'. It's + /// > sufficient, but not required, to instead check \[S\]B = R + \[k\]A'. /// /// # History of Malleability Checks /// diff --git a/src/public.rs b/src/public.rs index f25c88f..f901fcf 100644 --- a/src/public.rs +++ b/src/public.rs @@ -274,8 +274,8 @@ impl PublicKey { /// §5.1.7, for small torsion components in the `R` value of the signature, /// *which is not strictly required*, as they state: /// - /// > Check the group equation [8][S]B = [8]R + [8][k]A'. It's - /// > sufficient, but not required, to instead check [S]B = R + [k]A'. + /// > Check the group equation \[8\]\[S\]B = \[8\]R + \[8\]\[k\]A'. It's + /// > sufficient, but not required, to instead check \[S\]B = R + \[k\]A'. /// /// # History of Malleability Checks /// From ee67f36ba9362f41b5a46c7be99ea2ef398075fa Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 21 Nov 2019 00:31:10 +0000 Subject: [PATCH 237/351] Move verify_batch() to new batch module. --- src/ed25519.rs | 127 ++----------------------------------------------- src/lib.rs | 2 + 2 files changed, 6 insertions(+), 123 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 7cfca3f..7c55a65 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -7,9 +7,8 @@ // Authors: // - isis agora lovecruft -//! ed25519 keypairs and batch verification. +//! ed25519 keypairs. -#[allow(unused_imports)] use core::default::Default; use rand::{CryptoRng, RngCore}; @@ -19,139 +18,21 @@ use serde::de::Error as SerdeError; #[cfg(feature = "serde")] use serde::de::Visitor; #[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -#[cfg(feature = "serde")] -use serde::{Deserializer, Serializer}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; pub use sha2::Sha512; use curve25519_dalek::digest::generic_array::typenum::U64; pub use curve25519_dalek::digest::Digest; -#[cfg(all(feature = "batch", any(feature = "alloc", feature = "std")))] -use curve25519_dalek::constants; -#[cfg(all(feature = "batch", any(feature = "alloc", feature = "std")))] -use curve25519_dalek::edwards::EdwardsPoint; -#[cfg(all(feature = "batch", any(feature = "alloc", feature = "std")))] -use curve25519_dalek::scalar::Scalar; - +#[cfg(all(feature = "batch", any(feature = "std", feature = "alloc")))] +pub use crate::batch::*; pub use crate::constants::*; pub use crate::errors::*; pub use crate::public::*; pub use crate::secret::*; pub use crate::signature::*; -/// Verify a batch of `signatures` on `messages` with their respective `public_keys`. -/// -/// # Inputs -/// -/// * `messages` is a slice of byte slices, one per signed message. -/// * `signatures` is a slice of `Signature`s. -/// * `public_keys` is a slice of `PublicKey`s. -/// * `csprng` is an implementation of `Rng + CryptoRng`. -/// -/// # Returns -/// -/// * A `Result` whose `Ok` value is an emtpy tuple and whose `Err` value is a -/// `SignatureError` containing a description of the internal error which -/// occured. -/// -/// # Examples -/// -/// ``` -/// extern crate ed25519_dalek; -/// extern crate rand; -/// -/// use ed25519_dalek::verify_batch; -/// use ed25519_dalek::Keypair; -/// use ed25519_dalek::PublicKey; -/// use ed25519_dalek::Signature; -/// use rand::rngs::OsRng; -/// -/// # fn main() { -/// let mut csprng = OsRng{}; -/// let keypairs: Vec = (0..64).map(|_| Keypair::generate(&mut csprng)).collect(); -/// let msg: &[u8] = b"They're good dogs Brant"; -/// let messages: Vec<&[u8]> = (0..64).map(|_| msg).collect(); -/// let signatures: Vec = keypairs.iter().map(|key| key.sign(&msg)).collect(); -/// let public_keys: Vec = keypairs.iter().map(|key| key.public).collect(); -/// -/// let result = verify_batch(&messages[..], &signatures[..], &public_keys[..]); -/// assert!(result.is_ok()); -/// # } -/// ``` -#[cfg(all(feature = "batch", any(feature = "alloc", feature = "std")))] -#[allow(non_snake_case)] -pub fn verify_batch( - messages: &[&[u8]], - signatures: &[Signature], - public_keys: &[PublicKey], -) -> Result<(), SignatureError> -{ - if signatures.len() != messages.len() || - signatures.len() != public_keys.len() || - public_keys.len() != messages.len() { - return Err(SignatureError(InternalError::ArrayLengthError{ - name_a: "signatures", length_a: signatures.len(), - name_b: "messages", length_b: messages.len(), - name_c: "public_keys", length_c: public_keys.len(), - })); - } - - #[cfg(feature = "alloc")] - use alloc::vec::Vec; - #[cfg(feature = "std")] - use std::vec::Vec; - - use core::iter::once; - use rand::{Rng, thread_rng}; - - use curve25519_dalek::traits::IsIdentity; - use curve25519_dalek::traits::VartimeMultiscalarMul; - - // Select a random 128-bit scalar for each signature. - let zs: Vec = signatures - .iter() - .map(|_| Scalar::from(thread_rng().gen::())) - .collect(); - - // Compute the basepoint coefficient, ∑ s[i]z[i] (mod l) - let B_coefficient: Scalar = signatures - .iter() - .map(|sig| sig.s) - .zip(zs.iter()) - .map(|(s, z)| z * s) - .sum(); - - // Compute H(R || A || M) for each (signature, public_key, message) triplet - let hrams = (0..signatures.len()).map(|i| { - let mut h: Sha512 = Sha512::default(); - h.input(signatures[i].R.as_bytes()); - h.input(public_keys[i].as_bytes()); - h.input(&messages[i]); - Scalar::from_hash(h) - }); - - // Multiply each H(R || A || M) by the random value - let zhrams = hrams.zip(zs.iter()).map(|(hram, z)| hram * z); - - let Rs = signatures.iter().map(|sig| sig.R.decompress()); - let As = public_keys.iter().map(|pk| Some(pk.1)); - let B = once(Some(constants::ED25519_BASEPOINT_POINT)); - - // Compute (-∑ z[i]s[i] (mod l)) B + ∑ z[i]R[i] + ∑ (z[i]H(R||A||M)[i] (mod l)) A[i] = 0 - let id = EdwardsPoint::optional_multiscalar_mul( - once(-B_coefficient).chain(zs.iter().cloned()).chain(zhrams), - B.chain(Rs).chain(As), - ).ok_or_else(|| SignatureError(InternalError::VerifyError))?; - - if id.is_identity() { - Ok(()) - } else { - Err(SignatureError(InternalError::VerifyError)) - } -} - /// An ed25519 keypair. #[derive(Debug, Default)] // we derive Default in order to use the clear() method in Drop pub struct Keypair { diff --git a/src/lib.rs b/src/lib.rs index 92f1d45..bce5edf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -247,6 +247,8 @@ extern crate rand; extern crate serde; extern crate sha2; +#[cfg(all(feature = "batch", any(feature = "std", feature = "alloc")))] +mod batch; mod constants; mod ed25519; mod errors; From 85a218ac402bece6778540ec1376a944dfb0c2d0 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 22 Nov 2019 23:29:14 +0000 Subject: [PATCH 238/351] Implement deterministic batch verification and synthetic nonce generation. --- Cargo.toml | 10 ++- src/batch.rs | 203 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + 3 files changed, 212 insertions(+), 3 deletions(-) create mode 100644 src/batch.rs diff --git a/Cargo.toml b/Cargo.toml index 09b8ef2..1c8b73c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,8 @@ features = ["nightly", "batch"] [dependencies] clear_on_drop = { version = "0.2" } -curve25519-dalek = { version = "1", default-features = false } +curve25519-dalek = { version = "2.0.0-alpha.1", default-features = false } +merlin = { version = "1", default-features = false, optional = true } rand = { version = "0.7", default-features = false, optional = true } serde = { version = "1.0", optional = true } sha2 = { version = "0.8", default-features = false } @@ -46,11 +47,14 @@ default = ["std", "u64_backend"] std = ["curve25519-dalek/std", "sha2/std", "rand/std"] alloc = ["curve25519-dalek/alloc", "rand/alloc"] nightly = ["curve25519-dalek/nightly", "clear_on_drop/nightly", "rand/nightly"] -batch = ["rand"] +batch = ["merlin", "rand"] +# This feature enables deterministic batch verification. +batch_deterministic = ["merlin", "rand"] asm = ["sha2/asm"] # This features turns off stricter checking for scalar malleability in signatures legacy_compatibility = [] -yolocrypto = ["curve25519-dalek/yolocrypto"] u64_backend = ["curve25519-dalek/u64_backend"] u32_backend = ["curve25519-dalek/u32_backend"] +# Deprecated curve25519-dalek feature, use "simd_backend" instead: avx2_backend = ["curve25519-dalek/avx2_backend"] +simd_backend = ["curve25519-dalek/simd_backend"] \ No newline at end of file diff --git a/src/batch.rs b/src/batch.rs new file mode 100644 index 0000000..7368ccf --- /dev/null +++ b/src/batch.rs @@ -0,0 +1,203 @@ +// -*- mode: rust; -*- +// +// This file is part of ed25519-dalek. +// Copyright (c) 2017-2019 isis lovecruft +// See LICENSE for licensing information. +// +// Authors: +// - isis agora lovecruft + +//! Batch signature verification. + +#[cfg(feature = "alloc")] +use alloc::vec::Vec; +#[cfg(feature = "std")] +use std::vec::Vec; + +use core::iter::once; + +use curve25519_dalek::constants; +use curve25519_dalek::edwards::EdwardsPoint; +use curve25519_dalek::scalar::Scalar; +use curve25519_dalek::traits::IsIdentity; +use curve25519_dalek::traits::VartimeMultiscalarMul; + +pub use curve25519_dalek::digest::Digest; + +use merlin::Transcript; + +#[cfg(all(feature = "batch", not(feature = "batch_deterministic")))] +use rand::{Rng, thread_rng}; +#[cfg(all(not(feature = "batch"), feature = "batch_deterministic"))] +use rand::{CryptoRng, RngCore}; + +use sha2::Sha512; + +use crate::errors::InternalError; +use crate::errors::SignatureError; +use crate::public::PublicKey; +use crate::signature::Signature; + +trait BatchTranscript { + fn append_hrams(&mut self, hrams: &Vec); +} + +impl BatchTranscript for Transcript { + /// Add all the computed `H(R||A||M)`s to the protocol transcript. + /// + /// Each is also prefixed with their index in the vector. + fn append_hrams(&mut self, hrams: &Vec) { + for (i, hram) in hrams.iter().enumerate() { + self.append_u64(b"", i as u64); + self.append_message(b"hram", hram.as_bytes()); + } + } +} + +/// An implementation of `rand_core::RngCore` which does nothing, to provide +/// purely deterministic transcript-based nonces, rather than synthetically +/// random nonces. +#[cfg(all(not(feature = "batch"), feature = "batch_deterministic"))] +struct ZeroRng {} + +#[cfg(all(not(feature = "batch"), feature = "batch_deterministic"))] +impl rand_core::RngCore for ZeroRng { + fn next_u32(&mut self) -> u32 { + rand_core::impls::next_u32_via_fill(self) + } + + fn next_u64(&mut self) -> u64 { + rand_core::impls::next_u64_via_fill(self) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { + self.fill_bytes(dest); + Ok(()) + } +} + +#[cfg(all(not(feature = "batch"), feature = "batch_deterministic"))] +impl rand_core::CryptoRng for ZeroRng {} + +#[cfg(all(not(feature = "batch"), feature = "batch_deterministic"))] +fn zero_rng() -> ZeroRng { + ZeroRng +} + +/// Verify a batch of `signatures` on `messages` with their respective `public_keys`. +/// +/// # Inputs +/// +/// * `messages` is a slice of byte slices, one per signed message. +/// * `signatures` is a slice of `Signature`s. +/// * `public_keys` is a slice of `PublicKey`s. +/// * `csprng` is an implementation of `Rng + CryptoRng`. +/// +/// # Returns +/// +/// * A `Result` whose `Ok` value is an emtpy tuple and whose `Err` value is a +/// `SignatureError` containing a description of the internal error which +/// occured. +/// +/// # Examples +/// +/// ``` +/// extern crate ed25519_dalek; +/// extern crate rand; +/// +/// use ed25519_dalek::verify_batch; +/// use ed25519_dalek::Keypair; +/// use ed25519_dalek::PublicKey; +/// use ed25519_dalek::Signature; +/// use rand::rngs::OsRng; +/// +/// # fn main() { +/// let mut csprng = OsRng{}; +/// let keypairs: Vec = (0..64).map(|_| Keypair::generate(&mut csprng)).collect(); +/// let msg: &[u8] = b"They're good dogs Brant"; +/// let messages: Vec<&[u8]> = (0..64).map(|_| msg).collect(); +/// let signatures: Vec = keypairs.iter().map(|key| key.sign(&msg)).collect(); +/// let public_keys: Vec = keypairs.iter().map(|key| key.public).collect(); +/// +/// let result = verify_batch(&messages[..], &signatures[..], &public_keys[..]); +/// assert!(result.is_ok()); +/// # } +/// ``` +#[cfg(all(any(feature = "batch", feature = "batch_deterministic"), + any(feature = "alloc", feature = "std")))] +#[allow(non_snake_case)] +pub fn verify_batch( + messages: &[&[u8]], + signatures: &[Signature], + public_keys: &[PublicKey], +) -> Result<(), SignatureError> +{ + // Return an Error if any of the vectors were not the same size as the others. + if signatures.len() != messages.len() || + signatures.len() != public_keys.len() || + public_keys.len() != messages.len() { + return Err(SignatureError(InternalError::ArrayLengthError{ + name_a: "signatures", length_a: signatures.len(), + name_b: "messages", length_b: messages.len(), + name_c: "public_keys", length_c: public_keys.len(), + })); + } + + // Compute H(R || A || M) for each (signature, public_key, message) triplet + let hrams: Vec = (0..signatures.len()).map(|i| { + let mut h: Sha512 = Sha512::default(); + h.input(signatures[i].R.as_bytes()); + h.input(public_keys[i].as_bytes()); + h.input(&messages[i]); + Scalar::from_hash(h) + }).collect(); + + // Build a PRNG based on a transcript of the H(R || A || M)s seen thus far. + // This provides synthethic randomness in the default configuration, and + // purely deterministic in the case of compiling with the + // "batch_deterministic" feature. + let transcript: Transcript = Transcript::new(b"ed25519 batch verification"); + + transcript.append_hrams(&hrams); + + #[cfg(all(feature = "batch", not(feature = "batch_deterministic")))] + let mut prng = transcript.build_rng().finalize(&mut thread_rng()); + #[cfg(all(not(feature = "batch"), feature = "batch_deterministic"))] + let mut prng = transcript.build_rng().finalize(&mut zero_rng()); + + // Select a random 128-bit scalar for each signature. + let zs: Vec = signatures + .iter() + .map(|_| Scalar::from(thread_rng().gen::())) + .collect(); + + + // Compute the basepoint coefficient, ∑ s[i]z[i] (mod l) + let B_coefficient: Scalar = signatures + .iter() + .map(|sig| sig.s) + .zip(zs.iter()) + .map(|(s, z)| z * s) + .sum(); + + // Multiply each H(R || A || M) by the random value + let zhrams = hrams.iter().zip(zs.iter()).map(|(hram, z)| hram * z); + + let Rs = signatures.iter().map(|sig| sig.R.decompress()); + let As = public_keys.iter().map(|pk| Some(pk.1)); + let B = once(Some(constants::ED25519_BASEPOINT_POINT)); + + // Compute (-∑ z[i]s[i] (mod l)) B + ∑ z[i]R[i] + ∑ (z[i]H(R||A||M)[i] (mod l)) A[i] = 0 + let id = EdwardsPoint::optional_multiscalar_mul( + once(-B_coefficient).chain(zs.iter().cloned()).chain(zhrams), + B.chain(Rs).chain(As), + ).ok_or_else(|| SignatureError(InternalError::VerifyError))?; + + if id.is_identity() { + Ok(()) + } else { + Err(SignatureError(InternalError::VerifyError)) + } +} diff --git a/src/lib.rs b/src/lib.rs index bce5edf..097e784 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -241,6 +241,8 @@ extern crate std; extern crate alloc; extern crate clear_on_drop; extern crate curve25519_dalek; +#[cfg(all(any(feature = "batch", feature = "batch_deterministic"), any(feature = "std", feature = "alloc")))] +extern crate merlin; #[cfg(any(feature = "batch", feature = "std", feature = "alloc", test))] extern crate rand; #[cfg(feature = "serde")] From bd6a8977297922dccd5a2ac94cb4be819d731232 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 22 Nov 2019 23:40:46 +0000 Subject: [PATCH 239/351] Actually use the transcript PRNG. --- src/batch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/batch.rs b/src/batch.rs index 7368ccf..d8c513c 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -170,7 +170,7 @@ pub fn verify_batch( // Select a random 128-bit scalar for each signature. let zs: Vec = signatures .iter() - .map(|_| Scalar::from(thread_rng().gen::())) + .map(|_| Scalar::from(prng.gen::())) .collect(); From ec551145e966894fd320c11eb7d1cdc9922d3b1a Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 22 Nov 2019 23:21:01 +0000 Subject: [PATCH 240/351] Add message lengths into nonce generator protocol transcript. --- src/batch.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/batch.rs b/src/batch.rs index d8c513c..2cde684 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -40,6 +40,7 @@ use crate::signature::Signature; trait BatchTranscript { fn append_hrams(&mut self, hrams: &Vec); + fn append_message_lengths(&mut self, message_lengths: &Vec); } impl BatchTranscript for Transcript { @@ -48,10 +49,18 @@ impl BatchTranscript for Transcript { /// Each is also prefixed with their index in the vector. fn append_hrams(&mut self, hrams: &Vec) { for (i, hram) in hrams.iter().enumerate() { + // XXX add message length into transcript self.append_u64(b"", i as u64); self.append_message(b"hram", hram.as_bytes()); } } + + fn append_message_lengths(&mut self, message_lengths: &Vec) { + for (i, len) in message_lengths.iter().enumerate() { + self.append_u64(b"", i as u64); + self.append_u64(b"mlen", len as u64); + } + } } /// An implementation of `rand_core::RngCore` which does nothing, to provide @@ -154,6 +163,9 @@ pub fn verify_batch( Scalar::from_hash(h) }).collect(); + // Collect the message lengths to add into the transcript. + let message_lengths: Vec = messages.iter().map(|i| i.len()).collect(); + // Build a PRNG based on a transcript of the H(R || A || M)s seen thus far. // This provides synthethic randomness in the default configuration, and // purely deterministic in the case of compiling with the @@ -161,6 +173,7 @@ pub fn verify_batch( let transcript: Transcript = Transcript::new(b"ed25519 batch verification"); transcript.append_hrams(&hrams); + transcript.append_message_lengths(&message_lengths); #[cfg(all(feature = "batch", not(feature = "batch_deterministic")))] let mut prng = transcript.build_rng().finalize(&mut thread_rng()); From 8938069053d2ec59a063f4b98edc4a91a6d5b5c5 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 26 Nov 2019 22:51:37 +0000 Subject: [PATCH 241/351] Update curve25519-dalek dependency to 2.0.0. --- Cargo.toml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 09b8ef2..c4e1359 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ features = ["nightly", "batch"] [dependencies] clear_on_drop = { version = "0.2" } -curve25519-dalek = { version = "1", default-features = false } +curve25519-dalek = { version = "2", default-features = false } rand = { version = "0.7", default-features = false, optional = true } serde = { version = "1.0", optional = true } sha2 = { version = "0.8", default-features = false } @@ -50,7 +50,6 @@ batch = ["rand"] asm = ["sha2/asm"] # This features turns off stricter checking for scalar malleability in signatures legacy_compatibility = [] -yolocrypto = ["curve25519-dalek/yolocrypto"] u64_backend = ["curve25519-dalek/u64_backend"] u32_backend = ["curve25519-dalek/u32_backend"] -avx2_backend = ["curve25519-dalek/avx2_backend"] +simd_backend = ["curve25519-dalek/simd_backend"] From 1be2a65777ffc198d8fbca419a1178a9e9f1c08b Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 23 Nov 2019 01:34:13 +0000 Subject: [PATCH 242/351] Maybe I should try compiling my code before showing other cryptographers? lol --- Cargo.toml | 7 ++++--- src/batch.rs | 21 +++++++++++++++------ src/lib.rs | 4 +++- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1c8b73c..c0ab84f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,8 +24,9 @@ features = ["nightly", "batch"] [dependencies] clear_on_drop = { version = "0.2" } curve25519-dalek = { version = "2.0.0-alpha.1", default-features = false } -merlin = { version = "1", default-features = false, optional = true } +merlin = { version = "1", default-features = false, optional = true, git = "https://github.com/isislovecruft/merlin", branch = "develop" } rand = { version = "0.7", default-features = false, optional = true } +rand_core = { version = "0.5", default-features = false, optional = true } serde = { version = "1.0", optional = true } sha2 = { version = "0.8", default-features = false } @@ -49,7 +50,7 @@ alloc = ["curve25519-dalek/alloc", "rand/alloc"] nightly = ["curve25519-dalek/nightly", "clear_on_drop/nightly", "rand/nightly"] batch = ["merlin", "rand"] # This feature enables deterministic batch verification. -batch_deterministic = ["merlin", "rand"] +batch_deterministic = ["merlin", "rand", "rand_core"] asm = ["sha2/asm"] # This features turns off stricter checking for scalar malleability in signatures legacy_compatibility = [] @@ -57,4 +58,4 @@ u64_backend = ["curve25519-dalek/u64_backend"] u32_backend = ["curve25519-dalek/u32_backend"] # Deprecated curve25519-dalek feature, use "simd_backend" instead: avx2_backend = ["curve25519-dalek/avx2_backend"] -simd_backend = ["curve25519-dalek/simd_backend"] \ No newline at end of file +simd_backend = ["curve25519-dalek/simd_backend"] diff --git a/src/batch.rs b/src/batch.rs index 2cde684..a778934 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -26,10 +26,11 @@ pub use curve25519_dalek::digest::Digest; use merlin::Transcript; +use rand::Rng; #[cfg(all(feature = "batch", not(feature = "batch_deterministic")))] -use rand::{Rng, thread_rng}; +use rand::thread_rng; #[cfg(all(not(feature = "batch"), feature = "batch_deterministic"))] -use rand::{CryptoRng, RngCore}; +use rand_core; use sha2::Sha512; @@ -58,7 +59,7 @@ impl BatchTranscript for Transcript { fn append_message_lengths(&mut self, message_lengths: &Vec) { for (i, len) in message_lengths.iter().enumerate() { self.append_u64(b"", i as u64); - self.append_u64(b"mlen", len as u64); + self.append_u64(b"mlen", *len as u64); } } } @@ -79,7 +80,15 @@ impl rand_core::RngCore for ZeroRng { rand_core::impls::next_u64_via_fill(self) } - fn fill_bytes(&mut self, dest: &mut [u8]) { } + /// A no-op function which leaves the destination bytes for randomness unchanged. + /// + /// In this case, the internal merlin code is initialising the destination + /// by doing `[0u8; …]`, which means that when we call + /// `merlin::TranscriptRngBuilder.finalize()`, rather than rekeying the + /// STROBE state based on external randomness, we're doing an + /// `ENC_{state}(00000000000000000000000000000000)` operation, which is + /// identical to the STROBE `MAC` operation. + fn fill_bytes(&mut self, _dest: &mut [u8]) { } fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { self.fill_bytes(dest); @@ -92,7 +101,7 @@ impl rand_core::CryptoRng for ZeroRng {} #[cfg(all(not(feature = "batch"), feature = "batch_deterministic"))] fn zero_rng() -> ZeroRng { - ZeroRng + ZeroRng {} } /// Verify a batch of `signatures` on `messages` with their respective `public_keys`. @@ -170,7 +179,7 @@ pub fn verify_batch( // This provides synthethic randomness in the default configuration, and // purely deterministic in the case of compiling with the // "batch_deterministic" feature. - let transcript: Transcript = Transcript::new(b"ed25519 batch verification"); + let mut transcript: Transcript = Transcript::new(b"ed25519 batch verification"); transcript.append_hrams(&hrams); transcript.append_message_lengths(&message_lengths); diff --git a/src/lib.rs b/src/lib.rs index 097e784..32aff4e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -249,7 +249,7 @@ extern crate rand; extern crate serde; extern crate sha2; -#[cfg(all(feature = "batch", any(feature = "std", feature = "alloc")))] +#[cfg(all(any(feature = "batch", feature = "batch_deterministic"), any(feature = "std", feature = "alloc")))] mod batch; mod constants; mod ed25519; @@ -260,3 +260,5 @@ mod signature; // Export everything public in ed25519. pub use crate::ed25519::*; +#[cfg(all(any(feature = "batch", feature = "batch_deterministic"), any(feature = "std", feature = "alloc")))] +pub use crate::batch::*; From 52a7fc88b62cfb264641cc468bc5fa770d97ae99 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 27 Nov 2019 22:26:54 +0000 Subject: [PATCH 243/351] Update README w.r.t. new features, malleability, synthethic randomness. --- README.md | 119 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 89 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 6941314..49766fb 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,15 @@ verification in Rust. Documentation is available [here](https://docs.rs/ed25519-dalek). +# Installation + +To install, add the following to your project's `Cargo.toml`: + +```toml +[dependencies.ed25519-dalek] +version = "1" +``` + # Benchmarks On an Intel Skylake i9-7900X running at 3.30 GHz, without TurboBoost, this code achieves @@ -89,14 +98,20 @@ can read qhasm, making it more readily and more easily auditable. We're of the opinion that, ultimately, these features—combined with speed—are more valuable than simply cycle counts alone. -### A Note on Signature Malleability +# A Note on Signature Malleability The signatures produced by this library are malleable, as discussed in [the original paper](https://ed25519.cr.yp.to/ed25519-20110926.pdf): ![](https://github.com/dalek-cryptography/ed25519-dalek/blob/master/res/ed25519-malleability.png) -We could eliminate the malleability property by multiplying by the curve +While the scalar component of our `Signature` struct is strictly *not* +malleable, because reduction checks are put in place upon `Signature` +deserialisation from bytes, for all types of signatures in this crate, +there is still the question of potential malleability due to the group +element components. + +We could eliminate the latter malleability property by multiplying by the curve cofactor, however, this would cause our implementation to *not* match the behaviour of every other implementation in existence. As of this writing, [RFC 8032](https://tools.ietf.org/html/rfc8032), "Edwards-Curve Digital @@ -105,12 +120,16 @@ While we agree that the stronger check should be done, it is our opinion that one shouldn't get to change the definition of "ed25519 verification" a decade after the fact, breaking compatibility with every other implementation. -In short, if malleable signatures are bad for your protocol, don't use them. -Consider using a curve25519-based Verifiable Random Function (VRF), such as -[Trevor Perrin's VXEdDSA](https://www.whispersystems.org/docs/specifications/xeddsa/), -instead. +However, if you require this, please see the documentation for the +`verify_strict()` function, which does the full checks for the group elements. +This functionality is available by default. -#### The `legacy_compatibility` Feature +If for some reason—although we strongely advise you not to—you need to conform +to the original specification of ed25519 signatures as in the excerpt from the +paper above, you can disable scalar malleability checking via +`--features='legacy_compatibility'`. **WE STRONGLY ADVISE AGAINST THIS.** + +## The `legacy_compatibility` Feature By default, this library performs a stricter check for malleability in the scalar component of a signature, upon signature deserialisation. This stricter @@ -136,7 +155,7 @@ message, which is obviously incredibly dangerous in a number of contexts, including—but not limited to—identification protocols and cryptocurrency transactions. -#### The `verify_strict()` Function +## The `verify_strict()` Function The scalar component of a signature is not the only source of signature malleability, however. Both the public key used for signature verification and @@ -148,23 +167,51 @@ If you wish to also eliminate this source of signature malleability, please review the [documentation for the `verify_strict()` function](https://doc.dalek.rs/ed25519_dalek/struct.PublicKey.html#method.verify_strict). -# Installation +# A Note on Randomness Generation -To install, add the following to your project's `Cargo.toml`: +The original paper's specification and the standarisation of RFC8032 do not +specify precisely how randomness is to be generated, other than using a CSPRNG +(Cryptographically Secure Random Number Generator). Particularly in the case of +signature verification, where the security proof _relies_ on the uniqueness of +the blinding factors/nonces, it is paramount that these samples of randomness be +unguessable to an adversary. Because of this, a current growing belief among +cryptographers is that it is safer to prefer _synthetic randomness_. -```toml -[dependencies.ed25519-dalek] -version = "1" -``` +To explain synthetic randomness, we should first explain how `ed25519-dalek` +handles generation of _deterministic randomness_. This mode is disabled by +default due to a tiny-but-not-nonexistent chance that this mode will open users +up to fault attacks, wherein an adversary who controls all of the inputs to +batch verification (i.e. the public keys, signatures, and messages) can craft +them in a specialised manner such as to induce a fault (e.g. causing a +mistakenly flipped bit in RAM, overheating a processor, etc.). In the +deterministic mode, we seed the PRNG which generates our blinding factors/nonces +by creating +[a PRNG based on the Fiat-Shamir transform of the public inputs](https://merlin.cool/transcript/rng.html). +This mode is potentially useful to protocols which require strong auditability +guarantees, as well as those which do not have access to secure system-/chip- +provided randomness. This feature can be enabled via +`--features='batch_deterministic'`. Note that we _do not_ support deterministic +signing, due to the numerous pitfalls therein, including a re-used nonce +accidentally revealing the secret key. -Then, in your library or executable source, add: - -```rust -extern crate ed25519_dalek; -``` +In the default mode, we do as above in the fully deterministic mode, but we +ratchet the underlying keccak-f1600 function (used for the provided +transcript-based PRNG) forward additionally based on some system-/chip- provided +randomness. This provides _synthetic randomness_, that is, randomness based on +both deterministic and undeterinistic data. The reason for doing this is to +prevent badly seeded system RNGs from ruining the security of the signature +verification scheme. # Features +## #![no_std] + +This library aims to be `#![no_std]` compliant. If batch verification is +required (`--features='batch'`), please enable either of the `std` or `alloc` +features. + +## Nightly Compilers + To cause your application to build `ed25519-dalek` with the nightly feature enabled by default, instead do: @@ -183,19 +230,31 @@ to the `Cargo.toml`: nightly = ["ed25519-dalek/nightly"] ``` -To enable [serde](https://serde.rs) support, build `ed25519-dalek` with: +## Serde -```toml -[dependencies.ed25519-dalek] -version = "1" -features = ["serde"] -``` +To enable [serde](https://serde.rs) support, build `ed25519-dalek` with the +`serde` feature. + +## (Micro)Architecture Specific Backends By default, `ed25519-dalek` builds against `curve25519-dalek`'s `u64_backend` feature, which uses Rust's `i128` feature to achieve roughly double the speed as the `u32_backend` feature. When targetting 32-bit systems, however, you'll -likely want to compile with - `cargo build --no-default-features --features="u32_backend"`. -If you're building for a machine with avx2 instructions, there's also the -experimental `avx2_backend`. To use it, compile with -`RUSTFLAGS="-C target_cpu=native" cargo build --no-default-features --features="avx2_backend"` +likely want to compile with `cargo build --no-default-features +--features="u32_backend"`. If you're building for a machine with avx2 +instructions, there's also the experimental `simd_backend`s, currently +comprising either avx2 or avx512 backends. To use them, compile with +`RUSTFLAGS="-C target_cpu=native" cargo build --no-default-features +--features="simd_backend"` + +## Batch Signature Verification + +The standard variants of batch signature verification (i.e. many signatures made +with potentially many different public keys over potentially many different +message) is available via the `batch` feature. It uses synthetic randomness, as +noted above. + +### Deterministic Batch Signature Verification + +The same notion of batch signature verification as above, but with purely +deterministic randomness can be enabled via the `batch_deterministic` feature. From 29a06e494ddd4f370256596b7103125295073435 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 6 Dec 2019 23:42:52 +0000 Subject: [PATCH 244/351] Bump ed25519-dalek version to 1.0.0-pre.3. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 87fb087..c9d77a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "1.0.0-pre.2" +version = "1.0.0-pre.3" edition = "2018" authors = ["isis lovecruft "] readme = "README.md" From 8ca3be99e9d1585dbe187e33e10bc5f405009fac Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 9 Dec 2019 22:39:57 +0000 Subject: [PATCH 245/351] Switch to using zeroize rather than clear_on_drop. --- Cargo.toml | 6 +++--- src/ed25519.rs | 25 +------------------------ src/lib.rs | 2 +- src/secret.rs | 51 +++++++++++++++++++++++++++++++------------------- 4 files changed, 37 insertions(+), 47 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c9d77a6..fc87a11 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,13 +22,13 @@ travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master" features = ["nightly", "batch"] [dependencies] -clear_on_drop = { version = "0.2" } curve25519-dalek = { version = "2", default-features = false } merlin = { version = "1", default-features = false, optional = true, git = "https://github.com/isislovecruft/merlin", branch = "develop" } rand = { version = "0.7", default-features = false, optional = true } rand_core = { version = "0.5", default-features = false, optional = true } serde = { version = "1.0", optional = true } sha2 = { version = "0.8", default-features = false } +zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] } [dev-dependencies] hex = "^0.4" @@ -46,8 +46,8 @@ harness = false [features] default = ["std", "u64_backend"] std = ["curve25519-dalek/std", "sha2/std", "rand/std"] -alloc = ["curve25519-dalek/alloc", "rand/alloc"] -nightly = ["curve25519-dalek/nightly", "clear_on_drop/nightly", "rand/nightly"] +alloc = ["curve25519-dalek/alloc", "rand/alloc", "zeroize/alloc"] +nightly = ["curve25519-dalek/nightly", "rand/nightly"] batch = ["merlin", "rand"] # This feature enables deterministic batch verification. batch_deterministic = ["merlin", "rand", "rand_core"] diff --git a/src/ed25519.rs b/src/ed25519.rs index 7c55a65..dd150bf 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -34,7 +34,7 @@ pub use crate::secret::*; pub use crate::signature::*; /// An ed25519 keypair. -#[derive(Debug, Default)] // we derive Default in order to use the clear() method in Drop +#[derive(Debug)] pub struct Keypair { /// The secret half of this keypair. pub secret: SecretKey, @@ -444,26 +444,3 @@ impl<'d> Deserialize<'d> for Keypair { deserializer.deserialize_bytes(KeypairVisitor) } } - -#[cfg(test)] -mod test { - use super::*; - - use clear_on_drop::clear::Clear; - - #[test] - fn keypair_clear_on_drop() { - let mut keypair: Keypair = Keypair::from_bytes(&[1u8; KEYPAIR_LENGTH][..]).unwrap(); - - keypair.clear(); - - fn as_bytes(x: &T) -> &[u8] { - use std::mem; - use std::slice; - - unsafe { slice::from_raw_parts(x as *const T as *const u8, mem::size_of_val(x)) } - } - - assert!(!as_bytes(&keypair).contains(&0x15)); - } -} diff --git a/src/lib.rs b/src/lib.rs index 32aff4e..bee0f7c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -239,7 +239,6 @@ extern crate std; #[cfg(all(feature = "alloc", not(feature = "std")))] extern crate alloc; -extern crate clear_on_drop; extern crate curve25519_dalek; #[cfg(all(any(feature = "batch", feature = "batch_deterministic"), any(feature = "std", feature = "alloc")))] extern crate merlin; @@ -248,6 +247,7 @@ extern crate rand; #[cfg(feature = "serde")] extern crate serde; extern crate sha2; +extern crate zeroize; #[cfg(all(any(feature = "batch", feature = "batch_deterministic"), any(feature = "std", feature = "alloc")))] mod batch; diff --git a/src/secret.rs b/src/secret.rs index 0c54275..f1e751d 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -11,8 +11,6 @@ use core::fmt::Debug; -use clear_on_drop::clear::Clear; - use curve25519_dalek::constants; use curve25519_dalek::digest::generic_array::typenum::U64; use curve25519_dalek::digest::Digest; @@ -32,13 +30,19 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "serde")] use serde::{Deserializer, Serializer}; +use zeroize::Zeroize; + use crate::constants::*; use crate::errors::*; use crate::public::*; use crate::signature::*; /// An EdDSA secret key. -#[derive(Default)] // we derive Default in order to use the clear() method in Drop +/// +/// Instances of this secret are automatically overwritten with zeroes when they +/// fall out of scope. +#[derive(Zeroize)] +#[zeroize(drop)] // Overwrite secret key material with null bytes when it goes out of scope. pub struct SecretKey(pub(crate) [u8; SECRET_KEY_LENGTH]); impl Debug for SecretKey { @@ -47,13 +51,6 @@ impl Debug for SecretKey { } } -/// Overwrite secret key material with null bytes when it goes out of scope. -impl Drop for SecretKey { - fn drop(&mut self) { - self.0.clear(); - } -} - impl AsRef<[u8]> for SecretKey { fn as_ref(&self) -> &[u8] { self.as_bytes() @@ -223,6 +220,9 @@ impl<'d> Deserialize<'d> for SecretKey { /// upper half is used a sort of half-baked, ill-designed² pseudo-domain-separation /// "nonce"-like thing, which is used during signature production by /// concatenating it with the message to be signed before the message is hashed. +/// +/// Instances of this secret are automatically overwritten with zeroes when they +/// fall out of scope. // // ¹ This results in a slight bias towards non-uniformity at one spectrum of // the range of valid keys. Oh well: not my idea; not my problem. @@ -250,20 +250,13 @@ impl<'d> Deserialize<'d> for SecretKey { // same signature scheme, and which both fail in exactly the same way. For a // better-designed, Schnorr-based signature scheme, see Trevor Perrin's work on // "generalised EdDSA" and "VXEdDSA". -#[derive(Default)] // we derive Default in order to use the clear() method in Drop +#[derive(Zeroize)] +#[zeroize(drop)] // Overwrite secret key material with null bytes when it goes out of scope. pub struct ExpandedSecretKey { pub(crate) key: Scalar, pub(crate) nonce: [u8; 32], } -/// Overwrite secret key material with null bytes when it goes out of scope. -impl Drop for ExpandedSecretKey { - fn drop(&mut self) { - self.key.clear(); - self.nonce.clear(); - } -} - impl<'a> From<&'a SecretKey> for ExpandedSecretKey { /// Construct an `ExpandedSecretKey` from a `SecretKey`. /// @@ -554,3 +547,23 @@ impl<'d> Deserialize<'d> for ExpandedSecretKey { deserializer.deserialize_bytes(ExpandedSecretKeyVisitor) } } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn secret_key_zeroize_on_drop() { + let secret_ptr: *const u8; + + { // scope for the secret to ensure it's been dropped + let secret = SecretKey::from_bytes(&[0x15u8; 32][..]).unwrap(); + + secret_ptr = secret.0.as_ptr(); + } + + let memory: &[u8] = unsafe { ::std::slice::from_raw_parts(secret_ptr, 32) }; + + assert!(!memory.contains(&0x15)); + } +} From 0a191a86f63e553dd33212313587b2ec3815f3cc Mon Sep 17 00:00:00 2001 From: Chris Beck Date: Tue, 10 Dec 2019 13:30:56 -0800 Subject: [PATCH 246/351] Use `default-features = false` with `serde` It doesn't appear to me that ed25519-dalek crate needs any of the std-related features of serde. But it turns them on anyways because it doesn't put `default-features = false`. This breaks no_std builds. Otherwise I think we could use 1.0.0-pre3 in mobilecoin. I'm going to test this revision in our build and see if I'm right. I don't think this is a breaking change from dalek's point of view. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c9d77a6..ba80153 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ curve25519-dalek = { version = "2", default-features = false } merlin = { version = "1", default-features = false, optional = true, git = "https://github.com/isislovecruft/merlin", branch = "develop" } rand = { version = "0.7", default-features = false, optional = true } rand_core = { version = "0.5", default-features = false, optional = true } -serde = { version = "1.0", optional = true } +serde = { version = "1.0", default-features = false, optional = true } sha2 = { version = "0.8", default-features = false } [dev-dependencies] From 9363690191b1c3798bfeca8a6c36f70cb7f311f7 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 11 Dec 2019 22:48:33 +0000 Subject: [PATCH 247/351] Fix outdated docstring for verify_batch(). --- src/batch.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/batch.rs b/src/batch.rs index a778934..90d28ef 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -111,7 +111,6 @@ fn zero_rng() -> ZeroRng { /// * `messages` is a slice of byte slices, one per signed message. /// * `signatures` is a slice of `Signature`s. /// * `public_keys` is a slice of `PublicKey`s. -/// * `csprng` is an implementation of `Rng + CryptoRng`. /// /// # Returns /// @@ -195,7 +194,6 @@ pub fn verify_batch( .map(|_| Scalar::from(prng.gen::())) .collect(); - // Compute the basepoint coefficient, ∑ s[i]z[i] (mod l) let B_coefficient: Scalar = signatures .iter() From 8a2e9af9d6c71f7d12be90e13223d585dd5df15e Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 11 Dec 2019 23:02:44 +0000 Subject: [PATCH 248/351] Fix breakage on builds with the rand crate disabled. * CLOSES https://github.com/dalek-cryptography/ed25519-dalek/issues/108 * THANKS TO @tarcieri --- src/ed25519.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ed25519.rs b/src/ed25519.rs index dd150bf..076d30c 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -11,6 +11,7 @@ use core::default::Default; +#[cfg(feature = "rand")] use rand::{CryptoRng, RngCore}; #[cfg(feature = "serde")] From 3a9101933b38acf4136925f89c2cee290d30445b Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 11 Dec 2019 23:14:27 +0000 Subject: [PATCH 249/351] Enable serde/std if std is enabled. * FIXES part of https://github.com/dalek-cryptography/ed25519-dalek/pull/107 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4fb9d9d..2931ff5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,7 +45,7 @@ harness = false [features] default = ["std", "u64_backend"] -std = ["curve25519-dalek/std", "sha2/std", "rand/std"] +std = ["curve25519-dalek/std", "serde/std", "sha2/std", "rand/std"] alloc = ["curve25519-dalek/alloc", "rand/alloc", "zeroize/alloc"] nightly = ["curve25519-dalek/nightly", "rand/nightly"] batch = ["merlin", "rand"] From 3d9d11dcdf64a27b8c2fa7f5cdb0fcbff415d0d6 Mon Sep 17 00:00:00 2001 From: James Munns Date: Mon, 13 Jan 2020 00:59:08 +0100 Subject: [PATCH 250/351] Add additional visitor methods for deserialization --- src/ed25519.rs | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 7c55a65..e08a185 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -18,6 +18,8 @@ use serde::de::Error as SerdeError; #[cfg(feature = "serde")] use serde::de::Visitor; #[cfg(feature = "serde")] +use serde::de::SeqAccess; +#[cfg(feature = "serde")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; pub use sha2::Sha512; @@ -431,15 +433,47 @@ impl<'d> Deserialize<'d> for Keypair { where E: SerdeError, { + if bytes.len() != KEYPAIR_LENGTH { + return Err(SerdeError::invalid_length(bytes.len(), &self)); + } + let secret_key = SecretKey::from_bytes(&bytes[..SECRET_KEY_LENGTH]); let public_key = PublicKey::from_bytes(&bytes[SECRET_KEY_LENGTH..]); - if secret_key.is_ok() && public_key.is_ok() { - Ok(Keypair{ secret: secret_key.unwrap(), public: public_key.unwrap() }) + if let (Ok(secret), Ok(public)) = (secret_key, public_key) { + Ok(Keypair{ secret, public }) } else { Err(SerdeError::invalid_length(bytes.len(), &self)) } } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'d> + { + if let Some(len) = seq.size_hint() { + if len != KEYPAIR_LENGTH { + return Err(SerdeError::invalid_length(len, &self)); + } + } + + // TODO: We could do this with `MaybeUninit` to avoid unnecessary initialization costs + let mut bytes: [u8; KEYPAIR_LENGTH] = [0u8; KEYPAIR_LENGTH]; + + for i in 0..KEYPAIR_LENGTH { + bytes[i] = seq.next_element()?.ok_or_else(|| SerdeError::invalid_length(i, &self))?; + } + + let secret_key = SecretKey::from_bytes(&bytes[..SECRET_KEY_LENGTH]); + let public_key = PublicKey::from_bytes(&bytes[SECRET_KEY_LENGTH..]); + + if let (Ok(secret), Ok(public)) = (secret_key, public_key) { + Ok(Keypair{ secret, public }) + } else { + Err(SerdeError::invalid_length(bytes.len(), &self)) + } + } + } deserializer.deserialize_bytes(KeypairVisitor) } From dedbb9b96a1bacec5a181fae32ce601f640eff99 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Sat, 22 Feb 2020 15:10:21 +0300 Subject: [PATCH 251/351] fix alloc feature --- src/batch.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/batch.rs b/src/batch.rs index a778934..7f5838f 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -9,9 +9,11 @@ //! Batch signature verification. +#[cfg(feature = "alloc")] +extern crate alloc; #[cfg(feature = "alloc")] use alloc::vec::Vec; -#[cfg(feature = "std")] +#[cfg(all(not(feature = "alloc"), feature = "std"))] use std::vec::Vec; use core::iter::once; From 2dad99a60eb3ed85dfadcd0535ed5888f64be4f6 Mon Sep 17 00:00:00 2001 From: phayes Date: Sun, 23 Feb 2020 07:32:45 -0800 Subject: [PATCH 252/351] Removing double --- src/secret.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/secret.rs b/src/secret.rs index 0c54275..7bc0894 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -231,7 +231,7 @@ impl<'d> Deserialize<'d> for SecretKey { // you'd like to complain about me, again) that this is "ill-designed" because // this doesn't actually provide true hash domain separation, in that in many // real-world applications a user wishes to have one key which is used in -// several contexts (such as within tor, which does does domain separation +// several contexts (such as within tor, which does domain separation // manually by pre-concatenating static strings to messages to achieve more // robust domain separation). In other real-world applications, such as // bitcoind, a user might wish to have one master keypair from which others are From aa38c6419d7859d0fa95f561e8a72cb623bec167 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= Date: Thu, 16 Apr 2020 20:48:49 -0700 Subject: [PATCH 253/351] Updates the merlin dependency to ^2 and the correct repo This fixes the "batch" feature, see #126. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c9d77a6..5f79970 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ features = ["nightly", "batch"] [dependencies] clear_on_drop = { version = "0.2" } curve25519-dalek = { version = "2", default-features = false } -merlin = { version = "1", default-features = false, optional = true, git = "https://github.com/isislovecruft/merlin", branch = "develop" } +merlin = { version = "2", default-features = false, optional = true } rand = { version = "0.7", default-features = false, optional = true } rand_core = { version = "0.5", default-features = false, optional = true } serde = { version = "1.0", optional = true } From 6e0667d4298fdf9cb0d3c3cd65c39ba52f0ec702 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Tue, 17 Mar 2020 10:25:33 -0700 Subject: [PATCH 254/351] Use `ed25519` + `signature` interop crates The `signature` crate provides `Signer` and `Verifier` traits generic over signature types: https://github.com/RustCrypto/traits/tree/master/signature There's presently an open call to stabilize the parts of its API needed by Ed25519 signatures and release a 1.0 version: https://github.com/RustCrypto/traits/issues/78 The `ed25519` crate, based on the `signature` crate, provides an `ed25519::Signature` type which can be shared across multiple Ed25519 crates (e.g. it is also used by the `yubihsm` crate): https://github.com/RustCrypto/signatures/tree/master/ed25519 This commit integrates the `ed25519::Signature` type, and changes the existing `sign` and `verify` methods (where applicable) to use the `Signer` and `Verifier` traits from the `signature` crate. Additionally, it replaces `SignatureError` with the `signature` crate's error type. This has the drawback of requiring the `Signer` and/or `Verifier` traits are in scope in order to create and/or verify signatures, but with the benefit of supporting interoperability with other Ed25519 crates which also make use of these traits. --- Cargo.toml | 6 ++- src/batch.rs | 20 +++++--- src/errors.rs | 22 ++++---- src/{ed25519.rs => keypair.rs} | 51 ++++++++++--------- src/lib.rs | 47 +++++++++++------- src/public.rs | 91 +++++++++++++++++++--------------- src/secret.rs | 16 +++--- src/signature.rs | 67 +++++++------------------ tests/ed25519.rs | 6 ++- 9 files changed, 169 insertions(+), 157 deletions(-) rename src/{ed25519.rs => keypair.rs} (94%) diff --git a/Cargo.toml b/Cargo.toml index 2931ff5..23196e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,10 +23,11 @@ features = ["nightly", "batch"] [dependencies] curve25519-dalek = { version = "2", default-features = false } +ed25519 = { version = "1", default-features = false } merlin = { version = "1", default-features = false, optional = true, git = "https://github.com/isislovecruft/merlin", branch = "develop" } rand = { version = "0.7", default-features = false, optional = true } rand_core = { version = "0.5", default-features = false, optional = true } -serde = { version = "1.0", default-features = false, optional = true } +serde_crate = { package = "serde", version = "1.0", default-features = false, optional = true } sha2 = { version = "0.8", default-features = false } zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] } @@ -45,9 +46,10 @@ harness = false [features] default = ["std", "u64_backend"] -std = ["curve25519-dalek/std", "serde/std", "sha2/std", "rand/std"] +std = ["curve25519-dalek/std", "ed25519/std", "serde_crate/std", "sha2/std", "rand/std"] alloc = ["curve25519-dalek/alloc", "rand/alloc", "zeroize/alloc"] nightly = ["curve25519-dalek/nightly", "rand/nightly"] +serde = ["serde_crate", "ed25519/serde"] batch = ["merlin", "rand"] # This feature enables deterministic batch verification. batch_deterministic = ["merlin", "rand", "rand_core"] diff --git a/src/batch.rs b/src/batch.rs index 90d28ef..d7816eb 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -14,6 +14,7 @@ use alloc::vec::Vec; #[cfg(feature = "std")] use std::vec::Vec; +use core::convert::TryFrom; use core::iter::once; use curve25519_dalek::constants; @@ -37,7 +38,7 @@ use sha2::Sha512; use crate::errors::InternalError; use crate::errors::SignatureError; use crate::public::PublicKey; -use crate::signature::Signature; +use crate::signature::InternalSignature; trait BatchTranscript { fn append_hrams(&mut self, hrams: &Vec); @@ -127,6 +128,7 @@ fn zero_rng() -> ZeroRng { /// use ed25519_dalek::verify_batch; /// use ed25519_dalek::Keypair; /// use ed25519_dalek::PublicKey; +/// use ed25519_dalek::Signer; /// use ed25519_dalek::Signature; /// use rand::rngs::OsRng; /// @@ -147,7 +149,7 @@ fn zero_rng() -> ZeroRng { #[allow(non_snake_case)] pub fn verify_batch( messages: &[&[u8]], - signatures: &[Signature], + signatures: &[ed25519::Signature], public_keys: &[PublicKey], ) -> Result<(), SignatureError> { @@ -155,13 +157,19 @@ pub fn verify_batch( if signatures.len() != messages.len() || signatures.len() != public_keys.len() || public_keys.len() != messages.len() { - return Err(SignatureError(InternalError::ArrayLengthError{ + return Err(InternalError::ArrayLengthError{ name_a: "signatures", length_a: signatures.len(), name_b: "messages", length_b: messages.len(), name_c: "public_keys", length_c: public_keys.len(), - })); + }.into()); } + // Convert all signatures to `InternalSignature` + let signatures = signatures + .iter() + .map(InternalSignature::try_from) + .collect::, _>>()?; + // Compute H(R || A || M) for each (signature, public_key, message) triplet let hrams: Vec = (0..signatures.len()).map(|i| { let mut h: Sha512 = Sha512::default(); @@ -213,11 +221,11 @@ pub fn verify_batch( let id = EdwardsPoint::optional_multiscalar_mul( once(-B_coefficient).chain(zs.iter().cloned()).chain(zhrams), B.chain(Rs).chain(As), - ).ok_or_else(|| SignatureError(InternalError::VerifyError))?; + ).ok_or(InternalError::VerifyError)?; if id.is_identity() { Ok(()) } else { - Err(SignatureError(InternalError::VerifyError)) + Err(InternalError::VerifyError.into()) } } diff --git a/src/errors.rs b/src/errors.rs index 1d14759..108ca1f 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -80,18 +80,16 @@ impl Error for InternalError { } /// only be constructed from 255-bit integers.) /// /// * Failure of a signature to satisfy the verification equation. -#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)] -pub struct SignatureError(pub(crate) InternalError); +pub type SignatureError = ed25519::signature::Error; -impl Display for SignatureError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -#[cfg(feature = "std")] -impl Error for SignatureError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - Some(&self.0) +impl From for SignatureError { + #[cfg(not(feature = "std"))] + fn from(_err: InternalError) -> SignatureError { + SignatureError::new() + } + + #[cfg(feature = "std")] + fn from(err: InternalError) -> SignatureError { + SignatureError::from_source(err) } } diff --git a/src/ed25519.rs b/src/keypair.rs similarity index 94% rename from src/ed25519.rs rename to src/keypair.rs index 076d30c..4fd63df 100644 --- a/src/ed25519.rs +++ b/src/keypair.rs @@ -9,8 +9,6 @@ //! ed25519 keypairs. -use core::default::Default; - #[cfg(feature = "rand")] use rand::{CryptoRng, RngCore}; @@ -26,13 +24,12 @@ pub use sha2::Sha512; use curve25519_dalek::digest::generic_array::typenum::U64; pub use curve25519_dalek::digest::Digest; -#[cfg(all(feature = "batch", any(feature = "std", feature = "alloc")))] -pub use crate::batch::*; -pub use crate::constants::*; -pub use crate::errors::*; -pub use crate::public::*; -pub use crate::secret::*; -pub use crate::signature::*; +use ed25519::signature::{Signer, Verifier}; + +use crate::constants::*; +use crate::errors::*; +use crate::public::*; +use crate::secret::*; /// An ed25519 keypair. #[derive(Debug)] @@ -82,10 +79,10 @@ impl Keypair { /// is an `SignatureError` describing the error that occurred. pub fn from_bytes<'a>(bytes: &'a [u8]) -> Result { if bytes.len() != KEYPAIR_LENGTH { - return Err(SignatureError(InternalError::BytesLengthError { + return Err(InternalError::BytesLengthError { name: "Keypair", length: KEYPAIR_LENGTH, - })); + }.into()); } let secret = SecretKey::from_bytes(&bytes[..SECRET_KEY_LENGTH])?; let public = PublicKey::from_bytes(&bytes[SECRET_KEY_LENGTH..])?; @@ -136,13 +133,6 @@ impl Keypair { Keypair{ public: pk, secret: sk } } - /// Sign a message with this keypair's secret key. - pub fn sign(&self, message: &[u8]) -> Signature { - let expanded: ExpandedSecretKey = (&self.secret).into(); - - expanded.sign(&message, &self.public) - } - /// Sign a `prehashed_message` with this `Keypair` using the /// Ed25519ph algorithm defined in [RFC8032 §5.1][rfc8032]. /// @@ -241,20 +231,20 @@ impl Keypair { &self, prehashed_message: D, context: Option<&[u8]>, - ) -> Signature + ) -> ed25519::Signature where D: Digest, { let expanded: ExpandedSecretKey = (&self.secret).into(); // xxx thanks i hate this - expanded.sign_prehashed(prehashed_message, &self.public, context) + expanded.sign_prehashed(prehashed_message, &self.public, context).into() } /// Verify a signature on a message with this keypair's public key. pub fn verify( &self, message: &[u8], - signature: &Signature + signature: &ed25519::Signature ) -> Result<(), SignatureError> { self.public.verify(message, signature) @@ -320,7 +310,7 @@ impl Keypair { &self, prehashed_message: D, context: Option<&[u8]>, - signature: &Signature, + signature: &ed25519::Signature, ) -> Result<(), SignatureError> where D: Digest, @@ -394,13 +384,28 @@ impl Keypair { pub fn verify_strict( &self, message: &[u8], - signature: &Signature, + signature: &ed25519::Signature, ) -> Result<(), SignatureError> { self.public.verify_strict(message, signature) } } +impl Signer for Keypair { + /// Sign a message with this keypair's secret key. + fn try_sign(&self, message: &[u8]) -> Result { + let expanded: ExpandedSecretKey = (&self.secret).into(); + Ok(expanded.sign(&message, &self.public).into()) + } +} + +impl Verifier for Keypair { + /// Verify a signature on a message with this keypair's public key. + fn verify(&self, message: &[u8], signature: &ed25519::Signature) -> Result<(), SignatureError> { + self.public.verify(message, signature) + } +} + #[cfg(feature = "serde")] impl Serialize for Keypair { fn serialize(&self, serializer: S) -> Result diff --git a/src/lib.rs b/src/lib.rs index bee0f7c..22cd7e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,9 +44,9 @@ //! # fn main() { //! # use rand::rngs::OsRng; //! # use ed25519_dalek::Keypair; -//! # use ed25519_dalek::Signature; //! # let mut csprng = OsRng{}; //! # let keypair: Keypair = Keypair::generate(&mut csprng); +//! use ed25519_dalek::{Signature, Signer}; //! let message: &[u8] = b"This is a test of the tsunami alert system."; //! let signature: Signature = keypair.sign(message); //! # } @@ -60,12 +60,12 @@ //! # extern crate ed25519_dalek; //! # fn main() { //! # use rand::rngs::OsRng; -//! # use ed25519_dalek::Keypair; -//! # use ed25519_dalek::Signature; +//! # use ed25519_dalek::{Keypair, Signature, Signer}; //! # let mut csprng = OsRng{}; //! # let keypair: Keypair = Keypair::generate(&mut csprng); //! # let message: &[u8] = b"This is a test of the tsunami alert system."; //! # let signature: Signature = keypair.sign(message); +//! use ed25519_dalek::Verifier; //! assert!(keypair.verify(message, &signature).is_ok()); //! # } //! ``` @@ -80,7 +80,8 @@ //! # use rand::rngs::OsRng; //! # use ed25519_dalek::Keypair; //! # use ed25519_dalek::Signature; -//! use ed25519_dalek::PublicKey; +//! # use ed25519_dalek::Signer; +//! use ed25519_dalek::{PublicKey, Verifier}; //! # let mut csprng = OsRng{}; //! # let keypair: Keypair = Keypair::generate(&mut csprng); //! # let message: &[u8] = b"This is a test of the tsunami alert system."; @@ -104,7 +105,7 @@ //! # extern crate ed25519_dalek; //! # fn main() { //! # use rand::rngs::OsRng; -//! # use ed25519_dalek::{Keypair, Signature, PublicKey}; +//! # use ed25519_dalek::{Keypair, Signature, Signer, PublicKey}; //! use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; //! # let mut csprng = OsRng{}; //! # let keypair: Keypair = Keypair::generate(&mut csprng); @@ -124,8 +125,9 @@ //! ``` //! # extern crate rand; //! # extern crate ed25519_dalek; +//! # use std::convert::TryFrom; //! # use rand::rngs::OsRng; -//! # use ed25519_dalek::{Keypair, Signature, PublicKey, SecretKey, SignatureError}; +//! # use ed25519_dalek::{Keypair, Signature, Signer, PublicKey, SecretKey, SignatureError}; //! # use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; //! # fn do_test() -> Result<(SecretKey, PublicKey, Keypair, Signature), SignatureError> { //! # let mut csprng = OsRng{}; @@ -140,7 +142,7 @@ //! let public_key: PublicKey = PublicKey::from_bytes(&public_key_bytes)?; //! let secret_key: SecretKey = SecretKey::from_bytes(&secret_key_bytes)?; //! let keypair: Keypair = Keypair::from_bytes(&keypair_bytes)?; -//! let signature: Signature = Signature::from_bytes(&signature_bytes)?; +//! let signature: Signature = Signature::try_from(&signature_bytes[..])?; //! # //! # Ok((secret_key, public_key, keypair, signature)) //! # } @@ -166,14 +168,14 @@ //! # extern crate rand; //! # extern crate ed25519_dalek; //! # #[cfg(feature = "serde")] -//! extern crate serde; +//! # extern crate serde_crate as serde; //! # #[cfg(feature = "serde")] -//! extern crate bincode; +//! # extern crate bincode; //! //! # #[cfg(feature = "serde")] //! # fn main() { //! # use rand::rngs::OsRng; -//! # use ed25519_dalek::{Keypair, Signature, PublicKey}; +//! # use ed25519_dalek::{Keypair, Signature, Signer, Verifier, PublicKey}; //! use bincode::{serialize, Infinite}; //! # let mut csprng = OsRng{}; //! # let keypair: Keypair = Keypair::generate(&mut csprng); @@ -196,16 +198,16 @@ //! # extern crate rand; //! # extern crate ed25519_dalek; //! # #[cfg(feature = "serde")] -//! # extern crate serde; +//! # extern crate serde_crate as serde; //! # #[cfg(feature = "serde")] //! # extern crate bincode; //! # //! # #[cfg(feature = "serde")] //! # fn main() { //! # use rand::rngs::OsRng; -//! # use ed25519_dalek::{Keypair, Signature, PublicKey}; +//! # use ed25519_dalek::{Keypair, Signature, Signer, Verifier, PublicKey}; //! # use bincode::{serialize, Infinite}; -//! use bincode::{deserialize}; +//! use bincode::deserialize; //! //! # let mut csprng = OsRng{}; //! # let keypair: Keypair = Keypair::generate(&mut csprng); @@ -237,6 +239,8 @@ #[macro_use] extern crate std; +pub extern crate ed25519; + #[cfg(all(feature = "alloc", not(feature = "std")))] extern crate alloc; extern crate curve25519_dalek; @@ -245,20 +249,29 @@ extern crate merlin; #[cfg(any(feature = "batch", feature = "std", feature = "alloc", test))] extern crate rand; #[cfg(feature = "serde")] -extern crate serde; +extern crate serde_crate as serde; extern crate sha2; extern crate zeroize; #[cfg(all(any(feature = "batch", feature = "batch_deterministic"), any(feature = "std", feature = "alloc")))] mod batch; mod constants; -mod ed25519; +mod keypair; mod errors; mod public; mod secret; mod signature; -// Export everything public in ed25519. -pub use crate::ed25519::*; +pub use curve25519_dalek::digest::Digest; + #[cfg(all(any(feature = "batch", feature = "batch_deterministic"), any(feature = "std", feature = "alloc")))] pub use crate::batch::*; +pub use crate::constants::*; +pub use crate::errors::*; +pub use crate::keypair::*; +pub use crate::public::*; +pub use crate::secret::*; + +// Re-export the `Signer` and `Verifier` traits from the `signature` crate +pub use ed25519::signature::{Signer, Verifier}; +pub use ed25519::Signature; diff --git a/src/public.rs b/src/public.rs index f901fcf..0fb4188 100644 --- a/src/public.rs +++ b/src/public.rs @@ -9,6 +9,7 @@ //! ed25519 public keys. +use core::convert::TryFrom; use core::fmt::Debug; use curve25519_dalek::constants; @@ -18,6 +19,8 @@ use curve25519_dalek::edwards::CompressedEdwardsY; use curve25519_dalek::edwards::EdwardsPoint; use curve25519_dalek::scalar::Scalar; +use ed25519::signature::Verifier; + pub use sha2::Sha512; #[cfg(feature = "serde")] @@ -127,10 +130,10 @@ impl PublicKey { #[inline] pub fn from_bytes(bytes: &[u8]) -> Result { if bytes.len() != PUBLIC_KEY_LENGTH { - return Err(SignatureError(InternalError::BytesLengthError { + return Err(InternalError::BytesLengthError { name: "PublicKey", length: PUBLIC_KEY_LENGTH, - })); + }.into()); } let mut bits: [u8; 32] = [0u8; 32]; bits.copy_from_slice(&bytes[..32]); @@ -138,7 +141,7 @@ impl PublicKey { let compressed = CompressedEdwardsY(bits); let point = compressed .decompress() - .ok_or(SignatureError(InternalError::PointDecompressionError))?; + .ok_or(InternalError::PointDecompressionError)?; Ok(PublicKey(compressed, point)) } @@ -159,37 +162,6 @@ impl PublicKey { PublicKey(compressed, point) } - /// Verify a signature on a message with this keypair's public key. - /// - /// # Return - /// - /// Returns `Ok(())` if the signature is valid, and `Err` otherwise. - #[allow(non_snake_case)] - pub fn verify( - &self, - message: &[u8], - signature: &Signature - ) -> Result<(), SignatureError> - { - let mut h: Sha512 = Sha512::new(); - let R: EdwardsPoint; - let k: Scalar; - let minus_A: EdwardsPoint = -self.1; - - h.input(signature.R.as_bytes()); - h.input(self.as_bytes()); - h.input(&message); - - k = Scalar::from_hash(h); - R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); - - if R.compress() == signature.R { - Ok(()) - } else { - Err(SignatureError(InternalError::VerifyError)) - } - } - /// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm. /// /// # Inputs @@ -213,11 +185,13 @@ impl PublicKey { &self, prehashed_message: D, context: Option<&[u8]>, - signature: &Signature, + signature: &ed25519::Signature, ) -> Result<(), SignatureError> where D: Digest, { + let signature = InternalSignature::try_from(signature)?; + let mut h: Sha512 = Sha512::default(); let R: EdwardsPoint; let k: Scalar; @@ -241,7 +215,7 @@ impl PublicKey { if R.compress() == signature.R { Ok(()) } else { - Err(SignatureError(InternalError::VerifyError)) + Err(InternalError::VerifyError.into()) } } @@ -311,9 +285,11 @@ impl PublicKey { pub fn verify_strict( &self, message: &[u8], - signature: &Signature, + signature: &ed25519::Signature, ) -> Result<(), SignatureError> { + let signature = InternalSignature::try_from(signature)?; + let mut h: Sha512 = Sha512::new(); let R: EdwardsPoint; let k: Scalar; @@ -321,13 +297,13 @@ impl PublicKey { let signature_R: EdwardsPoint; match signature.R.decompress() { - None => return Err(SignatureError(InternalError::VerifyError)), + None => return Err(InternalError::VerifyError.into()), Some(x) => signature_R = x, } // Logical OR is fine here as we're not trying to be constant time. if signature_R.is_small_order() || self.1.is_small_order() { - return Err(SignatureError(InternalError::VerifyError)); + return Err(InternalError::VerifyError.into()); } h.input(signature.R.as_bytes()); @@ -340,7 +316,42 @@ impl PublicKey { if R == signature_R { Ok(()) } else { - Err(SignatureError(InternalError::VerifyError)) + Err(InternalError::VerifyError.into()) + } + } +} + +impl Verifier for PublicKey { + /// Verify a signature on a message with this keypair's public key. + /// + /// # Return + /// + /// Returns `Ok(())` if the signature is valid, and `Err` otherwise. + #[allow(non_snake_case)] + fn verify( + &self, + message: &[u8], + signature: &ed25519::Signature + ) -> Result<(), SignatureError> + { + let signature = InternalSignature::try_from(signature)?; + + let mut h: Sha512 = Sha512::new(); + let R: EdwardsPoint; + let k: Scalar; + let minus_A: EdwardsPoint = -self.1; + + h.input(signature.R.as_bytes()); + h.input(self.as_bytes()); + h.input(&message); + + k = Scalar::from_hash(h); + R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); + + if R.compress() == signature.R { + Ok(()) + } else { + Err(InternalError::VerifyError.into()) } } } diff --git a/src/secret.rs b/src/secret.rs index f1e751d..5066569 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -106,10 +106,10 @@ impl SecretKey { #[inline] pub fn from_bytes(bytes: &[u8]) -> Result { if bytes.len() != SECRET_KEY_LENGTH { - return Err(SignatureError(InternalError::BytesLengthError { + return Err(InternalError::BytesLengthError { name: "SecretKey", length: SECRET_KEY_LENGTH, - })); + }.into()); } let mut bits: [u8; 32] = [0u8; 32]; bits.copy_from_slice(&bytes[..32]); @@ -383,10 +383,10 @@ impl ExpandedSecretKey { #[inline] pub fn from_bytes(bytes: &[u8]) -> Result { if bytes.len() != EXPANDED_SECRET_KEY_LENGTH { - return Err(SignatureError(InternalError::BytesLengthError { + return Err(InternalError::BytesLengthError { name: "ExpandedSecretKey", length: EXPANDED_SECRET_KEY_LENGTH, - })); + }.into()); } let mut lower: [u8; 32] = [0u8; 32]; let mut upper: [u8; 32] = [0u8; 32]; @@ -402,7 +402,7 @@ impl ExpandedSecretKey { /// Sign a message with this `ExpandedSecretKey`. #[allow(non_snake_case)] - pub fn sign(&self, message: &[u8], public_key: &PublicKey) -> Signature { + pub fn sign(&self, message: &[u8], public_key: &PublicKey) -> ed25519::Signature { let mut h: Sha512 = Sha512::new(); let R: CompressedEdwardsY; let r: Scalar; @@ -423,7 +423,7 @@ impl ExpandedSecretKey { k = Scalar::from_hash(h); s = &(&k * &self.key) + &r; - Signature { R, s } + InternalSignature { R, s }.into() } /// Sign a `prehashed_message` with this `ExpandedSecretKey` using the @@ -450,7 +450,7 @@ impl ExpandedSecretKey { prehashed_message: D, public_key: &PublicKey, context: Option<&'a [u8]>, - ) -> Signature + ) -> ed25519::Signature where D: Digest, { @@ -505,7 +505,7 @@ impl ExpandedSecretKey { k = Scalar::from_hash(h); s = &(&k * &self.key) + &r; - Signature { R, s } + InternalSignature { R, s }.into() } } diff --git a/src/signature.rs b/src/signature.rs index 59da225..c01e754 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -9,19 +9,12 @@ //! An ed25519 signature. +use core::convert::TryFrom; use core::fmt::Debug; use curve25519_dalek::edwards::CompressedEdwardsY; use curve25519_dalek::scalar::Scalar; - -#[cfg(feature = "serde")] -use serde::de::Error as SerdeError; -#[cfg(feature = "serde")] -use serde::de::Visitor; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -#[cfg(feature = "serde")] -use serde::{Deserializer, Serializer}; +use ed25519::signature::Signature as _; use crate::constants::*; use crate::errors::*; @@ -35,7 +28,7 @@ use crate::errors::*; /// been signed. #[allow(non_snake_case)] #[derive(Copy, Eq, PartialEq)] -pub struct Signature { +pub(crate) struct InternalSignature { /// `R` is an `EdwardsPoint`, formed by using an hash function with /// 512-bits output to produce the digest of: /// @@ -59,13 +52,13 @@ pub struct Signature { pub(crate) s: Scalar, } -impl Clone for Signature { +impl Clone for InternalSignature { fn clone(&self) -> Self { *self } } -impl Debug for Signature { +impl Debug for InternalSignature { fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { write!(f, "Signature( R: {:?}, s: {:?} )", &self.R, &self.s) } @@ -103,12 +96,12 @@ fn check_scalar(bytes: [u8; 32]) -> Result { } match Scalar::from_canonical_bytes(bytes) { - None => return Err(SignatureError(InternalError::ScalarFormatError)), + None => return Err(InternalError::ScalarFormatError.into()), Some(x) => return Ok(x), }; } -impl Signature { +impl InternalSignature { /// Convert this `Signature` to a byte array. #[inline] pub fn to_bytes(&self) -> [u8; SIGNATURE_LENGTH] { @@ -170,12 +163,12 @@ impl Signature { /// only checking the most significant three bits. (See also the /// documentation for `PublicKey.verify_strict`.) #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { + pub fn from_bytes(bytes: &[u8]) -> Result { if bytes.len() != SIGNATURE_LENGTH { - return Err(SignatureError(InternalError::BytesLengthError { + return Err(InternalError::BytesLengthError { name: "Signature", length: SIGNATURE_LENGTH, - })); + }.into()); } let mut lower: [u8; 32] = [0u8; 32]; let mut upper: [u8; 32] = [0u8; 32]; @@ -190,45 +183,23 @@ impl Signature { Err(x) => return Err(x), } - Ok(Signature { + Ok(InternalSignature { R: CompressedEdwardsY(lower), s: s, }) } } -#[cfg(feature = "serde")] -impl Serialize for Signature { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_bytes(&self.to_bytes()[..]) +impl TryFrom<&ed25519::Signature> for InternalSignature { + type Error = SignatureError; + + fn try_from(sig: &ed25519::Signature) -> Result { + InternalSignature::from_bytes(sig.as_bytes()) } } -#[cfg(feature = "serde")] -impl<'d> Deserialize<'d> for Signature { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'d>, - { - struct SignatureVisitor; - - impl<'d> Visitor<'d> for SignatureVisitor { - type Value = Signature; - - fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - formatter.write_str("An ed25519 signature as 64 bytes, as specified in RFC8032.") - } - - fn visit_bytes(self, bytes: &[u8]) -> Result - where - E: SerdeError, - { - Signature::from_bytes(bytes).or(Err(SerdeError::invalid_length(bytes.len(), &self))) - } - } - deserializer.deserialize_bytes(SignatureVisitor) +impl From for ed25519::Signature { + fn from(sig: InternalSignature) -> ed25519::Signature { + ed25519::Signature::from_bytes(&sig.to_bytes()).unwrap() } } diff --git a/tests/ed25519.rs b/tests/ed25519.rs index 88a24df..2d42997 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -24,6 +24,8 @@ use sha2::Sha512; #[cfg(test)] mod vectors { + use ed25519::signature::Signature as _; + use std::io::BufReader; use std::io::BufRead; use std::fs::File; @@ -219,6 +221,8 @@ mod serialisation { use self::bincode::{serialize, serialized_size, deserialize, Infinite}; + use ed25519::signature::Signature as _; + static PUBLIC_KEY_BYTES: [u8; PUBLIC_KEY_LENGTH] = [ 130, 039, 155, 015, 062, 076, 188, 063, 124, 122, 026, 251, 233, 253, 225, 220, @@ -281,7 +285,7 @@ mod serialisation { #[test] fn serialize_signature_size() { let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES).unwrap(); - assert_eq!(serialized_size(&signature) as usize, 72); // These sizes are specific to bincode==1.0.1 + assert_eq!(serialized_size(&signature) as usize, 64); // These sizes are specific to bincode==1.0.1 } #[test] From 9e247c493c1b24727233e117dcd341a8cae13ea1 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 30 Jun 2020 22:40:24 +0000 Subject: [PATCH 255/351] Add additional tests for keypair (de)serialisation. --- Cargo.toml | 3 ++- tests/ed25519.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 2931ff5..2264509 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ curve25519-dalek = { version = "2", default-features = false } merlin = { version = "1", default-features = false, optional = true, git = "https://github.com/isislovecruft/merlin", branch = "develop" } rand = { version = "0.7", default-features = false, optional = true } rand_core = { version = "0.5", default-features = false, optional = true } -serde = { version = "1.0", default-features = false, optional = true } +serde = { version = "1.0", default-features = false, optional = true, features = ["derive"] } sha2 = { version = "0.8", default-features = false } zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] } @@ -35,6 +35,7 @@ hex = "^0.4" bincode = "^0.9" criterion = "0.3" rand = "0.7" +toml = "0.5" [[bench]] name = "ed25519_benchmarks" diff --git a/tests/ed25519.rs b/tests/ed25519.rs index 88a24df..3a480ad 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -15,6 +15,10 @@ extern crate ed25519_dalek; extern crate hex; extern crate sha2; extern crate rand; +#[cfg(all(test, feature = "serde"))] +extern crate serde; +#[cfg(all(test, feature = "serde"))] +extern crate toml; use ed25519_dalek::*; @@ -213,11 +217,19 @@ mod integrations { } } +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +struct Demo { + keypair: Keypair +} + #[cfg(all(test, feature = "serde"))] mod serialisation { use super::*; use self::bincode::{serialize, serialized_size, deserialize, Infinite}; + use self::toml; static PUBLIC_KEY_BYTES: [u8; PUBLIC_KEY_LENGTH] = [ 130, 039, 155, 015, 062, 076, 188, 063, @@ -242,6 +254,16 @@ mod serialisation { 216, 085, 134, 144, 129, 149, 041, 081, 063, 120, 126, 100, 092, 059, 050, 011, ]; + static KEYPAIR_BYTES: [u8; KEYPAIR_LENGTH] = [ + 239, 085, 017, 235, 167, 103, 034, 062, + 007, 010, 032, 146, 113, 039, 096, 174, + 003, 219, 232, 166, 240, 121, 167, 013, + 098, 238, 122, 116, 193, 114, 215, 213, + 175, 181, 075, 166, 224, 164, 140, 146, + 053, 120, 010, 037, 104, 094, 136, 225, + 249, 102, 171, 160, 097, 132, 015, 071, + 035, 056, 000, 074, 130, 168, 225, 071, ]; + #[test] fn serialize_deserialize_signature() { let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES).unwrap(); @@ -272,6 +294,28 @@ mod serialisation { } } + #[test] + fn serialize_deserialize_keypair_bincode() { + let keypair = Keypair::from_bytes(&KEYPAIR_BYTES).unwrap(); + let encoded_keypair: Vec = serialize(&keypair, Infinite).unwrap(); + let decoded_keypair: Keypair = deserialize(&encoded_keypair).unwrap(); + + for i in 0..64 { + assert_eq!(KEYPAIR_BYTES[i], decoded_keypair.to_bytes()[i]); + } + } + + #[test] + fn serialize_deserialize_keypair_toml() { + let demo = Demo { keypair: Keypair::from_bytes(&KEYPAIR_BYTES).unwrap() }; + + println!("\n\nWrite to toml"); + let demo_toml = toml::to_string(&demo).unwrap(); + println!("{}", demo_toml); + let demo_toml_rebuild: Result = toml::from_str(&demo_toml); + println!("{:?}", demo_toml_rebuild); + } + #[test] fn serialize_public_key_size() { let public_key: PublicKey = PublicKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap(); From 3a9435df9457467344349c8f96a5a3b9a3fa94c5 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 13 Jul 2020 23:16:30 +0000 Subject: [PATCH 256/351] Fixup serde and ed25519 trait errors in tests/benches. --- Cargo.toml | 3 ++- benches/ed25519_benchmarks.rs | 1 + tests/ed25519.rs | 4 +++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 23196e9..eb6b73c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ features = ["nightly", "batch"] [dependencies] curve25519-dalek = { version = "2", default-features = false } ed25519 = { version = "1", default-features = false } -merlin = { version = "1", default-features = false, optional = true, git = "https://github.com/isislovecruft/merlin", branch = "develop" } +merlin = { version = "2", default-features = false, optional = true } rand = { version = "0.7", default-features = false, optional = true } rand_core = { version = "0.5", default-features = false, optional = true } serde_crate = { package = "serde", version = "1.0", default-features = false, optional = true } @@ -36,6 +36,7 @@ hex = "^0.4" bincode = "^0.9" criterion = "0.3" rand = "0.7" +serde_crate = { package = "serde", version = "1.0", features = ["derive"] } [[bench]] name = "ed25519_benchmarks" diff --git a/benches/ed25519_benchmarks.rs b/benches/ed25519_benchmarks.rs index 0af2812..45dce35 100644 --- a/benches/ed25519_benchmarks.rs +++ b/benches/ed25519_benchmarks.rs @@ -20,6 +20,7 @@ mod ed25519_benches { use ed25519_dalek::Keypair; use ed25519_dalek::PublicKey; use ed25519_dalek::Signature; + use ed25519_dalek::Signer; use ed25519_dalek::verify_batch; use rand::thread_rng; use rand::prelude::ThreadRng; diff --git a/tests/ed25519.rs b/tests/ed25519.rs index 8904a69..2b4d419 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -16,7 +16,7 @@ extern crate hex; extern crate sha2; extern crate rand; #[cfg(all(test, feature = "serde"))] -extern crate serde; +extern crate serde_crate; #[cfg(all(test, feature = "serde"))] extern crate toml; @@ -219,8 +219,10 @@ mod integrations { } } +#[cfg(all(test, feature = "serde"))] use serde::{Deserialize, Serialize}; +#[cfg(all(test, feature = "serde"))] #[derive(Debug, Serialize, Deserialize)] struct Demo { keypair: Keypair From f1d8576f12577fbc52c6192928e02361963fb524 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 13 Jul 2020 23:19:55 +0000 Subject: [PATCH 257/351] Impl std::error::Error for SignatureError. --- src/errors.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/errors.rs b/src/errors.rs index 108ca1f..08f04de 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -93,3 +93,6 @@ impl From for SignatureError { SignatureError::from_source(err) } } + +#[cfg(feature = "std")] +impl Error for SignatureError { } From 989c5e4c18d4d36c5ac849c462caa333934200c2 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Jul 2020 00:25:40 +0000 Subject: [PATCH 258/351] Fix ed25519ph context length error handling in sign_prehashed(). RFC8032 specifies that the context cannot be greater than 255 octets, but in the previous implementation in ed25519-dalek, this error would only be caught by a debug_assert. This changes the sign_prehashed() function to return a Result so that the error can be handled at runtime and the library no longer allows misuse by creating signatures that other libraries cannot handle. --- src/errors.rs | 4 ++++ src/secret.rs | 12 ++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index 08f04de..5a9182e 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -41,6 +41,8 @@ pub(crate) enum InternalError { ArrayLengthError{ name_a: &'static str, length_a: usize, name_b: &'static str, length_b: usize, name_c: &'static str, length_c: usize, }, + /// An ed25519ph signature can only take up to 255 octets of context. + PrehashedContextLengthError, } impl Display for InternalError { @@ -59,6 +61,8 @@ impl Display for InternalError { name_c: nc, length_c: lc, } => write!(f, "Arrays must be the same length: {} has length {}, {} has length {}, {} has length {}.", na, la, nb, lb, nc, lc), + InternalError::PrehashedContextError + => write!(f, "An ed25519ph signature can only take up to 255 octets of context"), } } } diff --git a/src/secret.rs b/src/secret.rs index 3579e2b..b305eed 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -441,7 +441,9 @@ impl ExpandedSecretKey { /// /// # Returns /// - /// An Ed25519ph [`Signature`] on the `prehashed_message`. + /// A `Result` whose `Ok` value is an Ed25519ph [`Signature`] on the + /// `prehashed_message` if the context was 255 bytes or less, otherwise + /// a `SignatureError`. /// /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 #[allow(non_snake_case)] @@ -450,7 +452,7 @@ impl ExpandedSecretKey { prehashed_message: D, public_key: &PublicKey, context: Option<&'a [u8]>, - ) -> ed25519::Signature + ) -> Result where D: Digest, { @@ -463,7 +465,9 @@ impl ExpandedSecretKey { let ctx: &[u8] = context.unwrap_or(b""); // By default, the context is an empty string. - debug_assert!(ctx.len() <= 255, "The context must not be longer than 255 octets."); + if ctx.len() > 255 { + return Err(SignatureError(InternalError::PrehashedContextError)); + } let ctx_len: u8 = ctx.len() as u8; @@ -505,7 +509,7 @@ impl ExpandedSecretKey { k = Scalar::from_hash(h); s = &(&k * &self.key) + &r; - InternalSignature { R, s }.into() + Ok(InternalSignature { R, s }.into()) } } From 97787d37160dac90a4ba3844ad611d4481c115de Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Jul 2020 00:37:51 +0000 Subject: [PATCH 259/351] Remove impl of std::error::Error for SignatureError. We're now aliasing SignatureError to the error type from the signature crate. --- src/errors.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index 08f04de..108ca1f 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -93,6 +93,3 @@ impl From for SignatureError { SignatureError::from_source(err) } } - -#[cfg(feature = "std")] -impl Error for SignatureError { } From 980ed6445fc698d40c1df39499f1cacb78138fe5 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Jul 2020 22:23:31 +0000 Subject: [PATCH 260/351] Add missing toml dev-dependency. --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index eb6b73c..1f16de7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ bincode = "^0.9" criterion = "0.3" rand = "0.7" serde_crate = { package = "serde", version = "1.0", features = ["derive"] } +toml = { version = "0.5" } [[bench]] name = "ed25519_benchmarks" From e7a88c2c7fac48bc23110f006ec516e6b24c64dc Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Jul 2020 23:58:35 +0000 Subject: [PATCH 261/351] Try compiling tests using serde_crate instead. --- tests/ed25519.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ed25519.rs b/tests/ed25519.rs index 2b4d419..b987f88 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -220,7 +220,7 @@ mod integrations { } #[cfg(all(test, feature = "serde"))] -use serde::{Deserialize, Serialize}; +use serde_crate::{Deserialize, Serialize}; #[cfg(all(test, feature = "serde"))] #[derive(Debug, Serialize, Deserialize)] From b8f36d48d8ac1a36b224e9daf741df36fc15455b Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 15 Jul 2020 17:39:23 +0000 Subject: [PATCH 262/351] Fix proc_macro crate name resolution for serde integration tests. --- tests/ed25519.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/ed25519.rs b/tests/ed25519.rs index b987f88..732b29d 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -219,11 +219,9 @@ mod integrations { } } +#[serde(crate = "serde_crate")] #[cfg(all(test, feature = "serde"))] -use serde_crate::{Deserialize, Serialize}; - -#[cfg(all(test, feature = "serde"))] -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, serde_crate::Serialize, serde_crate::Deserialize)] struct Demo { keypair: Keypair } From 69004599c54fc0d152c6b0a0eec2aafa4bcd3bf0 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 16 Jul 2020 21:49:14 +0000 Subject: [PATCH 263/351] Fix misnamed error type. --- src/secret.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/secret.rs b/src/secret.rs index b305eed..03af823 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -466,7 +466,7 @@ impl ExpandedSecretKey { let ctx: &[u8] = context.unwrap_or(b""); // By default, the context is an empty string. if ctx.len() > 255 { - return Err(SignatureError(InternalError::PrehashedContextError)); + return Err(SignatureError(InternalError::PrehashedContextLengthError)); } let ctx_len: u8 = ctx.len() as u8; From 7243d7151d204637ed226242b42348ad44b43f9b Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 16 Jul 2020 22:19:40 +0000 Subject: [PATCH 264/351] Fix handling of external error types. --- src/errors.rs | 5 ++++- src/keypair.rs | 27 ++++++++++++++++++++------- src/secret.rs | 2 +- tests/ed25519.rs | 6 +++--- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index 8a35093..3194737 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -45,6 +45,9 @@ pub(crate) enum InternalError { PrehashedContextLengthError, } +unsafe impl Send for InternalError {} +unsafe impl Sync for InternalError {} + impl Display for InternalError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { @@ -61,7 +64,7 @@ impl Display for InternalError { name_c: nc, length_c: lc, } => write!(f, "Arrays must be the same length: {} has length {}, {} has length {}, {} has length {}.", na, la, nb, lb, nc, lc), - InternalError::PrehashedContextError + InternalError::PrehashedContextLengthError => write!(f, "An ed25519ph signature can only take up to 255 octets of context"), } } diff --git a/src/keypair.rs b/src/keypair.rs index ae242d1..e4f2a4f 100644 --- a/src/keypair.rs +++ b/src/keypair.rs @@ -207,11 +207,11 @@ impl Keypair { /// # use ed25519_dalek::Digest; /// # use ed25519_dalek::Keypair; /// # use ed25519_dalek::Signature; + /// # use ed25519_dalek::SignatureError; /// # use ed25519_dalek::Sha512; /// # use rand::rngs::OsRng; /// # - /// # #[cfg(feature = "std")] - /// # fn main() { + /// # fn do_test() -> Result { /// # let mut csprng = OsRng{}; /// # let keypair: Keypair = Keypair::generate(&mut csprng); /// # let message: &[u8] = b"All I want is to pet all of the dogs."; @@ -220,7 +220,13 @@ impl Keypair { /// # /// let context: &[u8] = b"Ed25519DalekSignPrehashedDoctest"; /// - /// let sig: Signature = keypair.sign_prehashed(prehashed, Some(context)); + /// let sig: Signature = keypair.sign_prehashed(prehashed, Some(context))?; + /// # + /// # Ok(sig) + /// # } + /// # #[cfg(feature = "std")] + /// # fn main() { + /// # do_test(); /// # } /// # /// # #[cfg(not(feature = "std"))] @@ -233,7 +239,7 @@ impl Keypair { &self, prehashed_message: D, context: Option<&[u8]>, - ) -> ed25519::Signature + ) -> Result where D: Digest, { @@ -278,11 +284,11 @@ impl Keypair { /// use ed25519_dalek::Digest; /// use ed25519_dalek::Keypair; /// use ed25519_dalek::Signature; + /// use ed25519_dalek::SignatureError; /// use ed25519_dalek::Sha512; /// use rand::rngs::OsRng; /// - /// # #[cfg(feature = "std")] - /// # fn main() { + /// # fn do_test() -> Result<(), SignatureError> { /// let mut csprng = OsRng{}; /// let keypair: Keypair = Keypair::generate(&mut csprng); /// let message: &[u8] = b"All I want is to pet all of the dogs."; @@ -292,7 +298,7 @@ impl Keypair { /// /// let context: &[u8] = b"Ed25519DalekSignPrehashedDoctest"; /// - /// let sig: Signature = keypair.sign_prehashed(prehashed, Some(context)); + /// let sig: Signature = keypair.sign_prehashed(prehashed, Some(context))?; /// /// // The sha2::Sha512 struct doesn't implement Copy, so we'll have to create a new one: /// let mut prehashed_again: Sha512 = Sha512::default(); @@ -301,6 +307,13 @@ impl Keypair { /// let verified = keypair.public.verify_prehashed(prehashed_again, Some(context), &sig); /// /// assert!(verified.is_ok()); + /// + /// # verified + /// # } + /// # + /// # #[cfg(feature = "std")] + /// # fn main() { + /// # do_test(); /// # } /// # /// # #[cfg(not(feature = "std"))] diff --git a/src/secret.rs b/src/secret.rs index 03af823..ca57062 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -466,7 +466,7 @@ impl ExpandedSecretKey { let ctx: &[u8] = context.unwrap_or(b""); // By default, the context is an empty string. if ctx.len() > 255 { - return Err(SignatureError(InternalError::PrehashedContextLengthError)); + return Err(SignatureError::from_source(InternalError::PrehashedContextLengthError)); } let ctx_len: u8 = ctx.len() as u8; diff --git a/tests/ed25519.rs b/tests/ed25519.rs index 732b29d..b0e206f 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -104,7 +104,7 @@ mod vectors { prehash_for_signing.input(&msg_bytes[..]); prehash_for_verifying.input(&msg_bytes[..]); - let sig2: Signature = keypair.sign_prehashed(prehash_for_signing, None); + let sig2: Signature = keypair.sign_prehashed(prehash_for_signing, None).unwrap(); assert!(sig1 == sig2, "Original signature from test vectors doesn't equal signature produced:\ @@ -169,8 +169,8 @@ mod integrations { let context: &[u8] = b"testing testing 1 2 3"; keypair = Keypair::generate(&mut csprng); - good_sig = keypair.sign_prehashed(prehashed_good1, Some(context)); - bad_sig = keypair.sign_prehashed(prehashed_bad1, Some(context)); + good_sig = keypair.sign_prehashed(prehashed_good1, Some(context)).unwrap(); + bad_sig = keypair.sign_prehashed(prehashed_bad1, Some(context)).unwrap(); assert!(keypair.verify_prehashed(prehashed_good2, Some(context), &good_sig).is_ok(), "Verification of a valid signature failed!"); From d3a5b3bd8143a89132df34e1e7a6b184e3e41cb3 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 16 Jul 2020 23:02:27 +0000 Subject: [PATCH 265/351] Remove unsafe trait impls. --- src/errors.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index 3194737..b66fae0 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -45,9 +45,6 @@ pub(crate) enum InternalError { PrehashedContextLengthError, } -unsafe impl Send for InternalError {} -unsafe impl Sync for InternalError {} - impl Display for InternalError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { From 5458ebef880d878e5bb8e08183d69286f6a69c75 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 16 Jul 2020 23:18:00 +0000 Subject: [PATCH 266/351] Fix no_std issue with new error types. --- src/secret.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/secret.rs b/src/secret.rs index ca57062..e1fa2c4 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -466,7 +466,7 @@ impl ExpandedSecretKey { let ctx: &[u8] = context.unwrap_or(b""); // By default, the context is an empty string. if ctx.len() > 255 { - return Err(SignatureError::from_source(InternalError::PrehashedContextLengthError)); + return Err(SignatureError::from(InternalError::PrehashedContextLengthError)); } let ctx_len: u8 = ctx.len() as u8; From 5f22d899a0fb680c77af7051d967e62d0f0859ee Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 16 Jul 2020 23:25:09 +0000 Subject: [PATCH 267/351] Bump ed25519-dalek version to 1.0.0-pre.4. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 1f16de7..d1bf7ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "1.0.0-pre.3" +version = "1.0.0-pre.4" edition = "2018" authors = ["isis lovecruft "] readme = "README.md" From bb82d616de9f4d267e9fe4f68104d4279010812f Mon Sep 17 00:00:00 2001 From: Chris Beck Date: Tue, 4 Aug 2020 11:58:07 -0700 Subject: [PATCH 268/351] Make `use rand::...` gated on `cfg(feature = "rand")` This is no longer actively breaking our no_std build, but I think it's still technically a minor bug, and further case of issue #108 --- src/secret.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/secret.rs b/src/secret.rs index e1fa2c4..b09ff64 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -17,6 +17,7 @@ use curve25519_dalek::digest::Digest; use curve25519_dalek::edwards::CompressedEdwardsY; use curve25519_dalek::scalar::Scalar; +#[cfg(feature = "rand")] use rand::{CryptoRng, RngCore}; use sha2::Sha512; From 1c97dac4dc8b4c34b4b055b676bf92f2bce0aab3 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 19 Aug 2020 21:58:00 +0000 Subject: [PATCH 269/351] Update to curve25519-dalek version 3. --- Cargo.toml | 4 ++-- src/batch.rs | 6 +++--- src/keypair.rs | 8 ++++---- src/public.rs | 30 +++++++++++++++--------------- src/secret.rs | 16 ++++++++-------- tests/ed25519.rs | 14 +++++++------- 6 files changed, 39 insertions(+), 39 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d1bf7ad..2ee3c49 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,13 +22,13 @@ travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master" features = ["nightly", "batch"] [dependencies] -curve25519-dalek = { version = "2", default-features = false } +curve25519-dalek = { version = "3", default-features = false } ed25519 = { version = "1", default-features = false } merlin = { version = "2", default-features = false, optional = true } rand = { version = "0.7", default-features = false, optional = true } rand_core = { version = "0.5", default-features = false, optional = true } serde_crate = { package = "serde", version = "1.0", default-features = false, optional = true } -sha2 = { version = "0.8", default-features = false } +sha2 = { version = "0.9", default-features = false } zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] } [dev-dependencies] diff --git a/src/batch.rs b/src/batch.rs index 9b41390..4d15589 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -175,9 +175,9 @@ pub fn verify_batch( // Compute H(R || A || M) for each (signature, public_key, message) triplet let hrams: Vec = (0..signatures.len()).map(|i| { let mut h: Sha512 = Sha512::default(); - h.input(signatures[i].R.as_bytes()); - h.input(public_keys[i].as_bytes()); - h.input(&messages[i]); + h.update(signatures[i].R.as_bytes()); + h.update(public_keys[i].as_bytes()); + h.update(&messages[i]); Scalar::from_hash(h) }).collect(); diff --git a/src/keypair.rs b/src/keypair.rs index e4f2a4f..f4024a1 100644 --- a/src/keypair.rs +++ b/src/keypair.rs @@ -172,7 +172,7 @@ impl Keypair { /// // Create a hash digest object which we'll feed the message into: /// let mut prehashed: Sha512 = Sha512::new(); /// - /// prehashed.input(message); + /// prehashed.update(message); /// # } /// # /// # #[cfg(not(feature = "std"))] @@ -216,7 +216,7 @@ impl Keypair { /// # let keypair: Keypair = Keypair::generate(&mut csprng); /// # let message: &[u8] = b"All I want is to pet all of the dogs."; /// # let mut prehashed: Sha512 = Sha512::new(); - /// # prehashed.input(message); + /// # prehashed.update(message); /// # /// let context: &[u8] = b"Ed25519DalekSignPrehashedDoctest"; /// @@ -294,7 +294,7 @@ impl Keypair { /// let message: &[u8] = b"All I want is to pet all of the dogs."; /// /// let mut prehashed: Sha512 = Sha512::new(); - /// prehashed.input(message); + /// prehashed.update(message); /// /// let context: &[u8] = b"Ed25519DalekSignPrehashedDoctest"; /// @@ -302,7 +302,7 @@ impl Keypair { /// /// // The sha2::Sha512 struct doesn't implement Copy, so we'll have to create a new one: /// let mut prehashed_again: Sha512 = Sha512::default(); - /// prehashed_again.input(message); + /// prehashed_again.update(message); /// /// let verified = keypair.public.verify_prehashed(prehashed_again, Some(context), &sig); /// diff --git a/src/public.rs b/src/public.rs index 0fb4188..170390d 100644 --- a/src/public.rs +++ b/src/public.rs @@ -60,8 +60,8 @@ impl<'a> From<&'a SecretKey> for PublicKey { let mut hash: [u8; 64] = [0u8; 64]; let mut digest: [u8; 32] = [0u8; 32]; - h.input(secret_key.as_bytes()); - hash.copy_from_slice(h.result().as_slice()); + h.update(secret_key.as_bytes()); + hash.copy_from_slice(h.finalize().as_slice()); digest.copy_from_slice(&hash[..32]); @@ -201,13 +201,13 @@ impl PublicKey { let minus_A: EdwardsPoint = -self.1; - h.input(b"SigEd25519 no Ed25519 collisions"); - h.input(&[1]); // Ed25519ph - h.input(&[ctx.len() as u8]); - h.input(ctx); - h.input(signature.R.as_bytes()); - h.input(self.as_bytes()); - h.input(prehashed_message.result().as_slice()); + h.update(b"SigEd25519 no Ed25519 collisions"); + h.update(&[1]); // Ed25519ph + h.update(&[ctx.len() as u8]); + h.update(ctx); + h.update(signature.R.as_bytes()); + h.update(self.as_bytes()); + h.update(prehashed_message.finalize().as_slice()); k = Scalar::from_hash(h); R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); @@ -306,9 +306,9 @@ impl PublicKey { return Err(InternalError::VerifyError.into()); } - h.input(signature.R.as_bytes()); - h.input(self.as_bytes()); - h.input(&message); + h.update(signature.R.as_bytes()); + h.update(self.as_bytes()); + h.update(&message); k = Scalar::from_hash(h); R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); @@ -341,9 +341,9 @@ impl Verifier for PublicKey { let k: Scalar; let minus_A: EdwardsPoint = -self.1; - h.input(signature.R.as_bytes()); - h.input(self.as_bytes()); - h.input(&message); + h.update(signature.R.as_bytes()); + h.update(self.as_bytes()); + h.update(&message); k = Scalar::from_hash(h); R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); diff --git a/src/secret.rs b/src/secret.rs index e1fa2c4..f7e4962 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -283,8 +283,8 @@ impl<'a> From<&'a SecretKey> for ExpandedSecretKey { let mut lower: [u8; 32] = [0u8; 32]; let mut upper: [u8; 32] = [0u8; 32]; - h.input(secret_key.as_bytes()); - hash.copy_from_slice(h.result().as_slice()); + h.update(secret_key.as_bytes()); + hash.copy_from_slice(h.finalize().as_slice()); lower.copy_from_slice(&hash[00..32]); upper.copy_from_slice(&hash[32..64]); @@ -409,16 +409,16 @@ impl ExpandedSecretKey { let s: Scalar; let k: Scalar; - h.input(&self.nonce); - h.input(&message); + h.update(&self.nonce); + h.update(&message); r = Scalar::from_hash(h); R = (&r * &constants::ED25519_BASEPOINT_TABLE).compress(); h = Sha512::new(); - h.input(R.as_bytes()); - h.input(public_key.as_bytes()); - h.input(&message); + h.update(R.as_bytes()); + h.update(public_key.as_bytes()); + h.update(&message); k = Scalar::from_hash(h); s = &(&k * &self.key) + &r; @@ -472,7 +472,7 @@ impl ExpandedSecretKey { let ctx_len: u8 = ctx.len() as u8; // Get the result of the pre-hashed message. - prehash.copy_from_slice(prehashed_message.result().as_slice()); + prehash.copy_from_slice(prehashed_message.finalize().as_slice()); // This is the dumbest, ten-years-late, non-admission of fucking up the // domain separation I have ever seen. Why am I still required to put diff --git a/tests/ed25519.rs b/tests/ed25519.rs index b0e206f..4ed2a8b 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -101,8 +101,8 @@ mod vectors { let mut prehash_for_signing: Sha512 = Sha512::default(); let mut prehash_for_verifying: Sha512 = Sha512::default(); - prehash_for_signing.input(&msg_bytes[..]); - prehash_for_verifying.input(&msg_bytes[..]); + prehash_for_signing.update(&msg_bytes[..]); + prehash_for_verifying.update(&msg_bytes[..]); let sig2: Signature = keypair.sign_prehashed(prehash_for_signing, None).unwrap(); @@ -155,16 +155,16 @@ mod integrations { // ugh… there's no `impl Copy for Sha512`… i hope we can all agree these are the same hashes let mut prehashed_good1: Sha512 = Sha512::default(); - prehashed_good1.input(good); + prehashed_good1.update(good); let mut prehashed_good2: Sha512 = Sha512::default(); - prehashed_good2.input(good); + prehashed_good2.update(good); let mut prehashed_good3: Sha512 = Sha512::default(); - prehashed_good3.input(good); + prehashed_good3.update(good); let mut prehashed_bad1: Sha512 = Sha512::default(); - prehashed_bad1.input(bad); + prehashed_bad1.update(bad); let mut prehashed_bad2: Sha512 = Sha512::default(); - prehashed_bad2.input(bad); + prehashed_bad2.update(bad); let context: &[u8] = b"testing testing 1 2 3"; From 952bdd062fe9fa0ac96b87df995bc9dc6a330227 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 20 Aug 2020 22:46:58 +0000 Subject: [PATCH 270/351] Release ed25519-dalek version 1.0.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 2ee3c49..ecc1bd3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "1.0.0-pre.4" +version = "1.0.0" edition = "2018" authors = ["isis lovecruft "] readme = "README.md" From da959c041d62ec4237c604a3b3e55a08e0a0df25 Mon Sep 17 00:00:00 2001 From: Ivan Temchenko <35359595i@gmail.com> Date: Mon, 24 Aug 2020 16:33:04 +0200 Subject: [PATCH 271/351] check_scalar bug fix for legacy_compatibility feature --- src/signature.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/signature.rs b/src/signature.rs index c01e754..880a78b 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -74,7 +74,7 @@ fn check_scalar(bytes: [u8; 32]) -> Result { // This is compatible with ed25519-donna and libsodium when // -DED25519_COMPAT is NOT specified. if bytes[31] & 224 != 0 { - return Err(SignatureError(InternalError::ScalarFormatError)); + return Err(InternalError::ScalarFormatError.into()); } Ok(Scalar::from_bits(bytes)) From 57a5473cb0b6024d250674d1308a6f07802b4bd0 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 21 Sep 2020 22:04:18 +0000 Subject: [PATCH 272/351] Fix and document malleability issue in deterministic batch_verify(). Thank you to @real_or_random and @jonasnick for initially pointing it out and ensuing discussion. --- src/batch.rs | 81 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 74 insertions(+), 7 deletions(-) diff --git a/src/batch.rs b/src/batch.rs index 4d15589..6a4a7c6 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -43,19 +43,24 @@ use crate::public::PublicKey; use crate::signature::InternalSignature; trait BatchTranscript { - fn append_hrams(&mut self, hrams: &Vec); + fn append_scalars(&mut self, scalars: &Vec); fn append_message_lengths(&mut self, message_lengths: &Vec); } impl BatchTranscript for Transcript { - /// Add all the computed `H(R||A||M)`s to the protocol transcript. + /// Append some `scalars` to this batch verification sigma protocol transcript. + /// + /// For ed25519 batch verification, we include the following as scalars: + /// + /// * All of the computed `H(R||A||M)`s to the protocol transcript, and + /// * All of the `s` components of each signature. /// /// Each is also prefixed with their index in the vector. - fn append_hrams(&mut self, hrams: &Vec) { - for (i, hram) in hrams.iter().enumerate() { + fn append_scalars(&mut self, scalars: &Vec) { + for (i, scalar) in scalars.iter().enumerate() { // XXX add message length into transcript self.append_u64(b"", i as u64); - self.append_message(b"hram", hram.as_bytes()); + self.append_message(b"hram", scalar.as_bytes()); } } @@ -121,6 +126,65 @@ fn zero_rng() -> ZeroRng { /// `SignatureError` containing a description of the internal error which /// occured. /// +/// # Notes on Nonce Generation & Malleability +/// +/// ## On Synthetic Nonces +/// +/// This library defaults to using what is called "synthetic" nonces, which +/// means that a mixture of deterministic (per any unique set of inputs to this +/// function) data and system randomness is used to seed the CSPRNG for nonce +/// generation. For more of the background theory on why many cryptographers +/// currently believe this to be superior to either purely deterministic +/// generation or purely relying on the system's randomness, see [this section +/// of the Merlin design](https://merlin.cool/transcript/rng.html) by Henry de +/// Valence, isis lovecruft, and Oleg Andreev, as well as Trevor Perrin's +/// [designs for generalised +/// EdDSA](https://moderncrypto.org/mail-archive/curves/2017/000925.html). +/// +/// ## On Deterministic Nonces +/// +/// In order to be ammenable to protocols which require stricter third-party +/// auditability trails, such as in some financial cryptographic settings, this +/// library also supports a `--features=batch_deterministic` setting, where the +/// nonces for batch signature verification are derived purely from the inputs +/// to this function themselves. +/// +/// **This is not recommended for use unless you have several cryptographers on +/// staff who can advise you in its usage and all the horrible, terrible, +/// awful ways it can go horribly, terribly, awfully wrong.** +/// +/// In any sigma protocol it is wise to include as much context pertaining +/// to the public state in the protocol as possible, to avoid malleability +/// attacks where an adversary alters publics in an algebraic manner that +/// manages to satisfy the equations for the protocol in question. +/// +/// For ed25519 batch verification (both with synthetic and deterministic nonce +/// generation), we include the following as scalars in the protocol transcript: +/// +/// * All of the computed `H(R||A||M)`s to the protocol transcript, and +/// * All of the `s` components of each signature. +/// +/// Each is also prefixed with their index in the vector. +/// +/// The former, while not quite as elegant as adding the `R`s, `A`s, and +/// `M`s separately, saves us a bit of context hashing since the +/// `H(R||A||M)`s need to be computed for the verification equation anyway. +/// +/// The latter prevents a malleability attack only found in deterministic batch +/// signature verification (i.e. only when compiling `ed25519-dalek` with +/// `--features batch_deterministic`) wherein an adversary, without access +/// to the signing key(s), can take any valid signature, `(s,R)`, and swap +/// `s` with `s' = -z1`. This doesn't contitute a signature forgery, merely +/// a vulnerability, as the resulting signature will not pass single +/// signature verification. (Thanks to Github users @real_or_random and +/// @jonasnick for pointing out this malleability issue.) +/// +/// For an additional way in which signatures can be made to probablistically +/// falsely "pass" the synthethic batch verification equation *for the same +/// inputs*, but *only some crafted inputs* will pass the deterministic batch +/// single, and neither of these will ever pass single signature verification, +/// see the documentation for [`PublicKey.validate()`]. +/// /// # Examples /// /// ``` @@ -181,8 +245,10 @@ pub fn verify_batch( Scalar::from_hash(h) }).collect(); - // Collect the message lengths to add into the transcript. + // Collect the message lengths and the scalar portions of the signatures, + // and add them into the transcript. let message_lengths: Vec = messages.iter().map(|i| i.len()).collect(); + let scalars: Vec = signatures.iter().map(|i| i.s).collect(); // Build a PRNG based on a transcript of the H(R || A || M)s seen thus far. // This provides synthethic randomness in the default configuration, and @@ -190,8 +256,9 @@ pub fn verify_batch( // "batch_deterministic" feature. let mut transcript: Transcript = Transcript::new(b"ed25519 batch verification"); - transcript.append_hrams(&hrams); + transcript.append_scalars(&hrams); transcript.append_message_lengths(&message_lengths); + transcript.append_scalars(&scalars); #[cfg(all(feature = "batch", not(feature = "batch_deterministic")))] let mut prng = transcript.build_rng().finalize(&mut thread_rng()); From a02190adf3a835a49165877997bed61cae9415fa Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 21 Sep 2020 22:05:59 +0000 Subject: [PATCH 273/351] Document that we include the message lengths in the transcript. --- src/batch.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/batch.rs b/src/batch.rs index 6a4a7c6..3a4b8e9 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -58,12 +58,17 @@ impl BatchTranscript for Transcript { /// Each is also prefixed with their index in the vector. fn append_scalars(&mut self, scalars: &Vec) { for (i, scalar) in scalars.iter().enumerate() { - // XXX add message length into transcript self.append_u64(b"", i as u64); self.append_message(b"hram", scalar.as_bytes()); } } + /// Append the lengths of the messages into the transcript. + /// + /// This is done out of an (potential over-)abundance of caution, to guard + /// against the unlikely event of collisions. However, a nicer way to do + /// this would be to append the message length before the message, but this + /// is messy w.r.t. the calculations of the `H(R||A||M)`s above. fn append_message_lengths(&mut self, message_lengths: &Vec) { for (i, len) in message_lengths.iter().enumerate() { self.append_u64(b"", i as u64); From 5d7bc29ba2ff725be9a198c8f3ab4ff9c6d0985a Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 21 Sep 2020 23:25:15 +0000 Subject: [PATCH 274/351] Workaround for rand crate "nightly" feature breakage. Cf. https://github.com/rust-random/rand/issues/1047 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ecc1bd3..ade7645 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,7 @@ harness = false default = ["std", "u64_backend"] std = ["curve25519-dalek/std", "ed25519/std", "serde_crate/std", "sha2/std", "rand/std"] alloc = ["curve25519-dalek/alloc", "rand/alloc", "zeroize/alloc"] -nightly = ["curve25519-dalek/nightly", "rand/nightly"] +nightly = ["curve25519-dalek/nightly"] serde = ["serde_crate", "ed25519/serde"] batch = ["merlin", "rand"] # This feature enables deterministic batch verification. From 660964203651a816a08d0a3cf77a55e3f1ff3e7d Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 21 Sep 2020 23:48:57 +0000 Subject: [PATCH 275/351] Enable rand crate by default. See https://github.com/dalek-cryptography/ed25519-dalek/pull/139. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ade7645..37287e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ harness = false # required-features = ["batch"] [features] -default = ["std", "u64_backend"] +default = ["std", "rand", "u64_backend"] std = ["curve25519-dalek/std", "ed25519/std", "serde_crate/std", "sha2/std", "rand/std"] alloc = ["curve25519-dalek/alloc", "rand/alloc", "zeroize/alloc"] nightly = ["curve25519-dalek/nightly"] From b5a15bf4518ca0e6ec19a068241877d448a78573 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 21 Sep 2020 23:52:21 +0000 Subject: [PATCH 276/351] Feature gate key generation on the "rand" dependency. See https://github.com/dalek-cryptography/ed25519-dalek/pull/139. --- src/keypair.rs | 1 + src/secret.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/keypair.rs b/src/keypair.rs index f4024a1..c12f751 100644 --- a/src/keypair.rs +++ b/src/keypair.rs @@ -125,6 +125,7 @@ impl Keypair { /// The standard hash function used for most ed25519 libraries is SHA-512, /// which is available with `use sha2::Sha512` as in the example above. /// Other suitable hash functions include Keccak-512 and Blake2b-512. + #[cfg(feature = "rand")] pub fn generate(csprng: &mut R) -> Keypair where R: CryptoRng + RngCore, diff --git a/src/secret.rs b/src/secret.rs index 1d421b6..64c9d1f 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -165,6 +165,7 @@ impl SecretKey { /// # Input /// /// A CSPRNG with a `fill_bytes()` method, e.g. `rand::OsRng` + #[cfg(feature = "rand")] pub fn generate(csprng: &mut T) -> SecretKey where T: CryptoRng + RngCore, From 008c9680f669f54129f785be4b39dec1ce2119eb Mon Sep 17 00:00:00 2001 From: Cheng XU Date: Fri, 7 Aug 2020 13:39:57 -0700 Subject: [PATCH 277/351] Update tests for serde * Upgrade bincode to 1.0 * Add more serde tests including json serialization. --- Cargo.toml | 3 +- src/lib.rs | 12 ++--- tests/ed25519.rs | 118 ++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 104 insertions(+), 29 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 37287e0..35592f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,8 @@ zeroize = { version = "1", default-features = false, features = ["zeroize_derive [dev-dependencies] hex = "^0.4" -bincode = "^0.9" +bincode = "1.0" +serde_json = "1.0" criterion = "0.3" rand = "0.7" serde_crate = { package = "serde", version = "1.0", features = ["derive"] } diff --git a/src/lib.rs b/src/lib.rs index 22cd7e9..26c161e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -176,7 +176,7 @@ //! # fn main() { //! # use rand::rngs::OsRng; //! # use ed25519_dalek::{Keypair, Signature, Signer, Verifier, PublicKey}; -//! use bincode::{serialize, Infinite}; +//! use bincode::serialize; //! # let mut csprng = OsRng{}; //! # let keypair: Keypair = Keypair::generate(&mut csprng); //! # let message: &[u8] = b"This is a test of the tsunami alert system."; @@ -184,8 +184,8 @@ //! # let public_key: PublicKey = keypair.public; //! # let verified: bool = public_key.verify(message, &signature).is_ok(); //! -//! let encoded_public_key: Vec = serialize(&public_key, Infinite).unwrap(); -//! let encoded_signature: Vec = serialize(&signature, Infinite).unwrap(); +//! let encoded_public_key: Vec = serialize(&public_key).unwrap(); +//! let encoded_signature: Vec = serialize(&signature).unwrap(); //! # } //! # #[cfg(not(feature = "serde"))] //! # fn main() {} @@ -206,7 +206,7 @@ //! # fn main() { //! # use rand::rngs::OsRng; //! # use ed25519_dalek::{Keypair, Signature, Signer, Verifier, PublicKey}; -//! # use bincode::{serialize, Infinite}; +//! # use bincode::serialize; //! use bincode::deserialize; //! //! # let mut csprng = OsRng{}; @@ -215,8 +215,8 @@ //! # let signature: Signature = keypair.sign(message); //! # let public_key: PublicKey = keypair.public; //! # let verified: bool = public_key.verify(message, &signature).is_ok(); -//! # let encoded_public_key: Vec = serialize(&public_key, Infinite).unwrap(); -//! # let encoded_signature: Vec = serialize(&signature, Infinite).unwrap(); +//! # let encoded_public_key: Vec = serialize(&public_key).unwrap(); +//! # let encoded_signature: Vec = serialize(&signature).unwrap(); //! let decoded_public_key: PublicKey = deserialize(&encoded_public_key).unwrap(); //! let decoded_signature: Signature = deserialize(&encoded_signature).unwrap(); //! diff --git a/tests/ed25519.rs b/tests/ed25519.rs index 4ed2a8b..696e287 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -230,11 +230,11 @@ struct Demo { mod serialisation { use super::*; - use self::bincode::{serialize, serialized_size, deserialize, Infinite}; - use self::toml; - use ed25519::signature::Signature as _; + // The size for bincode to serialize the length of a byte array. + static BINCODE_INT_LENGTH: usize = 8; + static PUBLIC_KEY_BYTES: [u8; PUBLIC_KEY_LENGTH] = [ 130, 039, 155, 015, 062, 076, 188, 063, 124, 122, 026, 251, 233, 253, 225, 220, @@ -269,42 +269,104 @@ mod serialisation { 035, 056, 000, 074, 130, 168, 225, 071, ]; #[test] - fn serialize_deserialize_signature() { + fn serialize_deserialize_signature_bincode() { let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES).unwrap(); - let encoded_signature: Vec = serialize(&signature, Infinite).unwrap(); - let decoded_signature: Signature = deserialize(&encoded_signature).unwrap(); + let encoded_signature: Vec = bincode::serialize(&signature).unwrap(); + let decoded_signature: Signature = bincode::deserialize(&encoded_signature).unwrap(); assert_eq!(signature, decoded_signature); } #[test] - fn serialize_deserialize_public_key() { - let public_key: PublicKey = PublicKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap(); - let encoded_public_key: Vec = serialize(&public_key, Infinite).unwrap(); - let decoded_public_key: PublicKey = deserialize(&encoded_public_key).unwrap(); + fn serialize_deserialize_signature_json() { + let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES).unwrap(); + let encoded_signature = serde_json::to_string(&signature).unwrap(); + let decoded_signature: Signature = serde_json::from_str(&encoded_signature).unwrap(); - assert_eq!(&PUBLIC_KEY_BYTES[..], &encoded_public_key[encoded_public_key.len() - 32..]); + assert_eq!(signature, decoded_signature); + } + + #[test] + fn serialize_deserialize_public_key_bincode() { + let public_key: PublicKey = PublicKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap(); + let encoded_public_key: Vec = bincode::serialize(&public_key).unwrap(); + let decoded_public_key: PublicKey = bincode::deserialize(&encoded_public_key).unwrap(); + + assert_eq!(&PUBLIC_KEY_BYTES[..], &encoded_public_key[encoded_public_key.len() - PUBLIC_KEY_LENGTH..]); assert_eq!(public_key, decoded_public_key); } #[test] - fn serialize_deserialize_secret_key() { - let secret_key: SecretKey = SecretKey::from_bytes(&SECRET_KEY_BYTES).unwrap(); - let encoded_secret_key: Vec = serialize(&secret_key, Infinite).unwrap(); - let decoded_secret_key: SecretKey = deserialize(&encoded_secret_key).unwrap(); + fn serialize_deserialize_public_key_json() { + let public_key: PublicKey = PublicKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap(); + let encoded_public_key = serde_json::to_string(&public_key).unwrap(); + let decoded_public_key: PublicKey = serde_json::from_str(&encoded_public_key).unwrap(); - for i in 0..32 { + assert_eq!(public_key, decoded_public_key); + } + + #[test] + fn serialize_deserialize_secret_key_bincode() { + let secret_key: SecretKey = SecretKey::from_bytes(&SECRET_KEY_BYTES).unwrap(); + let encoded_secret_key: Vec = bincode::serialize(&secret_key).unwrap(); + let decoded_secret_key: SecretKey = bincode::deserialize(&encoded_secret_key).unwrap(); + + for i in 0..SECRET_KEY_LENGTH { assert_eq!(SECRET_KEY_BYTES[i], decoded_secret_key.as_bytes()[i]); } } + #[test] + fn serialize_deserialize_secret_key_json() { + let secret_key: SecretKey = SecretKey::from_bytes(&SECRET_KEY_BYTES).unwrap(); + let encoded_secret_key = serde_json::to_string(&secret_key).unwrap(); + let decoded_secret_key: SecretKey = serde_json::from_str(&encoded_secret_key).unwrap(); + + for i in 0..SECRET_KEY_LENGTH { + assert_eq!(SECRET_KEY_BYTES[i], decoded_secret_key.as_bytes()[i]); + } + } + + #[test] + fn serialize_deserialize_expanded_secret_key_bincode() { + let expanded_secret_key = ExpandedSecretKey::from(&SecretKey::from_bytes(&SECRET_KEY_BYTES).unwrap()); + let encoded_expanded_secret_key: Vec = bincode::serialize(&expanded_secret_key).unwrap(); + let decoded_expanded_secret_key: ExpandedSecretKey = bincode::deserialize(&encoded_expanded_secret_key).unwrap(); + + for i in 0..EXPANDED_SECRET_KEY_LENGTH { + assert_eq!(expanded_secret_key.to_bytes()[i], decoded_expanded_secret_key.to_bytes()[i]); + } + } + + #[test] + fn serialize_deserialize_expanded_secret_key_json() { + let expanded_secret_key = ExpandedSecretKey::from(&SecretKey::from_bytes(&SECRET_KEY_BYTES).unwrap()); + let encoded_expanded_secret_key = serde_json::to_string(&expanded_secret_key).unwrap(); + let decoded_expanded_secret_key: ExpandedSecretKey = serde_json::from_str(&encoded_expanded_secret_key).unwrap(); + + for i in 0..EXPANDED_SECRET_KEY_LENGTH { + assert_eq!(expanded_secret_key.to_bytes()[i], decoded_expanded_secret_key.to_bytes()[i]); + } + } + #[test] fn serialize_deserialize_keypair_bincode() { let keypair = Keypair::from_bytes(&KEYPAIR_BYTES).unwrap(); - let encoded_keypair: Vec = serialize(&keypair, Infinite).unwrap(); - let decoded_keypair: Keypair = deserialize(&encoded_keypair).unwrap(); + let encoded_keypair: Vec = bincode::serialize(&keypair).unwrap(); + let decoded_keypair: Keypair = bincode::deserialize(&encoded_keypair).unwrap(); - for i in 0..64 { + for i in 0..KEYPAIR_LENGTH { + assert_eq!(KEYPAIR_BYTES[i], decoded_keypair.to_bytes()[i]); + } + } + + #[test] + fn serialize_deserialize_keypair_json() { + let keypair = Keypair::from_bytes(&KEYPAIR_BYTES).unwrap(); + let encoded_keypair = serde_json::to_string(&keypair).unwrap(); + let decoded_keypair: Keypair = serde_json::from_str(&encoded_keypair).unwrap(); + + for i in 0..KEYPAIR_LENGTH { assert_eq!(KEYPAIR_BYTES[i], decoded_keypair.to_bytes()[i]); } } @@ -323,18 +385,30 @@ mod serialisation { #[test] fn serialize_public_key_size() { let public_key: PublicKey = PublicKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap(); - assert_eq!(serialized_size(&public_key) as usize, 40); // These sizes are specific to bincode==1.0.1 + assert_eq!(bincode::serialized_size(&public_key).unwrap() as usize, BINCODE_INT_LENGTH + PUBLIC_KEY_LENGTH); } #[test] fn serialize_signature_size() { let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES).unwrap(); - assert_eq!(serialized_size(&signature) as usize, 64); // These sizes are specific to bincode==1.0.1 + assert_eq!(bincode::serialized_size(&signature).unwrap() as usize, SIGNATURE_LENGTH); } #[test] fn serialize_secret_key_size() { let secret_key: SecretKey = SecretKey::from_bytes(&SECRET_KEY_BYTES).unwrap(); - assert_eq!(serialized_size(&secret_key) as usize, 40); // These sizes are specific to bincode==1.0.1 + assert_eq!(bincode::serialized_size(&secret_key).unwrap() as usize, BINCODE_INT_LENGTH + SECRET_KEY_LENGTH); + } + + #[test] + fn serialize_expanded_secret_key_size() { + let expanded_secret_key = ExpandedSecretKey::from(&SecretKey::from_bytes(&SECRET_KEY_BYTES).unwrap()); + assert_eq!(bincode::serialized_size(&expanded_secret_key).unwrap() as usize, BINCODE_INT_LENGTH + EXPANDED_SECRET_KEY_LENGTH); + } + + #[test] + fn serialize_keypair_size() { + let keypair = Keypair::from_bytes(&KEYPAIR_BYTES).unwrap(); + assert_eq!(bincode::serialized_size(&keypair).unwrap() as usize, BINCODE_INT_LENGTH + KEYPAIR_LENGTH); } } From 69eccda4449b564aff57a776c6a0ed51fce01123 Mon Sep 17 00:00:00 2001 From: Cheng XU Date: Mon, 21 Sep 2020 18:26:16 -0700 Subject: [PATCH 278/351] Fix serde implementation for serde_json We use the [serde_bytes](https://github.com/serde-rs/bytes) crate for serialization implementations, which simplifies codes and fixes issues for serde_json. --- Cargo.toml | 3 ++- src/keypair.rs | 69 +++++--------------------------------------------- src/public.rs | 29 ++++----------------- src/secret.rs | 52 +++++++------------------------------ 4 files changed, 22 insertions(+), 131 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 35592f6..d154b14 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ merlin = { version = "2", default-features = false, optional = true } rand = { version = "0.7", default-features = false, optional = true } rand_core = { version = "0.5", default-features = false, optional = true } serde_crate = { package = "serde", version = "1.0", default-features = false, optional = true } +serde_bytes = { version = "0.11", optional = true } sha2 = { version = "0.9", default-features = false } zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] } @@ -52,7 +53,7 @@ default = ["std", "rand", "u64_backend"] std = ["curve25519-dalek/std", "ed25519/std", "serde_crate/std", "sha2/std", "rand/std"] alloc = ["curve25519-dalek/alloc", "rand/alloc", "zeroize/alloc"] nightly = ["curve25519-dalek/nightly"] -serde = ["serde_crate", "ed25519/serde"] +serde = ["serde_crate", "serde_bytes", "ed25519/serde"] batch = ["merlin", "rand"] # This feature enables deterministic batch verification. batch_deterministic = ["merlin", "rand", "rand_core"] diff --git a/src/keypair.rs b/src/keypair.rs index c12f751..55af2df 100644 --- a/src/keypair.rs +++ b/src/keypair.rs @@ -15,11 +15,9 @@ use rand::{CryptoRng, RngCore}; #[cfg(feature = "serde")] use serde::de::Error as SerdeError; #[cfg(feature = "serde")] -use serde::de::Visitor; -#[cfg(feature = "serde")] -use serde::de::SeqAccess; -#[cfg(feature = "serde")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; +#[cfg(feature = "serde")] +use serde_bytes::{Bytes as SerdeBytes, ByteBuf as SerdeByteBuf}; pub use sha2::Sha512; @@ -428,7 +426,8 @@ impl Serialize for Keypair { where S: Serializer, { - serializer.serialize_bytes(&self.to_bytes()[..]) + let bytes = &self.to_bytes()[..]; + SerdeBytes::new(bytes).serialize(serializer) } } @@ -438,63 +437,7 @@ impl<'d> Deserialize<'d> for Keypair { where D: Deserializer<'d>, { - struct KeypairVisitor; - - impl<'d> Visitor<'d> for KeypairVisitor { - type Value = Keypair; - - fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - formatter.write_str("An ed25519 keypair, 64 bytes in total where the secret key is \ - the first 32 bytes and is in unexpanded form, and the second \ - 32 bytes is a compressed point for a public key.") - } - - fn visit_bytes(self, bytes: &[u8]) -> Result - where - E: SerdeError, - { - if bytes.len() != KEYPAIR_LENGTH { - return Err(SerdeError::invalid_length(bytes.len(), &self)); - } - - let secret_key = SecretKey::from_bytes(&bytes[..SECRET_KEY_LENGTH]); - let public_key = PublicKey::from_bytes(&bytes[SECRET_KEY_LENGTH..]); - - if let (Ok(secret), Ok(public)) = (secret_key, public_key) { - Ok(Keypair{ secret, public }) - } else { - Err(SerdeError::invalid_length(bytes.len(), &self)) - } - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: SeqAccess<'d> - { - if let Some(len) = seq.size_hint() { - if len != KEYPAIR_LENGTH { - return Err(SerdeError::invalid_length(len, &self)); - } - } - - // TODO: We could do this with `MaybeUninit` to avoid unnecessary initialization costs - let mut bytes: [u8; KEYPAIR_LENGTH] = [0u8; KEYPAIR_LENGTH]; - - for i in 0..KEYPAIR_LENGTH { - bytes[i] = seq.next_element()?.ok_or_else(|| SerdeError::invalid_length(i, &self))?; - } - - let secret_key = SecretKey::from_bytes(&bytes[..SECRET_KEY_LENGTH]); - let public_key = PublicKey::from_bytes(&bytes[SECRET_KEY_LENGTH..]); - - if let (Ok(secret), Ok(public)) = (secret_key, public_key) { - Ok(Keypair{ secret, public }) - } else { - Err(SerdeError::invalid_length(bytes.len(), &self)) - } - } - - } - deserializer.deserialize_bytes(KeypairVisitor) + let bytes = ::deserialize(deserializer)?; + Keypair::from_bytes(bytes.as_ref()).map_err(SerdeError::custom) } } diff --git a/src/public.rs b/src/public.rs index 170390d..342adf6 100644 --- a/src/public.rs +++ b/src/public.rs @@ -26,11 +26,9 @@ pub use sha2::Sha512; #[cfg(feature = "serde")] use serde::de::Error as SerdeError; #[cfg(feature = "serde")] -use serde::de::Visitor; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -#[cfg(feature = "serde")] -use serde::{Deserializer, Serializer}; +use serde_bytes::{Bytes as SerdeBytes, ByteBuf as SerdeByteBuf}; use crate::constants::*; use crate::errors::*; @@ -362,7 +360,7 @@ impl Serialize for PublicKey { where S: Serializer, { - serializer.serialize_bytes(self.as_bytes()) + SerdeBytes::new(self.as_bytes()).serialize(serializer) } } @@ -372,24 +370,7 @@ impl<'d> Deserialize<'d> for PublicKey { where D: Deserializer<'d>, { - struct PublicKeyVisitor; - - impl<'d> Visitor<'d> for PublicKeyVisitor { - type Value = PublicKey; - - fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - formatter.write_str( - "An ed25519 public key as a 32-byte compressed point, as specified in RFC8032", - ) - } - - fn visit_bytes(self, bytes: &[u8]) -> Result - where - E: SerdeError, - { - PublicKey::from_bytes(bytes).or(Err(SerdeError::invalid_length(bytes.len(), &self))) - } - } - deserializer.deserialize_bytes(PublicKeyVisitor) + let bytes = ::deserialize(deserializer)?; + PublicKey::from_bytes(bytes.as_ref()).map_err(SerdeError::custom) } } diff --git a/src/secret.rs b/src/secret.rs index 64c9d1f..2ca3a12 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -25,11 +25,9 @@ use sha2::Sha512; #[cfg(feature = "serde")] use serde::de::Error as SerdeError; #[cfg(feature = "serde")] -use serde::de::Visitor; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -#[cfg(feature = "serde")] -use serde::{Deserializer, Serializer}; +use serde_bytes::{Bytes as SerdeBytes, ByteBuf as SerdeByteBuf}; use zeroize::Zeroize; @@ -184,7 +182,7 @@ impl Serialize for SecretKey { where S: Serializer, { - serializer.serialize_bytes(self.as_bytes()) + SerdeBytes::new(self.as_bytes()).serialize(serializer) } } @@ -194,23 +192,8 @@ impl<'d> Deserialize<'d> for SecretKey { where D: Deserializer<'d>, { - struct SecretKeyVisitor; - - impl<'d> Visitor<'d> for SecretKeyVisitor { - type Value = SecretKey; - - fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - formatter.write_str("An ed25519 secret key as 32 bytes, as specified in RFC8032.") - } - - fn visit_bytes(self, bytes: &[u8]) -> Result - where - E: SerdeError, - { - SecretKey::from_bytes(bytes).or(Err(SerdeError::invalid_length(bytes.len(), &self))) - } - } - deserializer.deserialize_bytes(SecretKeyVisitor) + let bytes = ::deserialize(deserializer)?; + SecretKey::from_bytes(bytes.as_ref()).map_err(SerdeError::custom) } } @@ -521,7 +504,8 @@ impl Serialize for ExpandedSecretKey { where S: Serializer, { - serializer.serialize_bytes(&self.to_bytes()[..]) + let bytes = &self.to_bytes()[..]; + SerdeBytes::new(bytes).serialize(serializer) } } @@ -531,26 +515,8 @@ impl<'d> Deserialize<'d> for ExpandedSecretKey { where D: Deserializer<'d>, { - struct ExpandedSecretKeyVisitor; - - impl<'d> Visitor<'d> for ExpandedSecretKeyVisitor { - type Value = ExpandedSecretKey; - - fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - formatter.write_str( - "An ed25519 expanded secret key as 64 bytes, as specified in RFC8032.", - ) - } - - fn visit_bytes(self, bytes: &[u8]) -> Result - where - E: SerdeError, - { - ExpandedSecretKey::from_bytes(bytes) - .or(Err(SerdeError::invalid_length(bytes.len(), &self))) - } - } - deserializer.deserialize_bytes(ExpandedSecretKeyVisitor) + let bytes = ::deserialize(deserializer)?; + ExpandedSecretKey::from_bytes(bytes.as_ref()).map_err(SerdeError::custom) } } From d6ff6de2cff8af36fef8933dd81dc5100121dee3 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 22 Sep 2020 01:36:49 +0000 Subject: [PATCH 279/351] Add #![forbid(unsafe_code)]. CLOSES https://github.com/dalek-cryptography/ed25519-dalek/issues/144 --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 22cd7e9..8f586ec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -234,6 +234,7 @@ #![no_std] #![warn(future_incompatible)] #![deny(missing_docs)] // refuse to compile if documentation is missing +#![forbid(unsafe_code)] #[cfg(any(feature = "std", test))] #[macro_use] From 8c15bce61d157e55148b1056f4726870b4d528f3 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 22 Sep 2020 01:54:44 +0000 Subject: [PATCH 280/351] Actually, we use unsafe{} in one test. --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 29c8c1c..88dfc93 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -234,6 +234,8 @@ #![no_std] #![warn(future_incompatible)] #![deny(missing_docs)] // refuse to compile if documentation is missing + +#![cfg(not(test))] #![forbid(unsafe_code)] #[cfg(any(feature = "std", test))] From 1042cb60a07cdaacb59ca209716b69f444460f8f Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 22 Sep 2020 01:56:35 +0000 Subject: [PATCH 281/351] Bump ed25519-dalek version to 1.0.1. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d154b14..94d9f96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "1.0.0" +version = "1.0.1" edition = "2018" authors = ["isis lovecruft "] readme = "README.md" From 6ce6519287cefcaa19db4137be1c1f628feb98fc Mon Sep 17 00:00:00 2001 From: Cheng XU Date: Mon, 21 Sep 2020 19:16:01 -0700 Subject: [PATCH 282/351] fix serde in no_std --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 94d9f96..837a0e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ merlin = { version = "2", default-features = false, optional = true } rand = { version = "0.7", default-features = false, optional = true } rand_core = { version = "0.5", default-features = false, optional = true } serde_crate = { package = "serde", version = "1.0", default-features = false, optional = true } -serde_bytes = { version = "0.11", optional = true } +serde_bytes = { version = "0.11", default-features = false, features = ["alloc"], optional = true } sha2 = { version = "0.9", default-features = false } zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] } From da6c7e114f464d270079960556517e2ea6ea9ffb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= Date: Wed, 14 Oct 2020 15:13:34 -0400 Subject: [PATCH 283/351] [test-only] Add test showing the non-repudiation property of the signature verifications used in `PublicKey::verify` and `PublicKey::verify_strict`. This PR is a follow-up of #98, which aims to demonstrate the issue brought by small-order public keys. It shows an example of crafting a (public_key, signature) that verifies against two distinct messages using `verify`, but fails using `verify_strict`. This has consequences on the possibility to repudiate a signed contract of blockchain transactions. For more details, see: https://eprint.iacr.org/2020/1244 Joint work with @kchalkias @valerini --- tests/ed25519.rs | 74 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/tests/ed25519.rs b/tests/ed25519.rs index 696e287..0a403be 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -28,7 +28,10 @@ use sha2::Sha512; #[cfg(test)] mod vectors { + use curve25519_dalek::{edwards::EdwardsPoint, scalar::Scalar}; use ed25519::signature::Signature as _; + use sha2::{digest::Digest, Sha512}; + use std::convert::TryFrom; use std::io::BufReader; use std::io::BufRead; @@ -112,6 +115,77 @@ mod vectors { assert!(keypair.verify_prehashed(prehash_for_verifying, None, &sig2).is_ok(), "Could not verify ed25519ph signature!"); } + + // Taken from curve25519_dalek::constants::EIGHT_TORSION[4] + const EIGHT_TORSION_4: [u8; 32] = [ + 236, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127, + ]; + + fn compute_hram(message: &[u8], pub_key: &EdwardsPoint, signature_r: &EdwardsPoint) -> Scalar { + let k_bytes = Sha512::default() + .chain(&signature_r.compress().as_bytes()) + .chain(&pub_key.compress().as_bytes()[..]) + .chain(&message); + let mut k_output = [0u8; 64]; + k_output.copy_from_slice(k_bytes.finalize().as_slice()); + Scalar::from_bytes_mod_order_wide(&k_output) + } + + fn serialize_signature(r: &EdwardsPoint, s: &Scalar) -> Vec { + [&r.compress().as_bytes()[..], &s.as_bytes()[..]].concat() + } + + #[test] + fn repudiation() { + use curve25519_dalek::traits::IsIdentity; + use std::ops::Neg; + + let message1 = b"Send 100 USD to Alice"; + let message2 = b"Send 100000 USD to Alice"; + + // Pick a random Scalar + fn non_null_scalar() -> Scalar { + let mut rng = rand::rngs::OsRng; + let mut s_candidate = Scalar::random(&mut rng); + while s_candidate == Scalar::zero() { + s_candidate = Scalar::random(&mut rng); + } + s_candidate + } + let mut s: Scalar = non_null_scalar(); + + fn pick_r_and_pubkey(s: Scalar) -> (EdwardsPoint, EdwardsPoint) { + let r0 = s * curve25519_dalek::constants::ED25519_BASEPOINT_POINT; + // Pick a torsion point of order 2 + let pub_key = curve25519_dalek::edwards::CompressedEdwardsY(EIGHT_TORSION_4) + .decompress() + .unwrap(); + let r = r0 + pub_key.neg(); + (r, pub_key) + } + + let (mut r, mut pub_key) = pick_r_and_pubkey(s); + + while !(pub_key.neg() + compute_hram(message1, &pub_key, &r) * pub_key).is_identity() + || !(pub_key.neg() + compute_hram(message2, &pub_key, &r) * pub_key).is_identity() + { + s = non_null_scalar(); + let key = pick_r_and_pubkey(s); + r = key.0; + pub_key = key.1; + } + + let signature = serialize_signature(&r, &s); + let pk = PublicKey::from_bytes(&pub_key.compress().as_bytes()[..]).unwrap(); + let sig = Signature::try_from(&signature[..]).unwrap(); + // The same signature verifies for both messages + assert!(pk.verify(message1, &sig).is_ok() && pk.verify(message2, &sig).is_ok()); + // But not with a strict signature: verify_strict refuses small order keys + assert!( + pk.verify_strict(message1, &sig).is_err() || pk.verify_strict(message2, &sig).is_err() + ); + } } #[cfg(test)] From ce5ff276814aa508030d0c9ff296b1fffc180635 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 28 Oct 2020 00:04:15 +0000 Subject: [PATCH 284/351] Make serde_bytes/alloc dependent on alloc feature. Fixup for PR #149. --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 837a0e8..7be49e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ merlin = { version = "2", default-features = false, optional = true } rand = { version = "0.7", default-features = false, optional = true } rand_core = { version = "0.5", default-features = false, optional = true } serde_crate = { package = "serde", version = "1.0", default-features = false, optional = true } -serde_bytes = { version = "0.11", default-features = false, features = ["alloc"], optional = true } +serde_bytes = { version = "0.11", default-features = false, optional = true } sha2 = { version = "0.9", default-features = false } zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] } @@ -51,7 +51,7 @@ harness = false [features] default = ["std", "rand", "u64_backend"] std = ["curve25519-dalek/std", "ed25519/std", "serde_crate/std", "sha2/std", "rand/std"] -alloc = ["curve25519-dalek/alloc", "rand/alloc", "zeroize/alloc"] +alloc = ["curve25519-dalek/alloc", "rand/alloc", "serde_bytes/alloc", "zeroize/alloc"] nightly = ["curve25519-dalek/nightly"] serde = ["serde_crate", "serde_bytes", "ed25519/serde"] batch = ["merlin", "rand"] From bbb8869550084adf5b5762e82b232da398af1662 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 2 Nov 2020 23:57:09 +0000 Subject: [PATCH 285/351] Fix std builds when serde is enabled. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 7be49e4..08ee320 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,7 @@ harness = false [features] default = ["std", "rand", "u64_backend"] -std = ["curve25519-dalek/std", "ed25519/std", "serde_crate/std", "sha2/std", "rand/std"] +std = ["curve25519-dalek/std", "ed25519/std", "serde_bytes/std", "serde_crate/std", "sha2/std", "rand/std"] alloc = ["curve25519-dalek/alloc", "rand/alloc", "serde_bytes/alloc", "zeroize/alloc"] nightly = ["curve25519-dalek/nightly"] serde = ["serde_crate", "serde_bytes", "ed25519/serde"] From 9d9a6b0beb10e3200848cd01e5c3d8c0abfcb872 Mon Sep 17 00:00:00 2001 From: Tyler Neely Date: Wed, 25 Nov 2020 12:35:24 +0100 Subject: [PATCH 286/351] Speed up compilation by avoiding zeroize_derive --- Cargo.toml | 2 +- src/secret.rs | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 94d9f96..d71ef4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ rand_core = { version = "0.5", default-features = false, optional = true } serde_crate = { package = "serde", version = "1.0", default-features = false, optional = true } serde_bytes = { version = "0.11", optional = true } sha2 = { version = "0.9", default-features = false } -zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] } +zeroize = { version = "1", default-features = false } [dev-dependencies] hex = "^0.4" diff --git a/src/secret.rs b/src/secret.rs index 2ca3a12..15c0cd4 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -40,10 +40,14 @@ use crate::signature::*; /// /// Instances of this secret are automatically overwritten with zeroes when they /// fall out of scope. -#[derive(Zeroize)] -#[zeroize(drop)] // Overwrite secret key material with null bytes when it goes out of scope. pub struct SecretKey(pub(crate) [u8; SECRET_KEY_LENGTH]); +impl Drop for SecretKey { + fn drop(&mut self) { + self.0.zeroize() + } +} + impl Debug for SecretKey { fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { write!(f, "SecretKey: {:?}", &self.0[..]) @@ -235,13 +239,18 @@ impl<'d> Deserialize<'d> for SecretKey { // same signature scheme, and which both fail in exactly the same way. For a // better-designed, Schnorr-based signature scheme, see Trevor Perrin's work on // "generalised EdDSA" and "VXEdDSA". -#[derive(Zeroize)] -#[zeroize(drop)] // Overwrite secret key material with null bytes when it goes out of scope. pub struct ExpandedSecretKey { pub(crate) key: Scalar, pub(crate) nonce: [u8; 32], } +impl Drop for ExpandedSecretKey { + fn drop(&mut self) { + self.key.zeroize(); + self.nonce.zeroize() + } +} + impl<'a> From<&'a SecretKey> for ExpandedSecretKey { /// Construct an `ExpandedSecretKey` from a `SecretKey`. /// From c12cf4862388cbe801e195e8a218ff70da96b028 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= Date: Mon, 3 May 2021 16:26:11 -0700 Subject: [PATCH 287/351] Threads the fiat_{u64,u32}_backend features in the feature set This allows the fiat backends introduced in [curve25519-dalek/#342](https://github.com/dalek-cryptography/curve25519-dalek/pull/342) to be used from an ed25519 import without cumbersome overrides. --- Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 94d9f96..78591d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,4 +62,6 @@ asm = ["sha2/asm"] legacy_compatibility = [] u64_backend = ["curve25519-dalek/u64_backend"] u32_backend = ["curve25519-dalek/u32_backend"] +fiat_u64_backend = ["curve25519-dalek/fiat_u64_backend"] +fiat_u32_backend = ["curve25519-dalek/fiat_u32_backend"] simd_backend = ["curve25519-dalek/simd_backend"] From 29932412f8d05f9d49c6ca6aea0dc34b84d8c580 Mon Sep 17 00:00:00 2001 From: Matteo Monti Date: Sat, 29 May 2021 17:38:14 +0200 Subject: [PATCH 288/351] Update README.md Fixes minor typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 49766fb..fabd832 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ However, if you require this, please see the documentation for the `verify_strict()` function, which does the full checks for the group elements. This functionality is available by default. -If for some reason—although we strongely advise you not to—you need to conform +If for some reason—although we strongly advise you not to—you need to conform to the original specification of ed25519 signatures as in the excerpt from the paper above, you can disable scalar malleability checking via `--features='legacy_compatibility'`. **WE STRONGLY ADVISE AGAINST THIS.** From d94b0f52dc92bcb597bd01dcd163b85979e2a1ed Mon Sep 17 00:00:00 2001 From: gbaranski Date: Sun, 1 Aug 2021 18:29:28 +0200 Subject: [PATCH 289/351] fix: remove rust-analyzer breaking line --- src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 88dfc93..6749e55 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -235,7 +235,6 @@ #![warn(future_incompatible)] #![deny(missing_docs)] // refuse to compile if documentation is missing -#![cfg(not(test))] #![forbid(unsafe_code)] #[cfg(any(feature = "std", test))] From c5fb9325615eaa1f4cbeb3175d1ab7ee0563b113 Mon Sep 17 00:00:00 2001 From: gbaranski Date: Sun, 1 Aug 2021 19:28:40 +0200 Subject: [PATCH 290/351] fix: stop forbidding unsafe in tests --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 6749e55..c8ee87d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -235,7 +235,7 @@ #![warn(future_incompatible)] #![deny(missing_docs)] // refuse to compile if documentation is missing -#![forbid(unsafe_code)] +#![cfg_attr(not(test), forbid(unsafe_code))] #[cfg(any(feature = "std", test))] #[macro_use] From 10cef4982421c7bc062df510d5e6ee15b05f75e3 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Sep 2021 00:45:42 +0000 Subject: [PATCH 291/351] Add CI via Github actions. --- .github/workflows/rust.yml | 131 +++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 +- 2 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/rust.yml diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..79571cc --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,131 @@ +name: Rust + +on: + push: + branches: [ '*' ] + pull_request: + branches: [ main, develop ] + +env: + CARGO_TERM_COLOR: always + +jobs: + test-u32: + name: Test u32 backend + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - uses: actions-rs/cargo@v1 + with: + command: test + args: --no-default-features --features "std u32_backend" + + test-u64: + name: Test u64 backend + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - uses: actions-rs/cargo@v1 + with: + command: test + args: --no-default-features --features "std u64_backend" + + test-simd: + name: Test simd backend (nightly) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + override: true + - uses: actions-rs/cargo@v1 + with: + command: test + args: --no-default-features --features "std nightly simd_backend" + + test-defaults-serde: + name: Test default feature selection and serde + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - uses: actions-rs/cargo@v1 + with: + command: test + args: --features "serde" + + test-alloc-u32: + name: Test no_std+alloc with u32 backend + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - uses: actions-rs/cargo@v1 + with: + command: test + args: --lib --no-default-features --features "alloc u32_backend" + + test-batch-deterministic: + name: Test deterministic batch verification + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - uses: actions-rs/cargo@v1 + with: + command: test + args: --features "batch_deterministic" + + msrv: + name: Current MSRV is 1.41 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: 1.41 + override: true + - uses: actions-rs/cargo@v1 + with: + command: build + + bench: + name: Check that benchmarks compile + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - uses: actions-rs/cargo@v1 + with: + command: bench + # This filter selects no benchmarks, so we don't run any, only build them. + args: --features "batch" "DONTRUNBENCHMARKS" diff --git a/Cargo.toml b/Cargo.toml index 48bb609..053c2a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ rand_core = { version = "0.5", default-features = false, optional = true } serde_crate = { package = "serde", version = "1.0", default-features = false, optional = true } serde_bytes = { version = "0.11", default-features = false, optional = true } sha2 = { version = "0.9", default-features = false } -zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] } +zeroize = { version = "~1.3", default-features = false, features = ["zeroize_derive"] } [dev-dependencies] hex = "^0.4" From 9638ab40a51eb203fb93f4b3e630474953602995 Mon Sep 17 00:00:00 2001 From: Alex Xiong Date: Sun, 16 Oct 2022 03:04:03 +0800 Subject: [PATCH 292/351] Made ExpandedSecretKey private to avoid signing key oracle (#205) This fix eliminates a scenario where a user misuses the `ExpandedSecretKey` API in a way that leaks the user's secret key. In short, if a user sends `ExpandedSecretKey::sign(sk, msg, pk1)` followed by `ExpandedSecretKey::sign(sk, msg, pk2)`, where `pk1 != pk2`, a passive adversary [can easily][0] derive `sk`. To mitigate this, we remove the API entirely. [0]: https://github.com/MystenLabs/ed25519-unsafe-libs --- benches/ed25519_benchmarks.rs | 24 +++-------- src/batch.rs | 2 +- src/errors.rs | 3 ++ src/keypair.rs | 75 +++++++++++++++++++++++------------ src/lib.rs | 17 ++++---- src/secret.rs | 50 ++++++++++------------- tests/ed25519.rs | 57 ++++++-------------------- 7 files changed, 99 insertions(+), 129 deletions(-) diff --git a/benches/ed25519_benchmarks.rs b/benches/ed25519_benchmarks.rs index 45dce35..125e718 100644 --- a/benches/ed25519_benchmarks.rs +++ b/benches/ed25519_benchmarks.rs @@ -16,7 +16,7 @@ use criterion::Criterion; mod ed25519_benches { use super::*; - use ed25519_dalek::ExpandedSecretKey; + use ed25519_dalek::verify_batch; use ed25519_dalek::Keypair; use ed25519_dalek::PublicKey; use ed25519_dalek::Signature; @@ -30,20 +30,7 @@ mod ed25519_benches { let keypair: Keypair = Keypair::generate(&mut csprng); let msg: &[u8] = b""; - c.bench_function("Ed25519 signing", move |b| { - b.iter(| | keypair.sign(msg)) - }); - } - - fn sign_expanded_key(c: &mut Criterion) { - let mut csprng: ThreadRng = thread_rng(); - let keypair: Keypair = Keypair::generate(&mut csprng); - let expanded: ExpandedSecretKey = (&keypair.secret).into(); - let msg: &[u8] = b""; - - c.bench_function("Ed25519 signing with an expanded secret key", move |b| { - b.iter(| | expanded.sign(msg, &keypair.public)) - }); + c.bench_function("Ed25519 signing", move |b| b.iter(|| keypair.sign(msg))); } fn verify(c: &mut Criterion) { @@ -78,8 +65,10 @@ mod ed25519_benches { let keypairs: Vec = (0..size).map(|_| Keypair::generate(&mut csprng)).collect(); let msg: &[u8] = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; let messages: Vec<&[u8]> = (0..size).map(|_| msg).collect(); - let signatures: Vec = keypairs.iter().map(|key| key.sign(&msg)).collect(); - let public_keys: Vec = keypairs.iter().map(|key| key.public).collect(); + let signatures: Vec = + keypairs.iter().map(|key| key.sign(&msg)).collect(); + let public_keys: Vec = + keypairs.iter().map(|key| key.public_key()).collect(); b.iter(|| verify_batch(&messages[..], &signatures[..], &public_keys[..])); }, @@ -100,7 +89,6 @@ mod ed25519_benches { config = Criterion::default(); targets = sign, - sign_expanded_key, verify, verify_strict, verify_batch_signatures, diff --git a/src/batch.rs b/src/batch.rs index 3a4b8e9..cb28188 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -209,7 +209,7 @@ fn zero_rng() -> ZeroRng { /// let msg: &[u8] = b"They're good dogs Brant"; /// let messages: Vec<&[u8]> = (0..64).map(|_| msg).collect(); /// let signatures: Vec = keypairs.iter().map(|key| key.sign(&msg)).collect(); -/// let public_keys: Vec = keypairs.iter().map(|key| key.public).collect(); +/// let public_keys: Vec = keypairs.iter().map(|key| key.public_key()).collect(); /// /// let result = verify_batch(&messages[..], &signatures[..], &public_keys[..]); /// assert!(result.is_ok()); diff --git a/src/errors.rs b/src/errors.rs index b66fae0..d4e8201 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -43,6 +43,8 @@ pub(crate) enum InternalError { name_c: &'static str, length_c: usize, }, /// An ed25519ph signature can only take up to 255 octets of context. PrehashedContextLengthError, + /// A mismatched (public, secret) key pair. + MismatchedKeypairError, } impl Display for InternalError { @@ -63,6 +65,7 @@ impl Display for InternalError { {} has length {}, {} has length {}.", na, la, nb, lb, nc, lc), InternalError::PrehashedContextLengthError => write!(f, "An ed25519ph signature can only take up to 255 octets of context"), + InternalError::MismatchedKeypairError => write!(f, "Mismatched Keypair detected"), } } } diff --git a/src/keypair.rs b/src/keypair.rs index 55af2df..bcbb6e2 100644 --- a/src/keypair.rs +++ b/src/keypair.rs @@ -17,7 +17,7 @@ use serde::de::Error as SerdeError; #[cfg(feature = "serde")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[cfg(feature = "serde")] -use serde_bytes::{Bytes as SerdeBytes, ByteBuf as SerdeByteBuf}; +use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes}; pub use sha2::Sha512; @@ -32,15 +32,34 @@ use crate::public::*; use crate::secret::*; /// An ed25519 keypair. +// Invariant: `public` is always the public key of `secret`. This prevents the signing function +// oracle attack described in https://github.com/MystenLabs/ed25519-unsafe-libs #[derive(Debug)] pub struct Keypair { /// The secret half of this keypair. - pub secret: SecretKey, + pub(crate) secret: SecretKey, /// The public half of this keypair. - pub public: PublicKey, + pub(crate) public: PublicKey, +} + +impl From for Keypair { + fn from(secret: SecretKey) -> Self { + let public = PublicKey::from(&secret); + Self { secret, public } + } } impl Keypair { + /// Get the secret key of this keypair. + pub fn secret_key(&self) -> SecretKey { + SecretKey(self.secret.0) + } + + /// Get the public key of this keypair. + pub fn public_key(&self) -> PublicKey { + self.public + } + /// Convert this keypair to bytes. /// /// # Returns @@ -49,7 +68,8 @@ impl Keypair { /// `SECRET_KEY_LENGTH` of bytes is the `SecretKey`, and the next /// `PUBLIC_KEY_LENGTH` bytes is the `PublicKey` (the same as other /// libraries, such as [Adam Langley's ed25519 Golang - /// implementation](https://github.com/agl/ed25519/)). + /// implementation](https://github.com/agl/ed25519/)). It is guaranteed that + /// the encoded public key is the one derived from the encoded secret key. pub fn to_bytes(&self) -> [u8; KEYPAIR_LENGTH] { let mut bytes: [u8; KEYPAIR_LENGTH] = [0u8; KEYPAIR_LENGTH]; @@ -62,32 +82,31 @@ impl Keypair { /// /// # Inputs /// - /// * `bytes`: an `&[u8]` representing the scalar for the secret key, and a - /// compressed Edwards-Y coordinate of a point on curve25519, both as bytes. - /// (As obtained from `Keypair::to_bytes()`.) - /// - /// # Warning - /// - /// Absolutely no validation is done on the key. If you give this function - /// bytes which do not represent a valid point, or which do not represent - /// corresponding parts of the key, then your `Keypair` will be broken and - /// it will be your fault. + /// * `bytes`: an `&[u8]` of length [`KEYPAIR_LENGTH`], representing the + /// scalar for the secret key, and a compressed Edwards-Y coordinate of a + /// point on curve25519, both as bytes. (As obtained from + /// [`Keypair::to_bytes`].) /// /// # Returns /// /// A `Result` whose okay value is an EdDSA `Keypair` or whose error value /// is an `SignatureError` describing the error that occurred. - pub fn from_bytes<'a>(bytes: &'a [u8]) -> Result { + pub fn from_bytes(bytes: &[u8]) -> Result { if bytes.len() != KEYPAIR_LENGTH { return Err(InternalError::BytesLengthError { name: "Keypair", length: KEYPAIR_LENGTH, - }.into()); + } + .into()); } let secret = SecretKey::from_bytes(&bytes[..SECRET_KEY_LENGTH])?; let public = PublicKey::from_bytes(&bytes[SECRET_KEY_LENGTH..])?; - Ok(Keypair{ secret: secret, public: public }) + if public != (&secret).into() { + return Err(InternalError::MismatchedKeypairError.into()); + } + + Ok(Keypair { secret, public }) } /// Generate an ed25519 keypair. @@ -131,7 +150,10 @@ impl Keypair { let sk: SecretKey = SecretKey::generate(csprng); let pk: PublicKey = (&sk).into(); - Keypair{ public: pk, secret: sk } + Keypair { + public: pk, + secret: sk, + } } /// Sign a `prehashed_message` with this `Keypair` using the @@ -244,16 +266,17 @@ impl Keypair { { let expanded: ExpandedSecretKey = (&self.secret).into(); // xxx thanks i hate this - expanded.sign_prehashed(prehashed_message, &self.public, context).into() + expanded + .sign_prehashed(prehashed_message, &self.public, context) + .into() } /// Verify a signature on a message with this keypair's public key. pub fn verify( &self, message: &[u8], - signature: &ed25519::Signature - ) -> Result<(), SignatureError> - { + signature: &ed25519::Signature, + ) -> Result<(), SignatureError> { self.public.verify(message, signature) } @@ -303,7 +326,7 @@ impl Keypair { /// let mut prehashed_again: Sha512 = Sha512::default(); /// prehashed_again.update(message); /// - /// let verified = keypair.public.verify_prehashed(prehashed_again, Some(context), &sig); + /// let verified = keypair.public_key().verify_prehashed(prehashed_again, Some(context), &sig); /// /// assert!(verified.is_ok()); /// @@ -329,7 +352,8 @@ impl Keypair { where D: Digest, { - self.public.verify_prehashed(prehashed_message, context, signature) + self.public + .verify_prehashed(prehashed_message, context, signature) } /// Strictly verify a signature on a message with this keypair's public key. @@ -399,8 +423,7 @@ impl Keypair { &self, message: &[u8], signature: &ed25519::Signature, - ) -> Result<(), SignatureError> - { + ) -> Result<(), SignatureError> { self.public.verify_strict(message, signature) } } diff --git a/src/lib.rs b/src/lib.rs index c8ee87d..6e8933b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,7 +87,7 @@ //! # let message: &[u8] = b"This is a test of the tsunami alert system."; //! # let signature: Signature = keypair.sign(message); //! -//! let public_key: PublicKey = keypair.public; +//! let public_key: PublicKey = keypair.public_key(); //! assert!(public_key.verify(message, &signature).is_ok()); //! # } //! ``` @@ -111,10 +111,9 @@ //! # let keypair: Keypair = Keypair::generate(&mut csprng); //! # let message: &[u8] = b"This is a test of the tsunami alert system."; //! # let signature: Signature = keypair.sign(message); -//! # let public_key: PublicKey = keypair.public; //! -//! let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = public_key.to_bytes(); -//! let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = keypair.secret.to_bytes(); +//! let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = keypair.public_key().to_bytes(); +//! let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = keypair.secret_key().to_bytes(); //! let keypair_bytes: [u8; KEYPAIR_LENGTH] = keypair.to_bytes(); //! let signature_bytes: [u8; SIGNATURE_LENGTH] = signature.to_bytes(); //! # } @@ -127,6 +126,7 @@ //! # extern crate ed25519_dalek; //! # use std::convert::TryFrom; //! # use rand::rngs::OsRng; +//! # use std::convert::TryInto; //! # use ed25519_dalek::{Keypair, Signature, Signer, PublicKey, SecretKey, SignatureError}; //! # use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; //! # fn do_test() -> Result<(SecretKey, PublicKey, Keypair, Signature), SignatureError> { @@ -134,8 +134,8 @@ //! # let keypair_orig: Keypair = Keypair::generate(&mut csprng); //! # let message: &[u8] = b"This is a test of the tsunami alert system."; //! # let signature_orig: Signature = keypair_orig.sign(message); -//! # let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = keypair_orig.public.to_bytes(); -//! # let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = keypair_orig.secret.to_bytes(); +//! # let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = keypair_orig.public_key().to_bytes(); +//! # let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = keypair_orig.secret_key().to_bytes(); //! # let keypair_bytes: [u8; KEYPAIR_LENGTH] = keypair_orig.to_bytes(); //! # let signature_bytes: [u8; SIGNATURE_LENGTH] = signature_orig.to_bytes(); //! # @@ -181,7 +181,7 @@ //! # let keypair: Keypair = Keypair::generate(&mut csprng); //! # let message: &[u8] = b"This is a test of the tsunami alert system."; //! # let signature: Signature = keypair.sign(message); -//! # let public_key: PublicKey = keypair.public; +//! # let public_key: PublicKey = keypair.public_key(); //! # let verified: bool = public_key.verify(message, &signature).is_ok(); //! //! let encoded_public_key: Vec = serialize(&public_key).unwrap(); @@ -213,7 +213,7 @@ //! # let keypair: Keypair = Keypair::generate(&mut csprng); //! let message: &[u8] = b"This is a test of the tsunami alert system."; //! # let signature: Signature = keypair.sign(message); -//! # let public_key: PublicKey = keypair.public; +//! # let public_key: PublicKey = keypair.public_key(); //! # let verified: bool = public_key.verify(message, &signature).is_ok(); //! # let encoded_public_key: Vec = serialize(&public_key).unwrap(); //! # let encoded_signature: Vec = serialize(&signature).unwrap(); @@ -234,7 +234,6 @@ #![no_std] #![warn(future_incompatible)] #![deny(missing_docs)] // refuse to compile if documentation is missing - #![cfg_attr(not(test), forbid(unsafe_code))] #[cfg(any(feature = "std", test))] diff --git a/src/secret.rs b/src/secret.rs index 15c0cd4..f8b9da8 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -239,7 +239,7 @@ impl<'d> Deserialize<'d> for SecretKey { // same signature scheme, and which both fail in exactly the same way. For a // better-designed, Schnorr-based signature scheme, see Trevor Perrin's work on // "generalised EdDSA" and "VXEdDSA". -pub struct ExpandedSecretKey { +pub(crate) struct ExpandedSecretKey { pub(crate) key: Scalar, pub(crate) nonce: [u8; 32], } @@ -256,7 +256,7 @@ impl<'a> From<&'a SecretKey> for ExpandedSecretKey { /// /// # Examples /// - /// ``` + /// ```ignore /// # extern crate rand; /// # extern crate sha2; /// # extern crate ed25519_dalek; @@ -302,7 +302,7 @@ impl ExpandedSecretKey { /// /// # Examples /// - /// ``` + /// ```ignore /// # extern crate rand; /// # extern crate sha2; /// # extern crate ed25519_dalek; @@ -342,7 +342,7 @@ impl ExpandedSecretKey { /// /// # Examples /// - /// ``` + /// ```ignore /// # extern crate rand; /// # extern crate sha2; /// # extern crate ed25519_dalek; @@ -375,12 +375,13 @@ impl ExpandedSecretKey { /// # fn main() { } /// ``` #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { + pub(crate) fn from_bytes(bytes: &[u8]) -> Result { if bytes.len() != EXPANDED_SECRET_KEY_LENGTH { return Err(InternalError::BytesLengthError { name: "ExpandedSecretKey", length: EXPANDED_SECRET_KEY_LENGTH, - }.into()); + } + .into()); } let mut lower: [u8; 32] = [0u8; 32]; let mut upper: [u8; 32] = [0u8; 32]; @@ -396,7 +397,7 @@ impl ExpandedSecretKey { /// Sign a message with this `ExpandedSecretKey`. #[allow(non_snake_case)] - pub fn sign(&self, message: &[u8], public_key: &PublicKey) -> ed25519::Signature { + pub(crate) fn sign(&self, message: &[u8], public_key: &PublicKey) -> ed25519::Signature { let mut h: Sha512 = Sha512::new(); let R: CompressedEdwardsY; let r: Scalar; @@ -441,7 +442,7 @@ impl ExpandedSecretKey { /// /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 #[allow(non_snake_case)] - pub fn sign_prehashed<'a, D>( + pub(crate) fn sign_prehashed<'a, D>( &self, prehashed_message: D, public_key: &PublicKey, @@ -507,28 +508,6 @@ impl ExpandedSecretKey { } } -#[cfg(feature = "serde")] -impl Serialize for ExpandedSecretKey { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let bytes = &self.to_bytes()[..]; - SerdeBytes::new(bytes).serialize(serializer) - } -} - -#[cfg(feature = "serde")] -impl<'d> Deserialize<'d> for ExpandedSecretKey { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'d>, - { - let bytes = ::deserialize(deserializer)?; - ExpandedSecretKey::from_bytes(bytes.as_ref()).map_err(SerdeError::custom) - } -} - #[cfg(test)] mod test { use super::*; @@ -547,4 +526,15 @@ mod test { assert!(!memory.contains(&0x15)); } + + #[test] + fn pubkey_from_secret_and_expanded_secret() { + let mut csprng = rand::rngs::OsRng {}; + let secret: SecretKey = SecretKey::generate(&mut csprng); + let expanded_secret: ExpandedSecretKey = (&secret).into(); + let public_from_secret: PublicKey = (&secret).into(); // XXX eww + let public_from_expanded_secret: PublicKey = (&expanded_secret).into(); // XXX eww + + assert!(public_from_secret == public_from_expanded_secret); + } } diff --git a/tests/ed25519.rs b/tests/ed25519.rs index 0a403be..24740d8 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -29,7 +29,6 @@ use sha2::Sha512; #[cfg(test)] mod vectors { use curve25519_dalek::{edwards::EdwardsPoint, scalar::Scalar}; - use ed25519::signature::Signature as _; use sha2::{digest::Digest, Sha512}; use std::convert::TryFrom; @@ -69,8 +68,10 @@ mod vectors { let sig_bytes: Vec = FromHex::from_hex(&parts[3]).unwrap(); let secret: SecretKey = SecretKey::from_bytes(&sec_bytes[..SECRET_KEY_LENGTH]).unwrap(); - let public: PublicKey = PublicKey::from_bytes(&pub_bytes[..PUBLIC_KEY_LENGTH]).unwrap(); - let keypair: Keypair = Keypair{ secret: secret, public: public }; + let expected_public: PublicKey = + PublicKey::from_bytes(&pub_bytes[..PUBLIC_KEY_LENGTH]).unwrap(); + let keypair: Keypair = Keypair::from(secret); + assert_eq!(expected_public, keypair.public_key()); // The signatures in the test vectors also include the message // at the end, but we just want R and S. @@ -97,8 +98,10 @@ mod vectors { let sig_bytes: Vec = FromHex::from_hex(signature).unwrap(); let secret: SecretKey = SecretKey::from_bytes(&sec_bytes[..SECRET_KEY_LENGTH]).unwrap(); - let public: PublicKey = PublicKey::from_bytes(&pub_bytes[..PUBLIC_KEY_LENGTH]).unwrap(); - let keypair: Keypair = Keypair{ secret: secret, public: public }; + let expected_public: PublicKey = + PublicKey::from_bytes(&pub_bytes[..PUBLIC_KEY_LENGTH]).unwrap(); + let keypair: Keypair = Keypair::from(secret); + assert_eq!(expected_public, keypair.public_key()); let sig1: Signature = Signature::from_bytes(&sig_bytes[..]).unwrap(); let mut prehash_for_signing: Sha512 = Sha512::default(); @@ -280,17 +283,6 @@ mod integrations { assert!(result.is_ok()); } - - #[test] - fn pubkey_from_secret_and_expanded_secret() { - let mut csprng = OsRng{}; - let secret: SecretKey = SecretKey::generate(&mut csprng); - let expanded_secret: ExpandedSecretKey = (&secret).into(); - let public_from_secret: PublicKey = (&secret).into(); // XXX eww - let public_from_expanded_secret: PublicKey = (&expanded_secret).into(); // XXX eww - - assert!(public_from_secret == public_from_expanded_secret); - } } #[serde(crate = "serde_crate")] @@ -401,28 +393,6 @@ mod serialisation { } } - #[test] - fn serialize_deserialize_expanded_secret_key_bincode() { - let expanded_secret_key = ExpandedSecretKey::from(&SecretKey::from_bytes(&SECRET_KEY_BYTES).unwrap()); - let encoded_expanded_secret_key: Vec = bincode::serialize(&expanded_secret_key).unwrap(); - let decoded_expanded_secret_key: ExpandedSecretKey = bincode::deserialize(&encoded_expanded_secret_key).unwrap(); - - for i in 0..EXPANDED_SECRET_KEY_LENGTH { - assert_eq!(expanded_secret_key.to_bytes()[i], decoded_expanded_secret_key.to_bytes()[i]); - } - } - - #[test] - fn serialize_deserialize_expanded_secret_key_json() { - let expanded_secret_key = ExpandedSecretKey::from(&SecretKey::from_bytes(&SECRET_KEY_BYTES).unwrap()); - let encoded_expanded_secret_key = serde_json::to_string(&expanded_secret_key).unwrap(); - let decoded_expanded_secret_key: ExpandedSecretKey = serde_json::from_str(&encoded_expanded_secret_key).unwrap(); - - for i in 0..EXPANDED_SECRET_KEY_LENGTH { - assert_eq!(expanded_secret_key.to_bytes()[i], decoded_expanded_secret_key.to_bytes()[i]); - } - } - #[test] fn serialize_deserialize_keypair_bincode() { let keypair = Keypair::from_bytes(&KEYPAIR_BYTES).unwrap(); @@ -471,13 +441,10 @@ mod serialisation { #[test] fn serialize_secret_key_size() { let secret_key: SecretKey = SecretKey::from_bytes(&SECRET_KEY_BYTES).unwrap(); - assert_eq!(bincode::serialized_size(&secret_key).unwrap() as usize, BINCODE_INT_LENGTH + SECRET_KEY_LENGTH); - } - - #[test] - fn serialize_expanded_secret_key_size() { - let expanded_secret_key = ExpandedSecretKey::from(&SecretKey::from_bytes(&SECRET_KEY_BYTES).unwrap()); - assert_eq!(bincode::serialized_size(&expanded_secret_key).unwrap() as usize, BINCODE_INT_LENGTH + EXPANDED_SECRET_KEY_LENGTH); + assert_eq!( + bincode::serialized_size(&secret_key).unwrap() as usize, + BINCODE_INT_LENGTH + SECRET_KEY_LENGTH + ); } #[test] From 8319adbff4ba8d84a6061c81721a5fcf0af59ddf Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Sun, 16 Oct 2022 18:51:26 -0400 Subject: [PATCH 293/351] Bumped MSRV to 1.56.1 and added some documentation about semver (#218) Also fixed benchmark build --- .github/workflows/rust.yml | 6 +++--- CHANGELOG.md | 11 +++++++++++ Cargo.toml | 4 ++-- README.md | 13 +++++++++---- benches/ed25519_benchmarks.rs | 20 +++++++++----------- 5 files changed, 34 insertions(+), 20 deletions(-) create mode 100644 CHANGELOG.md diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 79571cc..48a6043 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -101,14 +101,14 @@ jobs: args: --features "batch_deterministic" msrv: - name: Current MSRV is 1.41 + name: Current MSRV is 1.56.1 runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.41 + toolchain: 1.56.1 override: true - uses: actions-rs/cargo@v1 with: @@ -128,4 +128,4 @@ jobs: with: command: bench # This filter selects no benchmarks, so we don't run any, only build them. - args: --features "batch" "DONTRUNBENCHMARKS" + args: --features "batch" "nonexistentbenchmark" diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..dd49936 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased + +### Changes +* Bumped MSRV from 1.41 to 1.56.1 +* Removed `ExpandedSecretKey` API ((#205)[https://github.com/dalek-cryptography/ed25519-dalek/pull/205]) diff --git a/Cargo.toml b/Cargo.toml index d01172d..771ac50 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ed25519-dalek" version = "1.0.1" -edition = "2018" +edition = "2021" authors = ["isis lovecruft "] readme = "README.md" license = "BSD-3-Clause" @@ -30,7 +30,7 @@ rand_core = { version = "0.5", default-features = false, optional = true } serde_crate = { package = "serde", version = "1.0", default-features = false, optional = true } serde_bytes = { version = "0.11", optional = true } sha2 = { version = "0.9", default-features = false } -zeroize = { version = "~1.3", default-features = false } +zeroize = { version = "1", default-features = false } [dev-dependencies] hex = "^0.4" diff --git a/README.md b/README.md index fabd832..5cde9f8 100644 --- a/README.md +++ b/README.md @@ -11,10 +11,15 @@ Documentation is available [here](https://docs.rs/ed25519-dalek). To install, add the following to your project's `Cargo.toml`: -```toml -[dependencies.ed25519-dalek] -version = "1" -``` +# Minimum Supported Rust Version + +This crate requires Rust 1.56.1 at a minimum. 1.x releases of this crate supported an MSRV of 1.41. + +In the future, MSRV changes will be accompanied by a minor version bump. + +# Changelog + +See [CHANGELOG.md](CHANGELOG.md) for a list of changes made in past version of this crate. # Benchmarks diff --git a/benches/ed25519_benchmarks.rs b/benches/ed25519_benchmarks.rs index 125e718..043a198 100644 --- a/benches/ed25519_benchmarks.rs +++ b/benches/ed25519_benchmarks.rs @@ -21,9 +21,8 @@ mod ed25519_benches { use ed25519_dalek::PublicKey; use ed25519_dalek::Signature; use ed25519_dalek::Signer; - use ed25519_dalek::verify_batch; - use rand::thread_rng; use rand::prelude::ThreadRng; + use rand::thread_rng; fn sign(c: &mut Criterion) { let mut csprng: ThreadRng = thread_rng(); @@ -38,9 +37,9 @@ mod ed25519_benches { let keypair: Keypair = Keypair::generate(&mut csprng); let msg: &[u8] = b""; let sig: Signature = keypair.sign(msg); - + c.bench_function("Ed25519 signature verification", move |b| { - b.iter(| | keypair.verify(msg, &sig)) + b.iter(|| keypair.verify(msg, &sig)) }); } @@ -51,7 +50,7 @@ mod ed25519_benches { let sig: Signature = keypair.sign(msg); c.bench_function("Ed25519 strict signature verification", move |b| { - b.iter(| | keypair.verify_strict(msg, &sig)) + b.iter(|| keypair.verify_strict(msg, &sig)) }); } @@ -62,7 +61,8 @@ mod ed25519_benches { "Ed25519 batch signature verification", |b, &&size| { let mut csprng: ThreadRng = thread_rng(); - let keypairs: Vec = (0..size).map(|_| Keypair::generate(&mut csprng)).collect(); + let keypairs: Vec = + (0..size).map(|_| Keypair::generate(&mut csprng)).collect(); let msg: &[u8] = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; let messages: Vec<&[u8]> = (0..size).map(|_| msg).collect(); let signatures: Vec = @@ -80,11 +80,11 @@ mod ed25519_benches { let mut csprng: ThreadRng = thread_rng(); c.bench_function("Ed25519 keypair generation", move |b| { - b.iter(| | Keypair::generate(&mut csprng)) + b.iter(|| Keypair::generate(&mut csprng)) }); } - criterion_group!{ + criterion_group! { name = ed25519_benches; config = Criterion::default(); targets = @@ -96,6 +96,4 @@ mod ed25519_benches { } } -criterion_main!( - ed25519_benches::ed25519_benches, -); +criterion_main!(ed25519_benches::ed25519_benches); From 7529d65506147b6cb24ca6d8f4fc062cac33b395 Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Sun, 16 Oct 2022 19:38:36 -0400 Subject: [PATCH 294/351] Fixed installation section in README; accidentally deleted this earlier --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 5cde9f8..29af18e 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,11 @@ Documentation is available [here](https://docs.rs/ed25519-dalek). To install, add the following to your project's `Cargo.toml`: +```toml +[dependencies.ed25519-dalek] +version = "1" +``` + # Minimum Supported Rust Version This crate requires Rust 1.56.1 at a minimum. 1.x releases of this crate supported an MSRV of 1.41. From f7cbeee7f65059d5f0707ae8221075a024e222b6 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Sun, 20 Nov 2022 13:08:05 -0700 Subject: [PATCH 295/351] Bump `curve25519-dalek` to v4.0.0-pre (via git) (#223) Also bumps these corresponding dependencies which are needed for everything to compile with this update: * `merlin` v3.0 * `rand` v0.8 * `rand_core` v0.6 * `sha2` v0.10 --- .github/workflows/rust.yml | 46 ++++++++++++++------------------------ Cargo.toml | 19 ++++++++-------- src/secret.rs | 26 ++++++++++----------- tests/ed25519.rs | 6 ++--- 4 files changed, 43 insertions(+), 54 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 48a6043..b4085a2 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -4,41 +4,29 @@ on: push: branches: [ '*' ] pull_request: - branches: [ main, develop ] + branches: [ 'main', 'develop', 'release/2.0' ] env: CARGO_TERM_COLOR: always jobs: - test-u32: - name: Test u32 backend + test: runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - uses: actions-rs/cargo@v1 - with: - command: test - args: --no-default-features --features "std u32_backend" + strategy: + matrix: + include: + # 32-bit target + - target: i686-unknown-linux-gnu + deps: sudo apt update && sudo apt install gcc-multilib - test-u64: - name: Test u64 backend - runs-on: ubuntu-latest + # 64-bit target + - target: x86_64-unknown-linux-gnu steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - uses: actions-rs/cargo@v1 - with: - command: test - args: --no-default-features --features "std u64_backend" + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + - run: rustup target add ${{ matrix.target }} + - run: ${{ matrix.deps }} + - run: cargo test --target ${{ matrix.target }} test-simd: name: Test simd backend (nightly) @@ -71,7 +59,7 @@ jobs: args: --features "serde" test-alloc-u32: - name: Test no_std+alloc with u32 backend + name: Test no_std+alloc runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -83,7 +71,7 @@ jobs: - uses: actions-rs/cargo@v1 with: command: test - args: --lib --no-default-features --features "alloc u32_backend" + args: --lib --no-default-features --features "alloc" test-batch-deterministic: name: Test deterministic batch verification diff --git a/Cargo.toml b/Cargo.toml index 771ac50..f9e3cae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,14 +22,14 @@ travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master" features = ["nightly", "batch"] [dependencies] -curve25519-dalek = { version = "3", default-features = false } +curve25519-dalek = { version = "=4.0.0-pre.2", default-features = false } ed25519 = { version = "1", default-features = false } -merlin = { version = "2", default-features = false, optional = true } -rand = { version = "0.7", default-features = false, optional = true } -rand_core = { version = "0.5", default-features = false, optional = true } +merlin = { version = "3", default-features = false, optional = true } +rand = { version = "0.8", default-features = false, optional = true } +rand_core = { version = "0.6", default-features = false, optional = true } serde_crate = { package = "serde", version = "1.0", default-features = false, optional = true } serde_bytes = { version = "0.11", optional = true } -sha2 = { version = "0.9", default-features = false } +sha2 = { version = "0.10", default-features = false } zeroize = { version = "1", default-features = false } [dev-dependencies] @@ -37,7 +37,7 @@ hex = "^0.4" bincode = "1.0" serde_json = "1.0" criterion = "0.3" -rand = "0.7" +rand = "0.8" serde_crate = { package = "serde", version = "1.0", features = ["derive"] } toml = { version = "0.5" } @@ -49,7 +49,7 @@ harness = false # required-features = ["batch"] [features] -default = ["std", "rand", "u64_backend"] +default = ["std", "rand"] std = ["curve25519-dalek/std", "ed25519/std", "serde_crate/std", "sha2/std", "rand/std"] alloc = ["curve25519-dalek/alloc", "rand/alloc", "zeroize/alloc"] nightly = ["curve25519-dalek/nightly"] @@ -60,6 +60,7 @@ batch_deterministic = ["merlin", "rand", "rand_core"] asm = ["sha2/asm"] # This features turns off stricter checking for scalar malleability in signatures legacy_compatibility = [] -u64_backend = ["curve25519-dalek/u64_backend"] -u32_backend = ["curve25519-dalek/u32_backend"] simd_backend = ["curve25519-dalek/simd_backend"] + +[patch.crates-io] +curve25519-dalek = { git = "https://github.com/dalek-cryptography/curve25519-dalek.git", branch = "release/4.0" } diff --git a/src/secret.rs b/src/secret.rs index f8b9da8..3c78b39 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -482,24 +482,24 @@ impl ExpandedSecretKey { // This is a really fucking stupid bandaid, and the damned scheme is // still bleeding from malleability, for fuck's sake. h = Sha512::new() - .chain(b"SigEd25519 no Ed25519 collisions") - .chain(&[1]) // Ed25519ph - .chain(&[ctx_len]) - .chain(ctx) - .chain(&self.nonce) - .chain(&prehash[..]); + .chain_update(b"SigEd25519 no Ed25519 collisions") + .chain_update(&[1]) // Ed25519ph + .chain_update(&[ctx_len]) + .chain_update(ctx) + .chain_update(&self.nonce) + .chain_update(&prehash[..]); r = Scalar::from_hash(h); R = (&r * &constants::ED25519_BASEPOINT_TABLE).compress(); h = Sha512::new() - .chain(b"SigEd25519 no Ed25519 collisions") - .chain(&[1]) // Ed25519ph - .chain(&[ctx_len]) - .chain(ctx) - .chain(R.as_bytes()) - .chain(public_key.as_bytes()) - .chain(&prehash[..]); + .chain_update(b"SigEd25519 no Ed25519 collisions") + .chain_update(&[1]) // Ed25519ph + .chain_update(&[ctx_len]) + .chain_update(ctx) + .chain_update(R.as_bytes()) + .chain_update(public_key.as_bytes()) + .chain_update(&prehash[..]); k = Scalar::from_hash(h); s = &(&k * &self.key) + &r; diff --git a/tests/ed25519.rs b/tests/ed25519.rs index 24740d8..b6a7b84 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -127,9 +127,9 @@ mod vectors { fn compute_hram(message: &[u8], pub_key: &EdwardsPoint, signature_r: &EdwardsPoint) -> Scalar { let k_bytes = Sha512::default() - .chain(&signature_r.compress().as_bytes()) - .chain(&pub_key.compress().as_bytes()[..]) - .chain(&message); + .chain_update(&signature_r.compress().as_bytes()) + .chain_update(&pub_key.compress().as_bytes()[..]) + .chain_update(&message); let mut k_output = [0u8; 64]; k_output.copy_from_slice(k_bytes.finalize().as_slice()); Scalar::from_bytes_mod_order_wide(&k_output) From ae4bd2c81e535a67f025a8ffc9cf0355d22c696c Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Sun, 20 Nov 2022 20:28:09 -0700 Subject: [PATCH 296/351] Fix warnings and add `-D warnings` check in CI (#226) --- .github/workflows/rust.yml | 1 + Cargo.toml | 2 +- benches/ed25519_benchmarks.rs | 2 + src/errors.rs | 2 + src/secret.rs | 103 ---------------------------------- tests/ed25519.rs | 6 +- 6 files changed, 8 insertions(+), 108 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b4085a2..131a615 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -8,6 +8,7 @@ on: env: CARGO_TERM_COLOR: always + RUSTFLAGS: '-D warnings' jobs: test: diff --git a/Cargo.toml b/Cargo.toml index f9e3cae..67f68b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,7 +54,7 @@ std = ["curve25519-dalek/std", "ed25519/std", "serde_crate/std", "sha2/std", "ra alloc = ["curve25519-dalek/alloc", "rand/alloc", "zeroize/alloc"] nightly = ["curve25519-dalek/nightly"] serde = ["serde_crate", "serde_bytes", "ed25519/serde"] -batch = ["merlin", "rand"] +batch = ["merlin", "rand/std"] # This feature enables deterministic batch verification. batch_deterministic = ["merlin", "rand", "rand_core"] asm = ["sha2/asm"] diff --git a/benches/ed25519_benchmarks.rs b/benches/ed25519_benchmarks.rs index 043a198..a13e0d2 100644 --- a/benches/ed25519_benchmarks.rs +++ b/benches/ed25519_benchmarks.rs @@ -57,6 +57,8 @@ mod ed25519_benches { fn verify_batch_signatures(c: &mut Criterion) { static BATCH_SIZES: [usize; 8] = [4, 8, 16, 32, 64, 96, 128, 256]; + // TODO: use BenchmarkGroups instead. + #[allow(deprecated)] c.bench_function_over_inputs( "Ed25519 batch signature verification", |b, &&size| { diff --git a/src/errors.rs b/src/errors.rs index d4e8201..e471456 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -38,6 +38,7 @@ pub(crate) enum InternalError { VerifyError, /// Two arrays did not match in size, making the called signature /// verification method impossible. + #[cfg(any(feature = "batch", feature = "batch_deterministic"))] ArrayLengthError{ name_a: &'static str, length_a: usize, name_b: &'static str, length_b: usize, name_c: &'static str, length_c: usize, }, @@ -58,6 +59,7 @@ impl Display for InternalError { => write!(f, "{} must be {} bytes in length", n, l), InternalError::VerifyError => write!(f, "Verification equation was not satisfied"), + #[cfg(any(feature = "batch", feature = "batch_deterministic"))] InternalError::ArrayLengthError{ name_a: na, length_a: la, name_b: nb, length_b: lb, name_c: nc, length_c: lc, } diff --git a/src/secret.rs b/src/secret.rs index 3c78b39..4296112 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -292,109 +292,6 @@ impl<'a> From<&'a SecretKey> for ExpandedSecretKey { } impl ExpandedSecretKey { - /// Convert this `ExpandedSecretKey` into an array of 64 bytes. - /// - /// # Returns - /// - /// An array of 64 bytes. The first 32 bytes represent the "expanded" - /// secret key, and the last 32 bytes represent the "domain-separation" - /// "nonce". - /// - /// # Examples - /// - /// ```ignore - /// # extern crate rand; - /// # extern crate sha2; - /// # extern crate ed25519_dalek; - /// # - /// # #[cfg(feature = "std")] - /// # fn main() { - /// # - /// use rand::rngs::OsRng; - /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; - /// - /// let mut csprng = OsRng{}; - /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); - /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key); - /// let expanded_secret_key_bytes: [u8; 64] = expanded_secret_key.to_bytes(); - /// - /// assert!(&expanded_secret_key_bytes[..] != &[0u8; 64][..]); - /// # } - /// # - /// # #[cfg(not(feature = "std"))] - /// # fn main() { } - /// ``` - #[inline] - pub fn to_bytes(&self) -> [u8; EXPANDED_SECRET_KEY_LENGTH] { - let mut bytes: [u8; 64] = [0u8; 64]; - - bytes[..32].copy_from_slice(self.key.as_bytes()); - bytes[32..].copy_from_slice(&self.nonce[..]); - bytes - } - - /// Construct an `ExpandedSecretKey` from a slice of bytes. - /// - /// # Returns - /// - /// A `Result` whose okay value is an EdDSA `ExpandedSecretKey` or whose - /// error value is an `SignatureError` describing the error that occurred. - /// - /// # Examples - /// - /// ```ignore - /// # extern crate rand; - /// # extern crate sha2; - /// # extern crate ed25519_dalek; - /// # - /// # use ed25519_dalek::{ExpandedSecretKey, SignatureError}; - /// # - /// # #[cfg(feature = "std")] - /// # fn do_test() -> Result { - /// # - /// use rand::rngs::OsRng; - /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; - /// use ed25519_dalek::SignatureError; - /// - /// let mut csprng = OsRng{}; - /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); - /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key); - /// let bytes: [u8; 64] = expanded_secret_key.to_bytes(); - /// let expanded_secret_key_again = ExpandedSecretKey::from_bytes(&bytes)?; - /// # - /// # Ok(expanded_secret_key_again) - /// # } - /// # - /// # #[cfg(feature = "std")] - /// # fn main() { - /// # let result = do_test(); - /// # assert!(result.is_ok()); - /// # } - /// # - /// # #[cfg(not(feature = "std"))] - /// # fn main() { } - /// ``` - #[inline] - pub(crate) fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != EXPANDED_SECRET_KEY_LENGTH { - return Err(InternalError::BytesLengthError { - name: "ExpandedSecretKey", - length: EXPANDED_SECRET_KEY_LENGTH, - } - .into()); - } - let mut lower: [u8; 32] = [0u8; 32]; - let mut upper: [u8; 32] = [0u8; 32]; - - lower.copy_from_slice(&bytes[00..32]); - upper.copy_from_slice(&bytes[32..64]); - - Ok(ExpandedSecretKey { - key: Scalar::from_bits(lower), - nonce: upper, - }) - } - /// Sign a message with this `ExpandedSecretKey`. #[allow(non_snake_case)] pub(crate) fn sign(&self, message: &[u8], public_key: &PublicKey) -> ed25519::Signature { diff --git a/tests/ed25519.rs b/tests/ed25519.rs index b6a7b84..4bb7c24 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -277,7 +277,7 @@ mod integrations { signatures.push(keypair.sign(&messages[i])); keypairs.push(keypair); } - let public_keys: Vec = keypairs.iter().map(|key| key.public).collect(); + let public_keys: Vec = keypairs.iter().map(|key| key.public_key()).collect(); let result = verify_batch(&messages, &signatures[..], &public_keys[..]); @@ -285,9 +285,9 @@ mod integrations { } } -#[serde(crate = "serde_crate")] #[cfg(all(test, feature = "serde"))] #[derive(Debug, serde_crate::Serialize, serde_crate::Deserialize)] +#[serde(crate = "serde_crate")] struct Demo { keypair: Keypair } @@ -296,8 +296,6 @@ struct Demo { mod serialisation { use super::*; - use ed25519::signature::Signature as _; - // The size for bincode to serialize the length of a byte array. static BINCODE_INT_LENGTH: usize = 8; From d4cffc7d0588329dd7140d7b9ad0e147204e7cc3 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Mon, 21 Nov 2022 15:21:05 -0700 Subject: [PATCH 297/351] `ed25519` v2.0.0-pre.0 (#222) Bumps the `ed25519` crate to the v2.0.0-pre.0 prerelease. This version notably uses the `signature` crate's v2 API: https://github.com/RustCrypto/traits/pull/1141 --- Cargo.toml | 2 +- src/signature.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 67f68b5..f2a3173 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ features = ["nightly", "batch"] [dependencies] curve25519-dalek = { version = "=4.0.0-pre.2", default-features = false } -ed25519 = { version = "1", default-features = false } +ed25519 = { version = "=2.0.0-pre.0", default-features = false } merlin = { version = "3", default-features = false, optional = true } rand = { version = "0.8", default-features = false, optional = true } rand_core = { version = "0.6", default-features = false, optional = true } diff --git a/src/signature.rs b/src/signature.rs index 880a78b..314e288 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -14,7 +14,6 @@ use core::fmt::Debug; use curve25519_dalek::edwards::CompressedEdwardsY; use curve25519_dalek::scalar::Scalar; -use ed25519::signature::Signature as _; use crate::constants::*; use crate::errors::*; @@ -194,7 +193,7 @@ impl TryFrom<&ed25519::Signature> for InternalSignature { type Error = SignatureError; fn try_from(sig: &ed25519::Signature) -> Result { - InternalSignature::from_bytes(sig.as_bytes()) + InternalSignature::from_bytes(sig.as_ref()) } } From a03c7a3f0fa37db585a025cfc1a19c23ab6ce92c Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Mon, 21 Nov 2022 15:23:05 -0700 Subject: [PATCH 298/351] Tune up CI configuration (#227) - Consolidate `test` jobs: this allows reusing intermediate artifacts between tests which should improve build times, and also make it easier to test additional features in the future - Switch to `dtolnay/rust-toolchain` for setting up toolchain - Bump checkout to `actions/checkout@3` - Switch to `run` directives for invoking Cargo: it's more straightforward to just call Cargo than use a DSL from an unmaintained action, and eliminates the 3rd party dependency --- .github/workflows/rust.yml | 95 ++++++++------------------------------ Cargo.toml | 4 +- 2 files changed, 20 insertions(+), 79 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 131a615..34b1f3d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -19,7 +19,6 @@ jobs: # 32-bit target - target: i686-unknown-linux-gnu deps: sudo apt update && sudo apt install gcc-multilib - # 64-bit target - target: x86_64-unknown-linux-gnu steps: @@ -27,94 +26,38 @@ jobs: - uses: dtolnay/rust-toolchain@stable - run: rustup target add ${{ matrix.target }} - run: ${{ matrix.deps }} + - run: cargo test --target ${{ matrix.target }} --no-default-features --features alloc - run: cargo test --target ${{ matrix.target }} + - run: cargo test --target ${{ matrix.target }} --features batch + - run: cargo test --target ${{ matrix.target }} --features batch_deterministic + - run: cargo test --target ${{ matrix.target }} --features serde test-simd: name: Test simd backend (nightly) runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: nightly - override: true - - uses: actions-rs/cargo@v1 - with: - command: test - args: --no-default-features --features "std nightly simd_backend" - - test-defaults-serde: - name: Test default feature selection and serde - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - uses: actions-rs/cargo@v1 - with: - command: test - args: --features "serde" - - test-alloc-u32: - name: Test no_std+alloc - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - uses: actions-rs/cargo@v1 - with: - command: test - args: --lib --no-default-features --features "alloc" - - test-batch-deterministic: - name: Test deterministic batch verification - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - uses: actions-rs/cargo@v1 - with: - command: test - args: --features "batch_deterministic" + - uses: actions/checkout@v2 + - uses: dtolnay/rust-toolchain@nightly + - run: cargo test --features simd_backend msrv: name: Current MSRV is 1.56.1 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: 1.56.1 - override: true - - uses: actions-rs/cargo@v1 - with: - command: build + - uses: actions/checkout@v3 + # First run `cargo +nightly -Z minimal-verisons check` in order to get a + # Cargo.lock with the oldest possible deps + - uses: dtolnay/rust-toolchain@nightly + - run: cargo -Z minimal-versions check --no-default-features --features serde + # Now check that `cargo build` works with respect to the oldest possible + # deps and the stated MSRV + - uses: dtolnay/rust-toolchain@1.56.1 + - run: cargo build bench: name: Check that benchmarks compile runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - uses: actions-rs/cargo@v1 - with: - command: bench - # This filter selects no benchmarks, so we don't run any, only build them. - args: --features "batch" "nonexistentbenchmark" + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + - run: cargo build --benches --features batch diff --git a/Cargo.toml b/Cargo.toml index f2a3173..9e6b43a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,9 +44,7 @@ toml = { version = "0.5" } [[bench]] name = "ed25519_benchmarks" harness = false -# This doesn't seem to work with criterion, cf. https://github.com/bheisler/criterion.rs/issues/344 -# For now, we have to bench by doing `cargo bench --features="batch"`. -# required-features = ["batch"] +required-features = ["batch"] [features] default = ["std", "rand"] From 44512a3e9c5f205b2bd5bd5801898384fcf28684 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Wed, 7 Dec 2022 01:07:55 -0700 Subject: [PATCH 299/351] CI: only build `simd_backend`; don't run tests (#232) GitHub Actions runners are not guaranteed to have the necessary CPU features in order for these tests to work. Uses a `--target x86_64-unknown-linux-gnu` directive when compiling so the `target_feature` flags don't apply to build scripts. --- .github/workflows/rust.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 34b1f3d..4dfa071 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -32,13 +32,18 @@ jobs: - run: cargo test --target ${{ matrix.target }} --features batch_deterministic - run: cargo test --target ${{ matrix.target }} --features serde - test-simd: + build-simd: name: Test simd backend (nightly) runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: dtolnay/rust-toolchain@nightly - - run: cargo test --features simd_backend + - env: + RUSTFLAGS: "-C target_feature=+avx2" + run: cargo build --target x86_64-unknown-linux-gnu --features simd_backend + - env: + RUSTFLAGS: "-C target_feature=+avx512ifma" + run: cargo build --target x86_64-unknown-linux-gnu --features simd_backend msrv: name: Current MSRV is 1.56.1 From 01ad6305f2088167e675b4626ff96e4ce6ecc968 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Thu, 8 Dec 2022 00:39:48 -0700 Subject: [PATCH 300/351] Edition fixups: remove `extern crate`, add idioms lint (#231) Rust editions 2018+ do not require `extern crate` except for linking `alloc` and `std`. --- Cargo.toml | 6 +- benches/ed25519_benchmarks.rs | 7 +- src/batch.rs | 57 +++++----- src/constants.rs | 3 +- src/errors.rs | 51 +++++---- src/keypair.rs | 12 --- src/lib.rs | 51 ++------- src/public.rs | 22 ++-- src/secret.rs | 37 +++---- src/signature.rs | 7 +- tests/ed25519.rs | 194 ++++++++++++++++++++-------------- 11 files changed, 217 insertions(+), 230 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9e6b43a..e95a714 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,13 +48,13 @@ required-features = ["batch"] [features] default = ["std", "rand"] -std = ["curve25519-dalek/std", "ed25519/std", "serde_crate/std", "sha2/std", "rand/std"] +std = ["alloc", "curve25519-dalek/std", "ed25519/std", "serde_crate/std", "sha2/std", "rand/std"] alloc = ["curve25519-dalek/alloc", "rand/alloc", "zeroize/alloc"] nightly = ["curve25519-dalek/nightly"] serde = ["serde_crate", "serde_bytes", "ed25519/serde"] -batch = ["merlin", "rand/std"] +batch = ["alloc", "merlin", "rand/std"] # This feature enables deterministic batch verification. -batch_deterministic = ["merlin", "rand", "rand_core"] +batch_deterministic = ["alloc", "merlin", "rand", "rand_core"] asm = ["sha2/asm"] # This features turns off stricter checking for scalar malleability in signatures legacy_compatibility = [] diff --git a/benches/ed25519_benchmarks.rs b/benches/ed25519_benchmarks.rs index a13e0d2..98afd16 100644 --- a/benches/ed25519_benchmarks.rs +++ b/benches/ed25519_benchmarks.rs @@ -7,12 +7,7 @@ // Authors: // - isis agora lovecruft -#[macro_use] -extern crate criterion; -extern crate ed25519_dalek; -extern crate rand; - -use criterion::Criterion; +use criterion::{criterion_group, criterion_main, Criterion}; mod ed25519_benches { use super::*; diff --git a/src/batch.rs b/src/batch.rs index cb28188..63bb895 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -9,12 +9,7 @@ //! Batch signature verification. -#[cfg(feature = "alloc")] -extern crate alloc; -#[cfg(feature = "alloc")] use alloc::vec::Vec; -#[cfg(all(not(feature = "alloc"), feature = "std"))] -use std::vec::Vec; use core::convert::TryFrom; use core::iter::once; @@ -29,9 +24,9 @@ pub use curve25519_dalek::digest::Digest; use merlin::Transcript; -use rand::Rng; #[cfg(all(feature = "batch", not(feature = "batch_deterministic")))] use rand::thread_rng; +use rand::Rng; #[cfg(all(not(feature = "batch"), feature = "batch_deterministic"))] use rand_core; @@ -101,7 +96,7 @@ impl rand_core::RngCore for ZeroRng { /// STROBE state based on external randomness, we're doing an /// `ENC_{state}(00000000000000000000000000000000)` operation, which is /// identical to the STROBE `MAC` operation. - fn fill_bytes(&mut self, _dest: &mut [u8]) { } + fn fill_bytes(&mut self, _dest: &mut [u8]) {} fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { self.fill_bytes(dest); @@ -193,9 +188,6 @@ fn zero_rng() -> ZeroRng { /// # Examples /// /// ``` -/// extern crate ed25519_dalek; -/// extern crate rand; -/// /// use ed25519_dalek::verify_batch; /// use ed25519_dalek::Keypair; /// use ed25519_dalek::PublicKey; @@ -215,24 +207,26 @@ fn zero_rng() -> ZeroRng { /// assert!(result.is_ok()); /// # } /// ``` -#[cfg(all(any(feature = "batch", feature = "batch_deterministic"), - any(feature = "alloc", feature = "std")))] #[allow(non_snake_case)] pub fn verify_batch( messages: &[&[u8]], signatures: &[ed25519::Signature], public_keys: &[PublicKey], -) -> Result<(), SignatureError> -{ +) -> Result<(), SignatureError> { // Return an Error if any of the vectors were not the same size as the others. - if signatures.len() != messages.len() || - signatures.len() != public_keys.len() || - public_keys.len() != messages.len() { - return Err(InternalError::ArrayLengthError{ - name_a: "signatures", length_a: signatures.len(), - name_b: "messages", length_b: messages.len(), - name_c: "public_keys", length_c: public_keys.len(), - }.into()); + if signatures.len() != messages.len() + || signatures.len() != public_keys.len() + || public_keys.len() != messages.len() + { + return Err(InternalError::ArrayLengthError { + name_a: "signatures", + length_a: signatures.len(), + name_b: "messages", + length_b: messages.len(), + name_c: "public_keys", + length_c: public_keys.len(), + } + .into()); } // Convert all signatures to `InternalSignature` @@ -242,13 +236,15 @@ pub fn verify_batch( .collect::, _>>()?; // Compute H(R || A || M) for each (signature, public_key, message) triplet - let hrams: Vec = (0..signatures.len()).map(|i| { - let mut h: Sha512 = Sha512::default(); - h.update(signatures[i].R.as_bytes()); - h.update(public_keys[i].as_bytes()); - h.update(&messages[i]); - Scalar::from_hash(h) - }).collect(); + let hrams: Vec = (0..signatures.len()) + .map(|i| { + let mut h: Sha512 = Sha512::default(); + h.update(signatures[i].R.as_bytes()); + h.update(public_keys[i].as_bytes()); + h.update(&messages[i]); + Scalar::from_hash(h) + }) + .collect(); // Collect the message lengths and the scalar portions of the signatures, // and add them into the transcript. @@ -295,7 +291,8 @@ pub fn verify_batch( let id = EdwardsPoint::optional_multiscalar_mul( once(-B_coefficient).chain(zs.iter().cloned()).chain(zhrams), B.chain(Rs).chain(As), - ).ok_or(InternalError::VerifyError)?; + ) + .ok_or(InternalError::VerifyError)?; if id.is_identity() { Ok(()) diff --git a/src/constants.rs b/src/constants.rs index f8ccb84..4dc48a0 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -28,4 +28,5 @@ const EXPANDED_SECRET_KEY_KEY_LENGTH: usize = 32; const EXPANDED_SECRET_KEY_NONCE_LENGTH: usize = 32; /// The length of an "expanded" ed25519 key, `ExpandedSecretKey`, in bytes. -pub const EXPANDED_SECRET_KEY_LENGTH: usize = EXPANDED_SECRET_KEY_KEY_LENGTH + EXPANDED_SECRET_KEY_NONCE_LENGTH; +pub const EXPANDED_SECRET_KEY_LENGTH: usize = + EXPANDED_SECRET_KEY_KEY_LENGTH + EXPANDED_SECRET_KEY_NONCE_LENGTH; diff --git a/src/errors.rs b/src/errors.rs index e471456..85b1f64 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -39,9 +39,14 @@ pub(crate) enum InternalError { /// Two arrays did not match in size, making the called signature /// verification method impossible. #[cfg(any(feature = "batch", feature = "batch_deterministic"))] - ArrayLengthError{ name_a: &'static str, length_a: usize, - name_b: &'static str, length_b: usize, - name_c: &'static str, length_c: usize, }, + ArrayLengthError { + name_a: &'static str, + length_a: usize, + name_b: &'static str, + length_b: usize, + name_c: &'static str, + length_c: usize, + }, /// An ed25519ph signature can only take up to 255 octets of context. PrehashedContextLengthError, /// A mismatched (public, secret) key pair. @@ -51,29 +56,37 @@ pub(crate) enum InternalError { impl Display for InternalError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - InternalError::PointDecompressionError - => write!(f, "Cannot decompress Edwards point"), - InternalError::ScalarFormatError - => write!(f, "Cannot use scalar with high-bit set"), - InternalError::BytesLengthError{ name: n, length: l} - => write!(f, "{} must be {} bytes in length", n, l), - InternalError::VerifyError - => write!(f, "Verification equation was not satisfied"), + InternalError::PointDecompressionError => write!(f, "Cannot decompress Edwards point"), + InternalError::ScalarFormatError => write!(f, "Cannot use scalar with high-bit set"), + InternalError::BytesLengthError { name: n, length: l } => { + write!(f, "{} must be {} bytes in length", n, l) + } + InternalError::VerifyError => write!(f, "Verification equation was not satisfied"), #[cfg(any(feature = "batch", feature = "batch_deterministic"))] - InternalError::ArrayLengthError{ name_a: na, length_a: la, - name_b: nb, length_b: lb, - name_c: nc, length_c: lc, } - => write!(f, "Arrays must be the same length: {} has length {}, - {} has length {}, {} has length {}.", na, la, nb, lb, nc, lc), - InternalError::PrehashedContextLengthError - => write!(f, "An ed25519ph signature can only take up to 255 octets of context"), + InternalError::ArrayLengthError { + name_a: na, + length_a: la, + name_b: nb, + length_b: lb, + name_c: nc, + length_c: lc, + } => write!( + f, + "Arrays must be the same length: {} has length {}, + {} has length {}, {} has length {}.", + na, la, nb, lb, nc, lc + ), + InternalError::PrehashedContextLengthError => write!( + f, + "An ed25519ph signature can only take up to 255 octets of context" + ), InternalError::MismatchedKeypairError => write!(f, "Mismatched Keypair detected"), } } } #[cfg(feature = "std")] -impl Error for InternalError { } +impl Error for InternalError {} /// Errors which may occur while processing signatures and keypairs. /// diff --git a/src/keypair.rs b/src/keypair.rs index bcbb6e2..592486c 100644 --- a/src/keypair.rs +++ b/src/keypair.rs @@ -114,9 +114,6 @@ impl Keypair { /// # Example /// /// ``` - /// extern crate rand; - /// extern crate ed25519_dalek; - /// /// # #[cfg(feature = "std")] /// # fn main() { /// @@ -175,9 +172,6 @@ impl Keypair { /// # Examples /// /// ``` - /// extern crate ed25519_dalek; - /// extern crate rand; - /// /// use ed25519_dalek::Digest; /// use ed25519_dalek::Keypair; /// use ed25519_dalek::Sha512; @@ -222,9 +216,6 @@ impl Keypair { /// your own!): /// /// ``` - /// # extern crate ed25519_dalek; - /// # extern crate rand; - /// # /// # use ed25519_dalek::Digest; /// # use ed25519_dalek::Keypair; /// # use ed25519_dalek::Signature; @@ -300,9 +291,6 @@ impl Keypair { /// # Examples /// /// ``` - /// extern crate ed25519_dalek; - /// extern crate rand; - /// /// use ed25519_dalek::Digest; /// use ed25519_dalek::Keypair; /// use ed25519_dalek::Signature; diff --git a/src/lib.rs b/src/lib.rs index 6e8933b..ee3a8dd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,9 +19,6 @@ //! the operating system's builtin PRNG: //! //! ``` -//! extern crate rand; -//! extern crate ed25519_dalek; -//! //! # #[cfg(feature = "std")] //! # fn main() { //! use rand::rngs::OsRng; @@ -39,8 +36,6 @@ //! We can now use this `keypair` to sign a message: //! //! ``` -//! # extern crate rand; -//! # extern crate ed25519_dalek; //! # fn main() { //! # use rand::rngs::OsRng; //! # use ed25519_dalek::Keypair; @@ -56,8 +51,6 @@ //! that `message`: //! //! ``` -//! # extern crate rand; -//! # extern crate ed25519_dalek; //! # fn main() { //! # use rand::rngs::OsRng; //! # use ed25519_dalek::{Keypair, Signature, Signer}; @@ -74,8 +67,6 @@ //! verify this signature: //! //! ``` -//! # extern crate rand; -//! # extern crate ed25519_dalek; //! # fn main() { //! # use rand::rngs::OsRng; //! # use ed25519_dalek::Keypair; @@ -101,8 +92,6 @@ //! verify your signatures!) //! //! ``` -//! # extern crate rand; -//! # extern crate ed25519_dalek; //! # fn main() { //! # use rand::rngs::OsRng; //! # use ed25519_dalek::{Keypair, Signature, Signer, PublicKey}; @@ -122,8 +111,6 @@ //! And similarly, decoded from bytes with `::from_bytes()`: //! //! ``` -//! # extern crate rand; -//! # extern crate ed25519_dalek; //! # use std::convert::TryFrom; //! # use rand::rngs::OsRng; //! # use std::convert::TryInto; @@ -165,13 +152,6 @@ //! For example, using [bincode](https://github.com/TyOverby/bincode): //! //! ``` -//! # extern crate rand; -//! # extern crate ed25519_dalek; -//! # #[cfg(feature = "serde")] -//! # extern crate serde_crate as serde; -//! # #[cfg(feature = "serde")] -//! # extern crate bincode; -//! //! # #[cfg(feature = "serde")] //! # fn main() { //! # use rand::rngs::OsRng; @@ -195,13 +175,6 @@ //! recipient may deserialise them and verify: //! //! ``` -//! # extern crate rand; -//! # extern crate ed25519_dalek; -//! # #[cfg(feature = "serde")] -//! # extern crate serde_crate as serde; -//! # #[cfg(feature = "serde")] -//! # extern crate bincode; -//! # //! # #[cfg(feature = "serde")] //! # fn main() { //! # use rand::rngs::OsRng; @@ -232,40 +205,34 @@ //! ``` #![no_std] -#![warn(future_incompatible)] +#![warn(future_incompatible, rust_2018_idioms)] #![deny(missing_docs)] // refuse to compile if documentation is missing #![cfg_attr(not(test), forbid(unsafe_code))] +#[cfg(any(feature = "batch", feature = "batch_deterministic"))] +extern crate alloc; + #[cfg(any(feature = "std", test))] #[macro_use] extern crate std; -pub extern crate ed25519; - -#[cfg(all(feature = "alloc", not(feature = "std")))] -extern crate alloc; -extern crate curve25519_dalek; -#[cfg(all(any(feature = "batch", feature = "batch_deterministic"), any(feature = "std", feature = "alloc")))] -extern crate merlin; -#[cfg(any(feature = "batch", feature = "std", feature = "alloc", test))] -extern crate rand; #[cfg(feature = "serde")] extern crate serde_crate as serde; -extern crate sha2; -extern crate zeroize; -#[cfg(all(any(feature = "batch", feature = "batch_deterministic"), any(feature = "std", feature = "alloc")))] +pub use ed25519; + +#[cfg(any(feature = "batch", feature = "batch_deterministic"))] mod batch; mod constants; -mod keypair; mod errors; +mod keypair; mod public; mod secret; mod signature; pub use curve25519_dalek::digest::Digest; -#[cfg(all(any(feature = "batch", feature = "batch_deterministic"), any(feature = "std", feature = "alloc")))] +#[cfg(any(feature = "batch", feature = "batch_deterministic"))] pub use crate::batch::*; pub use crate::constants::*; pub use crate::errors::*; diff --git a/src/public.rs b/src/public.rs index 342adf6..fdbced2 100644 --- a/src/public.rs +++ b/src/public.rs @@ -28,7 +28,7 @@ use serde::de::Error as SerdeError; #[cfg(feature = "serde")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[cfg(feature = "serde")] -use serde_bytes::{Bytes as SerdeBytes, ByteBuf as SerdeByteBuf}; +use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes}; use crate::constants::*; use crate::errors::*; @@ -100,8 +100,6 @@ impl PublicKey { /// # Example /// /// ``` - /// # extern crate ed25519_dalek; - /// # /// use ed25519_dalek::PublicKey; /// use ed25519_dalek::PUBLIC_KEY_LENGTH; /// use ed25519_dalek::SignatureError; @@ -131,7 +129,8 @@ impl PublicKey { return Err(InternalError::BytesLengthError { name: "PublicKey", length: PUBLIC_KEY_LENGTH, - }.into()); + } + .into()); } let mut bits: [u8; 32] = [0u8; 32]; bits.copy_from_slice(&bytes[..32]); @@ -195,7 +194,10 @@ impl PublicKey { let k: Scalar; let ctx: &[u8] = context.unwrap_or(b""); - debug_assert!(ctx.len() <= 255, "The context must not be longer than 255 octets."); + debug_assert!( + ctx.len() <= 255, + "The context must not be longer than 255 octets." + ); let minus_A: EdwardsPoint = -self.1; @@ -284,8 +286,7 @@ impl PublicKey { &self, message: &[u8], signature: &ed25519::Signature, - ) -> Result<(), SignatureError> - { + ) -> Result<(), SignatureError> { let signature = InternalSignature::try_from(signature)?; let mut h: Sha512 = Sha512::new(); @@ -326,12 +327,7 @@ impl Verifier for PublicKey { /// /// Returns `Ok(())` if the signature is valid, and `Err` otherwise. #[allow(non_snake_case)] - fn verify( - &self, - message: &[u8], - signature: &ed25519::Signature - ) -> Result<(), SignatureError> - { + fn verify(&self, message: &[u8], signature: &ed25519::Signature) -> Result<(), SignatureError> { let signature = InternalSignature::try_from(signature)?; let mut h: Sha512 = Sha512::new(); diff --git a/src/secret.rs b/src/secret.rs index 4296112..8f00276 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -27,7 +27,7 @@ use serde::de::Error as SerdeError; #[cfg(feature = "serde")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[cfg(feature = "serde")] -use serde_bytes::{Bytes as SerdeBytes, ByteBuf as SerdeByteBuf}; +use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes}; use zeroize::Zeroize; @@ -78,8 +78,6 @@ impl SecretKey { /// # Example /// /// ``` - /// # extern crate ed25519_dalek; - /// # /// use ed25519_dalek::SecretKey; /// use ed25519_dalek::SECRET_KEY_LENGTH; /// use ed25519_dalek::SignatureError; @@ -112,7 +110,8 @@ impl SecretKey { return Err(InternalError::BytesLengthError { name: "SecretKey", length: SECRET_KEY_LENGTH, - }.into()); + } + .into()); } let mut bits: [u8; 32] = [0u8; 32]; bits.copy_from_slice(&bytes[..32]); @@ -125,9 +124,6 @@ impl SecretKey { /// # Example /// /// ``` - /// extern crate rand; - /// extern crate ed25519_dalek; - /// /// # #[cfg(feature = "std")] /// # fn main() { /// # @@ -147,9 +143,6 @@ impl SecretKey { /// Afterwards, you can generate the corresponding public: /// /// ``` - /// # extern crate rand; - /// # extern crate ed25519_dalek; - /// # /// # fn main() { /// # /// # use rand::rngs::OsRng; @@ -257,10 +250,6 @@ impl<'a> From<&'a SecretKey> for ExpandedSecretKey { /// # Examples /// /// ```ignore - /// # extern crate rand; - /// # extern crate sha2; - /// # extern crate ed25519_dalek; - /// # /// # fn main() { /// # /// use rand::rngs::OsRng; @@ -273,7 +262,7 @@ impl<'a> From<&'a SecretKey> for ExpandedSecretKey { /// ``` fn from(secret_key: &'a SecretKey) -> ExpandedSecretKey { let mut h: Sha512 = Sha512::default(); - let mut hash: [u8; 64] = [0u8; 64]; + let mut hash: [u8; 64] = [0u8; 64]; let mut lower: [u8; 32] = [0u8; 32]; let mut upper: [u8; 32] = [0u8; 32]; @@ -283,11 +272,14 @@ impl<'a> From<&'a SecretKey> for ExpandedSecretKey { lower.copy_from_slice(&hash[00..32]); upper.copy_from_slice(&hash[32..64]); - lower[0] &= 248; - lower[31] &= 63; - lower[31] |= 64; + lower[0] &= 248; + lower[31] &= 63; + lower[31] |= 64; - ExpandedSecretKey{ key: Scalar::from_bits(lower), nonce: upper, } + ExpandedSecretKey { + key: Scalar::from_bits(lower), + nonce: upper, + } } } @@ -358,7 +350,9 @@ impl ExpandedSecretKey { let ctx: &[u8] = context.unwrap_or(b""); // By default, the context is an empty string. if ctx.len() > 255 { - return Err(SignatureError::from(InternalError::PrehashedContextLengthError)); + return Err(SignatureError::from( + InternalError::PrehashedContextLengthError, + )); } let ctx_len: u8 = ctx.len() as u8; @@ -413,7 +407,8 @@ mod test { fn secret_key_zeroize_on_drop() { let secret_ptr: *const u8; - { // scope for the secret to ensure it's been dropped + { + // scope for the secret to ensure it's been dropped let secret = SecretKey::from_bytes(&[0x15u8; 32][..]).unwrap(); secret_ptr = secret.0.as_ptr(); diff --git a/src/signature.rs b/src/signature.rs index 314e288..763d8fc 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -91,7 +91,7 @@ fn check_scalar(bytes: [u8; 32]) -> Result { // // This succeed-fast trick should succeed for roughly half of all scalars. if bytes[31] & 240 == 0 { - return Ok(Scalar::from_bits(bytes)) + return Ok(Scalar::from_bits(bytes)); } match Scalar::from_canonical_bytes(bytes) { @@ -167,7 +167,8 @@ impl InternalSignature { return Err(InternalError::BytesLengthError { name: "Signature", length: SIGNATURE_LENGTH, - }.into()); + } + .into()); } let mut lower: [u8; 32] = [0u8; 32]; let mut upper: [u8; 32] = [0u8; 32]; @@ -178,7 +179,7 @@ impl InternalSignature { let s: Scalar; match check_scalar(upper) { - Ok(x) => s = x, + Ok(x) => s = x, Err(x) => return Err(x), } diff --git a/tests/ed25519.rs b/tests/ed25519.rs index 4bb7c24..6b05a6d 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -9,16 +9,7 @@ //! Integration tests for ed25519-dalek. -#[cfg(all(test, feature = "serde"))] -extern crate bincode; -extern crate ed25519_dalek; -extern crate hex; -extern crate sha2; -extern crate rand; -#[cfg(all(test, feature = "serde"))] -extern crate serde_crate; -#[cfg(all(test, feature = "serde"))] -extern crate toml; +use curve25519_dalek; use ed25519_dalek::*; @@ -32,9 +23,9 @@ mod vectors { use sha2::{digest::Digest, Sha512}; use std::convert::TryFrom; - use std::io::BufReader; - use std::io::BufRead; use std::fs::File; + use std::io::BufRead; + use std::io::BufReader; use super::*; @@ -42,15 +33,18 @@ mod vectors { // package. It is a selection of test cases from // http://ed25519.cr.yp.to/python/sign.input #[test] - fn against_reference_implementation() { // TestGolden + fn against_reference_implementation() { + // TestGolden let mut line: String; let mut lineno: usize = 0; let f = File::open("TESTVECTORS"); if f.is_err() { - println!("This test is only available when the code has been cloned \ - from the git repository, since the TESTVECTORS file is large \ - and is therefore not included within the distributed crate."); + println!( + "This test is only available when the code has been cloned \ + from the git repository, since the TESTVECTORS file is large \ + and is therefore not included within the distributed crate." + ); panic!(); } let file = BufReader::new(f.unwrap()); @@ -73,14 +67,17 @@ mod vectors { let keypair: Keypair = Keypair::from(secret); assert_eq!(expected_public, keypair.public_key()); - // The signatures in the test vectors also include the message - // at the end, but we just want R and S. + // The signatures in the test vectors also include the message + // at the end, but we just want R and S. let sig1: Signature = Signature::from_bytes(&sig_bytes[..64]).unwrap(); let sig2: Signature = keypair.sign(&msg_bytes); assert!(sig1 == sig2, "Signature bytes not equal on line {}", lineno); - assert!(keypair.verify(&msg_bytes, &sig2).is_ok(), - "Signature verification failed on line {}", lineno); + assert!( + keypair.verify(&msg_bytes, &sig2).is_ok(), + "Signature verification failed on line {}", + lineno + ); } } @@ -112,11 +109,19 @@ mod vectors { let sig2: Signature = keypair.sign_prehashed(prehash_for_signing, None).unwrap(); - assert!(sig1 == sig2, - "Original signature from test vectors doesn't equal signature produced:\ - \noriginal:\n{:?}\nproduced:\n{:?}", sig1, sig2); - assert!(keypair.verify_prehashed(prehash_for_verifying, None, &sig2).is_ok(), - "Could not verify ed25519ph signature!"); + assert!( + sig1 == sig2, + "Original signature from test vectors doesn't equal signature produced:\ + \noriginal:\n{:?}\nproduced:\n{:?}", + sig1, + sig2 + ); + assert!( + keypair + .verify_prehashed(prehash_for_verifying, None, &sig2) + .is_ok(), + "Could not verify ed25519ph signature!" + ); } // Taken from curve25519_dalek::constants::EIGHT_TORSION[4] @@ -197,38 +202,45 @@ mod integrations { use rand::rngs::OsRng; #[test] - fn sign_verify() { // TestSignVerify + fn sign_verify() { + // TestSignVerify let keypair: Keypair; let good_sig: Signature; - let bad_sig: Signature; + let bad_sig: Signature; let good: &[u8] = "test message".as_bytes(); - let bad: &[u8] = "wrong message".as_bytes(); + let bad: &[u8] = "wrong message".as_bytes(); - let mut csprng = OsRng{}; + let mut csprng = OsRng {}; - keypair = Keypair::generate(&mut csprng); + keypair = Keypair::generate(&mut csprng); good_sig = keypair.sign(&good); - bad_sig = keypair.sign(&bad); + bad_sig = keypair.sign(&bad); - assert!(keypair.verify(&good, &good_sig).is_ok(), - "Verification of a valid signature failed!"); - assert!(keypair.verify(&good, &bad_sig).is_err(), - "Verification of a signature on a different message passed!"); - assert!(keypair.verify(&bad, &good_sig).is_err(), - "Verification of a signature on a different message passed!"); + assert!( + keypair.verify(&good, &good_sig).is_ok(), + "Verification of a valid signature failed!" + ); + assert!( + keypair.verify(&good, &bad_sig).is_err(), + "Verification of a signature on a different message passed!" + ); + assert!( + keypair.verify(&bad, &good_sig).is_err(), + "Verification of a signature on a different message passed!" + ); } #[test] fn ed25519ph_sign_verify() { let keypair: Keypair; let good_sig: Signature; - let bad_sig: Signature; + let bad_sig: Signature; let good: &[u8] = b"test message"; - let bad: &[u8] = b"wrong message"; + let bad: &[u8] = b"wrong message"; - let mut csprng = OsRng{}; + let mut csprng = OsRng; // ugh… there's no `impl Copy for Sha512`… i hope we can all agree these are the same hashes let mut prehashed_good1: Sha512 = Sha512::default(); @@ -245,16 +257,32 @@ mod integrations { let context: &[u8] = b"testing testing 1 2 3"; - keypair = Keypair::generate(&mut csprng); - good_sig = keypair.sign_prehashed(prehashed_good1, Some(context)).unwrap(); - bad_sig = keypair.sign_prehashed(prehashed_bad1, Some(context)).unwrap(); + keypair = Keypair::generate(&mut csprng); + good_sig = keypair + .sign_prehashed(prehashed_good1, Some(context)) + .unwrap(); + bad_sig = keypair + .sign_prehashed(prehashed_bad1, Some(context)) + .unwrap(); - assert!(keypair.verify_prehashed(prehashed_good2, Some(context), &good_sig).is_ok(), - "Verification of a valid signature failed!"); - assert!(keypair.verify_prehashed(prehashed_good3, Some(context), &bad_sig).is_err(), - "Verification of a signature on a different message passed!"); - assert!(keypair.verify_prehashed(prehashed_bad2, Some(context), &good_sig).is_err(), - "Verification of a signature on a different message passed!"); + assert!( + keypair + .verify_prehashed(prehashed_good2, Some(context), &good_sig) + .is_ok(), + "Verification of a valid signature failed!" + ); + assert!( + keypair + .verify_prehashed(prehashed_good3, Some(context), &bad_sig) + .is_err(), + "Verification of a signature on a different message passed!" + ); + assert!( + keypair + .verify_prehashed(prehashed_bad2, Some(context), &good_sig) + .is_err(), + "Verification of a signature on a different message passed!" + ); } #[cfg(feature = "batch")] @@ -268,7 +296,7 @@ mod integrations { b"Fuck dumbin' it down, spit ice, skip jewellery: Molotov cocktails on me like accessories.", b"Hey, I never cared about your bucks, so if I run up with a mask on, probably got a gas can too.", b"And I'm not here to fill 'er up. Nope, we came to riot, here to incite, we don't want any of your stuff.", ]; - let mut csprng = OsRng{}; + let mut csprng = OsRng; let mut keypairs: Vec = Vec::new(); let mut signatures: Vec = Vec::new(); @@ -289,7 +317,7 @@ mod integrations { #[derive(Debug, serde_crate::Serialize, serde_crate::Deserialize)] #[serde(crate = "serde_crate")] struct Demo { - keypair: Keypair + keypair: Keypair, } #[cfg(all(test, feature = "serde"))] @@ -300,37 +328,29 @@ mod serialisation { static BINCODE_INT_LENGTH: usize = 8; static PUBLIC_KEY_BYTES: [u8; PUBLIC_KEY_LENGTH] = [ - 130, 039, 155, 015, 062, 076, 188, 063, - 124, 122, 026, 251, 233, 253, 225, 220, - 014, 041, 166, 120, 108, 035, 254, 077, - 160, 083, 172, 058, 219, 042, 086, 120, ]; + 130, 039, 155, 015, 062, 076, 188, 063, 124, 122, 026, 251, 233, 253, 225, 220, 014, 041, + 166, 120, 108, 035, 254, 077, 160, 083, 172, 058, 219, 042, 086, 120, + ]; static SECRET_KEY_BYTES: [u8; SECRET_KEY_LENGTH] = [ - 062, 070, 027, 163, 092, 182, 011, 003, - 077, 234, 098, 004, 011, 127, 079, 228, - 243, 187, 150, 073, 201, 137, 076, 022, - 085, 251, 152, 002, 241, 042, 072, 054, ]; + 062, 070, 027, 163, 092, 182, 011, 003, 077, 234, 098, 004, 011, 127, 079, 228, 243, 187, + 150, 073, 201, 137, 076, 022, 085, 251, 152, 002, 241, 042, 072, 054, + ]; /// Signature with the above keypair of a blank message. static SIGNATURE_BYTES: [u8; SIGNATURE_LENGTH] = [ - 010, 126, 151, 143, 157, 064, 047, 001, - 196, 140, 179, 058, 226, 152, 018, 102, - 160, 123, 080, 016, 210, 086, 196, 028, - 053, 231, 012, 157, 169, 019, 158, 063, - 045, 154, 238, 007, 053, 185, 227, 229, - 079, 108, 213, 080, 124, 252, 084, 167, - 216, 085, 134, 144, 129, 149, 041, 081, - 063, 120, 126, 100, 092, 059, 050, 011, ]; + 010, 126, 151, 143, 157, 064, 047, 001, 196, 140, 179, 058, 226, 152, 018, 102, 160, 123, + 080, 016, 210, 086, 196, 028, 053, 231, 012, 157, 169, 019, 158, 063, 045, 154, 238, 007, + 053, 185, 227, 229, 079, 108, 213, 080, 124, 252, 084, 167, 216, 085, 134, 144, 129, 149, + 041, 081, 063, 120, 126, 100, 092, 059, 050, 011, + ]; static KEYPAIR_BYTES: [u8; KEYPAIR_LENGTH] = [ - 239, 085, 017, 235, 167, 103, 034, 062, - 007, 010, 032, 146, 113, 039, 096, 174, - 003, 219, 232, 166, 240, 121, 167, 013, - 098, 238, 122, 116, 193, 114, 215, 213, - 175, 181, 075, 166, 224, 164, 140, 146, - 053, 120, 010, 037, 104, 094, 136, 225, - 249, 102, 171, 160, 097, 132, 015, 071, - 035, 056, 000, 074, 130, 168, 225, 071, ]; + 239, 085, 017, 235, 167, 103, 034, 062, 007, 010, 032, 146, 113, 039, 096, 174, 003, 219, + 232, 166, 240, 121, 167, 013, 098, 238, 122, 116, 193, 114, 215, 213, 175, 181, 075, 166, + 224, 164, 140, 146, 053, 120, 010, 037, 104, 094, 136, 225, 249, 102, 171, 160, 097, 132, + 015, 071, 035, 056, 000, 074, 130, 168, 225, 071, + ]; #[test] fn serialize_deserialize_signature_bincode() { @@ -356,7 +376,10 @@ mod serialisation { let encoded_public_key: Vec = bincode::serialize(&public_key).unwrap(); let decoded_public_key: PublicKey = bincode::deserialize(&encoded_public_key).unwrap(); - assert_eq!(&PUBLIC_KEY_BYTES[..], &encoded_public_key[encoded_public_key.len() - PUBLIC_KEY_LENGTH..]); + assert_eq!( + &PUBLIC_KEY_BYTES[..], + &encoded_public_key[encoded_public_key.len() - PUBLIC_KEY_LENGTH..] + ); assert_eq!(public_key, decoded_public_key); } @@ -415,7 +438,9 @@ mod serialisation { #[test] fn serialize_deserialize_keypair_toml() { - let demo = Demo { keypair: Keypair::from_bytes(&KEYPAIR_BYTES).unwrap() }; + let demo = Demo { + keypair: Keypair::from_bytes(&KEYPAIR_BYTES).unwrap(), + }; println!("\n\nWrite to toml"); let demo_toml = toml::to_string(&demo).unwrap(); @@ -427,13 +452,19 @@ mod serialisation { #[test] fn serialize_public_key_size() { let public_key: PublicKey = PublicKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap(); - assert_eq!(bincode::serialized_size(&public_key).unwrap() as usize, BINCODE_INT_LENGTH + PUBLIC_KEY_LENGTH); + assert_eq!( + bincode::serialized_size(&public_key).unwrap() as usize, + BINCODE_INT_LENGTH + PUBLIC_KEY_LENGTH + ); } #[test] fn serialize_signature_size() { let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES).unwrap(); - assert_eq!(bincode::serialized_size(&signature).unwrap() as usize, SIGNATURE_LENGTH); + assert_eq!( + bincode::serialized_size(&signature).unwrap() as usize, + SIGNATURE_LENGTH + ); } #[test] @@ -448,6 +479,9 @@ mod serialisation { #[test] fn serialize_keypair_size() { let keypair = Keypair::from_bytes(&KEYPAIR_BYTES).unwrap(); - assert_eq!(bincode::serialized_size(&keypair).unwrap() as usize, BINCODE_INT_LENGTH + KEYPAIR_LENGTH); + assert_eq!( + bincode::serialized_size(&keypair).unwrap() as usize, + BINCODE_INT_LENGTH + KEYPAIR_LENGTH + ); } } From cfcdf536a0b660d378c7dbeb3402e710791e7116 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Fri, 9 Dec 2022 19:14:38 -0700 Subject: [PATCH 301/351] Cargo.toml: compatibility updates for `curve25519-dalek` and `ed25519` (#236) curve25519-dalek: - Enables `digest` and `rand_core` features - Removes transitive `nightly`, `simd_backend`, and `std` features ed25519: - `AsRef` impl for `Signature` has been removed; uses `to_bytes` - Uses `try_from` for `InternalSignature` conversion --- .github/workflows/rust.yml | 8 ++++---- Cargo.toml | 7 +++---- src/signature.rs | 2 +- tests/ed25519.rs | 4 ++-- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 4dfa071..770193a 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -39,11 +39,11 @@ jobs: - uses: actions/checkout@v2 - uses: dtolnay/rust-toolchain@nightly - env: - RUSTFLAGS: "-C target_feature=+avx2" - run: cargo build --target x86_64-unknown-linux-gnu --features simd_backend + RUSTFLAGS: '--cfg curve25519_dalek_backend="simd" -C target_feature=+avx2' + run: cargo build --target x86_64-unknown-linux-gnu - env: - RUSTFLAGS: "-C target_feature=+avx512ifma" - run: cargo build --target x86_64-unknown-linux-gnu --features simd_backend + RUSTFLAGS: '--cfg curve25519_dalek_backend="simd" -C target_feature=+avx512ifma' + run: cargo build --target x86_64-unknown-linux-gnu msrv: name: Current MSRV is 1.56.1 diff --git a/Cargo.toml b/Cargo.toml index e95a714..31cfe1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master" features = ["nightly", "batch"] [dependencies] -curve25519-dalek = { version = "=4.0.0-pre.2", default-features = false } +curve25519-dalek = { version = "=4.0.0-pre.2", default-features = false, features = ["digest", "rand_core"] } ed25519 = { version = "=2.0.0-pre.0", default-features = false } merlin = { version = "3", default-features = false, optional = true } rand = { version = "0.8", default-features = false, optional = true } @@ -48,9 +48,8 @@ required-features = ["batch"] [features] default = ["std", "rand"] -std = ["alloc", "curve25519-dalek/std", "ed25519/std", "serde_crate/std", "sha2/std", "rand/std"] +std = ["alloc", "ed25519/std", "serde_crate/std", "sha2/std", "rand/std"] alloc = ["curve25519-dalek/alloc", "rand/alloc", "zeroize/alloc"] -nightly = ["curve25519-dalek/nightly"] serde = ["serde_crate", "serde_bytes", "ed25519/serde"] batch = ["alloc", "merlin", "rand/std"] # This feature enables deterministic batch verification. @@ -58,7 +57,7 @@ batch_deterministic = ["alloc", "merlin", "rand", "rand_core"] asm = ["sha2/asm"] # This features turns off stricter checking for scalar malleability in signatures legacy_compatibility = [] -simd_backend = ["curve25519-dalek/simd_backend"] [patch.crates-io] curve25519-dalek = { git = "https://github.com/dalek-cryptography/curve25519-dalek.git", branch = "release/4.0" } +ed25519 = { git = "https://github.com/RustCrypto/signatures.git"} diff --git a/src/signature.rs b/src/signature.rs index 763d8fc..de8a425 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -194,7 +194,7 @@ impl TryFrom<&ed25519::Signature> for InternalSignature { type Error = SignatureError; fn try_from(sig: &ed25519::Signature) -> Result { - InternalSignature::from_bytes(sig.as_ref()) + InternalSignature::from_bytes(&sig.to_bytes()) } } diff --git a/tests/ed25519.rs b/tests/ed25519.rs index 6b05a6d..0ccb68b 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -69,7 +69,7 @@ mod vectors { // The signatures in the test vectors also include the message // at the end, but we just want R and S. - let sig1: Signature = Signature::from_bytes(&sig_bytes[..64]).unwrap(); + let sig1: Signature = Signature::try_from(&sig_bytes[..64]).unwrap(); let sig2: Signature = keypair.sign(&msg_bytes); assert!(sig1 == sig2, "Signature bytes not equal on line {}", lineno); @@ -99,7 +99,7 @@ mod vectors { PublicKey::from_bytes(&pub_bytes[..PUBLIC_KEY_LENGTH]).unwrap(); let keypair: Keypair = Keypair::from(secret); assert_eq!(expected_public, keypair.public_key()); - let sig1: Signature = Signature::from_bytes(&sig_bytes[..]).unwrap(); + let sig1: Signature = Signature::try_from(&sig_bytes[..]).unwrap(); let mut prehash_for_signing: Sha512 = Sha512::default(); let mut prehash_for_verifying: Sha512 = Sha512::default(); From 55620dcde5710e7fb07812a921042cc5f26765c8 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Tue, 13 Dec 2022 16:19:31 -0700 Subject: [PATCH 302/351] PKCS#8 support (#224) Adds optional integration with `ed25519::pkcs8` with support for decoding/encoding `Keypair` from/to PKCS#8-encoded documents as well as `PublicKey` from/to SPKI-encoded documents. Includes test vectors generated for the `ed25519` crate from: https://github.com/RustCrypto/signatures/tree/master/ed25519/tests/examples --- .github/workflows/rust.yml | 5 ++- Cargo.toml | 17 ++++---- README.md | 2 +- src/keypair.rs | 80 ++++++++++++++++++++++++++++++++++++ src/lib.rs | 43 +++++++++++++++++++ src/public.rs | 62 ++++++++++++++++++++++++++++ src/signature.rs | 2 +- tests/ed25519.rs | 2 +- tests/examples/pkcs8-v1.der | Bin 0 -> 48 bytes tests/examples/pkcs8-v2.der | Bin 0 -> 116 bytes tests/examples/pubkey.der | Bin 0 -> 44 bytes tests/pkcs8.rs | 74 +++++++++++++++++++++++++++++++++ 12 files changed, 274 insertions(+), 13 deletions(-) create mode 100644 tests/examples/pkcs8-v1.der create mode 100644 tests/examples/pkcs8-v2.der create mode 100644 tests/examples/pubkey.der create mode 100644 tests/pkcs8.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 770193a..6019bcd 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -31,6 +31,7 @@ jobs: - run: cargo test --target ${{ matrix.target }} --features batch - run: cargo test --target ${{ matrix.target }} --features batch_deterministic - run: cargo test --target ${{ matrix.target }} --features serde + - run: cargo test --target ${{ matrix.target }} --features pkcs8 build-simd: name: Test simd backend (nightly) @@ -46,7 +47,7 @@ jobs: run: cargo build --target x86_64-unknown-linux-gnu msrv: - name: Current MSRV is 1.56.1 + name: Current MSRV is 1.57.0 runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -56,7 +57,7 @@ jobs: - run: cargo -Z minimal-versions check --no-default-features --features serde # Now check that `cargo build` works with respect to the oldest possible # deps and the stated MSRV - - uses: dtolnay/rust-toolchain@1.56.1 + - uses: dtolnay/rust-toolchain@1.57.0 - run: cargo build bench: diff --git a/Cargo.toml b/Cargo.toml index 31cfe1f..6040fb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ keywords = ["cryptography", "ed25519", "curve25519", "signature", "ECC"] categories = ["cryptography", "no-std"] description = "Fast and efficient ed25519 EdDSA key generations, signing, and verification in pure Rust." exclude = [ ".gitignore", "TESTVECTORS", "res/*" ] +rust-version = "1.57" [badges] travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master"} @@ -19,11 +20,12 @@ travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master" [package.metadata.docs.rs] # Disabled for now since this is borked; tracking https://github.com/rust-lang/docs.rs/issues/302 # rustdoc-args = ["--html-in-header", ".cargo/registry/src/github.com-1ecc6299db9ec823/curve25519-dalek-0.13.2/rustdoc-include-katex-header.html"] -features = ["nightly", "batch"] +rustdoc-args = ["--cfg", "docsrs"] +features = ["nightly", "batch", "pkcs8"] [dependencies] -curve25519-dalek = { version = "=4.0.0-pre.2", default-features = false, features = ["digest", "rand_core"] } -ed25519 = { version = "=2.0.0-pre.0", default-features = false } +curve25519-dalek = { version = "=4.0.0-pre.3", default-features = false, features = ["digest", "rand_core"] } +ed25519 = { version = "=2.0.0-pre.1", default-features = false } merlin = { version = "3", default-features = false, optional = true } rand = { version = "0.8", default-features = false, optional = true } rand_core = { version = "0.6", default-features = false, optional = true } @@ -37,6 +39,7 @@ hex = "^0.4" bincode = "1.0" serde_json = "1.0" criterion = "0.3" +hex-literal = "0.3" rand = "0.8" serde_crate = { package = "serde", version = "1.0", features = ["derive"] } toml = { version = "0.5" } @@ -49,7 +52,7 @@ required-features = ["batch"] [features] default = ["std", "rand"] std = ["alloc", "ed25519/std", "serde_crate/std", "sha2/std", "rand/std"] -alloc = ["curve25519-dalek/alloc", "rand/alloc", "zeroize/alloc"] +alloc = ["curve25519-dalek/alloc", "ed25519/alloc", "rand/alloc", "zeroize/alloc"] serde = ["serde_crate", "serde_bytes", "ed25519/serde"] batch = ["alloc", "merlin", "rand/std"] # This feature enables deterministic batch verification. @@ -57,7 +60,5 @@ batch_deterministic = ["alloc", "merlin", "rand", "rand_core"] asm = ["sha2/asm"] # This features turns off stricter checking for scalar malleability in signatures legacy_compatibility = [] - -[patch.crates-io] -curve25519-dalek = { git = "https://github.com/dalek-cryptography/curve25519-dalek.git", branch = "release/4.0" } -ed25519 = { git = "https://github.com/RustCrypto/signatures.git"} +pkcs8 = ["ed25519/pkcs8"] +pem = ["alloc", "ed25519/pem", "pkcs8"] diff --git a/README.md b/README.md index 29af18e..42ce823 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ version = "1" # Minimum Supported Rust Version -This crate requires Rust 1.56.1 at a minimum. 1.x releases of this crate supported an MSRV of 1.41. +This crate requires Rust 1.57.0 at a minimum. 1.x releases of this crate supported an MSRV of 1.41. In the future, MSRV changes will be accompanied by a minor version bump. diff --git a/src/keypair.rs b/src/keypair.rs index 592486c..8c9c6c1 100644 --- a/src/keypair.rs +++ b/src/keypair.rs @@ -9,6 +9,9 @@ //! ed25519 keypairs. +#[cfg(feature = "pkcs8")] +use ed25519::pkcs8::{self, DecodePrivateKey}; + #[cfg(feature = "rand")] use rand::{CryptoRng, RngCore}; @@ -431,6 +434,83 @@ impl Verifier for Keypair { } } +impl TryFrom<&[u8]> for Keypair { + type Error = SignatureError; + + fn try_from(bytes: &[u8]) -> Result { + Keypair::from_bytes(bytes) + } +} + +#[cfg(feature = "pkcs8")] +impl DecodePrivateKey for Keypair {} + +#[cfg(all(feature = "alloc", feature = "pkcs8"))] +impl pkcs8::EncodePrivateKey for Keypair { + fn to_pkcs8_der(&self) -> pkcs8::Result { + pkcs8::KeypairBytes::from(self).to_pkcs8_der() + } +} + +#[cfg(feature = "pkcs8")] +impl TryFrom for Keypair { + type Error = pkcs8::Error; + + fn try_from(pkcs8_key: pkcs8::KeypairBytes) -> pkcs8::Result { + Keypair::try_from(&pkcs8_key) + } +} + +#[cfg(feature = "pkcs8")] +impl TryFrom<&pkcs8::KeypairBytes> for Keypair { + type Error = pkcs8::Error; + + fn try_from(pkcs8_key: &pkcs8::KeypairBytes) -> pkcs8::Result { + let secret = SecretKey::from_bytes(&pkcs8_key.secret_key) + .map_err(|_| pkcs8::Error::KeyMalformed)?; + + let public = PublicKey::from(&secret); + + // Validate the public key in the PKCS#8 document if present + if let Some(public_bytes) = pkcs8_key.public_key { + let pk = PublicKey::from_bytes(public_bytes.as_ref()) + .map_err(|_| pkcs8::Error::KeyMalformed)?; + + if public != pk { + return Err(pkcs8::Error::KeyMalformed); + } + } + + Ok(Keypair { secret, public }) + } +} + +#[cfg(feature = "pkcs8")] +impl From for pkcs8::KeypairBytes { + fn from(keypair: Keypair) -> pkcs8::KeypairBytes { + pkcs8::KeypairBytes::from(&keypair) + } +} + +#[cfg(feature = "pkcs8")] +impl From<&Keypair> for pkcs8::KeypairBytes { + fn from(keypair: &Keypair) -> pkcs8::KeypairBytes { + pkcs8::KeypairBytes { + secret_key: keypair.secret.to_bytes(), + public_key: Some(pkcs8::PublicKeyBytes(keypair.public.to_bytes())), + } + } +} + +#[cfg(feature = "pkcs8")] +impl TryFrom> for Keypair { + type Error = pkcs8::Error; + + fn try_from(private_key: pkcs8::PrivateKeyInfo<'_>) -> pkcs8::Result { + pkcs8::KeypairBytes::try_from(private_key)?.try_into() + } +} + #[cfg(feature = "serde")] impl Serialize for Keypair { fn serialize(&self, serializer: S) -> Result diff --git a/src/lib.rs b/src/lib.rs index ee3a8dd..07e9cbf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -138,6 +138,44 @@ //! # } //! ``` //! +//! ### PKCS#8 Key Encoding +//! +//! PKCS#8 is a private key format with support for multiple algorithms. +//! It can be encoded as binary (DER) or text (PEM). +//! +//! You can recognize PEM-encoded PKCS#8 keys by the following: +//! +//! ```text +//! -----BEGIN PRIVATE KEY----- +//! ``` +//! +//! To use PKCS#8, you need to enable the `pkcs8` crate feature. +//! +//! The following traits can be used to decode/encode [`Keypair`] and +//! [`PublicKey`] as PKCS#8. Note that [`pkcs8`] is re-exported from the +//! toplevel of the crate: +//! +//! - [`pkcs8::DecodePrivateKey`]: decode private keys from PKCS#8 +//! - [`pkcs8::EncodePrivateKey`]: encode private keys to PKCS#8 +//! - [`pkcs8::DecodePublicKey`]: decode public keys from PKCS#8 +//! - [`pkcs8::EncodePublicKey`]: encode public keys to PKCS#8 +//! +//! #### Example +//! +//! NOTE: this requires the `pem` crate feature. +//! +#![cfg_attr(feature = "pem", doc = "```")] +#![cfg_attr(not(feature = "pem"), doc = "```ignore")] +//! use ed25519_dalek::{PublicKey, pkcs8::DecodePublicKey}; +//! +//! let pem = "-----BEGIN PUBLIC KEY----- +//! MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE= +//! -----END PUBLIC KEY-----"; +//! +//! let public_key = PublicKey::from_public_key_pem(pem) +//! .expect("invalid public key PEM"); +//! ``` +//! //! ### Using Serde //! //! If you prefer the bytes to be wrapped in another serialisation format, all @@ -208,6 +246,8 @@ #![warn(future_incompatible, rust_2018_idioms)] #![deny(missing_docs)] // refuse to compile if documentation is missing #![cfg_attr(not(test), forbid(unsafe_code))] +#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg, doc_cfg_hide))] +#![cfg_attr(docsrs, doc(cfg_hide(docsrs)))] #[cfg(any(feature = "batch", feature = "batch_deterministic"))] extern crate alloc; @@ -243,3 +283,6 @@ pub use crate::secret::*; // Re-export the `Signer` and `Verifier` traits from the `signature` crate pub use ed25519::signature::{Signer, Verifier}; pub use ed25519::Signature; + +#[cfg(feature = "pkcs8")] +pub use ed25519::pkcs8; diff --git a/src/public.rs b/src/public.rs index fdbced2..a16dbed 100644 --- a/src/public.rs +++ b/src/public.rs @@ -23,6 +23,9 @@ use ed25519::signature::Verifier; pub use sha2::Sha512; +#[cfg(feature = "pkcs8")] +use ed25519::pkcs8::{self, DecodePublicKey}; + #[cfg(feature = "serde")] use serde::de::Error as SerdeError; #[cfg(feature = "serde")] @@ -350,6 +353,65 @@ impl Verifier for PublicKey { } } +impl TryFrom<&[u8]> for PublicKey { + type Error = SignatureError; + + fn try_from(bytes: &[u8]) -> Result { + PublicKey::from_bytes(bytes) + } +} + +#[cfg(feature = "pkcs8")] +impl DecodePublicKey for PublicKey {} + +#[cfg(all(feature = "alloc", feature = "pkcs8"))] +impl pkcs8::EncodePublicKey for PublicKey { + fn to_public_key_der(&self) -> pkcs8::spki::Result { + pkcs8::PublicKeyBytes::from(self).to_public_key_der() + } +} + +#[cfg(feature = "pkcs8")] +impl TryFrom for PublicKey { + type Error = pkcs8::spki::Error; + + fn try_from(pkcs8_key: pkcs8::PublicKeyBytes) -> pkcs8::spki::Result { + PublicKey::try_from(&pkcs8_key) + } +} + +#[cfg(feature = "pkcs8")] +impl TryFrom<&pkcs8::PublicKeyBytes> for PublicKey { + type Error = pkcs8::spki::Error; + + fn try_from(pkcs8_key: &pkcs8::PublicKeyBytes) -> pkcs8::spki::Result { + PublicKey::from_bytes(pkcs8_key.as_ref()).map_err(|_| pkcs8::spki::Error::KeyMalformed) + } +} + +#[cfg(feature = "pkcs8")] +impl From for pkcs8::PublicKeyBytes { + fn from(public_key: PublicKey) -> pkcs8::PublicKeyBytes { + pkcs8::PublicKeyBytes::from(&public_key) + } +} + +#[cfg(feature = "pkcs8")] +impl From<&PublicKey> for pkcs8::PublicKeyBytes { + fn from(public_key: &PublicKey) -> pkcs8::PublicKeyBytes { + pkcs8::PublicKeyBytes(public_key.to_bytes()) + } +} + +#[cfg(feature = "pkcs8")] +impl TryFrom> for PublicKey { + type Error = pkcs8::spki::Error; + + fn try_from(public_key: pkcs8::spki::SubjectPublicKeyInfo<'_>) -> pkcs8::spki::Result { + pkcs8::PublicKeyBytes::try_from(public_key)?.try_into() + } +} + #[cfg(feature = "serde")] impl Serialize for PublicKey { fn serialize(&self, serializer: S) -> Result diff --git a/src/signature.rs b/src/signature.rs index de8a425..795bfad 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -94,7 +94,7 @@ fn check_scalar(bytes: [u8; 32]) -> Result { return Ok(Scalar::from_bits(bytes)); } - match Scalar::from_canonical_bytes(bytes) { + match Scalar::from_canonical_bytes(bytes).into() { None => return Err(InternalError::ScalarFormatError.into()), Some(x) => return Ok(x), }; diff --git a/tests/ed25519.rs b/tests/ed25519.rs index 0ccb68b..bd597db 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -156,7 +156,7 @@ mod vectors { fn non_null_scalar() -> Scalar { let mut rng = rand::rngs::OsRng; let mut s_candidate = Scalar::random(&mut rng); - while s_candidate == Scalar::zero() { + while s_candidate == Scalar::ZERO { s_candidate = Scalar::random(&mut rng); } s_candidate diff --git a/tests/examples/pkcs8-v1.der b/tests/examples/pkcs8-v1.der new file mode 100644 index 0000000000000000000000000000000000000000..cb780b362c9dbb7e62b9159ac40c45b1242bec2f GIT binary patch literal 48 zcmV-00MGw0E&>4nFa-t!D`jv5A_O4R?sD7t6Ie>sw%GCaY51)={(LCQ@znd^m#B|K Gbyz~LI~HgF literal 0 HcmV?d00001 diff --git a/tests/examples/pkcs8-v2.der b/tests/examples/pkcs8-v2.der new file mode 100644 index 0000000000000000000000000000000000000000..3358e8a730ac3daf865b6eab869bb9a921ab9ef4 GIT binary patch literal 116 zcmV-)0E_=HasmMXFa-t!D`jv5A_O4R?sD7t6Ie>sw%GCaY51)={(LCQ@znd^m#B|K zbyz~6A21yT3Mz(3hW8Bt2?-Q24-5@Mb#i2EWgtUnVQF%6fgu1HzeEXXgw6hiLAt?b W+&h-YP==~7wzkU*TsW<8F=pYUFECsH literal 0 HcmV?d00001 diff --git a/tests/examples/pubkey.der b/tests/examples/pubkey.der new file mode 100644 index 0000000000000000000000000000000000000000..d1002c4a4e624c322cc71015e6685a25808df374 GIT binary patch literal 44 zcmXreGGJw6)=n*8R%DRe@4}hca`s=V Date: Sat, 17 Dec 2022 23:24:58 -0700 Subject: [PATCH 303/351] Rename `Keypair` => `SigningKey`; `PublicKey` => `VerifyingKey` (#242) * Rename `signing` and `verifying` modules Renames the following modules: - `keypair` => `signing` - `public` => `verifying` Renaming these in an individual commit preserves the commit history. This is in anticipation of renaming the following per #225: - `Keypair` => `SigningKey` - `PublicKey` => `VerifyingKey` * Rename `Keypair` => `SigningKey`; `PublicKey` => `VerifyingKey` As proposed in #225, renames key types after their roles: - `SigningKey` produces signatures - `VerifyingKey` verifies signatures The `SecretKey` type is changed to a type alias for `[u8; 32]`, which matches the RFC8032 definition: https://www.rfc-editor.org/rfc/rfc8032#section-5.1.5 > The private key is 32 octets (256 bits, corresponding to b) of > cryptographically secure random data. --- benches/ed25519_benchmarks.rs | 23 +- src/batch.rs | 34 +- src/keypair.rs | 534 --------------------- src/lib.rs | 126 +++-- src/secret.rs | 432 ----------------- src/signing.rs | 818 ++++++++++++++++++++++++++++++++ src/{public.rs => verifying.rs} | 88 ++-- tests/ed25519.rs | 179 +++---- tests/pkcs8.rs | 41 +- 9 files changed, 1045 insertions(+), 1230 deletions(-) delete mode 100644 src/keypair.rs delete mode 100644 src/secret.rs create mode 100644 src/signing.rs rename src/{public.rs => verifying.rs} (83%) diff --git a/benches/ed25519_benchmarks.rs b/benches/ed25519_benchmarks.rs index 98afd16..ed01d49 100644 --- a/benches/ed25519_benchmarks.rs +++ b/benches/ed25519_benchmarks.rs @@ -12,16 +12,16 @@ use criterion::{criterion_group, criterion_main, Criterion}; mod ed25519_benches { use super::*; use ed25519_dalek::verify_batch; - use ed25519_dalek::Keypair; - use ed25519_dalek::PublicKey; use ed25519_dalek::Signature; use ed25519_dalek::Signer; + use ed25519_dalek::SigningKey; + use ed25519_dalek::VerifyingKey; use rand::prelude::ThreadRng; use rand::thread_rng; fn sign(c: &mut Criterion) { let mut csprng: ThreadRng = thread_rng(); - let keypair: Keypair = Keypair::generate(&mut csprng); + let keypair: SigningKey = SigningKey::generate(&mut csprng); let msg: &[u8] = b""; c.bench_function("Ed25519 signing", move |b| b.iter(|| keypair.sign(msg))); @@ -29,7 +29,7 @@ mod ed25519_benches { fn verify(c: &mut Criterion) { let mut csprng: ThreadRng = thread_rng(); - let keypair: Keypair = Keypair::generate(&mut csprng); + let keypair: SigningKey = SigningKey::generate(&mut csprng); let msg: &[u8] = b""; let sig: Signature = keypair.sign(msg); @@ -40,7 +40,7 @@ mod ed25519_benches { fn verify_strict(c: &mut Criterion) { let mut csprng: ThreadRng = thread_rng(); - let keypair: Keypair = Keypair::generate(&mut csprng); + let keypair: SigningKey = SigningKey::generate(&mut csprng); let msg: &[u8] = b""; let sig: Signature = keypair.sign(msg); @@ -58,16 +58,17 @@ mod ed25519_benches { "Ed25519 batch signature verification", |b, &&size| { let mut csprng: ThreadRng = thread_rng(); - let keypairs: Vec = - (0..size).map(|_| Keypair::generate(&mut csprng)).collect(); + let keypairs: Vec = (0..size) + .map(|_| SigningKey::generate(&mut csprng)) + .collect(); let msg: &[u8] = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; let messages: Vec<&[u8]> = (0..size).map(|_| msg).collect(); let signatures: Vec = keypairs.iter().map(|key| key.sign(&msg)).collect(); - let public_keys: Vec = - keypairs.iter().map(|key| key.public_key()).collect(); + let verifying_keys: Vec = + keypairs.iter().map(|key| key.verifying_key()).collect(); - b.iter(|| verify_batch(&messages[..], &signatures[..], &public_keys[..])); + b.iter(|| verify_batch(&messages[..], &signatures[..], &verifying_keys[..])); }, &BATCH_SIZES, ); @@ -77,7 +78,7 @@ mod ed25519_benches { let mut csprng: ThreadRng = thread_rng(); c.bench_function("Ed25519 keypair generation", move |b| { - b.iter(|| Keypair::generate(&mut csprng)) + b.iter(|| SigningKey::generate(&mut csprng)) }); } diff --git a/src/batch.rs b/src/batch.rs index 63bb895..39af1ca 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -34,8 +34,8 @@ use sha2::Sha512; use crate::errors::InternalError; use crate::errors::SignatureError; -use crate::public::PublicKey; use crate::signature::InternalSignature; +use crate::VerifyingKey; trait BatchTranscript { fn append_scalars(&mut self, scalars: &Vec); @@ -112,13 +112,13 @@ fn zero_rng() -> ZeroRng { ZeroRng {} } -/// Verify a batch of `signatures` on `messages` with their respective `public_keys`. +/// Verify a batch of `signatures` on `messages` with their respective `verifying_keys`. /// /// # Inputs /// /// * `messages` is a slice of byte slices, one per signed message. /// * `signatures` is a slice of `Signature`s. -/// * `public_keys` is a slice of `PublicKey`s. +/// * `verifying_keys` is a slice of `VerifyingKey`s. /// /// # Returns /// @@ -183,27 +183,27 @@ fn zero_rng() -> ZeroRng { /// falsely "pass" the synthethic batch verification equation *for the same /// inputs*, but *only some crafted inputs* will pass the deterministic batch /// single, and neither of these will ever pass single signature verification, -/// see the documentation for [`PublicKey.validate()`]. +/// see the documentation for [`VerifyingKey.validate()`]. /// /// # Examples /// /// ``` /// use ed25519_dalek::verify_batch; -/// use ed25519_dalek::Keypair; -/// use ed25519_dalek::PublicKey; +/// use ed25519_dalek::SigningKey; +/// use ed25519_dalek::VerifyingKey; /// use ed25519_dalek::Signer; /// use ed25519_dalek::Signature; /// use rand::rngs::OsRng; /// /// # fn main() { /// let mut csprng = OsRng{}; -/// let keypairs: Vec = (0..64).map(|_| Keypair::generate(&mut csprng)).collect(); +/// let signing_keys: Vec<_> = (0..64).map(|_| SigningKey::generate(&mut csprng)).collect(); /// let msg: &[u8] = b"They're good dogs Brant"; /// let messages: Vec<&[u8]> = (0..64).map(|_| msg).collect(); -/// let signatures: Vec = keypairs.iter().map(|key| key.sign(&msg)).collect(); -/// let public_keys: Vec = keypairs.iter().map(|key| key.public_key()).collect(); +/// let signatures: Vec = signing_keys.iter().map(|key| key.sign(&msg)).collect(); +/// let verifying_keys: Vec = signing_keys.iter().map(|key| key.verifying_key()).collect(); /// -/// let result = verify_batch(&messages[..], &signatures[..], &public_keys[..]); +/// let result = verify_batch(&messages[..], &signatures[..], &verifying_keys[..]); /// assert!(result.is_ok()); /// # } /// ``` @@ -211,20 +211,20 @@ fn zero_rng() -> ZeroRng { pub fn verify_batch( messages: &[&[u8]], signatures: &[ed25519::Signature], - public_keys: &[PublicKey], + verifying_keys: &[VerifyingKey], ) -> Result<(), SignatureError> { // Return an Error if any of the vectors were not the same size as the others. if signatures.len() != messages.len() - || signatures.len() != public_keys.len() - || public_keys.len() != messages.len() + || signatures.len() != verifying_keys.len() + || verifying_keys.len() != messages.len() { return Err(InternalError::ArrayLengthError { name_a: "signatures", length_a: signatures.len(), name_b: "messages", length_b: messages.len(), - name_c: "public_keys", - length_c: public_keys.len(), + name_c: "verifying_keys", + length_c: verifying_keys.len(), } .into()); } @@ -240,7 +240,7 @@ pub fn verify_batch( .map(|i| { let mut h: Sha512 = Sha512::default(); h.update(signatures[i].R.as_bytes()); - h.update(public_keys[i].as_bytes()); + h.update(verifying_keys[i].as_bytes()); h.update(&messages[i]); Scalar::from_hash(h) }) @@ -284,7 +284,7 @@ pub fn verify_batch( let zhrams = hrams.iter().zip(zs.iter()).map(|(hram, z)| hram * z); let Rs = signatures.iter().map(|sig| sig.R.decompress()); - let As = public_keys.iter().map(|pk| Some(pk.1)); + let As = verifying_keys.iter().map(|pk| Some(pk.1)); let B = once(Some(constants::ED25519_BASEPOINT_POINT)); // Compute (-∑ z[i]s[i] (mod l)) B + ∑ z[i]R[i] + ∑ (z[i]H(R||A||M)[i] (mod l)) A[i] = 0 diff --git a/src/keypair.rs b/src/keypair.rs deleted file mode 100644 index 8c9c6c1..0000000 --- a/src/keypair.rs +++ /dev/null @@ -1,534 +0,0 @@ -// -*- mode: rust; -*- -// -// This file is part of ed25519-dalek. -// Copyright (c) 2017-2019 isis lovecruft -// See LICENSE for licensing information. -// -// Authors: -// - isis agora lovecruft - -//! ed25519 keypairs. - -#[cfg(feature = "pkcs8")] -use ed25519::pkcs8::{self, DecodePrivateKey}; - -#[cfg(feature = "rand")] -use rand::{CryptoRng, RngCore}; - -#[cfg(feature = "serde")] -use serde::de::Error as SerdeError; -#[cfg(feature = "serde")] -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -#[cfg(feature = "serde")] -use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes}; - -pub use sha2::Sha512; - -use curve25519_dalek::digest::generic_array::typenum::U64; -pub use curve25519_dalek::digest::Digest; - -use ed25519::signature::{Signer, Verifier}; - -use crate::constants::*; -use crate::errors::*; -use crate::public::*; -use crate::secret::*; - -/// An ed25519 keypair. -// Invariant: `public` is always the public key of `secret`. This prevents the signing function -// oracle attack described in https://github.com/MystenLabs/ed25519-unsafe-libs -#[derive(Debug)] -pub struct Keypair { - /// The secret half of this keypair. - pub(crate) secret: SecretKey, - /// The public half of this keypair. - pub(crate) public: PublicKey, -} - -impl From for Keypair { - fn from(secret: SecretKey) -> Self { - let public = PublicKey::from(&secret); - Self { secret, public } - } -} - -impl Keypair { - /// Get the secret key of this keypair. - pub fn secret_key(&self) -> SecretKey { - SecretKey(self.secret.0) - } - - /// Get the public key of this keypair. - pub fn public_key(&self) -> PublicKey { - self.public - } - - /// Convert this keypair to bytes. - /// - /// # Returns - /// - /// An array of bytes, `[u8; KEYPAIR_LENGTH]`. The first - /// `SECRET_KEY_LENGTH` of bytes is the `SecretKey`, and the next - /// `PUBLIC_KEY_LENGTH` bytes is the `PublicKey` (the same as other - /// libraries, such as [Adam Langley's ed25519 Golang - /// implementation](https://github.com/agl/ed25519/)). It is guaranteed that - /// the encoded public key is the one derived from the encoded secret key. - pub fn to_bytes(&self) -> [u8; KEYPAIR_LENGTH] { - let mut bytes: [u8; KEYPAIR_LENGTH] = [0u8; KEYPAIR_LENGTH]; - - bytes[..SECRET_KEY_LENGTH].copy_from_slice(self.secret.as_bytes()); - bytes[SECRET_KEY_LENGTH..].copy_from_slice(self.public.as_bytes()); - bytes - } - - /// Construct a `Keypair` from the bytes of a `PublicKey` and `SecretKey`. - /// - /// # Inputs - /// - /// * `bytes`: an `&[u8]` of length [`KEYPAIR_LENGTH`], representing the - /// scalar for the secret key, and a compressed Edwards-Y coordinate of a - /// point on curve25519, both as bytes. (As obtained from - /// [`Keypair::to_bytes`].) - /// - /// # Returns - /// - /// A `Result` whose okay value is an EdDSA `Keypair` or whose error value - /// is an `SignatureError` describing the error that occurred. - pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != KEYPAIR_LENGTH { - return Err(InternalError::BytesLengthError { - name: "Keypair", - length: KEYPAIR_LENGTH, - } - .into()); - } - let secret = SecretKey::from_bytes(&bytes[..SECRET_KEY_LENGTH])?; - let public = PublicKey::from_bytes(&bytes[SECRET_KEY_LENGTH..])?; - - if public != (&secret).into() { - return Err(InternalError::MismatchedKeypairError.into()); - } - - Ok(Keypair { secret, public }) - } - - /// Generate an ed25519 keypair. - /// - /// # Example - /// - /// ``` - /// # #[cfg(feature = "std")] - /// # fn main() { - /// - /// use rand::rngs::OsRng; - /// use ed25519_dalek::Keypair; - /// use ed25519_dalek::Signature; - /// - /// let mut csprng = OsRng{}; - /// let keypair: Keypair = Keypair::generate(&mut csprng); - /// - /// # } - /// # - /// # #[cfg(not(feature = "std"))] - /// # fn main() { } - /// ``` - /// - /// # Input - /// - /// A CSPRNG with a `fill_bytes()` method, e.g. `rand_os::OsRng`. - /// - /// The caller must also supply a hash function which implements the - /// `Digest` and `Default` traits, and which returns 512 bits of output. - /// The standard hash function used for most ed25519 libraries is SHA-512, - /// which is available with `use sha2::Sha512` as in the example above. - /// Other suitable hash functions include Keccak-512 and Blake2b-512. - #[cfg(feature = "rand")] - pub fn generate(csprng: &mut R) -> Keypair - where - R: CryptoRng + RngCore, - { - let sk: SecretKey = SecretKey::generate(csprng); - let pk: PublicKey = (&sk).into(); - - Keypair { - public: pk, - secret: sk, - } - } - - /// Sign a `prehashed_message` with this `Keypair` using the - /// Ed25519ph algorithm defined in [RFC8032 §5.1][rfc8032]. - /// - /// # Inputs - /// - /// * `prehashed_message` is an instantiated hash digest with 512-bits of - /// output which has had the message to be signed previously fed into its - /// state. - /// * `context` is an optional context string, up to 255 bytes inclusive, - /// which may be used to provide additional domain separation. If not - /// set, this will default to an empty string. - /// - /// # Returns - /// - /// An Ed25519ph [`Signature`] on the `prehashed_message`. - /// - /// # Examples - /// - /// ``` - /// use ed25519_dalek::Digest; - /// use ed25519_dalek::Keypair; - /// use ed25519_dalek::Sha512; - /// use ed25519_dalek::Signature; - /// use rand::rngs::OsRng; - /// - /// # #[cfg(feature = "std")] - /// # fn main() { - /// let mut csprng = OsRng{}; - /// let keypair: Keypair = Keypair::generate(&mut csprng); - /// let message: &[u8] = b"All I want is to pet all of the dogs."; - /// - /// // Create a hash digest object which we'll feed the message into: - /// let mut prehashed: Sha512 = Sha512::new(); - /// - /// prehashed.update(message); - /// # } - /// # - /// # #[cfg(not(feature = "std"))] - /// # fn main() { } - /// ``` - /// - /// If you want, you can optionally pass a "context". It is generally a - /// good idea to choose a context and try to make it unique to your project - /// and this specific usage of signatures. - /// - /// For example, without this, if you were to [convert your OpenPGP key - /// to a Bitcoin key][terrible_idea] (just as an example, and also Don't - /// Ever Do That) and someone tricked you into signing an "email" which was - /// actually a Bitcoin transaction moving all your magic internet money to - /// their address, it'd be a valid transaction. - /// - /// By adding a context, this trick becomes impossible, because the context - /// is concatenated into the hash, which is then signed. So, going with the - /// previous example, if your bitcoin wallet used a context of - /// "BitcoinWalletAppTxnSigning" and OpenPGP used a context (this is likely - /// the least of their safety problems) of "GPGsCryptoIsntConstantTimeLol", - /// then the signatures produced by both could never match the other, even - /// if they signed the exact same message with the same key. - /// - /// Let's add a context for good measure (remember, you'll want to choose - /// your own!): - /// - /// ``` - /// # use ed25519_dalek::Digest; - /// # use ed25519_dalek::Keypair; - /// # use ed25519_dalek::Signature; - /// # use ed25519_dalek::SignatureError; - /// # use ed25519_dalek::Sha512; - /// # use rand::rngs::OsRng; - /// # - /// # fn do_test() -> Result { - /// # let mut csprng = OsRng{}; - /// # let keypair: Keypair = Keypair::generate(&mut csprng); - /// # let message: &[u8] = b"All I want is to pet all of the dogs."; - /// # let mut prehashed: Sha512 = Sha512::new(); - /// # prehashed.update(message); - /// # - /// let context: &[u8] = b"Ed25519DalekSignPrehashedDoctest"; - /// - /// let sig: Signature = keypair.sign_prehashed(prehashed, Some(context))?; - /// # - /// # Ok(sig) - /// # } - /// # #[cfg(feature = "std")] - /// # fn main() { - /// # do_test(); - /// # } - /// # - /// # #[cfg(not(feature = "std"))] - /// # fn main() { } - /// ``` - /// - /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 - /// [terrible_idea]: https://github.com/isislovecruft/scripts/blob/master/gpgkey2bc.py - pub fn sign_prehashed( - &self, - prehashed_message: D, - context: Option<&[u8]>, - ) -> Result - where - D: Digest, - { - let expanded: ExpandedSecretKey = (&self.secret).into(); // xxx thanks i hate this - - expanded - .sign_prehashed(prehashed_message, &self.public, context) - .into() - } - - /// Verify a signature on a message with this keypair's public key. - pub fn verify( - &self, - message: &[u8], - signature: &ed25519::Signature, - ) -> Result<(), SignatureError> { - self.public.verify(message, signature) - } - - /// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm. - /// - /// # Inputs - /// - /// * `prehashed_message` is an instantiated hash digest with 512-bits of - /// output which has had the message to be signed previously fed into its - /// state. - /// * `context` is an optional context string, up to 255 bytes inclusive, - /// which may be used to provide additional domain separation. If not - /// set, this will default to an empty string. - /// * `signature` is a purported Ed25519ph [`Signature`] on the `prehashed_message`. - /// - /// # Returns - /// - /// Returns `true` if the `signature` was a valid signature created by this - /// `Keypair` on the `prehashed_message`. - /// - /// # Examples - /// - /// ``` - /// use ed25519_dalek::Digest; - /// use ed25519_dalek::Keypair; - /// use ed25519_dalek::Signature; - /// use ed25519_dalek::SignatureError; - /// use ed25519_dalek::Sha512; - /// use rand::rngs::OsRng; - /// - /// # fn do_test() -> Result<(), SignatureError> { - /// let mut csprng = OsRng{}; - /// let keypair: Keypair = Keypair::generate(&mut csprng); - /// let message: &[u8] = b"All I want is to pet all of the dogs."; - /// - /// let mut prehashed: Sha512 = Sha512::new(); - /// prehashed.update(message); - /// - /// let context: &[u8] = b"Ed25519DalekSignPrehashedDoctest"; - /// - /// let sig: Signature = keypair.sign_prehashed(prehashed, Some(context))?; - /// - /// // The sha2::Sha512 struct doesn't implement Copy, so we'll have to create a new one: - /// let mut prehashed_again: Sha512 = Sha512::default(); - /// prehashed_again.update(message); - /// - /// let verified = keypair.public_key().verify_prehashed(prehashed_again, Some(context), &sig); - /// - /// assert!(verified.is_ok()); - /// - /// # verified - /// # } - /// # - /// # #[cfg(feature = "std")] - /// # fn main() { - /// # do_test(); - /// # } - /// # - /// # #[cfg(not(feature = "std"))] - /// # fn main() { } - /// ``` - /// - /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 - pub fn verify_prehashed( - &self, - prehashed_message: D, - context: Option<&[u8]>, - signature: &ed25519::Signature, - ) -> Result<(), SignatureError> - where - D: Digest, - { - self.public - .verify_prehashed(prehashed_message, context, signature) - } - - /// Strictly verify a signature on a message with this keypair's public key. - /// - /// # On The (Multiple) Sources of Malleability in Ed25519 Signatures - /// - /// This version of verification is technically non-RFC8032 compliant. The - /// following explains why. - /// - /// 1. Scalar Malleability - /// - /// The authors of the RFC explicitly stated that verification of an ed25519 - /// signature must fail if the scalar `s` is not properly reduced mod \ell: - /// - /// > To verify a signature on a message M using public key A, with F - /// > being 0 for Ed25519ctx, 1 for Ed25519ph, and if Ed25519ctx or - /// > Ed25519ph is being used, C being the context, first split the - /// > signature into two 32-octet halves. Decode the first half as a - /// > point R, and the second half as an integer S, in the range - /// > 0 <= s < L. Decode the public key A as point A'. If any of the - /// > decodings fail (including S being out of range), the signature is - /// > invalid.) - /// - /// All `verify_*()` functions within ed25519-dalek perform this check. - /// - /// 2. Point malleability - /// - /// The authors of the RFC added in a malleability check to step #3 in - /// §5.1.7, for small torsion components in the `R` value of the signature, - /// *which is not strictly required*, as they state: - /// - /// > Check the group equation \[8\]\[S\]B = \[8\]R + \[8\]\[k\]A'. It's - /// > sufficient, but not required, to instead check \[S\]B = R + \[k\]A'. - /// - /// # History of Malleability Checks - /// - /// As originally defined (cf. the "Malleability" section in the README of - /// this repo), ed25519 signatures didn't consider *any* form of - /// malleability to be an issue. Later the scalar malleability was - /// considered important. Still later, particularly with interests in - /// cryptocurrency design and in unique identities (e.g. for Signal users, - /// Tor onion services, etc.), the group element malleability became a - /// concern. - /// - /// However, libraries had already been created to conform to the original - /// definition. One well-used library in particular even implemented the - /// group element malleability check, *but only for batch verification*! - /// Which meant that even using the same library, a single signature could - /// verify fine individually, but suddenly, when verifying it with a bunch - /// of other signatures, the whole batch would fail! - /// - /// # "Strict" Verification - /// - /// This method performs *both* of the above signature malleability checks. - /// - /// It must be done as a separate method because one doesn't simply get to - /// change the definition of a cryptographic primitive ten years - /// after-the-fact with zero consideration for backwards compatibility in - /// hardware and protocols which have it already have the older definition - /// baked in. - /// - /// # Return - /// - /// Returns `Ok(())` if the signature is valid, and `Err` otherwise. - #[allow(non_snake_case)] - pub fn verify_strict( - &self, - message: &[u8], - signature: &ed25519::Signature, - ) -> Result<(), SignatureError> { - self.public.verify_strict(message, signature) - } -} - -impl Signer for Keypair { - /// Sign a message with this keypair's secret key. - fn try_sign(&self, message: &[u8]) -> Result { - let expanded: ExpandedSecretKey = (&self.secret).into(); - Ok(expanded.sign(&message, &self.public).into()) - } -} - -impl Verifier for Keypair { - /// Verify a signature on a message with this keypair's public key. - fn verify(&self, message: &[u8], signature: &ed25519::Signature) -> Result<(), SignatureError> { - self.public.verify(message, signature) - } -} - -impl TryFrom<&[u8]> for Keypair { - type Error = SignatureError; - - fn try_from(bytes: &[u8]) -> Result { - Keypair::from_bytes(bytes) - } -} - -#[cfg(feature = "pkcs8")] -impl DecodePrivateKey for Keypair {} - -#[cfg(all(feature = "alloc", feature = "pkcs8"))] -impl pkcs8::EncodePrivateKey for Keypair { - fn to_pkcs8_der(&self) -> pkcs8::Result { - pkcs8::KeypairBytes::from(self).to_pkcs8_der() - } -} - -#[cfg(feature = "pkcs8")] -impl TryFrom for Keypair { - type Error = pkcs8::Error; - - fn try_from(pkcs8_key: pkcs8::KeypairBytes) -> pkcs8::Result { - Keypair::try_from(&pkcs8_key) - } -} - -#[cfg(feature = "pkcs8")] -impl TryFrom<&pkcs8::KeypairBytes> for Keypair { - type Error = pkcs8::Error; - - fn try_from(pkcs8_key: &pkcs8::KeypairBytes) -> pkcs8::Result { - let secret = SecretKey::from_bytes(&pkcs8_key.secret_key) - .map_err(|_| pkcs8::Error::KeyMalformed)?; - - let public = PublicKey::from(&secret); - - // Validate the public key in the PKCS#8 document if present - if let Some(public_bytes) = pkcs8_key.public_key { - let pk = PublicKey::from_bytes(public_bytes.as_ref()) - .map_err(|_| pkcs8::Error::KeyMalformed)?; - - if public != pk { - return Err(pkcs8::Error::KeyMalformed); - } - } - - Ok(Keypair { secret, public }) - } -} - -#[cfg(feature = "pkcs8")] -impl From for pkcs8::KeypairBytes { - fn from(keypair: Keypair) -> pkcs8::KeypairBytes { - pkcs8::KeypairBytes::from(&keypair) - } -} - -#[cfg(feature = "pkcs8")] -impl From<&Keypair> for pkcs8::KeypairBytes { - fn from(keypair: &Keypair) -> pkcs8::KeypairBytes { - pkcs8::KeypairBytes { - secret_key: keypair.secret.to_bytes(), - public_key: Some(pkcs8::PublicKeyBytes(keypair.public.to_bytes())), - } - } -} - -#[cfg(feature = "pkcs8")] -impl TryFrom> for Keypair { - type Error = pkcs8::Error; - - fn try_from(private_key: pkcs8::PrivateKeyInfo<'_>) -> pkcs8::Result { - pkcs8::KeypairBytes::try_from(private_key)?.try_into() - } -} - -#[cfg(feature = "serde")] -impl Serialize for Keypair { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let bytes = &self.to_bytes()[..]; - SerdeBytes::new(bytes).serialize(serializer) - } -} - -#[cfg(feature = "serde")] -impl<'d> Deserialize<'d> for Keypair { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'d>, - { - let bytes = ::deserialize(deserializer)?; - Keypair::from_bytes(bytes.as_ref()).map_err(SerdeError::custom) - } -} diff --git a/src/lib.rs b/src/lib.rs index 07e9cbf..e6d051e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ //! //! Creating an ed25519 signature on a message is simple. //! -//! First, we need to generate a `Keypair`, which includes both public and +//! First, we need to generate a `SigningKey`, which includes both public and //! secret halves of an asymmetric key. To do so, we need a cryptographically //! secure pseudorandom number generator (CSPRNG). For this example, we'll use //! the operating system's builtin PRNG: @@ -22,28 +22,28 @@ //! # #[cfg(feature = "std")] //! # fn main() { //! use rand::rngs::OsRng; -//! use ed25519_dalek::Keypair; +//! use ed25519_dalek::SigningKey; //! use ed25519_dalek::Signature; //! //! let mut csprng = OsRng{}; -//! let keypair: Keypair = Keypair::generate(&mut csprng); +//! let signing_key: SigningKey = SigningKey::generate(&mut csprng); //! # } //! # //! # #[cfg(not(feature = "std"))] //! # fn main() { } //! ``` //! -//! We can now use this `keypair` to sign a message: +//! We can now use this `signing_key` to sign a message: //! //! ``` //! # fn main() { //! # use rand::rngs::OsRng; -//! # use ed25519_dalek::Keypair; +//! # use ed25519_dalek::SigningKey; //! # let mut csprng = OsRng{}; -//! # let keypair: Keypair = Keypair::generate(&mut csprng); +//! # let signing_key: SigningKey = SigningKey::generate(&mut csprng); //! use ed25519_dalek::{Signature, Signer}; //! let message: &[u8] = b"This is a test of the tsunami alert system."; -//! let signature: Signature = keypair.sign(message); +//! let signature: Signature = signing_key.sign(message); //! # } //! ``` //! @@ -53,39 +53,39 @@ //! ``` //! # fn main() { //! # use rand::rngs::OsRng; -//! # use ed25519_dalek::{Keypair, Signature, Signer}; +//! # use ed25519_dalek::{SigningKey, Signature, Signer}; //! # let mut csprng = OsRng{}; -//! # let keypair: Keypair = Keypair::generate(&mut csprng); +//! # let signing_key: SigningKey = SigningKey::generate(&mut csprng); //! # let message: &[u8] = b"This is a test of the tsunami alert system."; -//! # let signature: Signature = keypair.sign(message); +//! # let signature: Signature = signing_key.sign(message); //! use ed25519_dalek::Verifier; -//! assert!(keypair.verify(message, &signature).is_ok()); +//! assert!(signing_key.verify(message, &signature).is_ok()); //! # } //! ``` //! -//! Anyone else, given the `public` half of the `keypair` can also easily +//! Anyone else, given the `public` half of the `signing_key` can also easily //! verify this signature: //! //! ``` //! # fn main() { //! # use rand::rngs::OsRng; -//! # use ed25519_dalek::Keypair; +//! # use ed25519_dalek::SigningKey; //! # use ed25519_dalek::Signature; //! # use ed25519_dalek::Signer; -//! use ed25519_dalek::{PublicKey, Verifier}; +//! use ed25519_dalek::{VerifyingKey, Verifier}; //! # let mut csprng = OsRng{}; -//! # let keypair: Keypair = Keypair::generate(&mut csprng); +//! # let signing_key: SigningKey = SigningKey::generate(&mut csprng); //! # let message: &[u8] = b"This is a test of the tsunami alert system."; -//! # let signature: Signature = keypair.sign(message); +//! # let signature: Signature = signing_key.sign(message); //! -//! let public_key: PublicKey = keypair.public_key(); -//! assert!(public_key.verify(message, &signature).is_ok()); +//! let verifying_key: VerifyingKey = signing_key.verifying_key(); +//! assert!(verifying_key.verify(message, &signature).is_ok()); //! # } //! ``` //! //! ## Serialisation //! -//! `PublicKey`s, `SecretKey`s, `Keypair`s, and `Signature`s can be serialised +//! `VerifyingKey`s, `SecretKey`s, `SigningKey`s, and `Signature`s can be serialised //! into byte-arrays by calling `.to_bytes()`. It's perfectly acceptible and //! safe to transfer and/or store those bytes. (Of course, never transfer your //! secret key to anyone else, since they will only need the public key to @@ -94,16 +94,16 @@ //! ``` //! # fn main() { //! # use rand::rngs::OsRng; -//! # use ed25519_dalek::{Keypair, Signature, Signer, PublicKey}; +//! # use ed25519_dalek::{SigningKey, Signature, Signer, VerifyingKey}; //! use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; //! # let mut csprng = OsRng{}; -//! # let keypair: Keypair = Keypair::generate(&mut csprng); +//! # let signing_key: SigningKey = SigningKey::generate(&mut csprng); //! # let message: &[u8] = b"This is a test of the tsunami alert system."; -//! # let signature: Signature = keypair.sign(message); +//! # let signature: Signature = signing_key.sign(message); //! -//! let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = keypair.public_key().to_bytes(); -//! let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = keypair.secret_key().to_bytes(); -//! let keypair_bytes: [u8; KEYPAIR_LENGTH] = keypair.to_bytes(); +//! let verifying_key_bytes: [u8; PUBLIC_KEY_LENGTH] = signing_key.verifying_key().to_bytes(); +//! let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = signing_key.to_bytes(); +//! let signing_key_bytes: [u8; KEYPAIR_LENGTH] = signing_key.to_keypair_bytes(); //! let signature_bytes: [u8; SIGNATURE_LENGTH] = signature.to_bytes(); //! # } //! ``` @@ -114,24 +114,22 @@ //! # use std::convert::TryFrom; //! # use rand::rngs::OsRng; //! # use std::convert::TryInto; -//! # use ed25519_dalek::{Keypair, Signature, Signer, PublicKey, SecretKey, SignatureError}; +//! # use ed25519_dalek::{SigningKey, Signature, Signer, VerifyingKey, SecretKey, SignatureError}; //! # use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; -//! # fn do_test() -> Result<(SecretKey, PublicKey, Keypair, Signature), SignatureError> { +//! # fn do_test() -> Result<(SigningKey, VerifyingKey, Signature), SignatureError> { //! # let mut csprng = OsRng{}; -//! # let keypair_orig: Keypair = Keypair::generate(&mut csprng); +//! # let signing_key_orig: SigningKey = SigningKey::generate(&mut csprng); //! # let message: &[u8] = b"This is a test of the tsunami alert system."; -//! # let signature_orig: Signature = keypair_orig.sign(message); -//! # let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = keypair_orig.public_key().to_bytes(); -//! # let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = keypair_orig.secret_key().to_bytes(); -//! # let keypair_bytes: [u8; KEYPAIR_LENGTH] = keypair_orig.to_bytes(); +//! # let signature_orig: Signature = signing_key_orig.sign(message); +//! # let verifying_key_bytes: [u8; PUBLIC_KEY_LENGTH] = signing_key_orig.verifying_key().to_bytes(); +//! # let signing_key_bytes: [u8; SECRET_KEY_LENGTH] = signing_key_orig.to_bytes(); //! # let signature_bytes: [u8; SIGNATURE_LENGTH] = signature_orig.to_bytes(); //! # -//! let public_key: PublicKey = PublicKey::from_bytes(&public_key_bytes)?; -//! let secret_key: SecretKey = SecretKey::from_bytes(&secret_key_bytes)?; -//! let keypair: Keypair = Keypair::from_bytes(&keypair_bytes)?; -//! let signature: Signature = Signature::try_from(&signature_bytes[..])?; +//! let verifying_key: VerifyingKey = VerifyingKey::from_bytes(&verifying_key_bytes)?; +//! let signing_key: SigningKey = SigningKey::from_bytes(&signing_key_bytes); +//! let signature: Signature = Signature::try_from(&signature_bytes[..])?; //! # -//! # Ok((secret_key, public_key, keypair, signature)) +//! # Ok((signing_key, verifying_key, signature)) //! # } //! # fn main() { //! # do_test(); @@ -151,14 +149,14 @@ //! //! To use PKCS#8, you need to enable the `pkcs8` crate feature. //! -//! The following traits can be used to decode/encode [`Keypair`] and -//! [`PublicKey`] as PKCS#8. Note that [`pkcs8`] is re-exported from the +//! The following traits can be used to decode/encode [`SigningKey`] and +//! [`VerifyingKey`] as PKCS#8. Note that [`pkcs8`] is re-exported from the //! toplevel of the crate: //! //! - [`pkcs8::DecodePrivateKey`]: decode private keys from PKCS#8 //! - [`pkcs8::EncodePrivateKey`]: encode private keys to PKCS#8 -//! - [`pkcs8::DecodePublicKey`]: decode public keys from PKCS#8 -//! - [`pkcs8::EncodePublicKey`]: encode public keys to PKCS#8 +//! - [`pkcs8::DecodeVerifyingKey`]: decode public keys from PKCS#8 +//! - [`pkcs8::EncodeVerifyingKey`]: encode public keys to PKCS#8 //! //! #### Example //! @@ -166,13 +164,13 @@ //! #![cfg_attr(feature = "pem", doc = "```")] #![cfg_attr(not(feature = "pem"), doc = "```ignore")] -//! use ed25519_dalek::{PublicKey, pkcs8::DecodePublicKey}; +//! use ed25519_dalek::{VerifyingKey, pkcs8::DecodeVerifyingKey}; //! //! let pem = "-----BEGIN PUBLIC KEY----- //! MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE= //! -----END PUBLIC KEY-----"; //! -//! let public_key = PublicKey::from_public_key_pem(pem) +//! let verifying_key = VerifyingKey::from_verifying_key_pem(pem) //! .expect("invalid public key PEM"); //! ``` //! @@ -193,48 +191,48 @@ //! # #[cfg(feature = "serde")] //! # fn main() { //! # use rand::rngs::OsRng; -//! # use ed25519_dalek::{Keypair, Signature, Signer, Verifier, PublicKey}; +//! # use ed25519_dalek::{SigningKey, Signature, Signer, Verifier, VerifyingKey}; //! use bincode::serialize; //! # let mut csprng = OsRng{}; -//! # let keypair: Keypair = Keypair::generate(&mut csprng); +//! # let signing_key: SigningKey = SigningKey::generate(&mut csprng); //! # let message: &[u8] = b"This is a test of the tsunami alert system."; -//! # let signature: Signature = keypair.sign(message); -//! # let public_key: PublicKey = keypair.public_key(); -//! # let verified: bool = public_key.verify(message, &signature).is_ok(); +//! # let signature: Signature = signing_key.sign(message); +//! # let verifying_key: VerifyingKey = signing_key.verifying_key(); +//! # let verified: bool = verifying_key.verify(message, &signature).is_ok(); //! -//! let encoded_public_key: Vec = serialize(&public_key).unwrap(); +//! let encoded_verifying_key: Vec = serialize(&verifying_key).unwrap(); //! let encoded_signature: Vec = serialize(&signature).unwrap(); //! # } //! # #[cfg(not(feature = "serde"))] //! # fn main() {} //! ``` //! -//! After sending the `encoded_public_key` and `encoded_signature`, the +//! After sending the `encoded_verifying_key` and `encoded_signature`, the //! recipient may deserialise them and verify: //! //! ``` //! # #[cfg(feature = "serde")] //! # fn main() { //! # use rand::rngs::OsRng; -//! # use ed25519_dalek::{Keypair, Signature, Signer, Verifier, PublicKey}; +//! # use ed25519_dalek::{SigningKey, Signature, Signer, Verifier, VerifyingKey}; //! # use bincode::serialize; //! use bincode::deserialize; //! //! # let mut csprng = OsRng{}; -//! # let keypair: Keypair = Keypair::generate(&mut csprng); +//! # let signing_key: SigningKey = SigningKey::generate(&mut csprng); //! let message: &[u8] = b"This is a test of the tsunami alert system."; -//! # let signature: Signature = keypair.sign(message); -//! # let public_key: PublicKey = keypair.public_key(); -//! # let verified: bool = public_key.verify(message, &signature).is_ok(); -//! # let encoded_public_key: Vec = serialize(&public_key).unwrap(); +//! # let signature: Signature = signing_key.sign(message); +//! # let verifying_key: VerifyingKey = signing_key.verifying_key(); +//! # let verified: bool = verifying_key.verify(message, &signature).is_ok(); +//! # let encoded_verifying_key: Vec = serialize(&verifying_key).unwrap(); //! # let encoded_signature: Vec = serialize(&signature).unwrap(); -//! let decoded_public_key: PublicKey = deserialize(&encoded_public_key).unwrap(); +//! let decoded_verifying_key: VerifyingKey = deserialize(&encoded_verifying_key).unwrap(); //! let decoded_signature: Signature = deserialize(&encoded_signature).unwrap(); //! -//! # assert_eq!(public_key, decoded_public_key); +//! # assert_eq!(verifying_key, decoded_verifying_key); //! # assert_eq!(signature, decoded_signature); //! # -//! let verified: bool = decoded_public_key.verify(&message, &decoded_signature).is_ok(); +//! let verified: bool = decoded_verifying_key.verify(&message, &decoded_signature).is_ok(); //! //! assert!(verified); //! # } @@ -265,10 +263,9 @@ pub use ed25519; mod batch; mod constants; mod errors; -mod keypair; -mod public; -mod secret; mod signature; +mod signing; +mod verifying; pub use curve25519_dalek::digest::Digest; @@ -276,9 +273,8 @@ pub use curve25519_dalek::digest::Digest; pub use crate::batch::*; pub use crate::constants::*; pub use crate::errors::*; -pub use crate::keypair::*; -pub use crate::public::*; -pub use crate::secret::*; +pub use crate::signing::*; +pub use crate::verifying::*; // Re-export the `Signer` and `Verifier` traits from the `signature` crate pub use ed25519::signature::{Signer, Verifier}; diff --git a/src/secret.rs b/src/secret.rs deleted file mode 100644 index 8f00276..0000000 --- a/src/secret.rs +++ /dev/null @@ -1,432 +0,0 @@ -// -*- mode: rust; -*- -// -// This file is part of ed25519-dalek. -// Copyright (c) 2017-2019 isis lovecruft -// See LICENSE for licensing information. -// -// Authors: -// - isis agora lovecruft - -//! ed25519 secret key types. - -use core::fmt::Debug; - -use curve25519_dalek::constants; -use curve25519_dalek::digest::generic_array::typenum::U64; -use curve25519_dalek::digest::Digest; -use curve25519_dalek::edwards::CompressedEdwardsY; -use curve25519_dalek::scalar::Scalar; - -#[cfg(feature = "rand")] -use rand::{CryptoRng, RngCore}; - -use sha2::Sha512; - -#[cfg(feature = "serde")] -use serde::de::Error as SerdeError; -#[cfg(feature = "serde")] -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -#[cfg(feature = "serde")] -use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes}; - -use zeroize::Zeroize; - -use crate::constants::*; -use crate::errors::*; -use crate::public::*; -use crate::signature::*; - -/// An EdDSA secret key. -/// -/// Instances of this secret are automatically overwritten with zeroes when they -/// fall out of scope. -pub struct SecretKey(pub(crate) [u8; SECRET_KEY_LENGTH]); - -impl Drop for SecretKey { - fn drop(&mut self) { - self.0.zeroize() - } -} - -impl Debug for SecretKey { - fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - write!(f, "SecretKey: {:?}", &self.0[..]) - } -} - -impl AsRef<[u8]> for SecretKey { - fn as_ref(&self) -> &[u8] { - self.as_bytes() - } -} - -impl SecretKey { - /// Convert this secret key to a byte array. - #[inline] - pub fn to_bytes(&self) -> [u8; SECRET_KEY_LENGTH] { - self.0 - } - - /// View this secret key as a byte array. - #[inline] - pub fn as_bytes<'a>(&'a self) -> &'a [u8; SECRET_KEY_LENGTH] { - &self.0 - } - - /// Construct a `SecretKey` from a slice of bytes. - /// - /// # Example - /// - /// ``` - /// use ed25519_dalek::SecretKey; - /// use ed25519_dalek::SECRET_KEY_LENGTH; - /// use ed25519_dalek::SignatureError; - /// - /// # fn doctest() -> Result { - /// let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = [ - /// 157, 097, 177, 157, 239, 253, 090, 096, - /// 186, 132, 074, 244, 146, 236, 044, 196, - /// 068, 073, 197, 105, 123, 050, 105, 025, - /// 112, 059, 172, 003, 028, 174, 127, 096, ]; - /// - /// let secret_key: SecretKey = SecretKey::from_bytes(&secret_key_bytes)?; - /// # - /// # Ok(secret_key) - /// # } - /// # - /// # fn main() { - /// # let result = doctest(); - /// # assert!(result.is_ok()); - /// # } - /// ``` - /// - /// # Returns - /// - /// A `Result` whose okay value is an EdDSA `SecretKey` or whose error value - /// is an `SignatureError` wrapping the internal error that occurred. - #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != SECRET_KEY_LENGTH { - return Err(InternalError::BytesLengthError { - name: "SecretKey", - length: SECRET_KEY_LENGTH, - } - .into()); - } - let mut bits: [u8; 32] = [0u8; 32]; - bits.copy_from_slice(&bytes[..32]); - - Ok(SecretKey(bits)) - } - - /// Generate a `SecretKey` from a `csprng`. - /// - /// # Example - /// - /// ``` - /// # #[cfg(feature = "std")] - /// # fn main() { - /// # - /// use rand::rngs::OsRng; - /// use ed25519_dalek::PublicKey; - /// use ed25519_dalek::SecretKey; - /// use ed25519_dalek::Signature; - /// - /// let mut csprng = OsRng{}; - /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); - /// # } - /// # - /// # #[cfg(not(feature = "std"))] - /// # fn main() { } - /// ``` - /// - /// Afterwards, you can generate the corresponding public: - /// - /// ``` - /// # fn main() { - /// # - /// # use rand::rngs::OsRng; - /// # use ed25519_dalek::PublicKey; - /// # use ed25519_dalek::SecretKey; - /// # use ed25519_dalek::Signature; - /// # - /// # let mut csprng = OsRng{}; - /// # let secret_key: SecretKey = SecretKey::generate(&mut csprng); - /// - /// let public_key: PublicKey = (&secret_key).into(); - /// # } - /// ``` - /// - /// # Input - /// - /// A CSPRNG with a `fill_bytes()` method, e.g. `rand::OsRng` - #[cfg(feature = "rand")] - pub fn generate(csprng: &mut T) -> SecretKey - where - T: CryptoRng + RngCore, - { - let mut sk: SecretKey = SecretKey([0u8; 32]); - - csprng.fill_bytes(&mut sk.0); - - sk - } -} - -#[cfg(feature = "serde")] -impl Serialize for SecretKey { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - SerdeBytes::new(self.as_bytes()).serialize(serializer) - } -} - -#[cfg(feature = "serde")] -impl<'d> Deserialize<'d> for SecretKey { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'d>, - { - let bytes = ::deserialize(deserializer)?; - SecretKey::from_bytes(bytes.as_ref()).map_err(SerdeError::custom) - } -} - -/// An "expanded" secret key. -/// -/// This is produced by using an hash function with 512-bits output to digest a -/// `SecretKey`. The output digest is then split in half, the lower half being -/// the actual `key` used to sign messages, after twiddling with some bits.¹ The -/// upper half is used a sort of half-baked, ill-designed² pseudo-domain-separation -/// "nonce"-like thing, which is used during signature production by -/// concatenating it with the message to be signed before the message is hashed. -/// -/// Instances of this secret are automatically overwritten with zeroes when they -/// fall out of scope. -// -// ¹ This results in a slight bias towards non-uniformity at one spectrum of -// the range of valid keys. Oh well: not my idea; not my problem. -// -// ² It is the author's view (specifically, isis agora lovecruft, in the event -// you'd like to complain about me, again) that this is "ill-designed" because -// this doesn't actually provide true hash domain separation, in that in many -// real-world applications a user wishes to have one key which is used in -// several contexts (such as within tor, which does domain separation -// manually by pre-concatenating static strings to messages to achieve more -// robust domain separation). In other real-world applications, such as -// bitcoind, a user might wish to have one master keypair from which others are -// derived (à la BIP32) and different domain separators between keys derived at -// different levels (and similarly for tree-based key derivation constructions, -// such as hash-based signatures). Leaving the domain separation to -// application designers, who thus far have produced incompatible, -// slightly-differing, ad hoc domain separation (at least those application -// designers who knew enough cryptographic theory to do so!), is therefore a -// bad design choice on the part of the cryptographer designing primitives -// which should be simple and as foolproof as possible to use for -// non-cryptographers. Further, later in the ed25519 signature scheme, as -// specified in RFC8032, the public key is added into *another* hash digest -// (along with the message, again); it is unclear to this author why there's -// not only one but two poorly-thought-out attempts at domain separation in the -// same signature scheme, and which both fail in exactly the same way. For a -// better-designed, Schnorr-based signature scheme, see Trevor Perrin's work on -// "generalised EdDSA" and "VXEdDSA". -pub(crate) struct ExpandedSecretKey { - pub(crate) key: Scalar, - pub(crate) nonce: [u8; 32], -} - -impl Drop for ExpandedSecretKey { - fn drop(&mut self) { - self.key.zeroize(); - self.nonce.zeroize() - } -} - -impl<'a> From<&'a SecretKey> for ExpandedSecretKey { - /// Construct an `ExpandedSecretKey` from a `SecretKey`. - /// - /// # Examples - /// - /// ```ignore - /// # fn main() { - /// # - /// use rand::rngs::OsRng; - /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; - /// - /// let mut csprng = OsRng{}; - /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); - /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key); - /// # } - /// ``` - fn from(secret_key: &'a SecretKey) -> ExpandedSecretKey { - let mut h: Sha512 = Sha512::default(); - let mut hash: [u8; 64] = [0u8; 64]; - let mut lower: [u8; 32] = [0u8; 32]; - let mut upper: [u8; 32] = [0u8; 32]; - - h.update(secret_key.as_bytes()); - hash.copy_from_slice(h.finalize().as_slice()); - - lower.copy_from_slice(&hash[00..32]); - upper.copy_from_slice(&hash[32..64]); - - lower[0] &= 248; - lower[31] &= 63; - lower[31] |= 64; - - ExpandedSecretKey { - key: Scalar::from_bits(lower), - nonce: upper, - } - } -} - -impl ExpandedSecretKey { - /// Sign a message with this `ExpandedSecretKey`. - #[allow(non_snake_case)] - pub(crate) fn sign(&self, message: &[u8], public_key: &PublicKey) -> ed25519::Signature { - let mut h: Sha512 = Sha512::new(); - let R: CompressedEdwardsY; - let r: Scalar; - let s: Scalar; - let k: Scalar; - - h.update(&self.nonce); - h.update(&message); - - r = Scalar::from_hash(h); - R = (&r * &constants::ED25519_BASEPOINT_TABLE).compress(); - - h = Sha512::new(); - h.update(R.as_bytes()); - h.update(public_key.as_bytes()); - h.update(&message); - - k = Scalar::from_hash(h); - s = &(&k * &self.key) + &r; - - InternalSignature { R, s }.into() - } - - /// Sign a `prehashed_message` with this `ExpandedSecretKey` using the - /// Ed25519ph algorithm defined in [RFC8032 §5.1][rfc8032]. - /// - /// # Inputs - /// - /// * `prehashed_message` is an instantiated hash digest with 512-bits of - /// output which has had the message to be signed previously fed into its - /// state. - /// * `public_key` is a [`PublicKey`] which corresponds to this secret key. - /// * `context` is an optional context string, up to 255 bytes inclusive, - /// which may be used to provide additional domain separation. If not - /// set, this will default to an empty string. - /// - /// # Returns - /// - /// A `Result` whose `Ok` value is an Ed25519ph [`Signature`] on the - /// `prehashed_message` if the context was 255 bytes or less, otherwise - /// a `SignatureError`. - /// - /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 - #[allow(non_snake_case)] - pub(crate) fn sign_prehashed<'a, D>( - &self, - prehashed_message: D, - public_key: &PublicKey, - context: Option<&'a [u8]>, - ) -> Result - where - D: Digest, - { - let mut h: Sha512; - let mut prehash: [u8; 64] = [0u8; 64]; - let R: CompressedEdwardsY; - let r: Scalar; - let s: Scalar; - let k: Scalar; - - let ctx: &[u8] = context.unwrap_or(b""); // By default, the context is an empty string. - - if ctx.len() > 255 { - return Err(SignatureError::from( - InternalError::PrehashedContextLengthError, - )); - } - - let ctx_len: u8 = ctx.len() as u8; - - // Get the result of the pre-hashed message. - prehash.copy_from_slice(prehashed_message.finalize().as_slice()); - - // This is the dumbest, ten-years-late, non-admission of fucking up the - // domain separation I have ever seen. Why am I still required to put - // the upper half "prefix" of the hashed "secret key" in here? Why - // can't the user just supply their own nonce and decide for themselves - // whether or not they want a deterministic signature scheme? Why does - // the message go into what's ostensibly the signature domain separation - // hash? Why wasn't there always a way to provide a context string? - // - // ... - // - // This is a really fucking stupid bandaid, and the damned scheme is - // still bleeding from malleability, for fuck's sake. - h = Sha512::new() - .chain_update(b"SigEd25519 no Ed25519 collisions") - .chain_update(&[1]) // Ed25519ph - .chain_update(&[ctx_len]) - .chain_update(ctx) - .chain_update(&self.nonce) - .chain_update(&prehash[..]); - - r = Scalar::from_hash(h); - R = (&r * &constants::ED25519_BASEPOINT_TABLE).compress(); - - h = Sha512::new() - .chain_update(b"SigEd25519 no Ed25519 collisions") - .chain_update(&[1]) // Ed25519ph - .chain_update(&[ctx_len]) - .chain_update(ctx) - .chain_update(R.as_bytes()) - .chain_update(public_key.as_bytes()) - .chain_update(&prehash[..]); - - k = Scalar::from_hash(h); - s = &(&k * &self.key) + &r; - - Ok(InternalSignature { R, s }.into()) - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn secret_key_zeroize_on_drop() { - let secret_ptr: *const u8; - - { - // scope for the secret to ensure it's been dropped - let secret = SecretKey::from_bytes(&[0x15u8; 32][..]).unwrap(); - - secret_ptr = secret.0.as_ptr(); - } - - let memory: &[u8] = unsafe { ::std::slice::from_raw_parts(secret_ptr, 32) }; - - assert!(!memory.contains(&0x15)); - } - - #[test] - fn pubkey_from_secret_and_expanded_secret() { - let mut csprng = rand::rngs::OsRng {}; - let secret: SecretKey = SecretKey::generate(&mut csprng); - let expanded_secret: ExpandedSecretKey = (&secret).into(); - let public_from_secret: PublicKey = (&secret).into(); // XXX eww - let public_from_expanded_secret: PublicKey = (&expanded_secret).into(); // XXX eww - - assert!(public_from_secret == public_from_expanded_secret); - } -} diff --git a/src/signing.rs b/src/signing.rs new file mode 100644 index 0000000..719c18f --- /dev/null +++ b/src/signing.rs @@ -0,0 +1,818 @@ +// -*- mode: rust; -*- +// +// This file is part of ed25519-dalek. +// Copyright (c) 2017-2019 isis lovecruft +// See LICENSE for licensing information. +// +// Authors: +// - isis agora lovecruft + +//! ed25519 signing keys. + +#[cfg(feature = "pkcs8")] +use ed25519::pkcs8::{self, DecodePrivateKey}; + +#[cfg(feature = "rand")] +use rand::{CryptoRng, RngCore}; + +#[cfg(feature = "serde")] +use serde::de::Error as SerdeError; +#[cfg(feature = "serde")] +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +#[cfg(feature = "serde")] +use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes}; + +use sha2::Sha512; + +use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; +use curve25519_dalek::digest::generic_array::typenum::U64; +use curve25519_dalek::digest::Digest; +use curve25519_dalek::edwards::CompressedEdwardsY; +use curve25519_dalek::scalar::Scalar; + +use ed25519::signature::{KeypairRef, Signer, Verifier}; + +use zeroize::Zeroize; + +use crate::constants::*; +use crate::errors::*; +use crate::signature::*; +use crate::verifying::*; + +/// ed25519 secret key as defined in [RFC8032 § 5.1.5]: +/// +/// > The private key is 32 octets (256 bits, corresponding to b) of +/// > cryptographically secure random data. +/// +/// [RFC8032 § 5.1.5]: https://www.rfc-editor.org/rfc/rfc8032#section-5.1.5 +pub type SecretKey = [u8; SECRET_KEY_LENGTH]; + +/// ed25519 signing key which can be used to produce signatures. +// Invariant: `public` is always the public key of `secret`. This prevents the signing function +// oracle attack described in https://github.com/MystenLabs/ed25519-unsafe-libs +#[derive(Debug)] +pub struct SigningKey { + /// The secret half of this signing key. + pub(crate) secret_key: SecretKey, + /// The public half of this signing key. + pub(crate) verifying_key: VerifyingKey, +} + +impl SigningKey { + /// Construct a [`SigningKey`] from a slice of bytes. + /// + /// # Example + /// + /// ``` + /// # extern crate ed25519_dalek; + /// # + /// use ed25519_dalek::SigningKey; + /// use ed25519_dalek::SECRET_KEY_LENGTH; + /// use ed25519_dalek::SignatureError; + /// + /// # fn doctest() -> Result { + /// let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = [ + /// 157, 097, 177, 157, 239, 253, 090, 096, + /// 186, 132, 074, 244, 146, 236, 044, 196, + /// 068, 073, 197, 105, 123, 050, 105, 025, + /// 112, 059, 172, 003, 028, 174, 127, 096, ]; + /// + /// let signing_key: SigningKey = SigningKey::from_bytes(&secret_key_bytes); + /// # + /// # Ok(signing_key) + /// # } + /// # + /// # fn main() { + /// # let result = doctest(); + /// # assert!(result.is_ok()); + /// # } + /// ``` + /// + /// # Returns + /// + /// A `Result` whose okay value is an EdDSA `SecretKey` or whose error value + /// is an `SignatureError` wrapping the internal error that occurred. + #[inline] + pub fn from_bytes(secret_key: &SecretKey) -> Self { + let verifying_key = VerifyingKey::from(&ExpandedSecretKey::from(secret_key)); + Self { + secret_key: *secret_key, + verifying_key, + } + } + + /// Convert this secret key to a byte array. + #[inline] + pub fn to_bytes(&self) -> SecretKey { + self.secret_key + } + + /// Construct a [`SigningKey`] from the bytes of a `VerifyingKey` and `SecretKey`. + /// + /// # Inputs + /// + /// * `bytes`: an `&[u8]` of length [`KEYPAIR_LENGTH`], representing the + /// scalar for the secret key, and a compressed Edwards-Y coordinate of a + /// point on curve25519, both as bytes. (As obtained from + /// [`SigningKey::to_bytes`].) + /// + /// # Returns + /// + /// A `Result` whose okay value is an EdDSA [`SigningKey`] or whose error value + /// is an `SignatureError` describing the error that occurred. + #[inline] + pub fn from_keypair_bytes(bytes: &[u8; 64]) -> Result { + if bytes.len() != KEYPAIR_LENGTH { + return Err(InternalError::BytesLengthError { + name: "SigningKey", + length: KEYPAIR_LENGTH, + } + .into()); + } + + let secret_key = + SecretKey::try_from(&bytes[..SECRET_KEY_LENGTH]).map_err(|_| SignatureError::new())?; + let verifying_key = VerifyingKey::from_bytes(&bytes[SECRET_KEY_LENGTH..])?; + + if verifying_key != VerifyingKey::from(&secret_key) { + return Err(InternalError::MismatchedKeypairError.into()); + } + + Ok(SigningKey { + secret_key, + verifying_key, + }) + } + + /// Convert this signing key to bytes. + /// + /// # Returns + /// + /// An array of bytes, `[u8; KEYPAIR_LENGTH]`. The first + /// `SECRET_KEY_LENGTH` of bytes is the `SecretKey`, and the next + /// `PUBLIC_KEY_LENGTH` bytes is the `VerifyingKey` (the same as other + /// libraries, such as [Adam Langley's ed25519 Golang + /// implementation](https://github.com/agl/ed25519/)). It is guaranteed that + /// the encoded public key is the one derived from the encoded secret key. + pub fn to_keypair_bytes(&self) -> [u8; KEYPAIR_LENGTH] { + let mut bytes: [u8; KEYPAIR_LENGTH] = [0u8; KEYPAIR_LENGTH]; + + bytes[..SECRET_KEY_LENGTH].copy_from_slice(&self.secret_key); + bytes[SECRET_KEY_LENGTH..].copy_from_slice(self.verifying_key.as_bytes()); + bytes + } + + /// Get the [`VerifyingKey`] for this [`SigningKey`]. + pub fn verifying_key(&self) -> VerifyingKey { + self.verifying_key + } + + /// Generate an ed25519 signing key. + /// + /// # Example + /// + /// ``` + /// # #[cfg(feature = "std")] + /// # fn main() { + /// + /// use rand::rngs::OsRng; + /// use ed25519_dalek::SigningKey; + /// use ed25519_dalek::Signature; + /// + /// let mut csprng = OsRng{}; + /// let signing_key: SigningKey = SigningKey::generate(&mut csprng); + /// + /// # } + /// # + /// # #[cfg(not(feature = "std"))] + /// # fn main() { } + /// ``` + /// + /// # Input + /// + /// A CSPRNG with a `fill_bytes()` method, e.g. `rand_os::OsRng`. + /// + /// The caller must also supply a hash function which implements the + /// `Digest` and `Default` traits, and which returns 512 bits of output. + /// The standard hash function used for most ed25519 libraries is SHA-512, + /// which is available with `use sha2::Sha512` as in the example above. + /// Other suitable hash functions include Keccak-512 and Blake2b-512. + #[cfg(feature = "rand")] + pub fn generate(csprng: &mut R) -> SigningKey + where + R: CryptoRng + RngCore, + { + let mut secret = SecretKey::default(); + csprng.fill_bytes(&mut secret); + Self::from_bytes(&secret) + } + + /// Sign a `prehashed_message` with this [`SigningKey`] using the + /// Ed25519ph algorithm defined in [RFC8032 §5.1][rfc8032]. + /// + /// # Inputs + /// + /// * `prehashed_message` is an instantiated hash digest with 512-bits of + /// output which has had the message to be signed previously fed into its + /// state. + /// * `context` is an optional context string, up to 255 bytes inclusive, + /// which may be used to provide additional domain separation. If not + /// set, this will default to an empty string. + /// + /// # Returns + /// + /// An Ed25519ph [`Signature`] on the `prehashed_message`. + /// + /// # Examples + /// + /// ``` + /// use ed25519_dalek::Digest; + /// use ed25519_dalek::SigningKey; + /// use ed25519_dalek::Sha512; + /// use ed25519_dalek::Signature; + /// use rand::rngs::OsRng; + /// + /// # #[cfg(feature = "std")] + /// # fn main() { + /// let mut csprng = OsRng{}; + /// let signing_key: SigningKey = SigningKey::generate(&mut csprng); + /// let message: &[u8] = b"All I want is to pet all of the dogs."; + /// + /// // Create a hash digest object which we'll feed the message into: + /// let mut prehashed: Sha512 = Sha512::new(); + /// + /// prehashed.update(message); + /// # } + /// # + /// # #[cfg(not(feature = "std"))] + /// # fn main() { } + /// ``` + /// + /// If you want, you can optionally pass a "context". It is generally a + /// good idea to choose a context and try to make it unique to your project + /// and this specific usage of signatures. + /// + /// For example, without this, if you were to [convert your OpenPGP key + /// to a Bitcoin key][terrible_idea] (just as an example, and also Don't + /// Ever Do That) and someone tricked you into signing an "email" which was + /// actually a Bitcoin transaction moving all your magic internet money to + /// their address, it'd be a valid transaction. + /// + /// By adding a context, this trick becomes impossible, because the context + /// is concatenated into the hash, which is then signed. So, going with the + /// previous example, if your bitcoin wallet used a context of + /// "BitcoinWalletAppTxnSigning" and OpenPGP used a context (this is likely + /// the least of their safety problems) of "GPGsCryptoIsntConstantTimeLol", + /// then the signatures produced by both could never match the other, even + /// if they signed the exact same message with the same key. + /// + /// Let's add a context for good measure (remember, you'll want to choose + /// your own!): + /// + /// ``` + /// # use ed25519_dalek::Digest; + /// # use ed25519_dalek::SigningKey; + /// # use ed25519_dalek::Signature; + /// # use ed25519_dalek::SignatureError; + /// # use ed25519_dalek::Sha512; + /// # use rand::rngs::OsRng; + /// # + /// # fn do_test() -> Result { + /// # let mut csprng = OsRng{}; + /// # let signing_key: SigningKey = SigningKey::generate(&mut csprng); + /// # let message: &[u8] = b"All I want is to pet all of the dogs."; + /// # let mut prehashed: Sha512 = Sha512::new(); + /// # prehashed.update(message); + /// # + /// let context: &[u8] = b"Ed25519DalekSignPrehashedDoctest"; + /// + /// let sig: Signature = signing_key.sign_prehashed(prehashed, Some(context))?; + /// # + /// # Ok(sig) + /// # } + /// # #[cfg(feature = "std")] + /// # fn main() { + /// # do_test(); + /// # } + /// # + /// # #[cfg(not(feature = "std"))] + /// # fn main() { } + /// ``` + /// + /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 + /// [terrible_idea]: https://github.com/isislovecruft/scripts/blob/master/gpgkey2bc.py + pub fn sign_prehashed( + &self, + prehashed_message: D, + context: Option<&[u8]>, + ) -> Result + where + D: Digest, + { + let expanded: ExpandedSecretKey = (&self.secret_key).into(); // xxx thanks i hate this + + expanded + .sign_prehashed(prehashed_message, &self.verifying_key, context) + .into() + } + + /// Verify a signature on a message with this signing key's public key. + pub fn verify( + &self, + message: &[u8], + signature: &ed25519::Signature, + ) -> Result<(), SignatureError> { + self.verifying_key.verify(message, signature) + } + + /// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm. + /// + /// # Inputs + /// + /// * `prehashed_message` is an instantiated hash digest with 512-bits of + /// output which has had the message to be signed previously fed into its + /// state. + /// * `context` is an optional context string, up to 255 bytes inclusive, + /// which may be used to provide additional domain separation. If not + /// set, this will default to an empty string. + /// * `signature` is a purported Ed25519ph [`Signature`] on the `prehashed_message`. + /// + /// # Returns + /// + /// Returns `true` if the `signature` was a valid signature created by this + /// [`SigningKey`] on the `prehashed_message`. + /// + /// # Examples + /// + /// ``` + /// use ed25519_dalek::Digest; + /// use ed25519_dalek::SigningKey; + /// use ed25519_dalek::Signature; + /// use ed25519_dalek::SignatureError; + /// use ed25519_dalek::Sha512; + /// use rand::rngs::OsRng; + /// + /// # fn do_test() -> Result<(), SignatureError> { + /// let mut csprng = OsRng{}; + /// let signing_key: SigningKey = SigningKey::generate(&mut csprng); + /// let message: &[u8] = b"All I want is to pet all of the dogs."; + /// + /// let mut prehashed: Sha512 = Sha512::new(); + /// prehashed.update(message); + /// + /// let context: &[u8] = b"Ed25519DalekSignPrehashedDoctest"; + /// + /// let sig: Signature = signing_key.sign_prehashed(prehashed, Some(context))?; + /// + /// // The sha2::Sha512 struct doesn't implement Copy, so we'll have to create a new one: + /// let mut prehashed_again: Sha512 = Sha512::default(); + /// prehashed_again.update(message); + /// + /// let verified = signing_key.verifying_key().verify_prehashed(prehashed_again, Some(context), &sig); + /// + /// assert!(verified.is_ok()); + /// + /// # verified + /// # } + /// # + /// # #[cfg(feature = "std")] + /// # fn main() { + /// # do_test(); + /// # } + /// # + /// # #[cfg(not(feature = "std"))] + /// # fn main() { } + /// ``` + /// + /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 + pub fn verify_prehashed( + &self, + prehashed_message: D, + context: Option<&[u8]>, + signature: &ed25519::Signature, + ) -> Result<(), SignatureError> + where + D: Digest, + { + self.verifying_key + .verify_prehashed(prehashed_message, context, signature) + } + + /// Strictly verify a signature on a message with this signing key's public key. + /// + /// # On The (Multiple) Sources of Malleability in Ed25519 Signatures + /// + /// This version of verification is technically non-RFC8032 compliant. The + /// following explains why. + /// + /// 1. Scalar Malleability + /// + /// The authors of the RFC explicitly stated that verification of an ed25519 + /// signature must fail if the scalar `s` is not properly reduced mod \ell: + /// + /// > To verify a signature on a message M using public key A, with F + /// > being 0 for Ed25519ctx, 1 for Ed25519ph, and if Ed25519ctx or + /// > Ed25519ph is being used, C being the context, first split the + /// > signature into two 32-octet halves. Decode the first half as a + /// > point R, and the second half as an integer S, in the range + /// > 0 <= s < L. Decode the public key A as point A'. If any of the + /// > decodings fail (including S being out of range), the signature is + /// > invalid.) + /// + /// All `verify_*()` functions within ed25519-dalek perform this check. + /// + /// 2. Point malleability + /// + /// The authors of the RFC added in a malleability check to step #3 in + /// §5.1.7, for small torsion components in the `R` value of the signature, + /// *which is not strictly required*, as they state: + /// + /// > Check the group equation \[8\]\[S\]B = \[8\]R + \[8\]\[k\]A'. It's + /// > sufficient, but not required, to instead check \[S\]B = R + \[k\]A'. + /// + /// # History of Malleability Checks + /// + /// As originally defined (cf. the "Malleability" section in the README of + /// this repo), ed25519 signatures didn't consider *any* form of + /// malleability to be an issue. Later the scalar malleability was + /// considered important. Still later, particularly with interests in + /// cryptocurrency design and in unique identities (e.g. for Signal users, + /// Tor onion services, etc.), the group element malleability became a + /// concern. + /// + /// However, libraries had already been created to conform to the original + /// definition. One well-used library in particular even implemented the + /// group element malleability check, *but only for batch verification*! + /// Which meant that even using the same library, a single signature could + /// verify fine individually, but suddenly, when verifying it with a bunch + /// of other signatures, the whole batch would fail! + /// + /// # "Strict" Verification + /// + /// This method performs *both* of the above signature malleability checks. + /// + /// It must be done as a separate method because one doesn't simply get to + /// change the definition of a cryptographic primitive ten years + /// after-the-fact with zero consideration for backwards compatibility in + /// hardware and protocols which have it already have the older definition + /// baked in. + /// + /// # Return + /// + /// Returns `Ok(())` if the signature is valid, and `Err` otherwise. + #[allow(non_snake_case)] + pub fn verify_strict( + &self, + message: &[u8], + signature: &ed25519::Signature, + ) -> Result<(), SignatureError> { + self.verifying_key.verify_strict(message, signature) + } +} + +impl AsRef for SigningKey { + fn as_ref(&self) -> &VerifyingKey { + &self.verifying_key + } +} + +impl KeypairRef for SigningKey { + type VerifyingKey = VerifyingKey; +} + +impl Signer for SigningKey { + /// Sign a message with this signing key's secret key. + fn try_sign(&self, message: &[u8]) -> Result { + let expanded: ExpandedSecretKey = (&self.secret_key).into(); + Ok(expanded.sign(&message, &self.verifying_key).into()) + } +} + +impl Verifier for SigningKey { + /// Verify a signature on a message with this signing key's public key. + fn verify(&self, message: &[u8], signature: &ed25519::Signature) -> Result<(), SignatureError> { + self.verifying_key.verify(message, signature) + } +} + +impl From for SigningKey { + #[inline] + fn from(secret: SecretKey) -> Self { + Self::from_bytes(&secret) + } +} + +impl From<&SecretKey> for SigningKey { + #[inline] + fn from(secret: &SecretKey) -> Self { + Self::from_bytes(secret) + } +} + +impl TryFrom<&[u8]> for SigningKey { + type Error = SignatureError; + + fn try_from(bytes: &[u8]) -> Result { + SecretKey::try_from(bytes) + .map(|bytes| Self::from_bytes(&bytes)) + .map_err(|_| { + InternalError::BytesLengthError { + name: "SecretKey", + length: SECRET_KEY_LENGTH, + } + .into() + }) + } +} + +#[cfg(feature = "pkcs8")] +impl DecodePrivateKey for SigningKey {} + +#[cfg(all(feature = "alloc", feature = "pkcs8"))] +impl pkcs8::EncodePrivateKey for SigningKey { + fn to_pkcs8_der(&self) -> pkcs8::Result { + pkcs8::KeypairBytes::from(self).to_pkcs8_der() + } +} + +#[cfg(feature = "pkcs8")] +impl TryFrom for SigningKey { + type Error = pkcs8::Error; + + fn try_from(pkcs8_key: pkcs8::KeypairBytes) -> pkcs8::Result { + SigningKey::try_from(&pkcs8_key) + } +} + +#[cfg(feature = "pkcs8")] +impl TryFrom<&pkcs8::KeypairBytes> for SigningKey { + type Error = pkcs8::Error; + + fn try_from(pkcs8_key: &pkcs8::KeypairBytes) -> pkcs8::Result { + // Validate the public key in the PKCS#8 document if present + if let Some(public_bytes) = pkcs8_key.public_key { + let expected_verifying_key = VerifyingKey::from(&pkcs8_key.secret_key); + + let pkcs8_verifying_key = VerifyingKey::from_bytes(public_bytes.as_ref()) + .map_err(|_| pkcs8::Error::KeyMalformed)?; + + if expected_verifying_key != pkcs8_verifying_key { + return Err(pkcs8::Error::KeyMalformed); + } + } + + Ok(SigningKey::from_bytes(&pkcs8_key.secret_key)) + } +} + +#[cfg(feature = "pkcs8")] +impl From for pkcs8::KeypairBytes { + fn from(signing_key: SigningKey) -> pkcs8::KeypairBytes { + pkcs8::KeypairBytes::from(&signing_key) + } +} + +#[cfg(feature = "pkcs8")] +impl From<&SigningKey> for pkcs8::KeypairBytes { + fn from(signing_key: &SigningKey) -> pkcs8::KeypairBytes { + pkcs8::KeypairBytes { + secret_key: signing_key.to_bytes(), + public_key: Some(pkcs8::PublicKeyBytes(signing_key.verifying_key.to_bytes())), + } + } +} + +#[cfg(feature = "pkcs8")] +impl TryFrom> for SigningKey { + type Error = pkcs8::Error; + + fn try_from(private_key: pkcs8::PrivateKeyInfo<'_>) -> pkcs8::Result { + pkcs8::KeypairBytes::try_from(private_key)?.try_into() + } +} + +#[cfg(feature = "serde")] +impl Serialize for SigningKey { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + SerdeBytes::new(&self.secret_key).serialize(serializer) + } +} + +#[cfg(feature = "serde")] +impl<'d> Deserialize<'d> for SigningKey { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'d>, + { + let bytes = ::deserialize(deserializer)?; + Self::try_from(bytes.as_ref()).map_err(SerdeError::custom) + } +} + +/// An "expanded" secret key. +/// +/// This is produced by using an hash function with 512-bits output to digest a +/// `SecretKey`. The output digest is then split in half, the lower half being +/// the actual `key` used to sign messages, after twiddling with some bits.¹ The +/// upper half is used a sort of half-baked, ill-designed² pseudo-domain-separation +/// "nonce"-like thing, which is used during signature production by +/// concatenating it with the message to be signed before the message is hashed. +/// +/// Instances of this secret are automatically overwritten with zeroes when they +/// fall out of scope. +// +// ¹ This results in a slight bias towards non-uniformity at one spectrum of +// the range of valid keys. Oh well: not my idea; not my problem. +// +// ² It is the author's view (specifically, isis agora lovecruft, in the event +// you'd like to complain about me, again) that this is "ill-designed" because +// this doesn't actually provide true hash domain separation, in that in many +// real-world applications a user wishes to have one key which is used in +// several contexts (such as within tor, which does domain separation +// manually by pre-concatenating static strings to messages to achieve more +// robust domain separation). In other real-world applications, such as +// bitcoind, a user might wish to have one master keypair from which others are +// derived (à la BIP32) and different domain separators between keys derived at +// different levels (and similarly for tree-based key derivation constructions, +// such as hash-based signatures). Leaving the domain separation to +// application designers, who thus far have produced incompatible, +// slightly-differing, ad hoc domain separation (at least those application +// designers who knew enough cryptographic theory to do so!), is therefore a +// bad design choice on the part of the cryptographer designing primitives +// which should be simple and as foolproof as possible to use for +// non-cryptographers. Further, later in the ed25519 signature scheme, as +// specified in RFC8032, the public key is added into *another* hash digest +// (along with the message, again); it is unclear to this author why there's +// not only one but two poorly-thought-out attempts at domain separation in the +// same signature scheme, and which both fail in exactly the same way. For a +// better-designed, Schnorr-based signature scheme, see Trevor Perrin's work on +// "generalised EdDSA" and "VXEdDSA". +pub(crate) struct ExpandedSecretKey { + pub(crate) key: Scalar, + pub(crate) nonce: [u8; 32], +} + +impl Drop for ExpandedSecretKey { + fn drop(&mut self) { + self.key.zeroize(); + self.nonce.zeroize() + } +} + +impl From<&SecretKey> for ExpandedSecretKey { + /// Construct an `ExpandedSecretKey` from a `SecretKey`. + /// + /// # Examples + /// + /// ```ignore + /// # fn main() { + /// # + /// use rand::rngs::OsRng; + /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; + /// + /// let mut csprng = OsRng{}; + /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); + /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key); + /// # } + /// ``` + fn from(secret_key: &SecretKey) -> ExpandedSecretKey { + let mut h: Sha512 = Sha512::default(); + let mut hash: [u8; 64] = [0u8; 64]; + let mut lower: [u8; 32] = [0u8; 32]; + let mut upper: [u8; 32] = [0u8; 32]; + + h.update(secret_key); + hash.copy_from_slice(h.finalize().as_slice()); + + lower.copy_from_slice(&hash[00..32]); + upper.copy_from_slice(&hash[32..64]); + + lower[0] &= 248; + lower[31] &= 63; + lower[31] |= 64; + + ExpandedSecretKey { + key: Scalar::from_bits(lower), + nonce: upper, + } + } +} + +impl ExpandedSecretKey { + /// Sign a message with this `ExpandedSecretKey`. + #[allow(non_snake_case)] + pub(crate) fn sign(&self, message: &[u8], verifying_key: &VerifyingKey) -> ed25519::Signature { + let mut h: Sha512 = Sha512::new(); + let R: CompressedEdwardsY; + let r: Scalar; + let s: Scalar; + let k: Scalar; + + h.update(&self.nonce); + h.update(&message); + + r = Scalar::from_hash(h); + R = (&r * &ED25519_BASEPOINT_TABLE).compress(); + + h = Sha512::new(); + h.update(R.as_bytes()); + h.update(verifying_key.as_bytes()); + h.update(&message); + + k = Scalar::from_hash(h); + s = &(&k * &self.key) + &r; + + InternalSignature { R, s }.into() + } + + /// Sign a `prehashed_message` with this `ExpandedSecretKey` using the + /// Ed25519ph algorithm defined in [RFC8032 §5.1][rfc8032]. + /// + /// # Inputs + /// + /// * `prehashed_message` is an instantiated hash digest with 512-bits of + /// output which has had the message to be signed previously fed into its + /// state. + /// * `verifying_key` is a [`VerifyingKey`] which corresponds to this secret key. + /// * `context` is an optional context string, up to 255 bytes inclusive, + /// which may be used to provide additional domain separation. If not + /// set, this will default to an empty string. + /// + /// # Returns + /// + /// A `Result` whose `Ok` value is an Ed25519ph [`Signature`] on the + /// `prehashed_message` if the context was 255 bytes or less, otherwise + /// a `SignatureError`. + /// + /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 + #[allow(non_snake_case)] + pub(crate) fn sign_prehashed<'a, D>( + &self, + prehashed_message: D, + verifying_key: &VerifyingKey, + context: Option<&'a [u8]>, + ) -> Result + where + D: Digest, + { + let mut h: Sha512; + let mut prehash: [u8; 64] = [0u8; 64]; + let R: CompressedEdwardsY; + let r: Scalar; + let s: Scalar; + let k: Scalar; + + let ctx: &[u8] = context.unwrap_or(b""); // By default, the context is an empty string. + + if ctx.len() > 255 { + return Err(SignatureError::from( + InternalError::PrehashedContextLengthError, + )); + } + + let ctx_len: u8 = ctx.len() as u8; + + // Get the result of the pre-hashed message. + prehash.copy_from_slice(prehashed_message.finalize().as_slice()); + + // This is the dumbest, ten-years-late, non-admission of fucking up the + // domain separation I have ever seen. Why am I still required to put + // the upper half "prefix" of the hashed "secret key" in here? Why + // can't the user just supply their own nonce and decide for themselves + // whether or not they want a deterministic signature scheme? Why does + // the message go into what's ostensibly the signature domain separation + // hash? Why wasn't there always a way to provide a context string? + // + // ... + // + // This is a really fucking stupid bandaid, and the damned scheme is + // still bleeding from malleability, for fuck's sake. + h = Sha512::new() + .chain_update(b"SigEd25519 no Ed25519 collisions") + .chain_update(&[1]) // Ed25519ph + .chain_update(&[ctx_len]) + .chain_update(ctx) + .chain_update(&self.nonce) + .chain_update(&prehash[..]); + + r = Scalar::from_hash(h); + R = (&r * &ED25519_BASEPOINT_TABLE).compress(); + + h = Sha512::new() + .chain_update(b"SigEd25519 no Ed25519 collisions") + .chain_update(&[1]) // Ed25519ph + .chain_update(&[ctx_len]) + .chain_update(ctx) + .chain_update(R.as_bytes()) + .chain_update(verifying_key.as_bytes()) + .chain_update(&prehash[..]); + + k = Scalar::from_hash(h); + s = &(&k * &self.key) + &r; + + Ok(InternalSignature { R, s }.into()) + } +} diff --git a/src/public.rs b/src/verifying.rs similarity index 83% rename from src/public.rs rename to src/verifying.rs index a16dbed..f699798 100644 --- a/src/public.rs +++ b/src/verifying.rs @@ -35,51 +35,53 @@ use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes}; use crate::constants::*; use crate::errors::*; -use crate::secret::*; use crate::signature::*; +use crate::signing::*; /// An ed25519 public key. #[derive(Copy, Clone, Default, Eq, PartialEq)] -pub struct PublicKey(pub(crate) CompressedEdwardsY, pub(crate) EdwardsPoint); +pub struct VerifyingKey(pub(crate) CompressedEdwardsY, pub(crate) EdwardsPoint); -impl Debug for PublicKey { +impl Debug for VerifyingKey { fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - write!(f, "PublicKey({:?}), {:?})", self.0, self.1) + write!(f, "VerifyingKey({:?}), {:?})", self.0, self.1) } } -impl AsRef<[u8]> for PublicKey { +impl AsRef<[u8]> for VerifyingKey { fn as_ref(&self) -> &[u8] { self.as_bytes() } } -impl<'a> From<&'a SecretKey> for PublicKey { +impl From<&SecretKey> for VerifyingKey { /// Derive this public key from its corresponding `SecretKey`. - fn from(secret_key: &SecretKey) -> PublicKey { + fn from(secret_key: &SecretKey) -> VerifyingKey { let mut h: Sha512 = Sha512::new(); let mut hash: [u8; 64] = [0u8; 64]; let mut digest: [u8; 32] = [0u8; 32]; - h.update(secret_key.as_bytes()); + h.update(secret_key); hash.copy_from_slice(h.finalize().as_slice()); digest.copy_from_slice(&hash[..32]); - PublicKey::mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(&mut digest) + VerifyingKey::mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key( + &mut digest, + ) } } -impl<'a> From<&'a ExpandedSecretKey> for PublicKey { +impl From<&ExpandedSecretKey> for VerifyingKey { /// Derive this public key from its corresponding `ExpandedSecretKey`. - fn from(expanded_secret_key: &ExpandedSecretKey) -> PublicKey { + fn from(expanded_secret_key: &ExpandedSecretKey) -> VerifyingKey { let mut bits: [u8; 32] = expanded_secret_key.key.to_bytes(); - PublicKey::mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(&mut bits) + VerifyingKey::mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(&mut bits) } } -impl PublicKey { +impl VerifyingKey { /// Convert this public key to a byte array. #[inline] pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_LENGTH] { @@ -92,7 +94,7 @@ impl PublicKey { &(self.0).0 } - /// Construct a `PublicKey` from a slice of bytes. + /// Construct a `VerifyingKey` from a slice of bytes. /// /// # Warning /// @@ -103,16 +105,16 @@ impl PublicKey { /// # Example /// /// ``` - /// use ed25519_dalek::PublicKey; + /// use ed25519_dalek::VerifyingKey; /// use ed25519_dalek::PUBLIC_KEY_LENGTH; /// use ed25519_dalek::SignatureError; /// - /// # fn doctest() -> Result { + /// # fn doctest() -> Result { /// let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = [ /// 215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, /// 14, 225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26]; /// - /// let public_key = PublicKey::from_bytes(&public_key_bytes)?; + /// let public_key = VerifyingKey::from_bytes(&public_key_bytes)?; /// # /// # Ok(public_key) /// # } @@ -124,13 +126,13 @@ impl PublicKey { /// /// # Returns /// - /// A `Result` whose okay value is an EdDSA `PublicKey` or whose error value + /// A `Result` whose okay value is an EdDSA `VerifyingKey` or whose error value /// is an `SignatureError` describing the error that occurred. #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { + pub fn from_bytes(bytes: &[u8]) -> Result { if bytes.len() != PUBLIC_KEY_LENGTH { return Err(InternalError::BytesLengthError { - name: "PublicKey", + name: "VerifyingKey", length: PUBLIC_KEY_LENGTH, } .into()); @@ -143,7 +145,7 @@ impl PublicKey { .decompress() .ok_or(InternalError::PointDecompressionError)?; - Ok(PublicKey(compressed, point)) + Ok(VerifyingKey(compressed, point)) } /// Internal utility function for mangling the bits of a (formerly @@ -151,7 +153,7 @@ impl PublicKey { /// public key. fn mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key( bits: &mut [u8; 32], - ) -> PublicKey { + ) -> VerifyingKey { bits[0] &= 248; bits[31] &= 127; bits[31] |= 64; @@ -159,7 +161,7 @@ impl PublicKey { let point = &Scalar::from_bits(*bits) * &constants::ED25519_BASEPOINT_TABLE; let compressed = point.compress(); - PublicKey(compressed, point) + VerifyingKey(compressed, point) } /// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm. @@ -323,7 +325,7 @@ impl PublicKey { } } -impl Verifier for PublicKey { +impl Verifier for VerifyingKey { /// Verify a signature on a message with this keypair's public key. /// /// # Return @@ -353,58 +355,58 @@ impl Verifier for PublicKey { } } -impl TryFrom<&[u8]> for PublicKey { +impl TryFrom<&[u8]> for VerifyingKey { type Error = SignatureError; - fn try_from(bytes: &[u8]) -> Result { - PublicKey::from_bytes(bytes) + fn try_from(bytes: &[u8]) -> Result { + VerifyingKey::from_bytes(bytes) } } #[cfg(feature = "pkcs8")] -impl DecodePublicKey for PublicKey {} +impl DecodePublicKey for VerifyingKey {} #[cfg(all(feature = "alloc", feature = "pkcs8"))] -impl pkcs8::EncodePublicKey for PublicKey { +impl pkcs8::EncodePublicKey for VerifyingKey { fn to_public_key_der(&self) -> pkcs8::spki::Result { pkcs8::PublicKeyBytes::from(self).to_public_key_der() } } #[cfg(feature = "pkcs8")] -impl TryFrom for PublicKey { +impl TryFrom for VerifyingKey { type Error = pkcs8::spki::Error; fn try_from(pkcs8_key: pkcs8::PublicKeyBytes) -> pkcs8::spki::Result { - PublicKey::try_from(&pkcs8_key) + VerifyingKey::try_from(&pkcs8_key) } } #[cfg(feature = "pkcs8")] -impl TryFrom<&pkcs8::PublicKeyBytes> for PublicKey { +impl TryFrom<&pkcs8::PublicKeyBytes> for VerifyingKey { type Error = pkcs8::spki::Error; fn try_from(pkcs8_key: &pkcs8::PublicKeyBytes) -> pkcs8::spki::Result { - PublicKey::from_bytes(pkcs8_key.as_ref()).map_err(|_| pkcs8::spki::Error::KeyMalformed) + VerifyingKey::from_bytes(pkcs8_key.as_ref()).map_err(|_| pkcs8::spki::Error::KeyMalformed) } } #[cfg(feature = "pkcs8")] -impl From for pkcs8::PublicKeyBytes { - fn from(public_key: PublicKey) -> pkcs8::PublicKeyBytes { - pkcs8::PublicKeyBytes::from(&public_key) +impl From for pkcs8::PublicKeyBytes { + fn from(verifying_key: VerifyingKey) -> pkcs8::PublicKeyBytes { + pkcs8::PublicKeyBytes::from(&verifying_key) } } #[cfg(feature = "pkcs8")] -impl From<&PublicKey> for pkcs8::PublicKeyBytes { - fn from(public_key: &PublicKey) -> pkcs8::PublicKeyBytes { - pkcs8::PublicKeyBytes(public_key.to_bytes()) +impl From<&VerifyingKey> for pkcs8::PublicKeyBytes { + fn from(verifying_key: &VerifyingKey) -> pkcs8::PublicKeyBytes { + pkcs8::PublicKeyBytes(verifying_key.to_bytes()) } } #[cfg(feature = "pkcs8")] -impl TryFrom> for PublicKey { +impl TryFrom> for VerifyingKey { type Error = pkcs8::spki::Error; fn try_from(public_key: pkcs8::spki::SubjectPublicKeyInfo<'_>) -> pkcs8::spki::Result { @@ -413,7 +415,7 @@ impl TryFrom> for PublicKey { } #[cfg(feature = "serde")] -impl Serialize for PublicKey { +impl Serialize for VerifyingKey { fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -423,12 +425,12 @@ impl Serialize for PublicKey { } #[cfg(feature = "serde")] -impl<'d> Deserialize<'d> for PublicKey { +impl<'d> Deserialize<'d> for VerifyingKey { fn deserialize(deserializer: D) -> Result where D: Deserializer<'d>, { let bytes = ::deserialize(deserializer)?; - PublicKey::from_bytes(bytes.as_ref()).map_err(SerdeError::custom) + VerifyingKey::from_bytes(bytes.as_ref()).map_err(SerdeError::custom) } } diff --git a/tests/ed25519.rs b/tests/ed25519.rs index bd597db..87b8164 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -61,20 +61,19 @@ mod vectors { let msg_bytes: Vec = FromHex::from_hex(&parts[2]).unwrap(); let sig_bytes: Vec = FromHex::from_hex(&parts[3]).unwrap(); - let secret: SecretKey = SecretKey::from_bytes(&sec_bytes[..SECRET_KEY_LENGTH]).unwrap(); - let expected_public: PublicKey = - PublicKey::from_bytes(&pub_bytes[..PUBLIC_KEY_LENGTH]).unwrap(); - let keypair: Keypair = Keypair::from(secret); - assert_eq!(expected_public, keypair.public_key()); + let signing_key = SigningKey::try_from(&sec_bytes[..SECRET_KEY_LENGTH]).unwrap(); + let expected_verifying_key = + VerifyingKey::from_bytes(&pub_bytes[..PUBLIC_KEY_LENGTH]).unwrap(); + assert_eq!(expected_verifying_key, signing_key.verifying_key()); // The signatures in the test vectors also include the message // at the end, but we just want R and S. let sig1: Signature = Signature::try_from(&sig_bytes[..64]).unwrap(); - let sig2: Signature = keypair.sign(&msg_bytes); + let sig2: Signature = signing_key.sign(&msg_bytes); assert!(sig1 == sig2, "Signature bytes not equal on line {}", lineno); assert!( - keypair.verify(&msg_bytes, &sig2).is_ok(), + signing_key.verify(&msg_bytes, &sig2).is_ok(), "Signature verification failed on line {}", lineno ); @@ -85,20 +84,21 @@ mod vectors { #[test] fn ed25519ph_rf8032_test_vector() { let secret_key: &[u8] = b"833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42"; - let public_key: &[u8] = b"ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf"; + let verifying_key: &[u8] = + b"ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf"; let message: &[u8] = b"616263"; let signature: &[u8] = b"98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406"; let sec_bytes: Vec = FromHex::from_hex(secret_key).unwrap(); - let pub_bytes: Vec = FromHex::from_hex(public_key).unwrap(); + let pub_bytes: Vec = FromHex::from_hex(verifying_key).unwrap(); let msg_bytes: Vec = FromHex::from_hex(message).unwrap(); let sig_bytes: Vec = FromHex::from_hex(signature).unwrap(); - let secret: SecretKey = SecretKey::from_bytes(&sec_bytes[..SECRET_KEY_LENGTH]).unwrap(); - let expected_public: PublicKey = - PublicKey::from_bytes(&pub_bytes[..PUBLIC_KEY_LENGTH]).unwrap(); - let keypair: Keypair = Keypair::from(secret); - assert_eq!(expected_public, keypair.public_key()); + let signing_key: SigningKey = + SigningKey::try_from(&sec_bytes[..SECRET_KEY_LENGTH]).unwrap(); + let expected_verifying_key: VerifyingKey = + VerifyingKey::from_bytes(&pub_bytes[..PUBLIC_KEY_LENGTH]).unwrap(); + assert_eq!(expected_verifying_key, signing_key.verifying_key()); let sig1: Signature = Signature::try_from(&sig_bytes[..]).unwrap(); let mut prehash_for_signing: Sha512 = Sha512::default(); @@ -107,7 +107,9 @@ mod vectors { prehash_for_signing.update(&msg_bytes[..]); prehash_for_verifying.update(&msg_bytes[..]); - let sig2: Signature = keypair.sign_prehashed(prehash_for_signing, None).unwrap(); + let sig2: Signature = signing_key + .sign_prehashed(prehash_for_signing, None) + .unwrap(); assert!( sig1 == sig2, @@ -117,7 +119,7 @@ mod vectors { sig2 ); assert!( - keypair + signing_key .verify_prehashed(prehash_for_verifying, None, &sig2) .is_ok(), "Could not verify ed25519ph signature!" @@ -185,7 +187,7 @@ mod vectors { } let signature = serialize_signature(&r, &s); - let pk = PublicKey::from_bytes(&pub_key.compress().as_bytes()[..]).unwrap(); + let pk = VerifyingKey::from_bytes(&pub_key.compress().as_bytes()[..]).unwrap(); let sig = Signature::try_from(&signature[..]).unwrap(); // The same signature verifies for both messages assert!(pk.verify(message1, &sig).is_ok() && pk.verify(message2, &sig).is_ok()); @@ -204,7 +206,7 @@ mod integrations { #[test] fn sign_verify() { // TestSignVerify - let keypair: Keypair; + let signing_key: SigningKey; let good_sig: Signature; let bad_sig: Signature; @@ -213,27 +215,27 @@ mod integrations { let mut csprng = OsRng {}; - keypair = Keypair::generate(&mut csprng); - good_sig = keypair.sign(&good); - bad_sig = keypair.sign(&bad); + signing_key = SigningKey::generate(&mut csprng); + good_sig = signing_key.sign(&good); + bad_sig = signing_key.sign(&bad); assert!( - keypair.verify(&good, &good_sig).is_ok(), + signing_key.verify(&good, &good_sig).is_ok(), "Verification of a valid signature failed!" ); assert!( - keypair.verify(&good, &bad_sig).is_err(), + signing_key.verify(&good, &bad_sig).is_err(), "Verification of a signature on a different message passed!" ); assert!( - keypair.verify(&bad, &good_sig).is_err(), + signing_key.verify(&bad, &good_sig).is_err(), "Verification of a signature on a different message passed!" ); } #[test] fn ed25519ph_sign_verify() { - let keypair: Keypair; + let signing_key: SigningKey; let good_sig: Signature; let bad_sig: Signature; @@ -257,28 +259,28 @@ mod integrations { let context: &[u8] = b"testing testing 1 2 3"; - keypair = Keypair::generate(&mut csprng); - good_sig = keypair + signing_key = SigningKey::generate(&mut csprng); + good_sig = signing_key .sign_prehashed(prehashed_good1, Some(context)) .unwrap(); - bad_sig = keypair + bad_sig = signing_key .sign_prehashed(prehashed_bad1, Some(context)) .unwrap(); assert!( - keypair + signing_key .verify_prehashed(prehashed_good2, Some(context), &good_sig) .is_ok(), "Verification of a valid signature failed!" ); assert!( - keypair + signing_key .verify_prehashed(prehashed_good3, Some(context), &bad_sig) .is_err(), "Verification of a signature on a different message passed!" ); assert!( - keypair + signing_key .verify_prehashed(prehashed_bad2, Some(context), &good_sig) .is_err(), "Verification of a signature on a different message passed!" @@ -297,17 +299,18 @@ mod integrations { b"Hey, I never cared about your bucks, so if I run up with a mask on, probably got a gas can too.", b"And I'm not here to fill 'er up. Nope, we came to riot, here to incite, we don't want any of your stuff.", ]; let mut csprng = OsRng; - let mut keypairs: Vec = Vec::new(); + let mut signing_keys: Vec = Vec::new(); let mut signatures: Vec = Vec::new(); for i in 0..messages.len() { - let keypair: Keypair = Keypair::generate(&mut csprng); - signatures.push(keypair.sign(&messages[i])); - keypairs.push(keypair); + let signing_key: SigningKey = SigningKey::generate(&mut csprng); + signatures.push(signing_key.sign(&messages[i])); + signing_keys.push(signing_key); } - let public_keys: Vec = keypairs.iter().map(|key| key.public_key()).collect(); + let verifying_keys: Vec = + signing_keys.iter().map(|key| key.verifying_key()).collect(); - let result = verify_batch(&messages, &signatures[..], &public_keys[..]); + let result = verify_batch(&messages, &signatures[..], &verifying_keys[..]); assert!(result.is_ok()); } @@ -317,7 +320,7 @@ mod integrations { #[derive(Debug, serde_crate::Serialize, serde_crate::Deserialize)] #[serde(crate = "serde_crate")] struct Demo { - keypair: Keypair, + signing_key: SigningKey, } #[cfg(all(test, feature = "serde"))] @@ -337,7 +340,7 @@ mod serialisation { 150, 073, 201, 137, 076, 022, 085, 251, 152, 002, 241, 042, 072, 054, ]; - /// Signature with the above keypair of a blank message. + /// Signature with the above signing_key of a blank message. static SIGNATURE_BYTES: [u8; SIGNATURE_LENGTH] = [ 010, 126, 151, 143, 157, 064, 047, 001, 196, 140, 179, 058, 226, 152, 018, 102, 160, 123, 080, 016, 210, 086, 196, 028, 053, 231, 012, 157, 169, 019, 158, 063, 045, 154, 238, 007, @@ -345,13 +348,6 @@ mod serialisation { 041, 081, 063, 120, 126, 100, 092, 059, 050, 011, ]; - static KEYPAIR_BYTES: [u8; KEYPAIR_LENGTH] = [ - 239, 085, 017, 235, 167, 103, 034, 062, 007, 010, 032, 146, 113, 039, 096, 174, 003, 219, - 232, 166, 240, 121, 167, 013, 098, 238, 122, 116, 193, 114, 215, 213, 175, 181, 075, 166, - 224, 164, 140, 146, 053, 120, 010, 037, 104, 094, 136, 225, 249, 102, 171, 160, 097, 132, - 015, 071, 035, 056, 000, 074, 130, 168, 225, 071, - ]; - #[test] fn serialize_deserialize_signature_bincode() { let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES).unwrap(); @@ -371,75 +367,55 @@ mod serialisation { } #[test] - fn serialize_deserialize_public_key_bincode() { - let public_key: PublicKey = PublicKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap(); - let encoded_public_key: Vec = bincode::serialize(&public_key).unwrap(); - let decoded_public_key: PublicKey = bincode::deserialize(&encoded_public_key).unwrap(); + fn serialize_deserialize_verifying_key_bincode() { + let verifying_key: VerifyingKey = VerifyingKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap(); + let encoded_verifying_key: Vec = bincode::serialize(&verifying_key).unwrap(); + let decoded_verifying_key: VerifyingKey = + bincode::deserialize(&encoded_verifying_key).unwrap(); assert_eq!( &PUBLIC_KEY_BYTES[..], - &encoded_public_key[encoded_public_key.len() - PUBLIC_KEY_LENGTH..] + &encoded_verifying_key[encoded_verifying_key.len() - PUBLIC_KEY_LENGTH..] ); - assert_eq!(public_key, decoded_public_key); + assert_eq!(verifying_key, decoded_verifying_key); } #[test] - fn serialize_deserialize_public_key_json() { - let public_key: PublicKey = PublicKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap(); - let encoded_public_key = serde_json::to_string(&public_key).unwrap(); - let decoded_public_key: PublicKey = serde_json::from_str(&encoded_public_key).unwrap(); + fn serialize_deserialize_verifying_key_json() { + let verifying_key: VerifyingKey = VerifyingKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap(); + let encoded_verifying_key = serde_json::to_string(&verifying_key).unwrap(); + let decoded_verifying_key: VerifyingKey = + serde_json::from_str(&encoded_verifying_key).unwrap(); - assert_eq!(public_key, decoded_public_key); + assert_eq!(verifying_key, decoded_verifying_key); } #[test] - fn serialize_deserialize_secret_key_bincode() { - let secret_key: SecretKey = SecretKey::from_bytes(&SECRET_KEY_BYTES).unwrap(); - let encoded_secret_key: Vec = bincode::serialize(&secret_key).unwrap(); - let decoded_secret_key: SecretKey = bincode::deserialize(&encoded_secret_key).unwrap(); + fn serialize_deserialize_signing_key_bincode() { + let signing_key = SigningKey::from_bytes(&SECRET_KEY_BYTES); + let encoded_signing_key: Vec = bincode::serialize(&signing_key).unwrap(); + let decoded_signing_key: SigningKey = bincode::deserialize(&encoded_signing_key).unwrap(); for i in 0..SECRET_KEY_LENGTH { - assert_eq!(SECRET_KEY_BYTES[i], decoded_secret_key.as_bytes()[i]); + assert_eq!(SECRET_KEY_BYTES[i], decoded_signing_key.to_bytes()[i]); } } #[test] - fn serialize_deserialize_secret_key_json() { - let secret_key: SecretKey = SecretKey::from_bytes(&SECRET_KEY_BYTES).unwrap(); - let encoded_secret_key = serde_json::to_string(&secret_key).unwrap(); - let decoded_secret_key: SecretKey = serde_json::from_str(&encoded_secret_key).unwrap(); + fn serialize_deserialize_signing_key_json() { + let signing_key = SigningKey::from_bytes(&SECRET_KEY_BYTES); + let encoded_signing_key = serde_json::to_string(&signing_key).unwrap(); + let decoded_signing_key: SigningKey = serde_json::from_str(&encoded_signing_key).unwrap(); for i in 0..SECRET_KEY_LENGTH { - assert_eq!(SECRET_KEY_BYTES[i], decoded_secret_key.as_bytes()[i]); + assert_eq!(SECRET_KEY_BYTES[i], decoded_signing_key.to_bytes()[i]); } } #[test] - fn serialize_deserialize_keypair_bincode() { - let keypair = Keypair::from_bytes(&KEYPAIR_BYTES).unwrap(); - let encoded_keypair: Vec = bincode::serialize(&keypair).unwrap(); - let decoded_keypair: Keypair = bincode::deserialize(&encoded_keypair).unwrap(); - - for i in 0..KEYPAIR_LENGTH { - assert_eq!(KEYPAIR_BYTES[i], decoded_keypair.to_bytes()[i]); - } - } - - #[test] - fn serialize_deserialize_keypair_json() { - let keypair = Keypair::from_bytes(&KEYPAIR_BYTES).unwrap(); - let encoded_keypair = serde_json::to_string(&keypair).unwrap(); - let decoded_keypair: Keypair = serde_json::from_str(&encoded_keypair).unwrap(); - - for i in 0..KEYPAIR_LENGTH { - assert_eq!(KEYPAIR_BYTES[i], decoded_keypair.to_bytes()[i]); - } - } - - #[test] - fn serialize_deserialize_keypair_toml() { + fn serialize_deserialize_signing_key_toml() { let demo = Demo { - keypair: Keypair::from_bytes(&KEYPAIR_BYTES).unwrap(), + signing_key: SigningKey::from_bytes(&SECRET_KEY_BYTES), }; println!("\n\nWrite to toml"); @@ -450,10 +426,10 @@ mod serialisation { } #[test] - fn serialize_public_key_size() { - let public_key: PublicKey = PublicKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap(); + fn serialize_verifying_key_size() { + let verifying_key: VerifyingKey = VerifyingKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap(); assert_eq!( - bincode::serialized_size(&public_key).unwrap() as usize, + bincode::serialized_size(&verifying_key).unwrap() as usize, BINCODE_INT_LENGTH + PUBLIC_KEY_LENGTH ); } @@ -468,20 +444,11 @@ mod serialisation { } #[test] - fn serialize_secret_key_size() { - let secret_key: SecretKey = SecretKey::from_bytes(&SECRET_KEY_BYTES).unwrap(); + fn serialize_signing_key_size() { + let signing_key = SigningKey::from_bytes(&SECRET_KEY_BYTES); assert_eq!( - bincode::serialized_size(&secret_key).unwrap() as usize, + bincode::serialized_size(&signing_key).unwrap() as usize, BINCODE_INT_LENGTH + SECRET_KEY_LENGTH ); } - - #[test] - fn serialize_keypair_size() { - let keypair = Keypair::from_bytes(&KEYPAIR_BYTES).unwrap(); - assert_eq!( - bincode::serialized_size(&keypair).unwrap() as usize, - BINCODE_INT_LENGTH + KEYPAIR_LENGTH - ); - } } diff --git a/tests/pkcs8.rs b/tests/pkcs8.rs index 0af97f5..fecdba9 100644 --- a/tests/pkcs8.rs +++ b/tests/pkcs8.rs @@ -6,14 +6,11 @@ #![cfg(feature = "pkcs8")] use ed25519_dalek::pkcs8::{DecodePrivateKey, DecodePublicKey}; -use ed25519_dalek::{Keypair, PublicKey}; +use ed25519_dalek::{SigningKey, VerifyingKey}; use hex_literal::hex; #[cfg(feature = "alloc")] -use ed25519_dalek::{ - pkcs8::{EncodePrivateKey, EncodePublicKey}, - SecretKey, -}; +use ed25519_dalek::pkcs8::{EncodePrivateKey, EncodePublicKey}; /// Ed25519 PKCS#8 v1 private key encoded as ASN.1 DER. const PKCS8_V1_DER: &[u8] = include_bytes!("examples/pkcs8-v1.der"); @@ -21,7 +18,7 @@ const PKCS8_V1_DER: &[u8] = include_bytes!("examples/pkcs8-v1.der"); /// Ed25519 PKCS#8 v2 private key + public key encoded as ASN.1 DER. const PKCS8_V2_DER: &[u8] = include_bytes!("examples/pkcs8-v2.der"); -/// Ed25519 SubjectPublicKeyInfo encoded as ASN.1 DER. +/// Ed25519 SubjectVerifyingKeyInfo encoded as ASN.1 DER. const PUBLIC_KEY_DER: &[u8] = include_bytes!("examples/pubkey.der"); /// Secret key bytes. @@ -35,40 +32,40 @@ const PK_BYTES: [u8; 32] = hex!("19BF44096984CDFE8541BAC167DC3B96C85086AA30B6B6C #[test] fn decode_pkcs8_v1() { - let keypair = Keypair::from_pkcs8_der(PKCS8_V1_DER).unwrap(); - assert_eq!(SK_BYTES, keypair.secret_key().to_bytes()); - assert_eq!(PK_BYTES, keypair.public_key().to_bytes()); + let keypair = SigningKey::from_pkcs8_der(PKCS8_V1_DER).unwrap(); + assert_eq!(SK_BYTES, keypair.to_bytes()); + assert_eq!(PK_BYTES, keypair.verifying_key().to_bytes()); } #[test] fn decode_pkcs8_v2() { - let keypair = Keypair::from_pkcs8_der(PKCS8_V2_DER).unwrap(); - assert_eq!(SK_BYTES, keypair.secret_key().to_bytes()); - assert_eq!(PK_BYTES, keypair.public_key().to_bytes()); + let keypair = SigningKey::from_pkcs8_der(PKCS8_V2_DER).unwrap(); + assert_eq!(SK_BYTES, keypair.to_bytes()); + assert_eq!(PK_BYTES, keypair.verifying_key().to_bytes()); } #[test] -fn decode_public_key() { - let public_key = PublicKey::from_public_key_der(PUBLIC_KEY_DER).unwrap(); - assert_eq!(PK_BYTES, public_key.to_bytes()); +fn decode_verifying_key() { + let verifying_key = VerifyingKey::from_public_key_der(PUBLIC_KEY_DER).unwrap(); + assert_eq!(PK_BYTES, verifying_key.to_bytes()); } #[test] #[cfg(feature = "alloc")] fn encode_pkcs8() { - let keypair = Keypair::from(SecretKey::from_bytes(&SK_BYTES).unwrap()); + let keypair = SigningKey::from_bytes(&SK_BYTES); let pkcs8_key = keypair.to_pkcs8_der().unwrap(); - let keypair2 = Keypair::from_pkcs8_der(pkcs8_key.as_bytes()).unwrap(); + let keypair2 = SigningKey::from_pkcs8_der(pkcs8_key.as_bytes()).unwrap(); assert_eq!(keypair.to_bytes(), keypair2.to_bytes()); } #[test] #[cfg(feature = "alloc")] -fn encode_public_key() { - let public_key = PublicKey::from_bytes(&PK_BYTES).unwrap(); - let public_key_der = public_key.to_public_key_der().unwrap(); +fn encode_verifying_key() { + let verifying_key = VerifyingKey::from_bytes(&PK_BYTES).unwrap(); + let verifying_key_der = verifying_key.to_public_key_der().unwrap(); - let public_key2 = PublicKey::from_public_key_der(public_key_der.as_bytes()).unwrap(); - assert_eq!(public_key, public_key2); + let verifying_key2 = VerifyingKey::from_public_key_der(verifying_key_der.as_bytes()).unwrap(); + assert_eq!(verifying_key, verifying_key2); } From 134b5e174d7a9f4f345950a662173448b95315e8 Mon Sep 17 00:00:00 2001 From: pinkforest <36498018+pinkforest@users.noreply.github.com> Date: Sun, 18 Dec 2022 19:02:18 +1100 Subject: [PATCH 304/351] Fix SigningKey to/from_bytes doc/coverage --- src/signing.rs | 62 +++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/src/signing.rs b/src/signing.rs index 719c18f..1194b93 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -58,40 +58,36 @@ pub struct SigningKey { pub(crate) verifying_key: VerifyingKey, } +/// # Example +/// +/// ``` +/// # extern crate ed25519_dalek; +/// # +/// use ed25519_dalek::SigningKey; +/// use ed25519_dalek::SECRET_KEY_LENGTH; +/// use ed25519_dalek::SignatureError; +/// +/// # fn doctest() -> Result { +/// let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = [ +/// 157, 097, 177, 157, 239, 253, 090, 096, +/// 186, 132, 074, 244, 146, 236, 044, 196, +/// 068, 073, 197, 105, 123, 050, 105, 025, +/// 112, 059, 172, 003, 028, 174, 127, 096, ]; +/// +/// let signing_key: SigningKey = SigningKey::from_bytes(&secret_key_bytes); +/// assert_eq!(signing_key.to_bytes(), secret_key_bytes); +/// +/// # Ok(signing_key) +/// # } +/// # +/// # fn main() { +/// # let result = doctest(); +/// # assert!(result.is_ok()); +/// # } +/// ``` impl SigningKey { - /// Construct a [`SigningKey`] from a slice of bytes. + /// Construct a [`SigningKey`] from a [`SecretKey`] /// - /// # Example - /// - /// ``` - /// # extern crate ed25519_dalek; - /// # - /// use ed25519_dalek::SigningKey; - /// use ed25519_dalek::SECRET_KEY_LENGTH; - /// use ed25519_dalek::SignatureError; - /// - /// # fn doctest() -> Result { - /// let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = [ - /// 157, 097, 177, 157, 239, 253, 090, 096, - /// 186, 132, 074, 244, 146, 236, 044, 196, - /// 068, 073, 197, 105, 123, 050, 105, 025, - /// 112, 059, 172, 003, 028, 174, 127, 096, ]; - /// - /// let signing_key: SigningKey = SigningKey::from_bytes(&secret_key_bytes); - /// # - /// # Ok(signing_key) - /// # } - /// # - /// # fn main() { - /// # let result = doctest(); - /// # assert!(result.is_ok()); - /// # } - /// ``` - /// - /// # Returns - /// - /// A `Result` whose okay value is an EdDSA `SecretKey` or whose error value - /// is an `SignatureError` wrapping the internal error that occurred. #[inline] pub fn from_bytes(secret_key: &SecretKey) -> Self { let verifying_key = VerifyingKey::from(&ExpandedSecretKey::from(secret_key)); @@ -101,7 +97,7 @@ impl SigningKey { } } - /// Convert this secret key to a byte array. + /// Convert this [`SigningKey`] into a [`SecretKey`] #[inline] pub fn to_bytes(&self) -> SecretKey { self.secret_key From 24cd9421d5746b34f824422a6ff9ea2c2e784c5f Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Fri, 2 Dec 2022 05:55:16 +0100 Subject: [PATCH 305/351] Change from_bytes methods to take fixed-size array argument Change from_bytes methods to take `&[u8; N]` argument (with `N` appropriate for given type) rather than `&[u8]`. This harmonises the convention with SigningKey and ed25519::Signature; helps type inference; and allows users to assert bytes size to be asserted at compile time. Creating from a slice is still possible via `TryFrom<&[u8]>` trait. This is an API breaking change. The simplest way to update existing code is to replace Foo::from_bytes with Foo::try_from. This should cover majority of uses. --- src/signature.rs | 28 +++++----------------------- src/signing.rs | 15 ++++----------- src/verifying.rs | 28 +++++++++++++--------------- tests/ed25519.rs | 37 +++++++++++++++++-------------------- 4 files changed, 39 insertions(+), 69 deletions(-) diff --git a/src/signature.rs b/src/signature.rs index 795bfad..9026cbb 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -162,30 +162,12 @@ impl InternalSignature { /// only checking the most significant three bits. (See also the /// documentation for `PublicKey.verify_strict`.) #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != SIGNATURE_LENGTH { - return Err(InternalError::BytesLengthError { - name: "Signature", - length: SIGNATURE_LENGTH, - } - .into()); - } - let mut lower: [u8; 32] = [0u8; 32]; - let mut upper: [u8; 32] = [0u8; 32]; - - lower.copy_from_slice(&bytes[..32]); - upper.copy_from_slice(&bytes[32..]); - - let s: Scalar; - - match check_scalar(upper) { - Ok(x) => s = x, - Err(x) => return Err(x), - } - + pub fn from_bytes(bytes: &[u8; SIGNATURE_LENGTH]) -> Result { + // TODO: Use bytes.split_array_ref once it’s in MSRV. + let (lower, upper) = bytes.split_at(32); Ok(InternalSignature { - R: CompressedEdwardsY(lower), - s: s, + R: CompressedEdwardsY(lower.try_into().unwrap()), + s: check_scalar(upper.try_into().unwrap())?, }) } } diff --git a/src/signing.rs b/src/signing.rs index 719c18f..3e38b0c 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -122,17 +122,10 @@ impl SigningKey { /// is an `SignatureError` describing the error that occurred. #[inline] pub fn from_keypair_bytes(bytes: &[u8; 64]) -> Result { - if bytes.len() != KEYPAIR_LENGTH { - return Err(InternalError::BytesLengthError { - name: "SigningKey", - length: KEYPAIR_LENGTH, - } - .into()); - } - - let secret_key = - SecretKey::try_from(&bytes[..SECRET_KEY_LENGTH]).map_err(|_| SignatureError::new())?; - let verifying_key = VerifyingKey::from_bytes(&bytes[SECRET_KEY_LENGTH..])?; + // TODO: Use bytes.split_array_ref once it’s in MSRV. + let (secret_key, verifying_key) = bytes.split_at(SECRET_KEY_LENGTH); + let secret_key = secret_key.try_into().unwrap(); + let verifying_key = VerifyingKey::from_bytes(verifying_key.try_into().unwrap())?; if verifying_key != VerifyingKey::from(&secret_key) { return Err(InternalError::MismatchedKeypairError.into()); diff --git a/src/verifying.rs b/src/verifying.rs index f699798..51bec1a 100644 --- a/src/verifying.rs +++ b/src/verifying.rs @@ -129,18 +129,8 @@ impl VerifyingKey { /// A `Result` whose okay value is an EdDSA `VerifyingKey` or whose error value /// is an `SignatureError` describing the error that occurred. #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != PUBLIC_KEY_LENGTH { - return Err(InternalError::BytesLengthError { - name: "VerifyingKey", - length: PUBLIC_KEY_LENGTH, - } - .into()); - } - let mut bits: [u8; 32] = [0u8; 32]; - bits.copy_from_slice(&bytes[..32]); - - let compressed = CompressedEdwardsY(bits); + pub fn from_bytes(bytes: &[u8; PUBLIC_KEY_LENGTH]) -> Result { + let compressed = CompressedEdwardsY(*bytes); let point = compressed .decompress() .ok_or(InternalError::PointDecompressionError)?; @@ -358,11 +348,19 @@ impl Verifier for VerifyingKey { impl TryFrom<&[u8]> for VerifyingKey { type Error = SignatureError; - fn try_from(bytes: &[u8]) -> Result { - VerifyingKey::from_bytes(bytes) + #[inline] + fn try_from(bytes: &[u8]) -> Result { + let bytes = bytes.try_into().map_err(|_| { + InternalError::BytesLengthError { + name: "VerifyingKey", + length: PUBLIC_KEY_LENGTH, + } + })?; + Self::from_bytes(bytes) } } + #[cfg(feature = "pkcs8")] impl DecodePublicKey for VerifyingKey {} @@ -431,6 +429,6 @@ impl<'d> Deserialize<'d> for VerifyingKey { D: Deserializer<'d>, { let bytes = ::deserialize(deserializer)?; - VerifyingKey::from_bytes(bytes.as_ref()).map_err(SerdeError::custom) + VerifyingKey::try_from(bytes.as_ref()).map_err(SerdeError::custom) } } diff --git a/tests/ed25519.rs b/tests/ed25519.rs index 87b8164..10752a7 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -14,6 +14,7 @@ use curve25519_dalek; use ed25519_dalek::*; use hex::FromHex; +use hex_literal::hex; use sha2::Sha512; @@ -61,9 +62,12 @@ mod vectors { let msg_bytes: Vec = FromHex::from_hex(&parts[2]).unwrap(); let sig_bytes: Vec = FromHex::from_hex(&parts[3]).unwrap(); - let signing_key = SigningKey::try_from(&sec_bytes[..SECRET_KEY_LENGTH]).unwrap(); + let sec_bytes = &sec_bytes[..SECRET_KEY_LENGTH].try_into().unwrap(); + let pub_bytes = &pub_bytes[..PUBLIC_KEY_LENGTH].try_into().unwrap(); + + let signing_key = SigningKey::from_bytes(sec_bytes); let expected_verifying_key = - VerifyingKey::from_bytes(&pub_bytes[..PUBLIC_KEY_LENGTH]).unwrap(); + VerifyingKey::from_bytes(pub_bytes).unwrap(); assert_eq!(expected_verifying_key, signing_key.verifying_key()); // The signatures in the test vectors also include the message @@ -83,26 +87,19 @@ mod vectors { // From https://tools.ietf.org/html/rfc8032#section-7.3 #[test] fn ed25519ph_rf8032_test_vector() { - let secret_key: &[u8] = b"833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42"; - let verifying_key: &[u8] = - b"ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf"; - let message: &[u8] = b"616263"; - let signature: &[u8] = b"98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406"; + let sec_bytes = hex!("833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42"); + let pub_bytes = hex!("ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf"); + let msg_bytes = hex!("616263"); + let sig_bytes = hex!("98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406"); - let sec_bytes: Vec = FromHex::from_hex(secret_key).unwrap(); - let pub_bytes: Vec = FromHex::from_hex(verifying_key).unwrap(); - let msg_bytes: Vec = FromHex::from_hex(message).unwrap(); - let sig_bytes: Vec = FromHex::from_hex(signature).unwrap(); - - let signing_key: SigningKey = - SigningKey::try_from(&sec_bytes[..SECRET_KEY_LENGTH]).unwrap(); - let expected_verifying_key: VerifyingKey = - VerifyingKey::from_bytes(&pub_bytes[..PUBLIC_KEY_LENGTH]).unwrap(); + let signing_key = SigningKey::from_bytes(&sec_bytes); + let expected_verifying_key = + VerifyingKey::from_bytes(&pub_bytes).unwrap(); assert_eq!(expected_verifying_key, signing_key.verifying_key()); - let sig1: Signature = Signature::try_from(&sig_bytes[..]).unwrap(); + let sig1 = Signature::try_from(&sig_bytes[..]).unwrap(); - let mut prehash_for_signing: Sha512 = Sha512::default(); - let mut prehash_for_verifying: Sha512 = Sha512::default(); + let mut prehash_for_signing = Sha512::default(); + let mut prehash_for_verifying = Sha512::default(); prehash_for_signing.update(&msg_bytes[..]); prehash_for_verifying.update(&msg_bytes[..]); @@ -187,7 +184,7 @@ mod vectors { } let signature = serialize_signature(&r, &s); - let pk = VerifyingKey::from_bytes(&pub_key.compress().as_bytes()[..]).unwrap(); + let pk = VerifyingKey::from_bytes(&pub_key.compress().as_bytes()).unwrap(); let sig = Signature::try_from(&signature[..]).unwrap(); // The same signature verifies for both messages assert!(pk.verify(message1, &sig).is_ok() && pk.verify(message2, &sig).is_ok()); From 194b17f18a65947b8413c8a4817b9726879647ef Mon Sep 17 00:00:00 2001 From: "pinkforest(she/her)" <36498018+pinkforest@users.noreply.github.com> Date: Mon, 19 Dec 2022 07:56:41 +1100 Subject: [PATCH 306/351] Fix all Clippy warnings (#244) - Add Clippy to CI - Rename InternalError variants without redundant Error suffix - Rename to_bytes to as_bytes on well known naming - Fix Redundant refs - Fix redundant lifetimes - Fix late declarations --- .github/workflows/rust.yml | 10 +++++++ src/batch.rs | 6 ++-- src/errors.rs | 28 +++++++++--------- src/signature.rs | 12 ++++---- src/signing.rs | 54 ++++++++++++++-------------------- src/verifying.rs | 59 +++++++++++++++++--------------------- tests/ed25519.rs | 6 ++-- 7 files changed, 82 insertions(+), 93 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 6019bcd..ee03f74 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -67,3 +67,13 @@ jobs: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable - run: cargo build --benches --features batch + + clippy: + name: Check that clippy is happy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@1.65 + with: + components: clippy + - run: cargo clippy diff --git a/src/batch.rs b/src/batch.rs index 39af1ca..002e2ac 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -218,7 +218,7 @@ pub fn verify_batch( || signatures.len() != verifying_keys.len() || verifying_keys.len() != messages.len() { - return Err(InternalError::ArrayLengthError { + return Err(InternalError::ArrayLength { name_a: "signatures", length_a: signatures.len(), name_b: "messages", @@ -292,11 +292,11 @@ pub fn verify_batch( once(-B_coefficient).chain(zs.iter().cloned()).chain(zhrams), B.chain(Rs).chain(As), ) - .ok_or(InternalError::VerifyError)?; + .ok_or(InternalError::Verify)?; if id.is_identity() { Ok(()) } else { - Err(InternalError::VerifyError.into()) + Err(InternalError::Verify.into()) } } diff --git a/src/errors.rs b/src/errors.rs index 85b1f64..257399b 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -23,23 +23,23 @@ use std::error::Error; /// need to pay any attention to these. #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub(crate) enum InternalError { - PointDecompressionError, - ScalarFormatError, + PointDecompression, + ScalarFormat, /// An error in the length of bytes handed to a constructor. /// /// To use this, pass a string specifying the `name` of the type which is /// returning the error, and the `length` in bytes which its constructor /// expects. - BytesLengthError { + BytesLength { name: &'static str, length: usize, }, /// The verification equation wasn't satisfied - VerifyError, + Verify, /// Two arrays did not match in size, making the called signature /// verification method impossible. #[cfg(any(feature = "batch", feature = "batch_deterministic"))] - ArrayLengthError { + ArrayLength { name_a: &'static str, length_a: usize, name_b: &'static str, @@ -48,22 +48,22 @@ pub(crate) enum InternalError { length_c: usize, }, /// An ed25519ph signature can only take up to 255 octets of context. - PrehashedContextLengthError, + PrehashedContextLength, /// A mismatched (public, secret) key pair. - MismatchedKeypairError, + MismatchedKeypair, } impl Display for InternalError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - InternalError::PointDecompressionError => write!(f, "Cannot decompress Edwards point"), - InternalError::ScalarFormatError => write!(f, "Cannot use scalar with high-bit set"), - InternalError::BytesLengthError { name: n, length: l } => { + InternalError::PointDecompression => write!(f, "Cannot decompress Edwards point"), + InternalError::ScalarFormat => write!(f, "Cannot use scalar with high-bit set"), + InternalError::BytesLength { name: n, length: l } => { write!(f, "{} must be {} bytes in length", n, l) } - InternalError::VerifyError => write!(f, "Verification equation was not satisfied"), + InternalError::Verify => write!(f, "Verification equation was not satisfied"), #[cfg(any(feature = "batch", feature = "batch_deterministic"))] - InternalError::ArrayLengthError { + InternalError::ArrayLength { name_a: na, length_a: la, name_b: nb, @@ -76,11 +76,11 @@ impl Display for InternalError { {} has length {}, {} has length {}.", na, la, nb, lb, nc, lc ), - InternalError::PrehashedContextLengthError => write!( + InternalError::PrehashedContextLength => write!( f, "An ed25519ph signature can only take up to 255 octets of context" ), - InternalError::MismatchedKeypairError => write!(f, "Mismatched Keypair detected"), + InternalError::MismatchedKeypair => write!(f, "Mismatched Keypair detected"), } } } diff --git a/src/signature.rs b/src/signature.rs index 9026cbb..fdf1700 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -73,7 +73,7 @@ fn check_scalar(bytes: [u8; 32]) -> Result { // This is compatible with ed25519-donna and libsodium when // -DED25519_COMPAT is NOT specified. if bytes[31] & 224 != 0 { - return Err(InternalError::ScalarFormatError.into()); + return Err(InternalError::ScalarFormat.into()); } Ok(Scalar::from_bits(bytes)) @@ -95,15 +95,15 @@ fn check_scalar(bytes: [u8; 32]) -> Result { } match Scalar::from_canonical_bytes(bytes).into() { - None => return Err(InternalError::ScalarFormatError.into()), - Some(x) => return Ok(x), - }; + None => Err(InternalError::ScalarFormat.into()), + Some(x) => Ok(x), + } } impl InternalSignature { /// Convert this `Signature` to a byte array. #[inline] - pub fn to_bytes(&self) -> [u8; SIGNATURE_LENGTH] { + pub fn as_bytes(&self) -> [u8; SIGNATURE_LENGTH] { let mut signature_bytes: [u8; SIGNATURE_LENGTH] = [0u8; SIGNATURE_LENGTH]; signature_bytes[..32].copy_from_slice(&self.R.as_bytes()[..]); @@ -182,6 +182,6 @@ impl TryFrom<&ed25519::Signature> for InternalSignature { impl From for ed25519::Signature { fn from(sig: InternalSignature) -> ed25519::Signature { - ed25519::Signature::from_bytes(&sig.to_bytes()).unwrap() + ed25519::Signature::from_bytes(&sig.as_bytes()).unwrap() } } diff --git a/src/signing.rs b/src/signing.rs index 0a3ee82..07d5553 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -124,7 +124,7 @@ impl SigningKey { let verifying_key = VerifyingKey::from_bytes(verifying_key.try_into().unwrap())?; if verifying_key != VerifyingKey::from(&secret_key) { - return Err(InternalError::MismatchedKeypairError.into()); + return Err(InternalError::MismatchedKeypair.into()); } Ok(SigningKey { @@ -300,9 +300,7 @@ impl SigningKey { { let expanded: ExpandedSecretKey = (&self.secret_key).into(); // xxx thanks i hate this - expanded - .sign_prehashed(prehashed_message, &self.verifying_key, context) - .into() + expanded.sign_prehashed(prehashed_message, &self.verifying_key, context) } /// Verify a signature on a message with this signing key's public key. @@ -473,7 +471,7 @@ impl Signer for SigningKey { /// Sign a message with this signing key's secret key. fn try_sign(&self, message: &[u8]) -> Result { let expanded: ExpandedSecretKey = (&self.secret_key).into(); - Ok(expanded.sign(&message, &self.verifying_key).into()) + Ok(expanded.sign(message, &self.verifying_key)) } } @@ -505,7 +503,7 @@ impl TryFrom<&[u8]> for SigningKey { SecretKey::try_from(bytes) .map(|bytes| Self::from_bytes(&bytes)) .map_err(|_| { - InternalError::BytesLengthError { + InternalError::BytesLength { name: "SecretKey", length: SECRET_KEY_LENGTH, } @@ -695,24 +693,20 @@ impl ExpandedSecretKey { #[allow(non_snake_case)] pub(crate) fn sign(&self, message: &[u8], verifying_key: &VerifyingKey) -> ed25519::Signature { let mut h: Sha512 = Sha512::new(); - let R: CompressedEdwardsY; - let r: Scalar; - let s: Scalar; - let k: Scalar; - h.update(&self.nonce); - h.update(&message); + h.update(self.nonce); + h.update(message); - r = Scalar::from_hash(h); - R = (&r * &ED25519_BASEPOINT_TABLE).compress(); + let r = Scalar::from_hash(h); + let R: CompressedEdwardsY = (&r * &ED25519_BASEPOINT_TABLE).compress(); h = Sha512::new(); h.update(R.as_bytes()); h.update(verifying_key.as_bytes()); - h.update(&message); + h.update(message); - k = Scalar::from_hash(h); - s = &(&k * &self.key) + &r; + let k = Scalar::from_hash(h); + let s: Scalar = (k * self.key) + r; InternalSignature { R, s }.into() } @@ -749,17 +743,11 @@ impl ExpandedSecretKey { { let mut h: Sha512; let mut prehash: [u8; 64] = [0u8; 64]; - let R: CompressedEdwardsY; - let r: Scalar; - let s: Scalar; - let k: Scalar; let ctx: &[u8] = context.unwrap_or(b""); // By default, the context is an empty string. if ctx.len() > 255 { - return Err(SignatureError::from( - InternalError::PrehashedContextLengthError, - )); + return Err(SignatureError::from(InternalError::PrehashedContextLength)); } let ctx_len: u8 = ctx.len() as u8; @@ -781,26 +769,26 @@ impl ExpandedSecretKey { // still bleeding from malleability, for fuck's sake. h = Sha512::new() .chain_update(b"SigEd25519 no Ed25519 collisions") - .chain_update(&[1]) // Ed25519ph - .chain_update(&[ctx_len]) + .chain_update([1]) // Ed25519ph + .chain_update([ctx_len]) .chain_update(ctx) - .chain_update(&self.nonce) + .chain_update(self.nonce) .chain_update(&prehash[..]); - r = Scalar::from_hash(h); - R = (&r * &ED25519_BASEPOINT_TABLE).compress(); + let r = Scalar::from_hash(h); + let R: CompressedEdwardsY = (&r * &ED25519_BASEPOINT_TABLE).compress(); h = Sha512::new() .chain_update(b"SigEd25519 no Ed25519 collisions") - .chain_update(&[1]) // Ed25519ph - .chain_update(&[ctx_len]) + .chain_update([1]) // Ed25519ph + .chain_update([ctx_len]) .chain_update(ctx) .chain_update(R.as_bytes()) .chain_update(verifying_key.as_bytes()) .chain_update(&prehash[..]); - k = Scalar::from_hash(h); - s = &(&k * &self.key) + &r; + let k = Scalar::from_hash(h); + let s: Scalar = (k * self.key) + r; Ok(InternalSignature { R, s }.into()) } diff --git a/src/verifying.rs b/src/verifying.rs index 51bec1a..2192a59 100644 --- a/src/verifying.rs +++ b/src/verifying.rs @@ -90,7 +90,7 @@ impl VerifyingKey { /// View this public key as a byte array. #[inline] - pub fn as_bytes<'a>(&'a self) -> &'a [u8; PUBLIC_KEY_LENGTH] { + pub fn as_bytes(&self) -> &[u8; PUBLIC_KEY_LENGTH] { &(self.0).0 } @@ -133,7 +133,7 @@ impl VerifyingKey { let compressed = CompressedEdwardsY(*bytes); let point = compressed .decompress() - .ok_or(InternalError::PointDecompressionError)?; + .ok_or(InternalError::PointDecompression)?; Ok(VerifyingKey(compressed, point)) } @@ -185,8 +185,6 @@ impl VerifyingKey { let signature = InternalSignature::try_from(signature)?; let mut h: Sha512 = Sha512::default(); - let R: EdwardsPoint; - let k: Scalar; let ctx: &[u8] = context.unwrap_or(b""); debug_assert!( @@ -197,20 +195,21 @@ impl VerifyingKey { let minus_A: EdwardsPoint = -self.1; h.update(b"SigEd25519 no Ed25519 collisions"); - h.update(&[1]); // Ed25519ph - h.update(&[ctx.len() as u8]); + h.update([1]); // Ed25519ph + h.update([ctx.len() as u8]); h.update(ctx); h.update(signature.R.as_bytes()); h.update(self.as_bytes()); h.update(prehashed_message.finalize().as_slice()); - k = Scalar::from_hash(h); - R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); + let k = Scalar::from_hash(h); + let R: EdwardsPoint = + EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); if R.compress() == signature.R { Ok(()) } else { - Err(InternalError::VerifyError.into()) + Err(InternalError::Verify.into()) } } @@ -285,32 +284,30 @@ impl VerifyingKey { let signature = InternalSignature::try_from(signature)?; let mut h: Sha512 = Sha512::new(); - let R: EdwardsPoint; - let k: Scalar; let minus_A: EdwardsPoint = -self.1; - let signature_R: EdwardsPoint; - match signature.R.decompress() { - None => return Err(InternalError::VerifyError.into()), - Some(x) => signature_R = x, - } + let signature_R: EdwardsPoint = match signature.R.decompress() { + None => return Err(InternalError::Verify.into()), + Some(x) => x, + }; // Logical OR is fine here as we're not trying to be constant time. if signature_R.is_small_order() || self.1.is_small_order() { - return Err(InternalError::VerifyError.into()); + return Err(InternalError::Verify.into()); } h.update(signature.R.as_bytes()); h.update(self.as_bytes()); - h.update(&message); + h.update(message); - k = Scalar::from_hash(h); - R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); + let k = Scalar::from_hash(h); + let R: EdwardsPoint = + EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); if R == signature_R { Ok(()) } else { - Err(InternalError::VerifyError.into()) + Err(InternalError::Verify.into()) } } } @@ -326,21 +323,20 @@ impl Verifier for VerifyingKey { let signature = InternalSignature::try_from(signature)?; let mut h: Sha512 = Sha512::new(); - let R: EdwardsPoint; - let k: Scalar; let minus_A: EdwardsPoint = -self.1; h.update(signature.R.as_bytes()); h.update(self.as_bytes()); - h.update(&message); + h.update(message); - k = Scalar::from_hash(h); - R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); + let k = Scalar::from_hash(h); + let R: EdwardsPoint = + EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); if R.compress() == signature.R { Ok(()) } else { - Err(InternalError::VerifyError.into()) + Err(InternalError::Verify.into()) } } } @@ -350,17 +346,14 @@ impl TryFrom<&[u8]> for VerifyingKey { #[inline] fn try_from(bytes: &[u8]) -> Result { - let bytes = bytes.try_into().map_err(|_| { - InternalError::BytesLengthError { - name: "VerifyingKey", - length: PUBLIC_KEY_LENGTH, - } + let bytes = bytes.try_into().map_err(|_| InternalError::BytesLength { + name: "VerifyingKey", + length: PUBLIC_KEY_LENGTH, })?; Self::from_bytes(bytes) } } - #[cfg(feature = "pkcs8")] impl DecodePublicKey for VerifyingKey {} diff --git a/tests/ed25519.rs b/tests/ed25519.rs index 10752a7..0406339 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -66,8 +66,7 @@ mod vectors { let pub_bytes = &pub_bytes[..PUBLIC_KEY_LENGTH].try_into().unwrap(); let signing_key = SigningKey::from_bytes(sec_bytes); - let expected_verifying_key = - VerifyingKey::from_bytes(pub_bytes).unwrap(); + let expected_verifying_key = VerifyingKey::from_bytes(pub_bytes).unwrap(); assert_eq!(expected_verifying_key, signing_key.verifying_key()); // The signatures in the test vectors also include the message @@ -93,8 +92,7 @@ mod vectors { let sig_bytes = hex!("98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406"); let signing_key = SigningKey::from_bytes(&sec_bytes); - let expected_verifying_key = - VerifyingKey::from_bytes(&pub_bytes).unwrap(); + let expected_verifying_key = VerifyingKey::from_bytes(&pub_bytes).unwrap(); assert_eq!(expected_verifying_key, signing_key.verifying_key()); let sig1 = Signature::try_from(&sig_bytes[..]).unwrap(); From a0384be8fcb9d7e41c325fe73354975e32257354 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Tue, 20 Dec 2022 02:28:20 -0700 Subject: [PATCH 307/351] Impl `Drop`/`ZeroizeOnDrop` for `SigningKey` (#247) - Zeros out `SigningKey::secret_key` on drop - Adds the `ZeroizeOnDrop` marker trait to `SigningKey` --- Cargo.toml | 2 +- src/signing.rs | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6040fb4..a23d71d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ rand_core = { version = "0.6", default-features = false, optional = true } serde_crate = { package = "serde", version = "1.0", default-features = false, optional = true } serde_bytes = { version = "0.11", optional = true } sha2 = { version = "0.10", default-features = false } -zeroize = { version = "1", default-features = false } +zeroize = { version = "1.5", default-features = false } [dev-dependencies] hex = "^0.4" diff --git a/src/signing.rs b/src/signing.rs index 07d5553..1f6a4eb 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -32,7 +32,7 @@ use curve25519_dalek::scalar::Scalar; use ed25519::signature::{KeypairRef, Signer, Verifier}; -use zeroize::Zeroize; +use zeroize::{Zeroize, ZeroizeOnDrop}; use crate::constants::*; use crate::errors::*; @@ -512,6 +512,14 @@ impl TryFrom<&[u8]> for SigningKey { } } +impl Drop for SigningKey { + fn drop(&mut self) { + self.secret_key.zeroize(); + } +} + +impl ZeroizeOnDrop for SigningKey {} + #[cfg(feature = "pkcs8")] impl DecodePrivateKey for SigningKey {} From 951d489d5131c4f363b5bece4f46130a6e244688 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Tue, 20 Dec 2022 02:37:04 -0700 Subject: [PATCH 308/351] CI: check code is formatted correctly using `rustfmt` (#246) --- .github/workflows/rust.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index ee03f74..339ceb0 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -68,6 +68,16 @@ jobs: - uses: dtolnay/rust-toolchain@stable - run: cargo build --benches --features batch + rustfmt: + name: Check formatting + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + - run: cargo fmt --all -- --check + clippy: name: Check that clippy is happy runs-on: ubuntu-latest @@ -76,4 +86,4 @@ jobs: - uses: dtolnay/rust-toolchain@1.65 with: components: clippy - - run: cargo clippy + - run: cargo clippy \ No newline at end of file From f6a242a5b002a93df225ebb818c78dc04ec7ca27 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Tue, 20 Dec 2022 02:48:55 -0700 Subject: [PATCH 309/351] Use namespaced/weak features; MSRV 1.60 (#235) This enables activating the `alloc` and `std` features without unnecessarily pulling in optional dependencies like `rand` and `serde`. It also fixes tests for `--no-default-features` (w\ `--lib` only) --- .github/workflows/rust.yml | 7 ++++--- CHANGELOG.md | 2 +- Cargo.toml | 20 +++++++++++--------- README.md | 2 +- src/batch.rs | 3 +++ src/lib.rs | 3 --- tests/ed25519.rs | 7 ++++--- 7 files changed, 24 insertions(+), 20 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 339ceb0..df9e4ca 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -26,7 +26,8 @@ jobs: - uses: dtolnay/rust-toolchain@stable - run: rustup target add ${{ matrix.target }} - run: ${{ matrix.deps }} - - run: cargo test --target ${{ matrix.target }} --no-default-features --features alloc + - run: cargo test --target ${{ matrix.target }} --no-default-features --lib + - run: cargo test --target ${{ matrix.target }} --no-default-features --features alloc --lib - run: cargo test --target ${{ matrix.target }} - run: cargo test --target ${{ matrix.target }} --features batch - run: cargo test --target ${{ matrix.target }} --features batch_deterministic @@ -47,7 +48,7 @@ jobs: run: cargo build --target x86_64-unknown-linux-gnu msrv: - name: Current MSRV is 1.57.0 + name: Current MSRV is 1.60.0 runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -57,7 +58,7 @@ jobs: - run: cargo -Z minimal-versions check --no-default-features --features serde # Now check that `cargo build` works with respect to the oldest possible # deps and the stated MSRV - - uses: dtolnay/rust-toolchain@1.57.0 + - uses: dtolnay/rust-toolchain@1.60.0 - run: cargo build bench: diff --git a/CHANGELOG.md b/CHANGELOG.md index dd49936..05efbbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,5 +7,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased ### Changes -* Bumped MSRV from 1.41 to 1.56.1 +* Bumped MSRV from 1.41 to 1.60.0 * Removed `ExpandedSecretKey` API ((#205)[https://github.com/dalek-cryptography/ed25519-dalek/pull/205]) diff --git a/Cargo.toml b/Cargo.toml index a23d71d..936ec99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ keywords = ["cryptography", "ed25519", "curve25519", "signature", "ECC"] categories = ["cryptography", "no-std"] description = "Fast and efficient ed25519 EdDSA key generations, signing, and verification in pure Rust." exclude = [ ".gitignore", "TESTVECTORS", "res/*" ] -rust-version = "1.57" +rust-version = "1.60" [badges] travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master"} @@ -29,19 +29,19 @@ ed25519 = { version = "=2.0.0-pre.1", default-features = false } merlin = { version = "3", default-features = false, optional = true } rand = { version = "0.8", default-features = false, optional = true } rand_core = { version = "0.6", default-features = false, optional = true } -serde_crate = { package = "serde", version = "1.0", default-features = false, optional = true } +serde = { version = "1.0", default-features = false, optional = true } serde_bytes = { version = "0.11", optional = true } sha2 = { version = "0.10", default-features = false } zeroize = { version = "1.5", default-features = false } [dev-dependencies] -hex = "^0.4" +hex = "0.4" bincode = "1.0" serde_json = "1.0" criterion = "0.3" hex-literal = "0.3" rand = "0.8" -serde_crate = { package = "serde", version = "1.0", features = ["derive"] } +serde = { version = "1.0", features = ["derive"] } toml = { version = "0.5" } [[bench]] @@ -51,14 +51,16 @@ required-features = ["batch"] [features] default = ["std", "rand"] -std = ["alloc", "ed25519/std", "serde_crate/std", "sha2/std", "rand/std"] -alloc = ["curve25519-dalek/alloc", "ed25519/alloc", "rand/alloc", "zeroize/alloc"] -serde = ["serde_crate", "serde_bytes", "ed25519/serde"] +alloc = ["curve25519-dalek/alloc", "ed25519/alloc", "rand?/alloc", "serde?/alloc", "zeroize/alloc"] +std = ["alloc", "ed25519/std", "rand?/std", "serde?/std", "sha2/std"] + +asm = ["sha2/asm"] batch = ["alloc", "merlin", "rand/std"] # This feature enables deterministic batch verification. -batch_deterministic = ["alloc", "merlin", "rand", "rand_core"] -asm = ["sha2/asm"] +batch_deterministic = ["alloc", "merlin", "rand"] # This features turns off stricter checking for scalar malleability in signatures legacy_compatibility = [] pkcs8 = ["ed25519/pkcs8"] pem = ["alloc", "ed25519/pem", "pkcs8"] +rand = ["dep:rand", "dep:rand_core"] +serde = ["dep:serde", "serde_bytes", "ed25519/serde"] diff --git a/README.md b/README.md index 42ce823..568dbd7 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ version = "1" # Minimum Supported Rust Version -This crate requires Rust 1.57.0 at a minimum. 1.x releases of this crate supported an MSRV of 1.41. +This crate requires Rust 1.60.0 at a minimum. Older 1.x releases of this crate supported an MSRV of 1.41. In the future, MSRV changes will be accompanied by a minor version bump. diff --git a/src/batch.rs b/src/batch.rs index 002e2ac..3ad6947 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -9,6 +9,9 @@ //! Batch signature verification. +#[cfg(all(feature = "batch", feature = "batch_deterministic"))] +compile_error!("`batch` and `batch_deterministic` features are mutually exclusive"); + use alloc::vec::Vec; use core::convert::TryFrom; diff --git a/src/lib.rs b/src/lib.rs index e6d051e..874207c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -254,9 +254,6 @@ extern crate alloc; #[macro_use] extern crate std; -#[cfg(feature = "serde")] -extern crate serde_crate as serde; - pub use ed25519; #[cfg(any(feature = "batch", feature = "batch_deterministic"))] diff --git a/tests/ed25519.rs b/tests/ed25519.rs index 0406339..755ad1a 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -16,6 +16,7 @@ use ed25519_dalek::*; use hex::FromHex; use hex_literal::hex; +#[cfg(feature = "rand")] use sha2::Sha512; #[cfg(test)] @@ -193,7 +194,7 @@ mod vectors { } } -#[cfg(test)] +#[cfg(feature = "rand")] mod integrations { use super::*; use rand::rngs::OsRng; @@ -312,8 +313,8 @@ mod integrations { } #[cfg(all(test, feature = "serde"))] -#[derive(Debug, serde_crate::Serialize, serde_crate::Deserialize)] -#[serde(crate = "serde_crate")] +#[derive(Debug, serde::Serialize, serde::Deserialize)] +#[serde(crate = "serde")] struct Demo { signing_key: SigningKey, } From 616d55c36c59e82172ff24af13c9a72f3194052f Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Wed, 21 Dec 2022 17:10:18 -0500 Subject: [PATCH 310/351] Impld Clone for SigningKey (#249) --- CHANGELOG.md | 1 + src/signing.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05efbbd..efae846 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,3 +9,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changes * Bumped MSRV from 1.41 to 1.60.0 * Removed `ExpandedSecretKey` API ((#205)[https://github.com/dalek-cryptography/ed25519-dalek/pull/205]) +* Implemented `Clone` for `SigningKey` diff --git a/src/signing.rs b/src/signing.rs index 1f6a4eb..c6b32f3 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -50,7 +50,7 @@ pub type SecretKey = [u8; SECRET_KEY_LENGTH]; /// ed25519 signing key which can be used to produce signatures. // Invariant: `public` is always the public key of `secret`. This prevents the signing function // oracle attack described in https://github.com/MystenLabs/ed25519-unsafe-libs -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct SigningKey { /// The secret half of this signing key. pub(crate) secret_key: SecretKey, From e2ed3133a63cbd2ab34763d493b79b64589e5c88 Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Mon, 2 Jan 2023 00:59:19 -0500 Subject: [PATCH 311/351] Fix batch build (#220) * Fixed bench when `batch` feature is not present * Added bench build regression test to CI * Fixed batch build more generally * Simplified batch cfg gates in benches * Updated criterion * Made CI batch-nondeterministic test use nostd * Fix batch_deterministic build * Removed bad compile error when batch and batch_deterministic are selected --- .github/workflows/rust.yml | 9 +++--- Cargo.toml | 8 ++--- README.md | 8 +++-- benches/ed25519_benchmarks.rs | 30 ++++++++++-------- src/batch.rs | 60 +++++++++++++++++------------------ src/lib.rs | 50 ++++++++++++++--------------- src/signing.rs | 9 ++---- tests/ed25519.rs | 2 +- 8 files changed, 89 insertions(+), 87 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index df9e4ca..777219c 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -2,9 +2,9 @@ name: Rust on: push: - branches: [ '*' ] + branches: [ '**' ] pull_request: - branches: [ 'main', 'develop', 'release/2.0' ] + branches: [ '**' ] env: CARGO_TERM_COLOR: always @@ -32,7 +32,7 @@ jobs: - run: cargo test --target ${{ matrix.target }} --features batch - run: cargo test --target ${{ matrix.target }} --features batch_deterministic - run: cargo test --target ${{ matrix.target }} --features serde - - run: cargo test --target ${{ matrix.target }} --features pkcs8 + - run: cargo test --target ${{ matrix.target }} --features pem build-simd: name: Test simd backend (nightly) @@ -68,6 +68,7 @@ jobs: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable - run: cargo build --benches --features batch + - run: cargo build --benches --features batch_deterministic rustfmt: name: Check formatting @@ -87,4 +88,4 @@ jobs: - uses: dtolnay/rust-toolchain@1.65 with: components: clippy - - run: cargo clippy \ No newline at end of file + - run: cargo clippy diff --git a/Cargo.toml b/Cargo.toml index 936ec99..ef3e51f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ curve25519-dalek = { version = "=4.0.0-pre.3", default-features = false, feature ed25519 = { version = "=2.0.0-pre.1", default-features = false } merlin = { version = "3", default-features = false, optional = true } rand = { version = "0.8", default-features = false, optional = true } -rand_core = { version = "0.6", default-features = false, optional = true } +rand_core = { version = "0.6.4", default-features = false, optional = true } serde = { version = "1.0", default-features = false, optional = true } serde_bytes = { version = "0.11", optional = true } sha2 = { version = "0.10", default-features = false } @@ -38,16 +38,16 @@ zeroize = { version = "1.5", default-features = false } hex = "0.4" bincode = "1.0" serde_json = "1.0" -criterion = "0.3" +criterion = { version = "0.4", features = ["html_reports"] } hex-literal = "0.3" rand = "0.8" +rand_core = { version = "0.6.4", default-features = false } serde = { version = "1.0", features = ["derive"] } toml = { version = "0.5" } [[bench]] name = "ed25519_benchmarks" harness = false -required-features = ["batch"] [features] default = ["std", "rand"] @@ -55,7 +55,7 @@ alloc = ["curve25519-dalek/alloc", "ed25519/alloc", "rand?/alloc", "serde?/alloc std = ["alloc", "ed25519/std", "rand?/std", "serde?/std", "sha2/std"] asm = ["sha2/asm"] -batch = ["alloc", "merlin", "rand/std"] +batch = ["alloc", "merlin", "rand"] # This feature enables deterministic batch verification. batch_deterministic = ["alloc", "merlin", "rand"] # This features turns off stricter checking for scalar malleability in signatures diff --git a/README.md b/README.md index 568dbd7..23dcaf5 100644 --- a/README.md +++ b/README.md @@ -261,8 +261,12 @@ comprising either avx2 or avx512 backends. To use them, compile with The standard variants of batch signature verification (i.e. many signatures made with potentially many different public keys over potentially many different -message) is available via the `batch` feature. It uses synthetic randomness, as -noted above. +messages) is available via the `batch` feature. It uses synthetic randomness, as +noted above. Batch verification requires allocation, so this won't function in +heapless settings. + +Batch verification is slightly faster with the `std` feature enabled, since it +permits us to use `rand::thread_rng`. ### Deterministic Batch Signature Verification diff --git a/benches/ed25519_benchmarks.rs b/benches/ed25519_benchmarks.rs index ed01d49..f1beeab 100644 --- a/benches/ed25519_benchmarks.rs +++ b/benches/ed25519_benchmarks.rs @@ -7,15 +7,13 @@ // Authors: // - isis agora lovecruft -use criterion::{criterion_group, criterion_main, Criterion}; +use criterion::{criterion_group, Criterion}; mod ed25519_benches { use super::*; - use ed25519_dalek::verify_batch; use ed25519_dalek::Signature; use ed25519_dalek::Signer; use ed25519_dalek::SigningKey; - use ed25519_dalek::VerifyingKey; use rand::prelude::ThreadRng; use rand::thread_rng; @@ -49,14 +47,17 @@ mod ed25519_benches { }); } + #[cfg(any(feature = "batch", feature = "batch_deterministic"))] fn verify_batch_signatures(c: &mut Criterion) { + use ed25519_dalek::verify_batch; + static BATCH_SIZES: [usize; 8] = [4, 8, 16, 32, 64, 96, 128, 256]; - // TODO: use BenchmarkGroups instead. - #[allow(deprecated)] - c.bench_function_over_inputs( - "Ed25519 batch signature verification", - |b, &&size| { + // Benchmark batch verification for all the above batch sizes + let mut group = c.benchmark_group("Ed25519 batch signature verification"); + for size in BATCH_SIZES { + let name = format!("size={size}"); + group.bench_function(name, |b| { let mut csprng: ThreadRng = thread_rng(); let keypairs: Vec = (0..size) .map(|_| SigningKey::generate(&mut csprng)) @@ -65,15 +66,18 @@ mod ed25519_benches { let messages: Vec<&[u8]> = (0..size).map(|_| msg).collect(); let signatures: Vec = keypairs.iter().map(|key| key.sign(&msg)).collect(); - let verifying_keys: Vec = + let verifying_keys: Vec<_> = keypairs.iter().map(|key| key.verifying_key()).collect(); b.iter(|| verify_batch(&messages[..], &signatures[..], &verifying_keys[..])); - }, - &BATCH_SIZES, - ); + }); + } } + // If the above function isn't defined, make a placeholder function + #[cfg(not(any(feature = "batch", feature = "batch_deterministic")))] + fn verify_batch_signatures(_: &mut Criterion) {} + fn key_generation(c: &mut Criterion) { let mut csprng: ThreadRng = thread_rng(); @@ -94,4 +98,4 @@ mod ed25519_benches { } } -criterion_main!(ed25519_benches::ed25519_benches); +criterion::criterion_main!(ed25519_benches::ed25519_benches); diff --git a/src/batch.rs b/src/batch.rs index 3ad6947..ad8a413 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -27,11 +27,7 @@ pub use curve25519_dalek::digest::Digest; use merlin::Transcript; -#[cfg(all(feature = "batch", not(feature = "batch_deterministic")))] -use rand::thread_rng; use rand::Rng; -#[cfg(all(not(feature = "batch"), feature = "batch_deterministic"))] -use rand_core; use sha2::Sha512; @@ -40,6 +36,19 @@ use crate::errors::SignatureError; use crate::signature::InternalSignature; use crate::VerifyingKey; +/// Gets an RNG from the system, or the zero RNG if we're in deterministic mode. If available, we +/// prefer `thread_rng`, since it's faster than `OsRng`. +fn get_rng() -> impl rand_core::CryptoRngCore { + #[cfg(all(feature = "batch_deterministic", not(feature = "batch")))] + return ZeroRng; + + #[cfg(all(feature = "batch", feature = "std"))] + return rand::thread_rng(); + + #[cfg(all(feature = "batch", not(feature = "std")))] + return rand::rngs::OsRng; +} + trait BatchTranscript { fn append_scalars(&mut self, scalars: &Vec); fn append_message_lengths(&mut self, message_lengths: &Vec); @@ -63,10 +72,9 @@ impl BatchTranscript for Transcript { /// Append the lengths of the messages into the transcript. /// - /// This is done out of an (potential over-)abundance of caution, to guard - /// against the unlikely event of collisions. However, a nicer way to do - /// this would be to append the message length before the message, but this - /// is messy w.r.t. the calculations of the `H(R||A||M)`s above. + /// This is done out of an (potential over-)abundance of caution, to guard against the unlikely + /// event of collisions. However, a nicer way to do this would be to append the message length + /// before the message, but this is messy w.r.t. the calculations of the `H(R||A||M)`s above. fn append_message_lengths(&mut self, message_lengths: &Vec) { for (i, len) in message_lengths.iter().enumerate() { self.append_u64(b"", i as u64); @@ -75,13 +83,12 @@ impl BatchTranscript for Transcript { } } -/// An implementation of `rand_core::RngCore` which does nothing, to provide -/// purely deterministic transcript-based nonces, rather than synthetically -/// random nonces. -#[cfg(all(not(feature = "batch"), feature = "batch_deterministic"))] -struct ZeroRng {} +/// An implementation of `rand_core::RngCore` which does nothing, to provide purely deterministic +/// transcript-based nonces, rather than synthetically random nonces. +#[cfg(feature = "batch_deterministic")] +struct ZeroRng; -#[cfg(all(not(feature = "batch"), feature = "batch_deterministic"))] +#[cfg(feature = "batch_deterministic")] impl rand_core::RngCore for ZeroRng { fn next_u32(&mut self) -> u32 { rand_core::impls::next_u32_via_fill(self) @@ -107,14 +114,9 @@ impl rand_core::RngCore for ZeroRng { } } -#[cfg(all(not(feature = "batch"), feature = "batch_deterministic"))] +#[cfg(feature = "batch_deterministic")] impl rand_core::CryptoRng for ZeroRng {} -#[cfg(all(not(feature = "batch"), feature = "batch_deterministic"))] -fn zero_rng() -> ZeroRng { - ZeroRng {} -} - /// Verify a batch of `signatures` on `messages` with their respective `verifying_keys`. /// /// # Inputs @@ -199,7 +201,7 @@ fn zero_rng() -> ZeroRng { /// use rand::rngs::OsRng; /// /// # fn main() { -/// let mut csprng = OsRng{}; +/// let mut csprng = OsRng; /// let signing_keys: Vec<_> = (0..64).map(|_| SigningKey::generate(&mut csprng)).collect(); /// let msg: &[u8] = b"They're good dogs Brant"; /// let messages: Vec<&[u8]> = (0..64).map(|_| msg).collect(); @@ -249,25 +251,21 @@ pub fn verify_batch( }) .collect(); - // Collect the message lengths and the scalar portions of the signatures, - // and add them into the transcript. + // Collect the message lengths and the scalar portions of the signatures, and add them into the + // transcript. let message_lengths: Vec = messages.iter().map(|i| i.len()).collect(); let scalars: Vec = signatures.iter().map(|i| i.s).collect(); - // Build a PRNG based on a transcript of the H(R || A || M)s seen thus far. - // This provides synthethic randomness in the default configuration, and - // purely deterministic in the case of compiling with the - // "batch_deterministic" feature. + // Build a PRNG based on a transcript of the H(R || A || M)s seen thus far. This provides + // synthethic randomness in the default configuration, and purely deterministic in the case of + // compiling with the "batch_deterministic" feature. let mut transcript: Transcript = Transcript::new(b"ed25519 batch verification"); transcript.append_scalars(&hrams); transcript.append_message_lengths(&message_lengths); transcript.append_scalars(&scalars); - #[cfg(all(feature = "batch", not(feature = "batch_deterministic")))] - let mut prng = transcript.build_rng().finalize(&mut thread_rng()); - #[cfg(all(not(feature = "batch"), feature = "batch_deterministic"))] - let mut prng = transcript.build_rng().finalize(&mut zero_rng()); + let mut prng = transcript.build_rng().finalize(&mut get_rng()); // Select a random 128-bit scalar for each signature. let zs: Vec = signatures diff --git a/src/lib.rs b/src/lib.rs index 874207c..edb5b98 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,28 +18,26 @@ //! secure pseudorandom number generator (CSPRNG). For this example, we'll use //! the operating system's builtin PRNG: //! -//! ``` -//! # #[cfg(feature = "std")] +#![cfg_attr(feature = "rand", doc = "```")] +#![cfg_attr(not(feature = "rand"), doc = "```ignore")] //! # fn main() { //! use rand::rngs::OsRng; //! use ed25519_dalek::SigningKey; //! use ed25519_dalek::Signature; //! -//! let mut csprng = OsRng{}; +//! let mut csprng = OsRng; //! let signing_key: SigningKey = SigningKey::generate(&mut csprng); //! # } -//! # -//! # #[cfg(not(feature = "std"))] -//! # fn main() { } //! ``` //! //! We can now use this `signing_key` to sign a message: //! -//! ``` +#![cfg_attr(feature = "rand", doc = "```")] +#![cfg_attr(not(feature = "rand"), doc = "```ignore")] //! # fn main() { //! # use rand::rngs::OsRng; //! # use ed25519_dalek::SigningKey; -//! # let mut csprng = OsRng{}; +//! # let mut csprng = OsRng; //! # let signing_key: SigningKey = SigningKey::generate(&mut csprng); //! use ed25519_dalek::{Signature, Signer}; //! let message: &[u8] = b"This is a test of the tsunami alert system."; @@ -50,11 +48,12 @@ //! As well as to verify that this is, indeed, a valid signature on //! that `message`: //! -//! ``` +#![cfg_attr(feature = "rand", doc = "```")] +#![cfg_attr(not(feature = "rand"), doc = "```ignore")] //! # fn main() { //! # use rand::rngs::OsRng; //! # use ed25519_dalek::{SigningKey, Signature, Signer}; -//! # let mut csprng = OsRng{}; +//! # let mut csprng = OsRng; //! # let signing_key: SigningKey = SigningKey::generate(&mut csprng); //! # let message: &[u8] = b"This is a test of the tsunami alert system."; //! # let signature: Signature = signing_key.sign(message); @@ -66,7 +65,8 @@ //! Anyone else, given the `public` half of the `signing_key` can also easily //! verify this signature: //! -//! ``` +#![cfg_attr(feature = "rand", doc = "```")] +#![cfg_attr(not(feature = "rand"), doc = "```ignore")] //! # fn main() { //! # use rand::rngs::OsRng; //! # use ed25519_dalek::SigningKey; @@ -91,7 +91,8 @@ //! secret key to anyone else, since they will only need the public key to //! verify your signatures!) //! -//! ``` +#![cfg_attr(feature = "rand", doc = "```")] +#![cfg_attr(not(feature = "rand"), doc = "```ignore")] //! # fn main() { //! # use rand::rngs::OsRng; //! # use ed25519_dalek::{SigningKey, Signature, Signer, VerifyingKey}; @@ -110,14 +111,15 @@ //! //! And similarly, decoded from bytes with `::from_bytes()`: //! -//! ``` +#![cfg_attr(feature = "rand", doc = "```")] +#![cfg_attr(not(feature = "rand"), doc = "```ignore")] //! # use std::convert::TryFrom; //! # use rand::rngs::OsRng; //! # use std::convert::TryInto; //! # use ed25519_dalek::{SigningKey, Signature, Signer, VerifyingKey, SecretKey, SignatureError}; //! # use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; //! # fn do_test() -> Result<(SigningKey, VerifyingKey, Signature), SignatureError> { -//! # let mut csprng = OsRng{}; +//! # let mut csprng = OsRng; //! # let signing_key_orig: SigningKey = SigningKey::generate(&mut csprng); //! # let message: &[u8] = b"This is a test of the tsunami alert system."; //! # let signature_orig: Signature = signing_key_orig.sign(message); @@ -164,13 +166,13 @@ //! #![cfg_attr(feature = "pem", doc = "```")] #![cfg_attr(not(feature = "pem"), doc = "```ignore")] -//! use ed25519_dalek::{VerifyingKey, pkcs8::DecodeVerifyingKey}; +//! use ed25519_dalek::{VerifyingKey, pkcs8::DecodePublicKey}; //! //! let pem = "-----BEGIN PUBLIC KEY----- //! MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE= //! -----END PUBLIC KEY-----"; //! -//! let verifying_key = VerifyingKey::from_verifying_key_pem(pem) +//! let verifying_key = VerifyingKey::from_public_key_pem(pem) //! .expect("invalid public key PEM"); //! ``` //! @@ -187,13 +189,13 @@ //! They can be then serialised into any of the wire formats which serde supports. //! For example, using [bincode](https://github.com/TyOverby/bincode): //! -//! ``` -//! # #[cfg(feature = "serde")] +#![cfg_attr(all(feature = "rand", feature = "serde"), doc = "```")] +#![cfg_attr(not(all(feature = "rand", feature = "serde")), doc = "```ignore")] //! # fn main() { //! # use rand::rngs::OsRng; //! # use ed25519_dalek::{SigningKey, Signature, Signer, Verifier, VerifyingKey}; //! use bincode::serialize; -//! # let mut csprng = OsRng{}; +//! # let mut csprng = OsRng; //! # let signing_key: SigningKey = SigningKey::generate(&mut csprng); //! # let message: &[u8] = b"This is a test of the tsunami alert system."; //! # let signature: Signature = signing_key.sign(message); @@ -203,22 +205,20 @@ //! let encoded_verifying_key: Vec = serialize(&verifying_key).unwrap(); //! let encoded_signature: Vec = serialize(&signature).unwrap(); //! # } -//! # #[cfg(not(feature = "serde"))] -//! # fn main() {} //! ``` //! //! After sending the `encoded_verifying_key` and `encoded_signature`, the //! recipient may deserialise them and verify: //! -//! ``` -//! # #[cfg(feature = "serde")] +#![cfg_attr(all(feature = "rand", feature = "serde"), doc = "```")] +#![cfg_attr(not(all(feature = "rand", feature = "serde")), doc = "```ignore")] //! # fn main() { //! # use rand::rngs::OsRng; //! # use ed25519_dalek::{SigningKey, Signature, Signer, Verifier, VerifyingKey}; //! # use bincode::serialize; //! use bincode::deserialize; //! -//! # let mut csprng = OsRng{}; +//! # let mut csprng = OsRng; //! # let signing_key: SigningKey = SigningKey::generate(&mut csprng); //! let message: &[u8] = b"This is a test of the tsunami alert system."; //! # let signature: Signature = signing_key.sign(message); @@ -236,8 +236,6 @@ //! //! assert!(verified); //! # } -//! # #[cfg(not(feature = "serde"))] -//! # fn main() {} //! ``` #![no_std] diff --git a/src/signing.rs b/src/signing.rs index c6b32f3..990ca59 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -13,7 +13,7 @@ use ed25519::pkcs8::{self, DecodePrivateKey}; #[cfg(feature = "rand")] -use rand::{CryptoRng, RngCore}; +use rand_core::CryptoRngCore; #[cfg(feature = "serde")] use serde::de::Error as SerdeError; @@ -168,7 +168,7 @@ impl SigningKey { /// use ed25519_dalek::SigningKey; /// use ed25519_dalek::Signature; /// - /// let mut csprng = OsRng{}; + /// let mut csprng = OsRng; /// let signing_key: SigningKey = SigningKey::generate(&mut csprng); /// /// # } @@ -187,10 +187,7 @@ impl SigningKey { /// which is available with `use sha2::Sha512` as in the example above. /// Other suitable hash functions include Keccak-512 and Blake2b-512. #[cfg(feature = "rand")] - pub fn generate(csprng: &mut R) -> SigningKey - where - R: CryptoRng + RngCore, - { + pub fn generate(csprng: &mut R) -> SigningKey { let mut secret = SecretKey::default(); csprng.fill_bytes(&mut secret); Self::from_bytes(&secret) diff --git a/tests/ed25519.rs b/tests/ed25519.rs index 755ad1a..4081485 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -209,7 +209,7 @@ mod integrations { let good: &[u8] = "test message".as_bytes(); let bad: &[u8] = "wrong message".as_bytes(); - let mut csprng = OsRng {}; + let mut csprng = OsRng; signing_key = SigningKey::generate(&mut csprng); good_sig = signing_key.sign(&good); From 65aeda08670fc82d22ebcd1d3815fea72185b8ef Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Thu, 5 Jan 2023 03:31:58 -0700 Subject: [PATCH 312/351] Impl `From<&SigningKey>` for `VerifyingKey` (#252) Calls the inherent `SigningKey::verifying_key` method using `From` conversions. This replaces vestigial impl for `SecretKey` which is now an alias for `[u8; 32]`. --- src/signing.rs | 26 +++++++++++--------------- src/verifying.rs | 25 ++++++------------------- 2 files changed, 17 insertions(+), 34 deletions(-) diff --git a/src/signing.rs b/src/signing.rs index 990ca59..d7e784f 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -118,22 +118,18 @@ impl SigningKey { /// is an `SignatureError` describing the error that occurred. #[inline] pub fn from_keypair_bytes(bytes: &[u8; 64]) -> Result { - // TODO: Use bytes.split_array_ref once it’s in MSRV. let (secret_key, verifying_key) = bytes.split_at(SECRET_KEY_LENGTH); - let secret_key = secret_key.try_into().unwrap(); - let verifying_key = VerifyingKey::from_bytes(verifying_key.try_into().unwrap())?; + let signing_key = SigningKey::try_from(secret_key)?; + let verifying_key = VerifyingKey::try_from(verifying_key)?; - if verifying_key != VerifyingKey::from(&secret_key) { + if signing_key.verifying_key() != verifying_key { return Err(InternalError::MismatchedKeypair.into()); } - Ok(SigningKey { - secret_key, - verifying_key, - }) + Ok(signing_key) } - /// Convert this signing key to bytes. + /// Convert this signing key to a 64-byte keypair. /// /// # Returns /// @@ -541,19 +537,19 @@ impl TryFrom<&pkcs8::KeypairBytes> for SigningKey { type Error = pkcs8::Error; fn try_from(pkcs8_key: &pkcs8::KeypairBytes) -> pkcs8::Result { - // Validate the public key in the PKCS#8 document if present - if let Some(public_bytes) = pkcs8_key.public_key { - let expected_verifying_key = VerifyingKey::from(&pkcs8_key.secret_key); + let signing_key = SigningKey::from_bytes(&pkcs8_key.secret_key); - let pkcs8_verifying_key = VerifyingKey::from_bytes(public_bytes.as_ref()) + // Validate the public key in the PKCS#8 document if present + if let Some(public_bytes) = &pkcs8_key.public_key { + let expected_verifying_key = VerifyingKey::from_bytes(public_bytes.as_ref()) .map_err(|_| pkcs8::Error::KeyMalformed)?; - if expected_verifying_key != pkcs8_verifying_key { + if signing_key.verifying_key() != expected_verifying_key { return Err(pkcs8::Error::KeyMalformed); } } - Ok(SigningKey::from_bytes(&pkcs8_key.secret_key)) + Ok(signing_key) } } diff --git a/src/verifying.rs b/src/verifying.rs index 2192a59..bb58aa9 100644 --- a/src/verifying.rs +++ b/src/verifying.rs @@ -54,33 +54,20 @@ impl AsRef<[u8]> for VerifyingKey { } } -impl From<&SecretKey> for VerifyingKey { - /// Derive this public key from its corresponding `SecretKey`. - fn from(secret_key: &SecretKey) -> VerifyingKey { - let mut h: Sha512 = Sha512::new(); - let mut hash: [u8; 64] = [0u8; 64]; - let mut digest: [u8; 32] = [0u8; 32]; - - h.update(secret_key); - hash.copy_from_slice(h.finalize().as_slice()); - - digest.copy_from_slice(&hash[..32]); - - VerifyingKey::mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key( - &mut digest, - ) - } -} - impl From<&ExpandedSecretKey> for VerifyingKey { /// Derive this public key from its corresponding `ExpandedSecretKey`. fn from(expanded_secret_key: &ExpandedSecretKey) -> VerifyingKey { let mut bits: [u8; 32] = expanded_secret_key.key.to_bytes(); - VerifyingKey::mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(&mut bits) } } +impl From<&SigningKey> for VerifyingKey { + fn from(signing_key: &SigningKey) -> VerifyingKey { + signing_key.verifying_key() + } +} + impl VerifyingKey { /// Convert this public key to a byte array. #[inline] From f036eaf48297b09efa251b9c41052c8132b9c0cb Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Thu, 5 Jan 2023 22:58:54 -0500 Subject: [PATCH 313/351] Validation criteria tests (#253) --- Cargo.toml | 2 +- VALIDATIONVECTORS | 11136 +++++++++++++++++++++++++++++++++ tests/validation_criteria.rs | 232 + 3 files changed, 11369 insertions(+), 1 deletion(-) create mode 100644 VALIDATIONVECTORS create mode 100644 tests/validation_criteria.rs diff --git a/Cargo.toml b/Cargo.toml index ef3e51f..bf60c87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ documentation = "https://docs.rs/ed25519-dalek" keywords = ["cryptography", "ed25519", "curve25519", "signature", "ECC"] categories = ["cryptography", "no-std"] description = "Fast and efficient ed25519 EdDSA key generations, signing, and verification in pure Rust." -exclude = [ ".gitignore", "TESTVECTORS", "res/*" ] +exclude = [ ".gitignore", "TESTVECTORS", "VALIDATIONVECTORS", "res/*" ] rust-version = "1.60" [badges] diff --git a/VALIDATIONVECTORS b/VALIDATIONVECTORS new file mode 100644 index 0000000..f08d291 --- /dev/null +++ b/VALIDATIONVECTORS @@ -0,0 +1,11136 @@ +[ + { + "_comment": "This test vector comes from https://github.com/C2SP/CCTV/blob/5ea85644bd035c555900a2f707f7e4c31ea65ced/ed25519vectors/ed25519vectors.json", + "number": 0, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 1, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 11", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 2, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 3, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 4, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "00000000000000000000000000000000000000000000000000000000000000005e176f12cfb0d4e6eb6929b19ae4c998ef05c1c2cf628a9b1fa1c21312627108", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 5, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "00000000000000000000000000000000000000000000000000000000000000009472a69cd9a701a50d130ed52189e2455b23767db52cacb8716fb896ffeeac09", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 6, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56bc02e2b9e63e385c058bf62b14b3a2b29ccefe8e38ddb536bc3f9865320a3d801", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 7, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56bbbfd00bd9c259d8d222d15e67a3d8228585050dbb9b9585be20d8fadc721da03", + "msg": "ed25519vectors", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 8, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 9, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_A" + ] + }, + { + "number": 10, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 11, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_A" + ] + }, + { + "number": 12, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 13, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 14, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 15, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 16, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f1cb421dfbd92aa6c30d550bff53c81cf650ace6deb96a8ec22d2fef84dbbe20b", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 17, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fad7a355469b5c87e550469f6b2de409ee723acd584bf35f86b80c384e8ceb702", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 18, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f5e176f12cfb0d4e6eb6929b19ae4c998ef05c1c2cf628a9b1fa1c21312627108", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 19, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f9472a69cd9a701a50d130ed52189e2455b23767db52cacb8716fb896ffeeac09", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 20, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 21, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 15", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 22, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 23, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 24, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 25, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 26, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "000000000000000000000000000000000000000000000000000000000000008075b3d7e9547febbdbf3fde21df901c7ca1fc59e8b689a4ae283919e78cf62b03", + "msg": "ed25519vectors 11", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 27, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "0000000000000000000000000000000000000000000000000000000000000080050abffcd4d8ccbb4b8d6bf6649f5aa99e8de5cc182a7409633856ff53f49e0c", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 28, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a94639734cf989e314e9e049fc01a3864d191fed8f231b12fee6fa50aaadba44b0e", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 29, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a94732d9e0279fe001d90327efa319816e3e4506b78432b8b4e1f2fdc4b960d700f", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 30, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 31, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_A" + ] + }, + { + "number": 32, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 33, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_A" + ] + }, + { + "number": 34, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 35, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 36, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 37, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 38, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7117c2ec783065c50f2c930cfc9d318c9737991acb260b49e2ca815474532709", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 39, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1f5fae114eb0c534b5aed0a892d0a0e1d429df6a025c5ae08012e0ffce78310c", + "msg": "ed25519vectors 27", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 40, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff75b3d7e9547febbdbf3fde21df901c7ca1fc59e8b689a4ae283919e78cf62b03", + "msg": "ed25519vectors 11", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 41, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff050abffcd4d8ccbb4b8d6bf6649f5aa99e8de5cc182a7409633856ff53f49e0c", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 42, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 39", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 43, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 44, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 11", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A" + ] + }, + { + "number": 45, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 46, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors", + "flags": [ + "low_order_A", + "low_order_component_A" + ] + }, + { + "number": 47, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_A", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 48, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "0100000000000000000000000000000000000000000000000000000000000000edfd6f478b59eab2545cf8f3d48c6574d2b4e8abd948148da5c62479113b8d0f", + "msg": "ed25519vectors 13", + "flags": [ + "low_order_R", + "low_order_component_A" + ] + }, + { + "number": 49, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "0100000000000000000000000000000000000000000000000000000000000000879e8751046a31d163df68051bbd909e6d26c8414883ba3475bf00e17c1e7b04", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 50, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350608f32d206a7c0b7efa9a59e66546e8f1f599ef843fb502c9cc3c4ae8b7c11e05", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_component_A" + ] + }, + { + "number": 51, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a3506030b03796b78f7afeadfccaedc9d09ce6d487d1ece1f16b1ae2b59b7e5c40603", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 52, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_A" + ] + }, + { + "number": 53, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 54, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_A", + "low_order_component_A", + "non_canonical_A" + ] + }, + { + "number": 55, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 56, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors", + "flags": [ + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 57, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 58, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 59, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 60, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "0100000000000000000000000000000000000000000000000000000000000080c85efbb96e35e1b671722d5d8de687d4e148ea15ec566be6a1f3cfb8a10a9d06", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 61, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "01000000000000000000000000000000000000000000000000000000000000804b18627cef9707137b02358c8b73769381269bf7cac9c473483c83c46a615d09", + "msg": "ed25519vectors 29", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 62, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "0100000000000000000000000000000000000000000000000000000000000080edfd6f478b59eab2545cf8f3d48c6574d2b4e8abd948148da5c62479113b8d0f", + "msg": "ed25519vectors 13", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 63, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "0100000000000000000000000000000000000000000000000000000000000080879e8751046a31d163df68051bbd909e6d26c8414883ba3475bf00e17c1e7b04", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 64, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 65, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 66, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f97f5fc03f658b5b733cf20c4ea5577e8e5988ee90cb2a3c919c2a05dd2fcde04", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 67, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f2786a72a405895f7b3b4752eac49c8973270173a495ec475b34933dc05d7e904", + "msg": "ed25519vectors 16", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 68, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fedfd6f478b59eab2545cf8f3d48c6574d2b4e8abd948148da5c62479113b8d0f", + "msg": "ed25519vectors 13", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 69, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f879e8751046a31d163df68051bbd909e6d26c8414883ba3475bf00e17c1e7b04", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 70, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 13", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 71, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 72, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff87a0755495e4b763c07eeebdd510eb7d7b167bf13bf1489785fa2be61543890e", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 73, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffafdefad9d081387fa502d650442ed15619fc936d41444fd5c4292691a16f1d06", + "msg": "ed25519vectors 25", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 74, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffedfd6f478b59eab2545cf8f3d48c6574d2b4e8abd948148da5c62479113b8d0f", + "msg": "ed25519vectors 13", + "flags": [ + "low_order_R", + "low_order_component_A", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 75, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff879e8751046a31d163df68051bbd909e6d26c8414883ba3475bf00e17c1e7b04", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 76, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 11", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 77, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 78, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 15", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 79, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 22", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 80, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 81, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 82, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 83, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d2105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 84, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05aa80ce94a6f94b9e01abfc089182b3d85548437339d7ad2e3d804f60a87a8605", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 85, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d21bba68b891fdce7ad8fa923bc884aa33eeea4f341ae5ee527cb7d99a23040ab0e", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 86, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 21", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 87, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d2105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 88, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d2105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 89, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 90, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f1105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 91, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc852cd7e84d7f4609fc08f069561f201161369e38e508562ea21f0c6f582873f500", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 92, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f11efaa29ea31bbaf7b896625bd0fbe78f6bb7f3cf093407794dd2cd6096e3fd103", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 93, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 94, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f1105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 95, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f1105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 96, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 97, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 98, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a57f5931a402c5cda578690e33a33ba0458eec51036b5c01c5cd486a58c0d290f", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 99, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee8093711b51dce8d4ef35fd239caecd236d2ca58604bc779880708568423a9700", + "msg": "ed25519vectors", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 100, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 23", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 101, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 102, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 103, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 104, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 105, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa6913952aa42c06cd8759d2175fa60c29fba5d9767291b8cda0f58d186320d604", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 106, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de0b20981a60242434fb7351f2ebc29b90bbd45c90eae5377379d780156e297409", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 107, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 14", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 108, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 109, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 110, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 111, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 112, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 17", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 113, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 114, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f5639fc07db8ae613081841876d58857e3014e5f2efdeec154ae0318b2586f601", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 115, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f1509e3a68b74a8fd2b4e271b7c584a0e3cb857b6c3473e8fdf3436a2a2d0cc04", + "msg": "ed25519vectors 31", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 116, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf9f09162ea121fb3e2d1270dd30ade8b5bcfdcfd1dcf624ac22cf60af9610c6c02", + "msg": "ed25519vectors", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 117, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf9ecf8dc78bf5c647c714a00acf11719551952b488c2c9df967c7d48b479420e0e", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 118, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 119, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_A" + ] + }, + { + "number": 120, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 121, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 20", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_A" + ] + }, + { + "number": 122, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 17", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 123, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 124, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 39", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 125, + "key": "0000000000000000000000000000000000000000000000000000000000000000", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 126, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff24d6a9e426c0871c55d7163f0fe34e776bec47e582548d9bba074102c81fce02", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 127, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff35036672b82d501137113e048abbd4f44090e3aaa7e262f555e3e78fec78e507", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 128, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5639fc07db8ae613081841876d58857e3014e5f2efdeec154ae0318b2586f601", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 129, + "key": "10eb7c3acfb2bed3e0d6ab89bf5a3d6afddd1176ce4812e38d9fd485058fdb1f", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1509e3a68b74a8fd2b4e271b7c584a0e3cb857b6c3473e8fdf3436a2a2d0cc04", + "msg": "ed25519vectors 31", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 130, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 131, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 132, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 133, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 134, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 135, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 136, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "00000000000000000000000000000000000000000000000000000000000000005635c691e820339382bb85c151038e4b1815ceaaeb76bfa90cb7bf3382ec510b", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 137, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "0000000000000000000000000000000000000000000000000000000000000000d2abf247722e6b4213f1890beb32c730b02a929e22a1a8de5fe84fa1e8bec40b", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 138, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b2d155343b4933733be02419ac0ad255666ea0ad80d9998cc7c6086f4cf453507", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 139, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b25e180a8f97db7419c7adb79f5063f045cb18fc6af517852e3687be84bd7e803", + "msg": "ed25519vectors", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 140, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 141, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_A" + ] + }, + { + "number": 142, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 11", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 143, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 27", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_A" + ] + }, + { + "number": 144, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 145, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 146, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 21", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 147, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 148, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f9b8a1787d3747a0740ed283e74e2c478d4b681f0aa2676a5c694889696138205", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 149, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f938dd30260e6bce7f9422c05def45ba6732946a9f1236aff3a187902d7128808", + "msg": "ed25519vectors 14", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 150, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f5635c691e820339382bb85c151038e4b1815ceaaeb76bfa90cb7bf3382ec510b", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 151, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fd2abf247722e6b4213f1890beb32c730b02a929e22a1a8de5fe84fa1e8bec40b", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 152, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 153, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 14", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 154, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 11", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 155, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 156, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 157, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 158, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "0000000000000000000000000000000000000000000000000000000000000080fc5d42211dbee053d3bac3913e05f32c8026274b1926b55706c621eefaa7c805", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 159, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "00000000000000000000000000000000000000000000000000000000000000807ecde75a10d2cf5c13c49b78e4026148484c48d151616ae934cf4ec66f0e3702", + "msg": "ed25519vectors 16", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 160, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9468966bbfc7bcce00aeebb561f0197cb0b823ec3f17056333fce73486dc3cbe06", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 161, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9426c8d0de0953df46bc0049fed1261b7c06189e84e5c14985454485ba5a4fb501", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 162, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 163, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_A" + ] + }, + { + "number": 164, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 165, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_A" + ] + }, + { + "number": 166, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 167, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 168, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 169, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 13", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 170, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff28d80ed8be5636443022fa8a24dbe8f649bea0e6edc5a6a65b29db14caa1b80e", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 171, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3cb05c8a71d4d341e74340536fea96cd93577c8044879400da68b577ed7d7207", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 172, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "edfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc5d42211dbee053d3bac3913e05f32c8026274b1926b55706c621eefaa7c805", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 173, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7ecde75a10d2cf5c13c49b78e4026148484c48d151616ae934cf4ec66f0e3702", + "msg": "ed25519vectors 16", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 174, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 175, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 176, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A" + ] + }, + { + "number": 177, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 178, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 15", + "flags": [ + "low_order_A", + "low_order_component_A" + ] + }, + { + "number": 179, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_A", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 180, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "0100000000000000000000000000000000000000000000000000000000000000164aae8ae8d165867053dd1c3806319e064523aa053b1d7003463de8c5fcba0f", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_component_A" + ] + }, + { + "number": 181, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "0100000000000000000000000000000000000000000000000000000000000000f94cd85e18f5b89bfc21b4d29b43e30038f84228fe81eff5b7d735073ba29909", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 182, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a35063cb743a485cffaa684d0d66a5ae347d17a5638e90dc2be541023114a5961f805", + "msg": "ed25519vectors 14", + "flags": [ + "low_order_component_A" + ] + }, + { + "number": 183, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a35061fc409b236539503e78560d6183d748c8a6d3e635e87c9397531394f3cb3f902", + "msg": "ed25519vectors", + "flags": [ + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 184, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_A" + ] + }, + { + "number": 185, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 186, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 34", + "flags": [ + "low_order_A", + "low_order_component_A", + "non_canonical_A" + ] + }, + { + "number": 187, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 188, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 15", + "flags": [ + "low_order_A", + "low_order_component_A", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 189, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 190, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 191, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 192, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "0100000000000000000000000000000000000000000000000000000000000080001bb260c36aea3c94ff5785b5a44d99a88c33e02dceb9fdfe9ae57aad604b03", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 193, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "0100000000000000000000000000000000000000000000000000000000000080b38e0b9773175cdae4741a4973fa1ac1bbcd7263c5f28a3cf2a5bedca4d31106", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 194, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "0100000000000000000000000000000000000000000000000000000000000080164aae8ae8d165867053dd1c3806319e064523aa053b1d7003463de8c5fcba0f", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 195, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "0100000000000000000000000000000000000000000000000000000000000080f94cd85e18f5b89bfc21b4d29b43e30038f84228fe81eff5b7d735073ba29909", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 196, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 14", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 197, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 198, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f522d2c5eddb07da5dc5f2207a513322ee5860d4b5f94103ca604060d3672f402", + "msg": "ed25519vectors 24", + "flags": [ + "low_order_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 199, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7ffba7ac98b395cf8ffaecd3676b362f25f404252b7cbf07837b6bf780d2897b0c", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 200, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f164aae8ae8d165867053dd1c3806319e064523aa053b1d7003463de8c5fcba0f", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 201, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7ff94cd85e18f5b89bfc21b4d29b43e30038f84228fe81eff5b7d735073ba29909", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 202, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 203, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 204, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe29cfd8591462aa4ba19c79672667e0665bfb240f29de8ab80b43497f12b340e", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 205, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbe2295259b59b14675c8403202c2fe88c9d9eaf761f00f67db0dd24b67b09b01", + "msg": "ed25519vectors 11", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 206, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff164aae8ae8d165867053dd1c3806319e064523aa053b1d7003463de8c5fcba0f", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 207, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "eefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff94cd85e18f5b89bfc21b4d29b43e30038f84228fe81eff5b7d735073ba29909", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 208, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 209, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 210, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 22", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 211, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 212, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 213, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 214, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 215, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d2105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 216, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05486fb2a5d2cb002b460db9c56c36954aebaa4e6062f300beebb2749e32274106", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 217, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d21b9d3fadc566a3fb952d94b93f7add4e9e017bcf843058986b5ec9bec0638ba05", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 218, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 219, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d2105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 220, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d2105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 221, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 222, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f1105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 223, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85b4f602297e877cbbe446de71c9868e98c04a56e9bc409f60beaa400e7c633600", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 224, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f11a106ed973e5cdc868eace49e639ee52f4ca53fc715f8ec612e0917f4204f920e", + "msg": "ed25519vectors 13", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 225, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 226, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f1105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 227, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f1105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 228, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 229, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 230, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a1873e9ebb328202c033407e8b9f67e528093ea899da61e9ede6999b358de0f08", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 231, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee1eb53c48312206ed56a5f83ef3fd7b6ca3012fd4c2fb08aceea83ad251358000", + "msg": "ed25519vectors", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 232, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 233, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 234, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 235, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 236, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 237, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0826b7ce39c8491fabdd6759f89e5a424c344ecb7d94636b268e61a2a90ab20a", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 238, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72deff8aa11cfa5ea34bc35398cf040ad303d96d5b5bd818963f69cdda872d536904", + "msg": "ed25519vectors 17", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 239, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 240, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 241, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 242, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 13", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 243, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 244, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 245, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 38", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 246, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f109d35eafa88df6c5770dd5ef7023b3965891d0c4050d2e9c7906613229d6c0e", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 247, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f86e5c516a446a3c8e69728091c2be6eb479dfe18bb36efd34e08fd8acc730407", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 248, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf99f45ef3994a163d7cd58c2cead128afd1e3f3bc1a8ff98cbf1bc35ed609d420b", + "msg": "ed25519vectors 12", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 249, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf96f525d28c69be17ff4c0922ec4de39da15d46baad32dde92e4c37201b5cbd103", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 250, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 251, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 21", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_A" + ] + }, + { + "number": 252, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 253, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 16", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_A" + ] + }, + { + "number": 254, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 255, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 38", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 256, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 257, + "key": "0000000000000000000000000000000000000000000000000000000000000080", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 258, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb2323a8aa3bc6edc5f2f3590ba2db757d50bf47c9f16cd1fd6a7db0f4ca24e0b", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 259, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa9189abccf6d929dc864b3eec19e26e4cc94b244565779a7f85ddb8e03c3c108", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 260, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff109d35eafa88df6c5770dd5ef7023b3965891d0c4050d2e9c7906613229d6c0e", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 261, + "key": "dd1483c5304d412c1f29547640a5c2950222ee8931b7ed1c72602b7afa7024e0", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86e5c516a446a3c8e69728091c2be6eb479dfe18bb36efd34e08fd8acc730407", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 262, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 263, + "key": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 264, + "key": "0100000000000000000000000000000000000000000000000000000000000000", + "sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 265, + "key": "0100000000000000000000000000000000000000000000000000000000000000", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 266, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "0000000000000000000000000000000000000000000000000000000000000000ef1195f0610612912016b3ee33054829bcea20ae4de1e9343b1c4e2e15649003", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 267, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b2d471882773a677af1e3824e757a33f8ddf7bbeaf3d28a09595eb8daa0d74a03", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 268, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 269, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 20", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 270, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 271, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 272, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 273, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 274, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 275, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 25", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 276, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 277, + "key": "0100000000000000000000000000000000000000000000000000000000000000", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 278, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fa83f56d4ca92da1f056c0bbaa0cc73cb350790210431ad647415c6a71242860c", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 279, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fef1195f0610612912016b3ee33054829bcea20ae4de1e9343b1c4e2e15649003", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 280, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 281, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 17", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 282, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 283, + "key": "0100000000000000000000000000000000000000000000000000000000000000", + "sig": "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 30", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 284, + "key": "0100000000000000000000000000000000000000000000000000000000000000", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 285, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "00000000000000000000000000000000000000000000000000000000000000800a4940ad86a3df965346037e94d0796fa785353be5f5d25b5c3c1a507c63bf0d", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 286, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9426cc293b14d304864a4cd689cbcc23cb13fcb1098f9c434cc9ca7439c9cf2505", + "msg": "ed25519vectors 11", + "flags": [ + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 287, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 288, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 289, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 290, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 291, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 292, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 293, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 294, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 295, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 296, + "key": "0100000000000000000000000000000000000000000000000000000000000000", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 297, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff944508fdd2e7908fed1b0bbbef3342fd911990128cbaa8714b0d28a617d4b508", + "msg": "ed25519vectors 19", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 298, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0a4940ad86a3df965346037e94d0796fa785353be5f5d25b5c3c1a507c63bf0d", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 299, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 300, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 301, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 302, + "key": "0100000000000000000000000000000000000000000000000000000000000000", + "sig": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 23", + "flags": [ + "low_order_R", + "low_order_A" + ] + }, + { + "number": 303, + "key": "0100000000000000000000000000000000000000000000000000000000000000", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_A" + ] + }, + { + "number": 304, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "0100000000000000000000000000000000000000000000000000000000000000330b30d6c1f07511e2208d233b6a34d4ed7109da2dea615bcd97daee3a842704", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R" + ] + }, + { + "number": 305, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350640657f1cb030f12543549bbabd848dc6403d354e7c99c4d0dcee9f63c9033b00", + "msg": "ed25519vectors 5", + "flags": null + }, + { + "number": 306, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_A", + "non_canonical_A" + ] + }, + { + "number": 307, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_A", + "non_canonical_A" + ] + }, + { + "number": 308, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_A", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 309, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 16", + "flags": [ + "low_order_R", + "low_order_A", + "non_canonical_A" + ] + }, + { + "number": 310, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_A", + "non_canonical_A" + ] + }, + { + "number": 311, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_A", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 312, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "non_canonical_A" + ] + }, + { + "number": 313, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_A", + "non_canonical_A" + ] + }, + { + "number": 314, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_A", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 315, + "key": "0100000000000000000000000000000000000000000000000000000000000000", + "sig": "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "non_canonical_R" + ] + }, + { + "number": 316, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "01000000000000000000000000000000000000000000000000000000000000802d32f9eeccaa31dba2ff4ab4e196b61e2ee1648073bbb5f599dcff4f7ea02900", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "non_canonical_R" + ] + }, + { + "number": 317, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "0100000000000000000000000000000000000000000000000000000000000080330b30d6c1f07511e2208d233b6a34d4ed7109da2dea615bcd97daee3a842704", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 318, + "key": "0100000000000000000000000000000000000000000000000000000000000000", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_A", + "non_canonical_R" + ] + }, + { + "number": 319, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f367b7a92fea8539e8793cf812e799e8ed342c635592e74323a8dfd38a06aea0b", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "non_canonical_R" + ] + }, + { + "number": 320, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f330b30d6c1f07511e2208d233b6a34d4ed7109da2dea615bcd97daee3a842704", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 321, + "key": "0100000000000000000000000000000000000000000000000000000000000000", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 18", + "flags": [ + "low_order_R", + "low_order_A", + "non_canonical_R" + ] + }, + { + "number": 322, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "eefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8ee37866245b5ef5bb4863572672809b98d93997190c514ccff477ebcf82f0d", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "non_canonical_R" + ] + }, + { + "number": 323, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff330b30d6c1f07511e2208d233b6a34d4ed7109da2dea615bcd97daee3a842704", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 324, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 325, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 326, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 11", + "flags": [ + "low_order_R", + "low_order_A", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 327, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_A", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 328, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 329, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 17", + "flags": [ + "low_order_R", + "low_order_A", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 330, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 331, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 332, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 19", + "flags": [ + "low_order_R", + "low_order_A", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 333, + "key": "0100000000000000000000000000000000000000000000000000000000000000", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 334, + "key": "0100000000000000000000000000000000000000000000000000000000000000", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d2105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 335, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05aac16c21847c8f77ede0e03571488e65c01bc418470acebc90a5f65a69c3c60f", + "msg": "ed25519vectors 17", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 336, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d21fd3eacd8d7a35408e669bbf20fa791fc50be2a753015c51a7ff9554c4c540902", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 337, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 338, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d2105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 339, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d2105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 340, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 341, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d2105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 342, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d2105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 343, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 344, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d2105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 345, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d2105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 346, + "key": "0100000000000000000000000000000000000000000000000000000000000000", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 347, + "key": "0100000000000000000000000000000000000000000000000000000000000000", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f1105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 17", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 348, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc8578b7a7a27ec214b774ab6434c7afe22bc3e6fa5f12d24e81dfbd99085789170b", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 349, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f114aa4b690ec5d1502a0c77d89ede55349d93b2f4df581bc979f8f0adbe60da300", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 350, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 13", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 351, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f1105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 37", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 352, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f1105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 17", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 353, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 354, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f1105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 355, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f1105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 17", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 356, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 357, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f1105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 358, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f1105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 17", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 359, + "key": "0100000000000000000000000000000000000000000000000000000000000000", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 360, + "key": "0100000000000000000000000000000000000000000000000000000000000000", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 361, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037afe03e5c298e079a79794c3a820a61614325041fc4c1be84e6169092dd4e42a01", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 362, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0eedf2a56e3f9f022ace42aa6116f576e76d0f752df8a5c879feb2288236a882d03", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 363, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 364, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 365, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 366, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 367, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 368, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 369, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 370, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 371, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 372, + "key": "0100000000000000000000000000000000000000000000000000000000000000", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 373, + "key": "0100000000000000000000000000000000000000000000000000000000000000", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 374, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa6245db2e055cd96ba42d2e1330ef131eefb5502759cc731c8cf787d7fd2b5a0c", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 375, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de5efb4cbf95ba7a317033c634a3bd174c2e36f6fb3a0b1d96608de7cfe4374d05", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 376, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 377, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 16", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 378, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 379, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 380, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 381, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 382, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 383, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 384, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 385, + "key": "0100000000000000000000000000000000000000000000000000000000000000", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 386, + "key": "0100000000000000000000000000000000000000000000000000000000000000", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 387, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fd2c8bb23568fc2c2ab61436089349253db3045d05634c2f50f67a88ab426e709", + "msg": "ed25519vectors 13", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 388, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf9598a4afbe7b2a777d59b9dd15ed248f498090c35ea40d691bab0cee574467807", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_component_R", + "low_order_residue" + ] + }, + { + "number": 389, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 13", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 390, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 13", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 391, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 392, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 393, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 394, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 395, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 396, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 397, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 398, + "key": "0100000000000000000000000000000000000000000000000000000000000000", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 399, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcffc829608ae700722e38df9eb9761d200a3c86e7ef6dde961ba9cc8691cbc07", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 400, + "key": "ef75b20e7540e3dff77404193652ba2bd13df99c1508eee1515e27ae25f28076", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd2c8bb23568fc2c2ab61436089349253db3045d05634c2f50f67a88ab426e709", + "msg": "ed25519vectors 13", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 401, + "key": "0100000000000000000000000000000000000000000000000000000000000080", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 14", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 402, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 403, + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 404, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 405, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 11", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 406, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 407, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 408, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "0000000000000000000000000000000000000000000000000000000000000000620683477c6eae0f5f962cfb675dc5f112275e7207521b05f57d861fb8dfa804", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 409, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "0000000000000000000000000000000000000000000000000000000000000000ab98e2715f01f57e976deea3afb2c51e93c46f5f7e054a3764fd076682034004", + "msg": "ed25519vectors 35", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 410, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b8043872d01ef87b42d987e20d1f5604f591441bec276cb285e00c60c3854a307", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 411, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56bd79e8fd671739b78d219e13e1b578516549b9c90c988249f62d1d6a2b40ea606", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 412, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 413, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 11", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 414, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f9d21c5ce09b181f21ab1407e47442069d686bde3a0d7d250f0746e9835319f0a", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 415, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f806f1e38871587c8ed5631faf0f941c72744d2733d285b0d26eb5c7f79982b02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 416, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f620683477c6eae0f5f962cfb675dc5f112275e7207521b05f57d861fb8dfa804", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 417, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fab98e2715f01f57e976deea3afb2c51e93c46f5f7e054a3764fd076682034004", + "msg": "ed25519vectors 35", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 418, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 419, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 420, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 421, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 15", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 422, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "0000000000000000000000000000000000000000000000000000000000000080367dbd8f23ed46b14c764374ffd8542122d8a7b57b50e54851e702e70ca6660f", + "msg": "ed25519vectors 17", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 423, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "00000000000000000000000000000000000000000000000000000000000000807df52a3fa4bde9595e64e46ee493b1f14777427d518e42f19b6e5a9d891a0004", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 424, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a94cf6ffd24d41962558b5ec187dd97dd62faa0e79f56e10b9588663b310ece9800", + "msg": "ed25519vectors 16", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 425, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a94d205a702b93f4292e22186bd167770bfa3278f37a46264a735b31e757393e50f", + "msg": "ed25519vectors", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 426, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 427, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 428, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "edfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff797080e0e516f4ce696c1f6508c4211246c8734fe1c740830bc07b428001007", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 429, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff881c2e6337a2fc68add023c1c3b77016cefb8304d735cca7571edde70fca1408", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 430, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff367dbd8f23ed46b14c764374ffd8542122d8a7b57b50e54851e702e70ca6660f", + "msg": "ed25519vectors 17", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 431, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7df52a3fa4bde9595e64e46ee493b1f14777427d518e42f19b6e5a9d891a0004", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 432, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A" + ] + }, + { + "number": 433, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 21", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 434, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_A", + "low_order_component_A" + ] + }, + { + "number": 435, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 15", + "flags": [ + "low_order_A", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 436, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "010000000000000000000000000000000000000000000000000000000000000048435f31d933b4c6418d7d48d1223ddf2005f6ff4c6555c5850313295f5f4507", + "msg": "ed25519vectors 25", + "flags": [ + "low_order_R", + "low_order_component_A" + ] + }, + { + "number": 437, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "0100000000000000000000000000000000000000000000000000000000000000b1daaf7afa2140df3989634cfeb1dd0704b1feab734e6032a97f08ff33650704", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 438, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350606eafe34c35b824e46e082060ca75777e699beb94810d8195d570395c050470f", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_component_A" + ] + }, + { + "number": 439, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a3506046f0694bed0011b9085a74ca8f2aded381a9392b8182a6475ce4067fe6a7b07", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 440, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 441, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 442, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "0100000000000000000000000000000000000000000000000000000000000080c57b6e27f4d1afcbc43510891ee7816e69732bdac3893a4c4d75a698ec76fd09", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 443, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "0100000000000000000000000000000000000000000000000000000000000080ef0227e52e05478966a93698d9ddcdf73008388227b82a9a3f4311cff41ebd01", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 444, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "010000000000000000000000000000000000000000000000000000000000008048435f31d933b4c6418d7d48d1223ddf2005f6ff4c6555c5850313295f5f4507", + "msg": "ed25519vectors 25", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 445, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "0100000000000000000000000000000000000000000000000000000000000080b1daaf7afa2140df3989634cfeb1dd0704b1feab734e6032a97f08ff33650704", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 446, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 447, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 448, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f69d1960a5be23a7e46d14e609465c4fb82f3a486b4cf9ba3cc00d79f371a3002", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 449, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fc5563d65a243389fca7e1e6da4223a97871ece326555c70d323ade5764f6420c", + "msg": "ed25519vectors 14", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 450, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f48435f31d933b4c6418d7d48d1223ddf2005f6ff4c6555c5850313295f5f4507", + "msg": "ed25519vectors 25", + "flags": [ + "low_order_R", + "low_order_component_A", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 451, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fb1daaf7afa2140df3989634cfeb1dd0704b1feab734e6032a97f08ff33650704", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 452, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 453, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 16", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 454, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01e3f5bcb8f8809336ac1034f88ea2e77c63713da62cdd73a7f5c7550ca89208", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 455, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff161119954a491e61643a5f8ba93edccf16b52d784f8a896e5d7ba53b73c84504", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 456, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff48435f31d933b4c6418d7d48d1223ddf2005f6ff4c6555c5850313295f5f4507", + "msg": "ed25519vectors 25", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 457, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb1daaf7afa2140df3989634cfeb1dd0704b1feab734e6032a97f08ff33650704", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 458, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 459, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 460, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d2105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 18", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 461, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d2105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 462, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05008fba896e3cc7324a5b000fd0b903492c2ae0ae71dff7af176665ba205f3901", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 463, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc057c047ff30db9b92cac61791c64666eb0f6f11dd840d7d05b927c85f50533600e", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 464, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d21f7d88566da564c6fa777235d8864b9df8d63ee4369bcc3dbf0f421fdfe9d7309", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 465, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d21ddfb80c3b500d19d9a01010fb34cf3c00c7bc6e972e7d672e19ec969bd5b1908", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 466, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 467, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 468, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f1105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 469, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f1105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 470, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85a12ee75b7ce36d925ba81778453a97d8344a8decfdd797ee6d398a2c6820260e", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 471, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc8527d64d53fa7d8a7a56ab4dcfce52e6411ff5c5466029d30aa4159afad83f2409", + "msg": "ed25519vectors 15", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 472, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f1123de7ac4b146e4fb8b492be4fcc69c95022bb9bba7eff6839101409bd5c5d50a", + "msg": "ed25519vectors 14", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 473, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f116f03b9c47f23619e95184c7914c139f4a66906150bdc0635512421be6e282004", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 474, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 475, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 476, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 477, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 12", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 478, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037abeb9fdccdf6e0bb47df7c1cba374f20e53e2af651a4a69d9fa8554bcd539e50b", + "msg": "ed25519vectors 24", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 479, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037ab6cdec95505f3fc3a3c58d45a591399c735e98c0cadaba2b61ee8b93f54d4105", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 480, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee27f196298cd2b5aec8b6545b122eb01dc4c87aff398c5152ed458036643f6a0a", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 481, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee035953cc64888fcdb0e70de5779b546a4c56b2d8f579748b4ce963887eb36705", + "msg": "ed25519vectors 16", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 482, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 483, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 484, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 21", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 485, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 486, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fab582b7398d6916f262ad46ab12895cd197ae7a89fd94530ad4071a5d71ee870b", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 487, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa8fb5bb6032e801fdce885ff4f83560b6f56e66d3d9ad3a33eb9280190b2c670a", + "msg": "ed25519vectors 41", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 488, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72def9a498ab4b8a710de4a88867179af3a355b93c22f79f39581f7fb196e72f3400", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 489, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72dec0096ae4cafa9e22026530dc5fd2efd3159c8b1e4bcbfee4bb18049c43a33a03", + "msg": "ed25519vectors 41", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 490, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 12", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 491, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 492, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 493, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 494, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7ffa4a318b4063e408dec239cc0589533d04f801fe25009c1edd4c0a3b25097600", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 495, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fd3092366957eb289ec452b34bb6783c1790a339ad5d004d9f6d435325bdc2c0a", + "msg": "ed25519vectors 30", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 496, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf95a0b32dab50849741c136247a6109cfa76dc577cdec798e467689e43613dfc00", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 497, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf9b452dd12a8b87cd3b6a527364a5e6f99eead5739469d5d120d95e50394681605", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 498, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 499, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 11", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 500, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1e9a40075f4b371d6575911abeb73574e1ce0ada5640bda601486ef92ef7ed0d", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 501, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2f9a3ea3657c36624cec77ed3facee69b3eeeb32131c6b87ba0a76993d3a3a06", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 502, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "ecfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa4a318b4063e408dec239cc0589533d04f801fe25009c1edd4c0a3b25097600", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 503, + "key": "6718d0a3d58deaeaefa655eae3f119071deaa2cfebfd0ca28b670f879d657086", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd3092366957eb289ec452b34bb6783c1790a339ad5d004d9f6d435325bdc2c0a", + "msg": "ed25519vectors 30", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 504, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 35", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 505, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 506, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 507, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 27", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 508, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "00000000000000000000000000000000000000000000000000000000000000006d14fb942d5ff13cef4d783375f3abef9aea3d9bcecc1a0866415dae3509d507", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 509, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "00000000000000000000000000000000000000000000000000000000000000005f5f7ffd508fc84bd8fb5d9e90a3abc24b2cbacc03b00ca42c28e7b879c5d90d", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 510, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b08a85cdeab6f57330926e2eb3891c1017863d51e99f8d8f732ee42f5433c7507", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 511, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b097c3f79437cd87c6061fb032f792cd8cf46cbb4ce390b4decb9c1bf5e70ad0b", + "msg": "ed25519vectors 15", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 512, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 14", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 513, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 16", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 514, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fcf5dd993590903346c6c7dbbdab784f8eb498e7074896ff06f98221870663b07", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 515, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fca87bba547ac145cd6e813328df6df16ac5a137df85037b1c5ad2877464e840d", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 516, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f6d14fb942d5ff13cef4d783375f3abef9aea3d9bcecc1a0866415dae3509d507", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 517, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f5f5f7ffd508fc84bd8fb5d9e90a3abc24b2cbacc03b00ca42c28e7b879c5d90d", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 518, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 519, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 520, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 521, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 522, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "0000000000000000000000000000000000000000000000000000000000000080bf79045665b39525ad5ec9e9ade82bd5bc33fb049b13503dfb97aacc4ad0ae0e", + "msg": "ed25519vectors 31", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 523, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "00000000000000000000000000000000000000000000000000000000000000806b115a2573c0a4de9f3411a96c31e1919e57bfc3e5c27faefebcdd47768b9a05", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 524, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9450b2750dbb4ca36a55e8561ae5f39235db090de324b3c60126ad1f5c1d1ae30a", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 525, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9416e0b2ca09fe5b8ba05f5f8d3b4ab0e39b79b549775a9e136fd9d6fedf3b8609", + "msg": "ed25519vectors 12", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 526, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 527, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 528, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9bc693ae3af4296f60ffd9afb577cabb05c2dff634a5d483e5a8c094dd068403", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 529, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2f93b9ac542ae80e1054d7f7aff2c72cfcb5140130c37c92d4eb389c806d1509", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 530, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf79045665b39525ad5ec9e9ade82bd5bc33fb049b13503dfb97aacc4ad0ae0e", + "msg": "ed25519vectors 31", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 531, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6b115a2573c0a4de9f3411a96c31e1919e57bfc3e5c27faefebcdd47768b9a05", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 532, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A" + ] + }, + { + "number": 533, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 534, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_A", + "low_order_component_A" + ] + }, + { + "number": 535, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors", + "flags": [ + "low_order_A", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 536, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "010000000000000000000000000000000000000000000000000000000000000092a325759ff830a4d19c0f0cce6364311dc1e7e4bc1efaa8a54632082c05cf0f", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_component_A" + ] + }, + { + "number": 537, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "010000000000000000000000000000000000000000000000000000000000000048e9799d7754086f6eca319160c0d2ef1d35617c0151c719806495eb1026ac05", + "msg": "ed25519vectors 15", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 538, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350610dba6a8317cea9cda386e05f66eabb4248890064325c1d7a567a3443ce26606", + "msg": "ed25519vectors", + "flags": [ + "low_order_component_A" + ] + }, + { + "number": 539, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a3506ceeddc384993fffb6ea69400f4a1529ddbd0f43c6322bf53d9ec144e9127680d", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 540, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 11", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 541, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 542, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "0100000000000000000000000000000000000000000000000000000000000080cdd1248e749ceac01f109bce3ce41507e49ba65d6a42baa443d77aa63113cd0f", + "msg": "ed25519vectors 18", + "flags": [ + "low_order_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 543, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "010000000000000000000000000000000000000000000000000000000000008063e6cef756c8afdf5e0b364b01afa219cedeadb53d76c1ac58140dc8a7b37106", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 544, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "010000000000000000000000000000000000000000000000000000000000008092a325759ff830a4d19c0f0cce6364311dc1e7e4bc1efaa8a54632082c05cf0f", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 545, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "010000000000000000000000000000000000000000000000000000000000008048e9799d7754086f6eca319160c0d2ef1d35617c0151c719806495eb1026ac05", + "msg": "ed25519vectors 15", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 546, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 547, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 548, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fd4859f5c56a843a885bd86045dfcda0bf117f95c07c298f45664ad2f7f043106", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 549, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f4ee43c88b8d6703511f5a818428fd65e564301e1840c7ede88cf15ffac567a01", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 550, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f92a325759ff830a4d19c0f0cce6364311dc1e7e4bc1efaa8a54632082c05cf0f", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 551, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f48e9799d7754086f6eca319160c0d2ef1d35617c0151c719806495eb1026ac05", + "msg": "ed25519vectors 15", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 552, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 553, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 554, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2b502e797c4ce38333e3166e4e102483cd6ea5eea07bd0347a85d303c64e8b07", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 555, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2de8d820b140fdd27706ee8011913f9542adf9641f0b2913054c4ed3c2c1be06", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 556, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92a325759ff830a4d19c0f0cce6364311dc1e7e4bc1efaa8a54632082c05cf0f", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 557, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff48e9799d7754086f6eca319160c0d2ef1d35617c0151c719806495eb1026ac05", + "msg": "ed25519vectors 15", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 558, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 559, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 560, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d2105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 561, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d2105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 562, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc051f8c6e2af77751e5474253bfac76ad7d3e16be0be2ff499ac17b66beafb7270e", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 563, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc0510eb9a2150d3683d1d873d608d7fb395b5968b379a0ec726bbbfbe453114fd01", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 564, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d21a66d971845a5eed7cd0432182614117196429d78205aa912a4272b134ab1740c", + "msg": "ed25519vectors", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 565, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d219c41f676f85cd5a5eee282112170365be8a4820e7cc4d13fdb88b1bf1f106c0b", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 566, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 567, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 568, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f1105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 569, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f1105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 570, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85c70ad24a368812eb8a9fd7bb0e300f492a0a9dbcda01df2829bca14ccf5bf10f", + "msg": "ed25519vectors 13", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 571, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85840364150cb09a830180d30ac7907c0a858eef2a282dc539e6fd73d8a6dfad0f", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 572, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f11e35ad3a31ecbff623f4733dded01086ed68e2bdaaa53171f4b8748a26514400d", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 573, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f115f11918f318e44147d456b9e33399dd987efaa7fb06b2485e1dd2f2e0c1e4b04", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 574, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 575, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 14", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 576, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 577, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 578, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a3c8b7f81b7fccdb4c6bc85121bc77ac9b2f2b22f3241501b1521195c86f3820f", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 579, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a3b8d3ce4adae6f057f2f0b1ad9b46678443d6c79779a69254a6d112ddf435b0c", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 580, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee9e7960e86abadeb7d5e1d2492122d925685b47203130aa8e629d093c61ada90d", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 581, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee40bdefba7a8c40c0580426e32719149f9600226b48f5e27dae52b6fbbdcf5805", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 582, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 583, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 584, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 585, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 586, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa7c1368a43039c42ac2dbb604b9617df90b4d2022deee87dd75d32faf83b9b901", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 587, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03faacf8c3c342a85ff0684df2e0dbdacf3a81417755cb9f5005d22bedf43fd8c803", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 588, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de1a33a7d03bde08688f729ab233eced41ff1db67eaa08157c3a1a27a00670c701", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 589, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de54af5f2d4dd8095dbea5873cc38d78ea426817d2fd61e9de5ec5165c93e62f09", + "msg": "ed25519vectors 11", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 590, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 591, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 12", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 592, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 13", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 593, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 594, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f9dff20f0d2acccc37928a00eb22a115047b52bcc2ab4b79b4432873d20172800", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 595, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fd082268d79821ab3a47487f2de6f64a13c921c0ec23e89e57cd596e56f61fe0a", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 596, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf96c95b94a5ed966fe1ac4f858c49fe690ab5f2d3a4571548d943cd31375e3f20b", + "msg": "ed25519vectors 17", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 597, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf9e8df7d32a60fca528b0606d03f8bd6e57619183cb72e99da237d97f87213d301", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 598, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 599, + "key": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 600, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff681dcf4473cd9f43e21c3392dde10617a686de272c1014020896c9f458986202", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 601, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff35ec9aadea22201caf2fadcc24f4818d1202723a9a354e7b351094f746706300", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 602, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9dff20f0d2acccc37928a00eb22a115047b52bcc2ab4b79b4432873d20172800", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 603, + "key": "eca00a6a1b1f522ff2217691059915b097b73bc69bef396c36ddcd559b79e2b0", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd082268d79821ab3a47487f2de6f64a13c921c0ec23e89e57cd596e56f61fe0a", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 604, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 605, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 606, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 607, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 608, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "000000000000000000000000000000000000000000000000000000000000000031ac3ed1453a28af252ff542be21a4c0ef8b97c74940e51766b1855dbe02350d", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 609, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "0000000000000000000000000000000000000000000000000000000000000000420360a0c176d4586f28edbbcafc6e5d63d8f70954510ade434219770a86f204", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 610, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b07fe06280ad617d2c94c06e2b20983e035b2848443f7de550e5358bea36f0c03", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 611, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b945fc1469dbba776f427c47e386524dc1916bf9aad21268232b7f49b15310c06", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 612, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 613, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 614, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f8afc6fda146d589fa805b824266862baa309bb1ec071226b06ba4eaff9763c0e", + "msg": "ed25519vectors 11", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 615, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7ffa0b5b45eb2cb24cab8a910bfbcb08641410d64790b516c583b63940d25a9504", + "msg": "ed25519vectors 12", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 616, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f31ac3ed1453a28af252ff542be21a4c0ef8b97c74940e51766b1855dbe02350d", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 617, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f420360a0c176d4586f28edbbcafc6e5d63d8f70954510ade434219770a86f204", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 618, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 11", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 619, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 620, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 621, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 16", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 622, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "000000000000000000000000000000000000000000000000000000000000008069c99316215043ec75ea5a3d4e1357e3d0c5602366e82b24de319a520b585106", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 623, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "0000000000000000000000000000000000000000000000000000000000000080c9a28760bfdbaf4f028fe8d8ecb8c34db3b7c0c71d24e26734b2c841e8fac207", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 624, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a94e8b6da4567e7bfba5829e7a1197f8062462342ba28f67b2cbcf14a28af6e970a", + "msg": "ed25519vectors 12", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 625, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a948f8fa9222af828b040bcd86187c66d23faa97d1a51be5d5126bfd78688897b00", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 626, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 627, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 628, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff09b548baa15c220f0c456454bc0b3501750239f69aad45100932d36f0e9f3200", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 629, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa537bd259769d9113a834f55e1f42b160fad630daf58c8f9c7e80d8e5daa1302", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 630, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff69c99316215043ec75ea5a3d4e1357e3d0c5602366e82b24de319a520b585106", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 631, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc9a28760bfdbaf4f028fe8d8ecb8c34db3b7c0c71d24e26734b2c841e8fac207", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 632, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A" + ] + }, + { + "number": 633, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 12", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 634, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_A", + "low_order_component_A" + ] + }, + { + "number": 635, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_A", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 636, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "0100000000000000000000000000000000000000000000000000000000000000c46cc62cf59c79dc96ec34243a10a14750194479376a49cc84651a23a488ee05", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_A" + ] + }, + { + "number": 637, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "01000000000000000000000000000000000000000000000000000000000000007cbd8c879c5bf2335aa640bc7becf0547146c5becaae73560e9e80c036f2d502", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 638, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a3506104c833a1253a48cd66e54cab38543281b75e53c8bccb6a1dccd45eb251f1307", + "msg": "ed25519vectors 14", + "flags": [ + "low_order_component_A" + ] + }, + { + "number": 639, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a3506f3ae4bb3afa88c3285c8b1241cf6df8316bb4b35e71346248582cd627121ad02", + "msg": "ed25519vectors 13", + "flags": [ + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 640, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 25", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 641, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 642, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "01000000000000000000000000000000000000000000000000000000000000809dfcac23338d3f13dc0ff5de059db61934190741f1f00836d7b735975892e002", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 643, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "0100000000000000000000000000000000000000000000000000000000000080230333f78f5b3eacd737fb60742c5c041e1ea56410ac40cc56b5b40968cc1209", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 644, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "0100000000000000000000000000000000000000000000000000000000000080c46cc62cf59c79dc96ec34243a10a14750194479376a49cc84651a23a488ee05", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 645, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "01000000000000000000000000000000000000000000000000000000000000807cbd8c879c5bf2335aa640bc7becf0547146c5becaae73560e9e80c036f2d502", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 646, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 647, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 13", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 648, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f3a63dbb9c17d6ca41a6e1ce55b0a99bf8025eb49cc4640b3de424cb665fba20c", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 649, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f5b028c7e15842ffa8f6a8effc6f3a447419f4228cbfba57ebdf522573ed38f08", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 650, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fc46cc62cf59c79dc96ec34243a10a14750194479376a49cc84651a23a488ee05", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 651, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f7cbd8c879c5bf2335aa640bc7becf0547146c5becaae73560e9e80c036f2d502", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 652, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 653, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 16", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 654, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92d85e93707e2f67157e9e4b3e289b823be4ccec80abd861efb9ba2e4c32510e", + "msg": "ed25519vectors 16", + "flags": [ + "low_order_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 655, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaced0fbc977b44ec36d82176d929c9878bc6a90dbabdac84f071fa6daf97050a", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 656, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc46cc62cf59c79dc96ec34243a10a14750194479376a49cc84651a23a488ee05", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 657, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7cbd8c879c5bf2335aa640bc7becf0547146c5becaae73560e9e80c036f2d502", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 658, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 659, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 660, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d2105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 661, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d2105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 662, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc053fe4d58f46c5232b61f8e315b21a99832e7edb9875c9c2cfd0d77626215b580a", + "msg": "ed25519vectors 15", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 663, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc053d8acb56c081e29da3329a1f78ceff08c5f925cde1c64690dbf91d5260bcd50b", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 664, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d21ea7f1306764d7e910babd66ac6b9ed1b7db6c0381fbefe5d98b70a2c8fe97407", + "msg": "ed25519vectors 13", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 665, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d2110ac576cd0481197776c0121a40caf560b3fc593fa0982e1e274007955fe530a", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 666, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 667, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 668, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f1105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 669, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f1105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 17", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 670, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc858c6564558b985cc383035bfb3572083c323717acb6c014db50b4a539a1e8900a", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 671, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc851feb0bd9cb3ea94d28ecce323f06901a96b3ea2f9e5b885aafa13d571ae4f70b", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 672, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f11fa299c89564128d4fe83c4758eb9c28990407191109ad31f0ca2b1548da63404", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 673, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f119a4fca0c7482f8d9f37923d8702d104d07b549e08225193b2818a6380e9ab40f", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 674, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 675, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 676, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 677, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 13", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 678, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037ae37e2cac72091af68b51b43b40f15e499c41621707e1fb30cb17362333cbba09", + "msg": "ed25519vectors 12", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 679, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a1e7d219f4540986f8904c6a16a2b74967c013b9ff1a99e6b9fdbb4835c19d70d", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 680, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee9101d1fb7d624768ec8ea7e31aca21042f3495ad5e48fdd151423f5c5f216c04", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 681, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0eef282a24a316fe5449ea437d32470df2c968f2f27688653d1e026ad3d3bfb530c", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 682, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 683, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 684, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 11", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 685, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 16", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 686, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fabfad7e6fdf808e46ddaaa65ad1e5fc4751473c239f63c49c2e017d7209058900", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 687, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa750d023533f655c6f32c38992a8a30f9f20c59bda30682a7f5f692764724e604", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 688, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72def2c459284b532cb7a74779ced5cc538521b7e466561e8abdcda7f64238404200", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 689, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de55ba20be6f48cddf9d90aef69958f1c53baa96226c0d788f25b13754d1a33207", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 690, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 691, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 692, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 693, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 694, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7ff14a0b307122c632de6f34f1107225f88b1e21893f4d6719ddefd90f44b11f05", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 695, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f517fa7eb17544d51490f2417a13f0d85919dd5998cc3c465b8c3614f4651d20d", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 696, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf9a05c2ebdd26e6d36b0d39a6dc8cad91ac0934700d6b796b6d7499d6eb81f1301", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 697, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf9e97e9b1f47281ed688a320de5912618b62c9dac64d5971e2aab2d487038cc90b", + "msg": "ed25519vectors 18", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 698, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 699, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 700, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff248f13d1b0c2af4782513a8c33da39dd7cf9caf4ee3c85e58dbcbaec928aee0b", + "msg": "ed25519vectors 20", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 701, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff10e1e32f76302558b064ad4873824195afc9fbdd1a6aa20a8dd05cd54f708f00", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 702, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "ecfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14a0b307122c632de6f34f1107225f88b1e21893f4d6719ddefd90f44b11f05", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 703, + "key": "015ff595e4e0add00dde896efa66ea4f6848c4396410c693c92232aa64861d4f", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff517fa7eb17544d51490f2417a13f0d85919dd5998cc3c465b8c3614f4651d20d", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 704, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 14", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 705, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 30", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 706, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 12", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 707, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 708, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "0000000000000000000000000000000000000000000000000000000000000000ff3d622b55aad17c5c7aad97a56a7ada9eda2a92a14a4c73e349d8da4f64c70a", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 709, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "00000000000000000000000000000000000000000000000000000000000000000734b8823047a23fb776be86db7b3f4bb1c9e49bc29b352c760002bdc8e54d02", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 710, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b55a1b09e210288e47a4796def776679cdec5a64bd2bb9bc69932c3bd47b96109", + "msg": "ed25519vectors 11", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 711, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b074fc0dd361fa65cf275443e324c504017fa8e80bba2a1c5ad46f116c3dac901", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 712, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 713, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 714, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fa6f79db3f81348fd22224e50a25621f810d038de2060250f8a4937f3b4cc5208", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 715, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f59c48433c25fcb5f435c328aff1bad6eaf88cc0d7586a723ba3dc921f5009f0a", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 716, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fff3d622b55aad17c5c7aad97a56a7ada9eda2a92a14a4c73e349d8da4f64c70a", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 717, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0734b8823047a23fb776be86db7b3f4bb1c9e49bc29b352c760002bdc8e54d02", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 718, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 719, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 720, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 721, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 722, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "0000000000000000000000000000000000000000000000000000000000000080216c38d9eae130fee53569d8a1750b2f2ef49185cddd7cf43379e84a3ef50d04", + "msg": "ed25519vectors 12", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 723, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "000000000000000000000000000000000000000000000000000000000000008026d690d9305a0d0a44ebb4e82d11b7fb6e0850fde3b27cdf662dfd300393eb01", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 724, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9452834a44fb880f6b87a7e173a2856a90f987bef39f1d95b066a528099af13302", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 725, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a94a69145f508ef4ce1a0fd25025d82516e06e492ef088de45da784f8a863ebe401", + "msg": "ed25519vectors", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 726, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 727, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 728, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff56668e1aa7f6b00250315677063e6f347c869cc31c7a9f31ed42d02e1bdd9e0d", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 729, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff368ce93ef4a864890115ed9cb1341fa32fa0996911fe07c1496e8c9a3df43a08", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 730, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff216c38d9eae130fee53569d8a1750b2f2ef49185cddd7cf43379e84a3ef50d04", + "msg": "ed25519vectors 12", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 731, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff26d690d9305a0d0a44ebb4e82d11b7fb6e0850fde3b27cdf662dfd300393eb01", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 732, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A" + ] + }, + { + "number": 733, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 734, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_A", + "low_order_component_A" + ] + }, + { + "number": 735, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 12", + "flags": [ + "low_order_A", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 736, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "0100000000000000000000000000000000000000000000000000000000000000cdf25d18d1d94aa074c5277cafc39c51286021e21678ccc919bd6f326cfcb709", + "msg": "ed25519vectors 11", + "flags": [ + "low_order_R", + "low_order_component_A" + ] + }, + { + "number": 737, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "01000000000000000000000000000000000000000000000000000000000000004071f5e71c483f0886dd678575b498d03177d51ad542edf113b6214b93c79b0b", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 738, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a3506bc1f0d2c7b653d5aaf07a71c130d8aff5bef8653d6984f2b223081ac24a62005", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_component_A" + ] + }, + { + "number": 739, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a3506134ff3e6e5e3fce6d8f46295871735f1700c2674d3892a7f06f055533336a002", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 740, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 741, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 13", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 742, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "010000000000000000000000000000000000000000000000000000000000008040ab130e002935091a7eac0f1476b9b5e60411ead58a3c0e95765c234752f702", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 743, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "0100000000000000000000000000000000000000000000000000000000000080f28144c16a59ecedbbe82aa3481cf42279d82c6c669beb6c59622e94bcc03808", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 744, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "0100000000000000000000000000000000000000000000000000000000000080cdf25d18d1d94aa074c5277cafc39c51286021e21678ccc919bd6f326cfcb709", + "msg": "ed25519vectors 11", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 745, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "01000000000000000000000000000000000000000000000000000000000000804071f5e71c483f0886dd678575b498d03177d51ad542edf113b6214b93c79b0b", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 746, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 747, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 25", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 748, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f4524c928a583ec7cbddab4765dbdaf532c7fd278a5c4fd933e9c4c01c2f58302", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 749, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f335ec19261efdcd25bbe64dde6dcc7815b237bf53ffedc45d44351d9690e0e0b", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 750, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fcdf25d18d1d94aa074c5277cafc39c51286021e21678ccc919bd6f326cfcb709", + "msg": "ed25519vectors 11", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 751, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f4071f5e71c483f0886dd678575b498d03177d51ad542edf113b6214b93c79b0b", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 752, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 753, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 754, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff69a153a2d8922b9fcfbc06a425e6a50f28dfe3799f91cb5f983b16eadf930405", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 755, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb61ad06789857c7a7c9025df6b6321483f533c775ababa02b57ea2508507340c", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 756, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcdf25d18d1d94aa074c5277cafc39c51286021e21678ccc919bd6f326cfcb709", + "msg": "ed25519vectors 11", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 757, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4071f5e71c483f0886dd678575b498d03177d51ad542edf113b6214b93c79b0b", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 758, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 759, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 760, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d2105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 761, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d2105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 762, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05dd5e46f50f59e854fda2b2ae82c6f0c64da07146d4b7d388a9c0acec0387de06", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 763, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc057a8afc08cd56ba356afe59be2d03de9d4d0f44cd72c00f85ddb1e844275b9601", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 764, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d21e25115117021871ba256c845f462422e380099235eb48e8fc898ef6b49ee5a0c", + "msg": "ed25519vectors 21", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 765, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d214f406acc5ef89838ac824d84529e2557b738d678e3b9070634232af8e8a16d02", + "msg": "ed25519vectors 15", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 766, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 767, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 768, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f1105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 13", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 769, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f1105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 770, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85e12d9510d58203c89c7d06a611815a2b390a6f363885b90da266c85135bc5c05", + "msg": "ed25519vectors 17", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 771, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850bbeccb5223743bd17f3a73dac85074bce77ce102d031b2e2a897a8b66ec650b", + "msg": "ed25519vectors 39", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 772, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f11701a598b9a02ae60505dd0c2938a1a0c2d6ffd4676cfb49125b19e9cb358da06", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 773, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f111a1ba75dc71a1c3275e4dd3115b1638f67d16f1440d16dcc2c0188bbb17d6206", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 774, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 775, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 776, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 777, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 778, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a1d48955627f685fca00a5d62886fa4cae966af752251034904358448891d9000", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 779, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a19775c38558dafa093e9e2492933bcd4dd13656d43b75c7d13b450a986cc360d", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 780, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee91cfdeb875d86bf70f14733791c85add6b98dc813842c1338c48ea3e53e4aa0f", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 781, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee3939ad4e3797f4d869b51cb36f6b937987df797d46b851c6abc76ef707c60503", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 782, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 783, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 784, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 785, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 786, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03faf51deee4ffb00d126b97ff2d901b8e7de8bfb8a96e1dfabf4949a30b1c28ec01", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 787, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa4b4f0d7171f609289d3bbb7cbcefb424a13b468e848d58fddbb73429040c7906", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 788, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de1bdf452e3274bda9648c0e27ac7139f6c99c7ff2e96637afe541ce414e378b05", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 789, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de83458650b497c3f7226cb93e9324df567b1adda39378e844230453b95aa8c801", + "msg": "ed25519vectors", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 790, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 791, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 792, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 793, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 794, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f96d76d748c70430e986190b547bef03c36d3e53d4834f5c60b23d392695bbe06", + "msg": "ed25519vectors 22", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 795, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7ff899fcb87a689f9a17bb3d7a54ab6bbd060f3f3061502f1fa6a1fc2a8eee0603", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 796, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf9e9bcd5643add13de8c05d9e630359815212df872304bef491f58f867bd542709", + "msg": "ed25519vectors 15", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 797, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf904864848c5ff5361609e941b2136012ac88139a34707e12cadf6645dff0b1008", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 798, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 799, + "key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 800, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff77de70f3611eb29ee726ca74d20267c184a35f0fbc4261458eaad86f545d5b03", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 801, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5c1a58a253fdd422102558a41077d95055a4f988bb5f475006a41a79d3a2ba01", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 802, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff96d76d748c70430e986190b547bef03c36d3e53d4834f5c60b23d392695bbe06", + "msg": "ed25519vectors 22", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 803, + "key": "86e72f5c2a7215151059aa151c0ee6f8e2155d301402f35d7498f078629a8f79", + "sig": "ecfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff899fcb87a689f9a17bb3d7a54ab6bbd060f3f3061502f1fa6a1fc2a8eee0603", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 804, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 805, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 806, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "0000000000000000000000000000000000000000000000000000000000000000819aa7c9081f2e43b7524fdd27ef578f48dd9f02371b31f8013bd0c5321c660f", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 807, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b9dbda34f2ac60ee9d893b9b16f898617e81347886067f49d79d37740bb42a80b", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 808, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 809, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 810, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "36684ea91032ba5b1dbab2d02f4debc74c3327f2b3802e2e4d371aa42b12b56b05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 811, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 812, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f1630a351114792030905842b0d440c30c3c3c08f8e275cc32718756675d10f06", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 813, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f819aa7c9081f2e43b7524fdd27ef578f48dd9f02371b31f8013bd0c5321c660f", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 814, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 815, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 19", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 816, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 817, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "0000000000000000000000000000000000000000000000000000000000000080008207ec4d9a9b8aaeee217ecb5d87a958de17beb51faec53236e7f7e07e6c05", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 818, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9467f05af9575f4d1a13f23973e28c591c4944c7dec5e4178c71a88110cc175006", + "msg": "ed25519vectors 12", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 819, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 820, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 821, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "b797b156efcd45a4e2454d2fd0b21438b3ccd80d4c7fd1d1b2c8e55bd4ed4a9405ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 822, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 823, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff48ed3504f9d9a85115643ab1fefe191b70c39dc708708236227941792dc8c502", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 824, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008207ec4d9a9b8aaeee217ecb5d87a958de17beb51faec53236e7f7e07e6c05", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 825, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 826, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 14", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A" + ] + }, + { + "number": 827, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 828, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 14", + "flags": [ + "low_order_A", + "low_order_component_A" + ] + }, + { + "number": 829, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_A", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 830, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "0100000000000000000000000000000000000000000000000000000000000000ce34fe4edd707095877049d405f52b52a726b4cbef9b8a1f950340d521fe110d", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_component_A" + ] + }, + { + "number": 831, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "01000000000000000000000000000000000000000000000000000000000000007798d1693338d7c46e61a3aae05bd23a89fdf7b62b83efdd062dd19a39d8d505", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 832, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a3506ccbd210592f2a2b70a9c9ba91f97d642a2e51b9a67ec788188039228a24e0e09", + "msg": "ed25519vectors 23", + "flags": [ + "low_order_component_A" + ] + }, + { + "number": 833, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350660f862046e40dcc3af08e1b97b6cd10ee44158cbccab65668862e844ace00500", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 834, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 12", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_A" + ] + }, + { + "number": 835, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 836, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_A", + "low_order_component_A", + "non_canonical_A" + ] + }, + { + "number": 837, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 838, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 14", + "flags": [ + "low_order_A", + "low_order_component_A", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 839, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "b62cf890de42c413b11b1411c9f01f1c4d77aa87ef182258d1251f69af2a350605ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 840, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 24", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 841, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 16", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 842, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "010000000000000000000000000000000000000000000000000000000000008041bd7afd3d12b42f00f9ac87804fceeea002eb2800665b0fe8acd0cf53ee3207", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 843, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "0100000000000000000000000000000000000000000000000000000000000080739deeff2a8c311e6172a2e9d05f6d8a048df123aa27e1015bda974e6b32b306", + "msg": "ed25519vectors 21", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 844, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "0100000000000000000000000000000000000000000000000000000000000080ce34fe4edd707095877049d405f52b52a726b4cbef9b8a1f950340d521fe110d", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 845, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "01000000000000000000000000000000000000000000000000000000000000807798d1693338d7c46e61a3aae05bd23a89fdf7b62b83efdd062dd19a39d8d505", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 846, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 847, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 848, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f736cd390d6a2cb3d3a40bbbe09c87fa3caced72cdd853bfbf047adf1dec92207", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 849, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fbe39026eed2d5f2b23510d25bc1a7bface53b1d7b949facee0c7f6d1121bbe02", + "msg": "ed25519vectors 23", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 850, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fce34fe4edd707095877049d405f52b52a726b4cbef9b8a1f950340d521fe110d", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 851, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f7798d1693338d7c46e61a3aae05bd23a89fdf7b62b83efdd062dd19a39d8d505", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 852, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 853, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 854, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8a4fe32e72294d34ff5060efff2141687dd52117f36311af924b73638f7bc604", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 855, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2e6065b5a7c4aa919800747605e99800c074041d01eecca3ac39b78ef00da906", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 856, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffce34fe4edd707095877049d405f52b52a726b4cbef9b8a1f950340d521fe110d", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 857, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7798d1693338d7c46e61a3aae05bd23a89fdf7b62b83efdd062dd19a39d8d505", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_A", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 858, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 859, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "01000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 860, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 861, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 862, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 863, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 864, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 865, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d2105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 866, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05dcf76895217bf0dacec839953c960ee6d7840b9fa8ec66377df8a2e2db722305", + "msg": "ed25519vectors 10", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 867, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d211566a1ad3f92ada58707e452dcd290efc6a1951aaefe43be3b4663e38c3ac002", + "msg": "ed25519vectors 14", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 868, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc050000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 14", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 869, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d2105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 870, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "5ae36d433a7bd0efb150b6b04610d1986e3044c46b6ad69bae17aaf76b608d2105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 871, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 872, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f1105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 14", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 873, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc8507e25ac429f3fda828fd20bbe35e9d834875b64098f05e40d1bbe63f20b9c50d", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 874, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f113fc2c91b53d127bfc4fb6910467e737fc5a6463963ca4df83d0c82a419299e06", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 875, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc850000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 876, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f1105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 877, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "fa9dde274f4820efb19a890f8ba2d8791710a4303ceef4aedf9dddc4e81a1f1105ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 14", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 878, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 879, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 13", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 880, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a3540a57b2ba00d60510ca5174b63f5ad6289f50241887ec114583b643dfe6003", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 881, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0eef081c939b0fb2f42749cd392be91b90b20875f6a7abd4019a470299569f16f01", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 882, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 9", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 883, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 884, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "f36121d8b0b7df104e6576f0745d2786e8ef5bcfc3110b512062223b17e5e0ee05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 13", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 885, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 11", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 886, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 887, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa8c7fe7a9b40ddb9617a6ca678729b53ad7c9916531c829288e416e56fbb74809", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 888, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de73da5e09d360debd54177128d8b403f3d8cdd80ec83cd60b138b515d89d5cb0a", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 889, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 890, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 19", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 891, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "931c92bcc5842f104eaf494fb9ef2e6791cfbb3b9495296451e85508949f72de05ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 892, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 893, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 894, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 14", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 895, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 896, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f63ec463daf4a74749995e1bca07d169051630e9cf36860b86536f5c7f6e87405", + "msg": "ed25519vectors 36", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 897, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f8fcc9e160cf71f1343cf2a6cc8d51cae1a9dc2e3debc99d97ec1190782406e05", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 898, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf92adec0cbdc3718697e370f88b291cbe1965f51921474e0fe35973dbc471c3e01", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_component_R", + "low_order_component_A", + "low_order_residue" + ] + }, + { + "number": 899, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf9327f51a83cacea96d1f3fc6be0e2682f22ce35400ccb707aa30a7321ed6dff05", + "msg": "ed25519vectors 3", + "flags": [ + "low_order_component_R", + "low_order_component_A" + ] + }, + { + "number": 900, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 901, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_A" + ] + }, + { + "number": 902, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A" + ] + }, + { + "number": 903, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 4", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_A" + ] + }, + { + "number": 904, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 14", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 905, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "37d3076f21bd3bec4ee4ebee360fe0e3b288557810e7dda72edae09650d5caf905ba9a796274d80437afa36f1236563f2f3b0aa84cecddc3d20914615ba4fe02", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "reencoded_k" + ] + }, + { + "number": 906, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 907, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 5", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 908, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0d5f3d4d7d4fd1b055ea05193ec32458d796b69aca128d34d5e4dbaec8a86e0c", + "msg": "ed25519vectors 2", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R" + ] + }, + { + "number": 909, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff472e9141da7cc7352acd9c8688b89b9c2ab873aa6c4270e9c9830051f861860f", + "msg": "ed25519vectors 6", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R" + ] + }, + { + "number": 910, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff63ec463daf4a74749995e1bca07d169051630e9cf36860b86536f5c7f6e87405", + "msg": "ed25519vectors 36", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 911, + "key": "fe894df18abf1c20088bfbe6c9ad45d42ec20663eaf7111eaea1d851da0d7f89", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8fcc9e160cf71f1343cf2a6cc8d51cae1a9dc2e3debc99d97ec1190782406e05", + "msg": "ed25519vectors 7", + "flags": [ + "low_order_R", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_R", + "reencoded_k" + ] + }, + { + "number": 912, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 1", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "low_order_residue", + "non_canonical_A", + "non_canonical_R" + ] + }, + { + "number": 913, + "key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sig": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + "msg": "ed25519vectors 8", + "flags": [ + "low_order_R", + "low_order_A", + "low_order_component_R", + "low_order_component_A", + "non_canonical_A", + "non_canonical_R" + ] + } +] diff --git a/tests/validation_criteria.rs b/tests/validation_criteria.rs new file mode 100644 index 0000000..69cdad1 --- /dev/null +++ b/tests/validation_criteria.rs @@ -0,0 +1,232 @@ +use ed25519::signature::Verifier; +use ed25519_dalek::{Signature, VerifyingKey}; + +use serde::{de::Error as SError, Deserialize, Deserializer}; +use std::{collections::BTreeSet as Set, fs::File}; + +/// The set of edge cases that [`VerifyingKey::verify()`] permits. +const VERIFY_ALLOWED_EDGECASES: &[Flag] = &[ + Flag::LowOrderA, + Flag::LowOrderR, + Flag::NonCanonicalA, + Flag::LowOrderComponentA, + Flag::LowOrderComponentR, + // `ReencodedK` is not actually permitted by `verify()`, but it looks that way in the tests + // because it sometimes occurs with a low-order A. 1/8 of the time, the resulting signature + // will be identical the one made with a normal k. find_validation_criteria shows that indeed + // this occurs 10/58 of the time + Flag::ReencodedK, +]; + +/// The set of edge cases that [`VerifyingKey::verify_strict()`] permits +const VERIFY_STRICT_ALLOWED_EDGECASES: &[Flag] = + &[Flag::LowOrderComponentA, Flag::LowOrderComponentR]; + +/// Each variant describes a specfiic edge case that can occur in an Ed25519 signature. Refer to +/// the test vector [README][] for more info. +/// +/// [README]: https://github.com/C2SP/CCTV/blob/5ea85644bd035c555900a2f707f7e4c31ea65ced/ed25519vectors/README.md +#[derive(Deserialize, Debug, Copy, Clone, PartialOrd, Ord, Eq, PartialEq)] +enum Flag { + #[serde(rename = "low_order")] + LowOrder, + #[serde(rename = "low_order_A")] + LowOrderA, + #[serde(rename = "low_order_R")] + LowOrderR, + #[serde(rename = "non_canonical_A")] + NonCanonicalA, + #[serde(rename = "non_canonical_R")] + NonCanonicalR, + #[serde(rename = "low_order_component_A")] + LowOrderComponentA, + #[serde(rename = "low_order_component_R")] + LowOrderComponentR, + #[serde(rename = "low_order_residue")] + LowOrderResidue, + #[serde(rename = "reencoded_k")] + ReencodedK, +} + +/// This is an intermediate representation between JSON and TestVector +#[derive(Deserialize)] +struct IntermediateTestVector { + number: usize, + #[serde(deserialize_with = "bytes_from_hex", rename = "key")] + pubkey: Vec, + #[serde(deserialize_with = "bytes_from_hex")] + sig: Vec, + msg: String, + flags: Option>, +} + +/// The test vector struct from [CCTV][]. `sig` may or may not be a valid signature of `msg` with +/// respect to `pubkey`, depending on the verification function's validation criteria. `flags` +/// describes all the edge cases which this test vector falls into. +/// +/// [CCTV]: https://github.com/C2SP/CCTV/tree/5ea85644bd035c555900a2f707f7e4c31ea65ced/ed25519vectors +struct TestVector { + number: usize, + pubkey: VerifyingKey, + sig: Signature, + msg: Vec, + flags: Set, +} + +impl From for TestVector { + fn from(tv: IntermediateTestVector) -> Self { + let number = tv.number; + let pubkey = { + let mut buf = [0u8; 32]; + buf.copy_from_slice(&tv.pubkey); + VerifyingKey::from_bytes(&buf).unwrap() + }; + let sig = { + let mut buf = [0u8; 64]; + buf.copy_from_slice(&tv.sig); + Signature::from_bytes(&buf).unwrap() + }; + let msg = tv.msg.as_bytes().to_vec(); + + // Unwrap the Option> + let flags = tv.flags.unwrap_or_else(Default::default); + + Self { + number, + pubkey, + sig, + msg, + flags, + } + } +} + +// Tells serde how to deserialize bytes from hex +fn bytes_from_hex<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let mut hex_str = String::deserialize(deserializer)?; + // Prepend a 0 if it's not even length + if hex_str.len() % 2 == 1 { + hex_str.insert(0, '0'); + } + hex::decode(hex_str).map_err(|e| SError::custom(format!("{:?}", e))) +} + +fn get_test_vectors() -> impl Iterator { + let f = File::open("VALIDATIONVECTORS").expect( + "This test is only available when the code has been cloned from the git repository, since + the VALIDATIONVECTORS file is large and is therefore not included within the distributed \ + crate.", + ); + + serde_json::from_reader::<_, Vec>(f) + .unwrap() + .into_iter() + .map(TestVector::from) +} + +/// Tests that the verify() and verify_strict() functions succeed only on test cases whose flags +/// (i.e., edge cases it falls into) are a subset of VERIFY_ALLOWED_EDGECASES and +/// VERIFY_STRICT_ALLOWED_EDGECASES, respectively +#[test] +fn check_validation_criteria() { + let verify_allowed_edgecases = Set::from_iter(VERIFY_ALLOWED_EDGECASES.to_vec().into_iter()); + let verify_strict_allowed_edgecases = + Set::from_iter(VERIFY_STRICT_ALLOWED_EDGECASES.to_vec().into_iter()); + + for TestVector { + number, + pubkey, + msg, + sig, + flags, + } in get_test_vectors() + { + // If all the verify-permitted flags here are ones we permit, then verify() should succeed. + // Otherwise, it should not. + let success = pubkey.verify(&msg, &sig).is_ok(); + if flags.is_subset(&verify_allowed_edgecases) { + assert!(success, "verify() expected success in testcase #{number}",); + } else { + assert!(!success, "verify() expected failure in testcase #{number}",); + } + + // If all the verify_strict-permitted flags here are ones we permit, then verify_strict() + // should succeed. Otherwise, it should not. + let success = pubkey.verify_strict(&msg, &sig).is_ok(); + if flags.is_subset(&verify_strict_allowed_edgecases) { + assert!( + success, + "verify_strict() expected success in testcase #{number}", + ); + } else { + assert!( + !success, + "verify_strict() expected failure in testcase #{number}", + ); + } + } +} + +/// Prints the flags that are consistently permitted by verify() and verify_strict() +#[test] +fn find_validation_criteria() { + let mut verify_allowed_edgecases = Set::new(); + let mut verify_strict_allowed_edgecases = Set::new(); + + // Counts the number of times a signature with a re-encoded k and a low-order A verified. This + // happens with 1/8 probability, assuming the usual verification equation(s). + let mut num_lucky_reencoded_k = 0; + let mut num_reencoded_k = 0; + + for TestVector { + number: _, + pubkey, + msg, + sig, + flags, + } in get_test_vectors() + { + // If verify() was a success, add all the associated flags to verify-permitted set + let success = pubkey.verify(&msg, &sig).is_ok(); + + // If this is ReencodedK && LowOrderA, log some statistics + if flags.contains(&Flag::ReencodedK) && flags.contains(&Flag::LowOrderA) { + num_reencoded_k += 1; + num_lucky_reencoded_k += success as u8; + } + + if success { + for flag in &flags { + // Don't count re-encoded k when A is low-order. This is because the + // re-encoded k might be a multiple of 8 by accident + if *flag == Flag::ReencodedK && flags.contains(&Flag::LowOrderA) { + continue; + } else { + verify_allowed_edgecases.insert(*flag); + } + } + } + + // If verify_strict() was a success, add all the associated flags to + // verify_strict-permitted set + let success = pubkey.verify_strict(&msg, &sig).is_ok(); + if success { + for flag in &flags { + verify_strict_allowed_edgecases.insert(*flag); + } + } + } + + println!("VERIFY_ALLOWED_EDGECASES: {:?}", verify_allowed_edgecases); + println!( + "VERIFY_STRICT_ALLOWED_EDGECASES: {:?}", + verify_strict_allowed_edgecases + ); + println!( + "re-encoded k && low-order A yielded a valid signature {}/{} of the time", + num_lucky_reencoded_k, num_reencoded_k + ); +} From 461a2d7e05ce038f1937453157a570a26dcd45fe Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Fri, 6 Jan 2023 22:50:39 -0700 Subject: [PATCH 314/351] Bump `ed25519` crate to v2.0.0-rc.0 (#257) --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index bf60c87..5bb136e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ features = ["nightly", "batch", "pkcs8"] [dependencies] curve25519-dalek = { version = "=4.0.0-pre.3", default-features = false, features = ["digest", "rand_core"] } -ed25519 = { version = "=2.0.0-pre.1", default-features = false } +ed25519 = { version = "=2.0.0-rc.0", default-features = false } merlin = { version = "3", default-features = false, optional = true } rand = { version = "0.8", default-features = false, optional = true } rand_core = { version = "0.6.4", default-features = false, optional = true } From 4f218d8e6794c429a1acd4faadf12b9168370afe Mon Sep 17 00:00:00 2001 From: andrew lyon Date: Sat, 7 Jan 2023 08:21:54 -0800 Subject: [PATCH 315/351] Adding verify_prehashed_strict() (#212) Combines `verify_prehashed` and `verify_strict` to allow strict verification with prehashed values. --- src/verifying.rs | 127 +++++++++++++++++++------- tests/ed25519.rs | 225 ++++++++++++++++++++++++++++++++++++----------- 2 files changed, 268 insertions(+), 84 deletions(-) diff --git a/src/verifying.rs b/src/verifying.rs index bb58aa9..b955436 100644 --- a/src/verifying.rs +++ b/src/verifying.rs @@ -141,6 +141,28 @@ impl VerifyingKey { VerifyingKey(compressed, point) } + // A helper function that computes H(R || A || M) as well as its prehashed version + #[allow(non_snake_case)] + fn compute_challenge( + context: Option<&[u8]>, + R: &CompressedEdwardsY, + A: &CompressedEdwardsY, + M: &[u8], + ) -> Scalar { + let mut h = Sha512::new(); + if let Some(c) = context { + h.update(b"SigEd25519 no Ed25519 collisions"); + h.update([1]); // Ed25519ph + h.update([c.len() as u8]); + h.update(c); + } + h.update(R.as_bytes()); + h.update(A.as_bytes()); + h.update(M); + + Scalar::from_hash(h) + } + /// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm. /// /// # Inputs @@ -171,8 +193,6 @@ impl VerifyingKey { { let signature = InternalSignature::try_from(signature)?; - let mut h: Sha512 = Sha512::default(); - let ctx: &[u8] = context.unwrap_or(b""); debug_assert!( ctx.len() <= 255, @@ -180,16 +200,12 @@ impl VerifyingKey { ); let minus_A: EdwardsPoint = -self.1; - - h.update(b"SigEd25519 no Ed25519 collisions"); - h.update([1]); // Ed25519ph - h.update([ctx.len() as u8]); - h.update(ctx); - h.update(signature.R.as_bytes()); - h.update(self.as_bytes()); - h.update(prehashed_message.finalize().as_slice()); - - let k = Scalar::from_hash(h); + let k = Self::compute_challenge( + Some(ctx), + &signature.R, + &self.0, + prehashed_message.finalize().as_slice(), + ); let R: EdwardsPoint = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); @@ -270,24 +286,18 @@ impl VerifyingKey { ) -> Result<(), SignatureError> { let signature = InternalSignature::try_from(signature)?; - let mut h: Sha512 = Sha512::new(); - let minus_A: EdwardsPoint = -self.1; - - let signature_R: EdwardsPoint = match signature.R.decompress() { - None => return Err(InternalError::Verify.into()), - Some(x) => x, - }; + let signature_R = signature + .R + .decompress() + .ok_or_else(|| SignatureError::from(InternalError::Verify))?; // Logical OR is fine here as we're not trying to be constant time. if signature_R.is_small_order() || self.1.is_small_order() { return Err(InternalError::Verify.into()); } - h.update(signature.R.as_bytes()); - h.update(self.as_bytes()); - h.update(message); - - let k = Scalar::from_hash(h); + let minus_A: EdwardsPoint = -self.1; + let k = Self::compute_challenge(None, &signature.R, &self.0, message); let R: EdwardsPoint = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); @@ -297,6 +307,67 @@ impl VerifyingKey { Err(InternalError::Verify.into()) } } + + /// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm, + /// using strict signture checking as defined by [`Self::verify_strict`]. + /// + /// # Inputs + /// + /// * `prehashed_message` is an instantiated hash digest with 512-bits of + /// output which has had the message to be signed previously fed into its + /// state. + /// * `context` is an optional context string, up to 255 bytes inclusive, + /// which may be used to provide additional domain separation. If not + /// set, this will default to an empty string. + /// * `signature` is a purported Ed25519ph [`Signature`] on the `prehashed_message`. + /// + /// # Returns + /// + /// Returns `true` if the `signature` was a valid signature created by this + /// `Keypair` on the `prehashed_message`. + #[allow(non_snake_case)] + pub fn verify_prehashed_strict( + &self, + prehashed_message: D, + context: Option<&[u8]>, + signature: &ed25519::Signature, + ) -> Result<(), SignatureError> + where + D: Digest, + { + let signature = InternalSignature::try_from(signature)?; + + let ctx: &[u8] = context.unwrap_or(b""); + debug_assert!( + ctx.len() <= 255, + "The context must not be longer than 255 octets." + ); + + let signature_R = signature + .R + .decompress() + .ok_or_else(|| SignatureError::from(InternalError::Verify))?; + + // Logical OR is fine here as we're not trying to be constant time. + if signature_R.is_small_order() || self.1.is_small_order() { + return Err(InternalError::Verify.into()); + } + + let minus_A: EdwardsPoint = -self.1; + let k = Self::compute_challenge( + Some(ctx), + &signature.R, + &self.0, + prehashed_message.finalize().as_slice(), + ); + let R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); + + if R == signature_R { + Ok(()) + } else { + Err(InternalError::Verify.into()) + } + } } impl Verifier for VerifyingKey { @@ -309,14 +380,8 @@ impl Verifier for VerifyingKey { fn verify(&self, message: &[u8], signature: &ed25519::Signature) -> Result<(), SignatureError> { let signature = InternalSignature::try_from(signature)?; - let mut h: Sha512 = Sha512::new(); let minus_A: EdwardsPoint = -self.1; - - h.update(signature.R.as_bytes()); - h.update(self.as_bytes()); - h.update(message); - - let k = Scalar::from_hash(h); + let k = Self::compute_challenge(None, &signature.R, &self.0, message); let R: EdwardsPoint = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); diff --git a/tests/ed25519.rs b/tests/ed25519.rs index 4081485..1a65d90 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -21,16 +21,23 @@ use sha2::Sha512; #[cfg(test)] mod vectors { - use curve25519_dalek::{edwards::EdwardsPoint, scalar::Scalar}; - use sha2::{digest::Digest, Sha512}; - use std::convert::TryFrom; - - use std::fs::File; - use std::io::BufRead; - use std::io::BufReader; - use super::*; + use curve25519_dalek::{ + constants::ED25519_BASEPOINT_POINT, + edwards::{CompressedEdwardsY, EdwardsPoint}, + scalar::Scalar, + traits::IsIdentity, + }; + use sha2::{digest::Digest, Sha512}; + + use std::{ + convert::TryFrom, + fs::File, + io::{BufRead, BufReader}, + ops::Neg, + }; + // TESTVECTORS is taken from sign.input.gz in agl's ed25519 Golang // package. It is a selection of test cases from // http://ed25519.cr.yp.to/python/sign.input @@ -81,6 +88,13 @@ mod vectors { "Signature verification failed on line {}", lineno ); + assert!( + expected_verifying_key + .verify_strict(&msg_bytes, &sig2) + .is_ok(), + "Signature strict verification failed on line {}", + lineno + ); } } @@ -116,81 +130,154 @@ mod vectors { ); assert!( signing_key - .verify_prehashed(prehash_for_verifying, None, &sig2) + .verify_prehashed(prehash_for_verifying.clone(), None, &sig2) .is_ok(), "Could not verify ed25519ph signature!" ); + assert!( + expected_verifying_key + .verify_prehashed_strict(prehash_for_verifying, None, &sig2) + .is_ok(), + "Could not strict-verify ed25519ph signature!" + ); } + // + // The remaining items in this mod are for the repudiation tests + // + // Taken from curve25519_dalek::constants::EIGHT_TORSION[4] const EIGHT_TORSION_4: [u8; 32] = [ 236, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127, ]; - fn compute_hram(message: &[u8], pub_key: &EdwardsPoint, signature_r: &EdwardsPoint) -> Scalar { - let k_bytes = Sha512::default() - .chain_update(&signature_r.compress().as_bytes()) - .chain_update(&pub_key.compress().as_bytes()[..]) - .chain_update(&message); - let mut k_output = [0u8; 64]; - k_output.copy_from_slice(k_bytes.finalize().as_slice()); - Scalar::from_bytes_mod_order_wide(&k_output) + // Computes the prehashed or non-prehashed challenge, depending on whether context is given + fn compute_challenge( + message: &[u8], + pub_key: &EdwardsPoint, + signature_r: &EdwardsPoint, + context: Option<&[u8]>, + ) -> Scalar { + let mut h = Sha512::default(); + if let Some(c) = context { + h.update(b"SigEd25519 no Ed25519 collisions"); + h.update(&[1]); + h.update(&[c.len() as u8]); + h.update(c); + } + h.update(&signature_r.compress().as_bytes()); + h.update(&pub_key.compress().as_bytes()[..]); + h.update(&message); + Scalar::from_hash(h) } fn serialize_signature(r: &EdwardsPoint, s: &Scalar) -> Vec { [&r.compress().as_bytes()[..], &s.as_bytes()[..]].concat() } + const WEAK_PUBKEY: CompressedEdwardsY = CompressedEdwardsY(EIGHT_TORSION_4); + + // Pick a random Scalar + fn non_null_scalar() -> Scalar { + let mut rng = rand::rngs::OsRng; + let mut s_candidate = Scalar::random(&mut rng); + while s_candidate == Scalar::ZERO { + s_candidate = Scalar::random(&mut rng); + } + s_candidate + } + + fn pick_r(s: Scalar) -> EdwardsPoint { + let r0 = s * ED25519_BASEPOINT_POINT; + // Pick a torsion point of order 2 + r0 + WEAK_PUBKEY.decompress().unwrap().neg() + } + + // Tests that verify_strict() rejects small-order pubkeys. We test this by explicitly + // constructing a pubkey-signature pair that verifies with respect to two distinct messages. + // This should be accepted by verify(), but rejected by verify_strict(). #[test] fn repudiation() { - use curve25519_dalek::traits::IsIdentity; - use std::ops::Neg; - let message1 = b"Send 100 USD to Alice"; let message2 = b"Send 100000 USD to Alice"; - // Pick a random Scalar - fn non_null_scalar() -> Scalar { - let mut rng = rand::rngs::OsRng; - let mut s_candidate = Scalar::random(&mut rng); - while s_candidate == Scalar::ZERO { - s_candidate = Scalar::random(&mut rng); - } - s_candidate - } let mut s: Scalar = non_null_scalar(); + let pubkey = WEAK_PUBKEY.decompress().unwrap(); + let mut r = pick_r(s); - fn pick_r_and_pubkey(s: Scalar) -> (EdwardsPoint, EdwardsPoint) { - let r0 = s * curve25519_dalek::constants::ED25519_BASEPOINT_POINT; - // Pick a torsion point of order 2 - let pub_key = curve25519_dalek::edwards::CompressedEdwardsY(EIGHT_TORSION_4) - .decompress() - .unwrap(); - let r = r0 + pub_key.neg(); - (r, pub_key) + // Find an R such that + // H(R || A || M₁) · A == A == H(R || A || M₂) · A + // This happens with high probability when A is low order. + while !(pubkey.neg() + compute_challenge(message1, &pubkey, &r, None) * pubkey) + .is_identity() + || !(pubkey.neg() + compute_challenge(message2, &pubkey, &r, None) * pubkey) + .is_identity() + { + // We pick an s and let R = sB - A where B is the basepoint + s = non_null_scalar(); + r = pick_r(s); } - let (mut r, mut pub_key) = pick_r_and_pubkey(s); + // At this point, both verification equations hold: + // sB = R + H(R || A || M₁) · A + // = R + H(R || A || M₂) · A + // Check that this is true + let signature = serialize_signature(&r, &s); + let vk = VerifyingKey::from_bytes(&pubkey.compress().as_bytes()).unwrap(); + let sig = Signature::try_from(&signature[..]).unwrap(); + assert!(vk.verify(message1, &sig).is_ok()); + assert!(vk.verify(message2, &sig).is_ok()); - while !(pub_key.neg() + compute_hram(message1, &pub_key, &r) * pub_key).is_identity() - || !(pub_key.neg() + compute_hram(message2, &pub_key, &r) * pub_key).is_identity() + // Now check that the sigs fail under verify_strict. This is because verify_strict rejects + // small order pubkeys. + assert!(vk.verify_strict(message1, &sig).is_err()); + assert!(vk.verify_strict(message2, &sig).is_err()); + } + + // Identical to repudiation() above, but testing verify_prehashed against + // verify_prehashed_strict. See comments above for a description of what's happening. + #[test] + fn repudiation_prehash() { + let message1 = Sha512::new().chain_update(b"Send 100 USD to Alice"); + let message2 = Sha512::new().chain_update(b"Send 100000 USD to Alice"); + let message1_bytes = message1.clone().finalize(); + let message2_bytes = message2.clone().finalize(); + + let mut s: Scalar = non_null_scalar(); + let pubkey = WEAK_PUBKEY.decompress().unwrap(); + let mut r = pick_r(s); + let context_str = Some(&b"edtest"[..]); + + while !(pubkey.neg() + + compute_challenge(&message1_bytes, &pubkey, &r, context_str) * pubkey) + .is_identity() + || !(pubkey.neg() + + compute_challenge(&message2_bytes, &pubkey, &r, context_str) * pubkey) + .is_identity() { s = non_null_scalar(); - let key = pick_r_and_pubkey(s); - r = key.0; - pub_key = key.1; + r = pick_r(s); } + // Check that verify_prehashed succeeds on both sigs let signature = serialize_signature(&r, &s); - let pk = VerifyingKey::from_bytes(&pub_key.compress().as_bytes()).unwrap(); + let vk = VerifyingKey::from_bytes(&pubkey.compress().as_bytes()).unwrap(); let sig = Signature::try_from(&signature[..]).unwrap(); - // The same signature verifies for both messages - assert!(pk.verify(message1, &sig).is_ok() && pk.verify(message2, &sig).is_ok()); - // But not with a strict signature: verify_strict refuses small order keys - assert!( - pk.verify_strict(message1, &sig).is_err() || pk.verify_strict(message2, &sig).is_err() - ); + assert!(vk + .verify_prehashed(message1.clone(), context_str, &sig) + .is_ok()); + assert!(vk + .verify_prehashed(message2.clone(), context_str, &sig) + .is_ok()); + + // Check that verify_prehashed_strict fails on both sigs + assert!(vk + .verify_prehashed_strict(message1.clone(), context_str, &sig) + .is_err()); + assert!(vk + .verify_prehashed_strict(message2.clone(), context_str, &sig) + .is_err()); } } @@ -212,6 +299,7 @@ mod integrations { let mut csprng = OsRng; signing_key = SigningKey::generate(&mut csprng); + let verifying_key = signing_key.verifying_key(); good_sig = signing_key.sign(&good); bad_sig = signing_key.sign(&bad); @@ -219,14 +307,26 @@ mod integrations { signing_key.verify(&good, &good_sig).is_ok(), "Verification of a valid signature failed!" ); + assert!( + verifying_key.verify_strict(&good, &good_sig).is_ok(), + "Strict verification of a valid signature failed!" + ); assert!( signing_key.verify(&good, &bad_sig).is_err(), "Verification of a signature on a different message passed!" ); + assert!( + verifying_key.verify_strict(&good, &bad_sig).is_err(), + "Strict verification of a signature on a different message passed!" + ); assert!( signing_key.verify(&bad, &good_sig).is_err(), "Verification of a signature on a different message passed!" ); + assert!( + verifying_key.verify_strict(&bad, &good_sig).is_err(), + "Strict verification of a signature on a different message passed!" + ); } #[test] @@ -256,6 +356,7 @@ mod integrations { let context: &[u8] = b"testing testing 1 2 3"; signing_key = SigningKey::generate(&mut csprng); + let verifying_key = signing_key.verifying_key(); good_sig = signing_key .sign_prehashed(prehashed_good1, Some(context)) .unwrap(); @@ -265,22 +366,40 @@ mod integrations { assert!( signing_key - .verify_prehashed(prehashed_good2, Some(context), &good_sig) + .verify_prehashed(prehashed_good2.clone(), Some(context), &good_sig) .is_ok(), "Verification of a valid signature failed!" ); + assert!( + verifying_key + .verify_prehashed_strict(prehashed_good2, Some(context), &good_sig) + .is_ok(), + "Strict verification of a valid signature failed!" + ); assert!( signing_key - .verify_prehashed(prehashed_good3, Some(context), &bad_sig) + .verify_prehashed(prehashed_good3.clone(), Some(context), &bad_sig) .is_err(), "Verification of a signature on a different message passed!" ); + assert!( + verifying_key + .verify_prehashed_strict(prehashed_good3, Some(context), &bad_sig) + .is_err(), + "Strict verification of a signature on a different message passed!" + ); assert!( signing_key - .verify_prehashed(prehashed_bad2, Some(context), &good_sig) + .verify_prehashed(prehashed_bad2.clone(), Some(context), &good_sig) .is_err(), "Verification of a signature on a different message passed!" ); + assert!( + verifying_key + .verify_prehashed_strict(prehashed_bad2, Some(context), &good_sig) + .is_err(), + "Strict verification of a signature on a different message passed!" + ); } #[cfg(feature = "batch")] From 6ee4d1de5cf1f916ceb786c7c7428fd14b0d42b9 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Mon, 9 Jan 2023 02:44:10 -0700 Subject: [PATCH 316/351] Use `curve25519-dalek` from git; check in Cargo.lock (#260) Updates to the latest upstream changes in `curve25519-dalek`, including using the new `EdwardsPoint::mul_base` API. To keep the build deterministic, this also checks in Cargo.lock, which pins `curve25519-dalek` to a particular git commit SHA which can be updated using `cargo update -p curve25519-dalek`. We can potentially remove `Cargo.lock` again after a crate release. --- .gitignore | 1 - Cargo.lock | 972 +++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 5 +- src/signing.rs | 6 +- src/verifying.rs | 4 +- 5 files changed, 981 insertions(+), 7 deletions(-) create mode 100644 Cargo.lock diff --git a/.gitignore b/.gitignore index 8188387..778540f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ target -Cargo.lock .cargo diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..bf843ff --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,972 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64ct" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "ciborium" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369" + +[[package]] +name = "ciborium-ll" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "3.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" +dependencies = [ + "bitflags", + "clap_lex", + "indexmap", + "textwrap", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "const-oid" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b" + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "criterion" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" +dependencies = [ + "anes", + "atty", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "4.0.0-pre.5" +source = "git+https://github.com/dalek-cryptography/curve25519-dalek.git#83f6b149d33c37b8997316cb7a87d8d247b75c3e" +dependencies = [ + "cfg-if", + "digest", + "fiat-crypto", + "packed_simd_2", + "platforms", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "ed25519" +version = "2.0.0-rc.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a753d68e68a75b72508fa3d37255ae8a6f7492715e61f3a14f3769859b2fb3" +dependencies = [ + "pkcs8", + "serde", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +dependencies = [ + "bincode", + "criterion", + "curve25519-dalek", + "ed25519", + "hex", + "hex-literal", + "merlin", + "rand", + "rand_core", + "serde", + "serde_bytes", + "serde_json", + "sha2", + "toml", + "zeroize", +] + +[[package]] +name = "either" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" + +[[package]] +name = "fiat-crypto" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a214f5bb88731d436478f3ae1f8a277b62124089ba9fb67f4f93fb100ef73c90" + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" + +[[package]] +name = "js-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "keccak" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "libm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + +[[package]] +name = "merlin" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core", + "zeroize", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "os_str_bytes" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" + +[[package]] +name = "packed_simd_2" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282" +dependencies = [ + "cfg-if", + "libm", +] + +[[package]] +name = "pem-rfc7468" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac" +dependencies = [ + "base64ct", +] + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "platforms" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" + +[[package]] +name = "plotters" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" + +[[package]] +name = "plotters-svg" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "regex" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "ryu" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_bytes" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "718dc5fff5b36f99093fc49b280cfc96ce6fc824317783bff5a1fed0c7a64819" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", + "sha2-asm", +] + +[[package]] +name = "sha2-asm" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf27176fb5d15398e3a479c652c20459d9dac830dedd1fa55b42a77dbcdbfcea" +dependencies = [ + "cc", +] + +[[package]] +name = "signature" +version = "2.0.0-rc.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51659052c3c82a3cb69d911c1c1d8cb5d383012b7ec537918d5ecc5f42870d2d" + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "textwrap" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "toml" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f" +dependencies = [ + "serde", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" + +[[package]] +name = "web-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "zeroize" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] diff --git a/Cargo.toml b/Cargo.toml index 5bb136e..c0da73c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ rustdoc-args = ["--cfg", "docsrs"] features = ["nightly", "batch", "pkcs8"] [dependencies] -curve25519-dalek = { version = "=4.0.0-pre.3", default-features = false, features = ["digest", "rand_core"] } +curve25519-dalek = { version = "=4.0.0-pre.5", default-features = false, features = ["digest", "rand_core", "zeroize"] } ed25519 = { version = "=2.0.0-rc.0", default-features = false } merlin = { version = "3", default-features = false, optional = true } rand = { version = "0.8", default-features = false, optional = true } @@ -64,3 +64,6 @@ pkcs8 = ["ed25519/pkcs8"] pem = ["alloc", "ed25519/pem", "pkcs8"] rand = ["dep:rand", "dep:rand_core"] serde = ["dep:serde", "serde_bytes", "ed25519/serde"] + +[patch.crates-io.curve25519-dalek] +git = "https://github.com/dalek-cryptography/curve25519-dalek.git" diff --git a/src/signing.rs b/src/signing.rs index d7e784f..a88ad5f 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -24,10 +24,10 @@ use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes}; use sha2::Sha512; -use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; use curve25519_dalek::digest::generic_array::typenum::U64; use curve25519_dalek::digest::Digest; use curve25519_dalek::edwards::CompressedEdwardsY; +use curve25519_dalek::edwards::EdwardsPoint; use curve25519_dalek::scalar::Scalar; use ed25519::signature::{KeypairRef, Signer, Verifier}; @@ -699,7 +699,7 @@ impl ExpandedSecretKey { h.update(message); let r = Scalar::from_hash(h); - let R: CompressedEdwardsY = (&r * &ED25519_BASEPOINT_TABLE).compress(); + let R: CompressedEdwardsY = EdwardsPoint::mul_base(&r).compress(); h = Sha512::new(); h.update(R.as_bytes()); @@ -777,7 +777,7 @@ impl ExpandedSecretKey { .chain_update(&prehash[..]); let r = Scalar::from_hash(h); - let R: CompressedEdwardsY = (&r * &ED25519_BASEPOINT_TABLE).compress(); + let R: CompressedEdwardsY = EdwardsPoint::mul_base(&r).compress(); h = Sha512::new() .chain_update(b"SigEd25519 no Ed25519 collisions") diff --git a/src/verifying.rs b/src/verifying.rs index b955436..2a07b28 100644 --- a/src/verifying.rs +++ b/src/verifying.rs @@ -12,7 +12,6 @@ use core::convert::TryFrom; use core::fmt::Debug; -use curve25519_dalek::constants; use curve25519_dalek::digest::generic_array::typenum::U64; use curve25519_dalek::digest::Digest; use curve25519_dalek::edwards::CompressedEdwardsY; @@ -135,7 +134,8 @@ impl VerifyingKey { bits[31] &= 127; bits[31] |= 64; - let point = &Scalar::from_bits(*bits) * &constants::ED25519_BASEPOINT_TABLE; + let scalar = Scalar::from_bits(*bits); + let point = EdwardsPoint::mul_base(&scalar); let compressed = point.compress(); VerifyingKey(compressed, point) From 4f6b4b247f5fc3603995795d9f77dc7bdafd8971 Mon Sep 17 00:00:00 2001 From: "pinkforest(she/her)" <36498018+pinkforest@users.noreply.github.com> Date: Tue, 10 Jan 2023 01:57:59 +1100 Subject: [PATCH 317/351] Make `zeroize` optional (#263) Defaults to on --- Cargo.toml | 9 +++++---- src/signing.rs | 4 ++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c0da73c..06ee258 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ rustdoc-args = ["--cfg", "docsrs"] features = ["nightly", "batch", "pkcs8"] [dependencies] -curve25519-dalek = { version = "=4.0.0-pre.5", default-features = false, features = ["digest", "rand_core", "zeroize"] } +curve25519-dalek = { version = "=4.0.0-pre.5", default-features = false, features = ["digest", "rand_core"] } ed25519 = { version = "=2.0.0-rc.0", default-features = false } merlin = { version = "3", default-features = false, optional = true } rand = { version = "0.8", default-features = false, optional = true } @@ -32,7 +32,7 @@ rand_core = { version = "0.6.4", default-features = false, optional = true } serde = { version = "1.0", default-features = false, optional = true } serde_bytes = { version = "0.11", optional = true } sha2 = { version = "0.10", default-features = false } -zeroize = { version = "1.5", default-features = false } +zeroize = { version = "1.5", default-features = false, optional = true } [dev-dependencies] hex = "0.4" @@ -50,8 +50,8 @@ name = "ed25519_benchmarks" harness = false [features] -default = ["std", "rand"] -alloc = ["curve25519-dalek/alloc", "ed25519/alloc", "rand?/alloc", "serde?/alloc", "zeroize/alloc"] +default = ["std", "rand", "zeroize"] +alloc = ["curve25519-dalek/alloc", "ed25519/alloc", "rand?/alloc", "serde?/alloc", "zeroize?/alloc"] std = ["alloc", "ed25519/std", "rand?/std", "serde?/std", "sha2/std"] asm = ["sha2/asm"] @@ -64,6 +64,7 @@ pkcs8 = ["ed25519/pkcs8"] pem = ["alloc", "ed25519/pem", "pkcs8"] rand = ["dep:rand", "dep:rand_core"] serde = ["dep:serde", "serde_bytes", "ed25519/serde"] +zeroize = ["dep:zeroize", "curve25519-dalek/zeroize"] [patch.crates-io.curve25519-dalek] git = "https://github.com/dalek-cryptography/curve25519-dalek.git" diff --git a/src/signing.rs b/src/signing.rs index a88ad5f..95c0041 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -32,6 +32,7 @@ use curve25519_dalek::scalar::Scalar; use ed25519::signature::{KeypairRef, Signer, Verifier}; +#[cfg(feature = "zeroize")] use zeroize::{Zeroize, ZeroizeOnDrop}; use crate::constants::*; @@ -505,12 +506,14 @@ impl TryFrom<&[u8]> for SigningKey { } } +#[cfg(feature = "zeroize")] impl Drop for SigningKey { fn drop(&mut self) { self.secret_key.zeroize(); } } +#[cfg(feature = "zeroize")] impl ZeroizeOnDrop for SigningKey {} #[cfg(feature = "pkcs8")] @@ -643,6 +646,7 @@ pub(crate) struct ExpandedSecretKey { pub(crate) nonce: [u8; 32], } +#[cfg(feature = "zeroize")] impl Drop for ExpandedSecretKey { fn drop(&mut self) { self.key.zeroize(); From b5dc40bedfdb2d7a44c69df81df7e4fc4d05dee7 Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Sat, 14 Jan 2023 21:26:39 -0500 Subject: [PATCH 318/351] Make `verify_batch` deterministic (#256) Also removed `batch_deterministic` feature --- .github/workflows/rust.yml | 2 - Cargo.toml | 15 +-- README.md | 50 +-------- benches/ed25519_benchmarks.rs | 4 +- src/batch.rs | 199 ++++++++++++---------------------- src/errors.rs | 4 +- src/lib.rs | 38 +++---- src/signature.rs | 2 +- src/signing.rs | 25 +---- src/verifying.rs | 3 + tests/ed25519.rs | 6 +- 11 files changed, 114 insertions(+), 234 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 777219c..17f056d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -30,7 +30,6 @@ jobs: - run: cargo test --target ${{ matrix.target }} --no-default-features --features alloc --lib - run: cargo test --target ${{ matrix.target }} - run: cargo test --target ${{ matrix.target }} --features batch - - run: cargo test --target ${{ matrix.target }} --features batch_deterministic - run: cargo test --target ${{ matrix.target }} --features serde - run: cargo test --target ${{ matrix.target }} --features pem @@ -68,7 +67,6 @@ jobs: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable - run: cargo build --benches --features batch - - run: cargo build --benches --features batch_deterministic rustfmt: name: Check formatting diff --git a/Cargo.toml b/Cargo.toml index 06ee258..a814730 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,10 +24,9 @@ rustdoc-args = ["--cfg", "docsrs"] features = ["nightly", "batch", "pkcs8"] [dependencies] -curve25519-dalek = { version = "=4.0.0-pre.5", default-features = false, features = ["digest", "rand_core"] } +curve25519-dalek = { version = "=4.0.0-pre.5", default-features = false, features = ["digest"] } ed25519 = { version = "=2.0.0-rc.0", default-features = false } merlin = { version = "3", default-features = false, optional = true } -rand = { version = "0.8", default-features = false, optional = true } rand_core = { version = "0.6.4", default-features = false, optional = true } serde = { version = "1.0", default-features = false, optional = true } serde_bytes = { version = "0.11", optional = true } @@ -44,25 +43,23 @@ rand = "0.8" rand_core = { version = "0.6.4", default-features = false } serde = { version = "1.0", features = ["derive"] } toml = { version = "0.5" } +curve25519-dalek = { version = "=4.0.0-pre.5", default-features = false, features = ["digest", "rand_core"] } [[bench]] name = "ed25519_benchmarks" harness = false [features] -default = ["std", "rand", "zeroize"] -alloc = ["curve25519-dalek/alloc", "ed25519/alloc", "rand?/alloc", "serde?/alloc", "zeroize?/alloc"] -std = ["alloc", "ed25519/std", "rand?/std", "serde?/std", "sha2/std"] +default = ["std", "rand_core", "zeroize"] +alloc = ["curve25519-dalek/alloc", "ed25519/alloc", "serde?/alloc", "zeroize/alloc"] +std = ["alloc", "ed25519/std", "serde?/std", "sha2/std"] asm = ["sha2/asm"] -batch = ["alloc", "merlin", "rand"] -# This feature enables deterministic batch verification. -batch_deterministic = ["alloc", "merlin", "rand"] +batch = ["alloc", "merlin", "rand_core"] # This features turns off stricter checking for scalar malleability in signatures legacy_compatibility = [] pkcs8 = ["ed25519/pkcs8"] pem = ["alloc", "ed25519/pem", "pkcs8"] -rand = ["dep:rand", "dep:rand_core"] serde = ["dep:serde", "serde_bytes", "ed25519/serde"] zeroize = ["dep:zeroize", "curve25519-dalek/zeroize"] diff --git a/README.md b/README.md index 23dcaf5..d9b6d78 100644 --- a/README.md +++ b/README.md @@ -170,55 +170,19 @@ transactions. The scalar component of a signature is not the only source of signature malleability, however. Both the public key used for signature verification and the group element component of the signature are malleable, as they may contain -a small torsion component as a consquence of the curve25519 group not being of +a small torsion component as a consequence of the curve25519 group not being of prime order, but having a small cofactor of 8. If you wish to also eliminate this source of signature malleability, please review the [documentation for the `verify_strict()` function](https://doc.dalek.rs/ed25519_dalek/struct.PublicKey.html#method.verify_strict). -# A Note on Randomness Generation - -The original paper's specification and the standarisation of RFC8032 do not -specify precisely how randomness is to be generated, other than using a CSPRNG -(Cryptographically Secure Random Number Generator). Particularly in the case of -signature verification, where the security proof _relies_ on the uniqueness of -the blinding factors/nonces, it is paramount that these samples of randomness be -unguessable to an adversary. Because of this, a current growing belief among -cryptographers is that it is safer to prefer _synthetic randomness_. - -To explain synthetic randomness, we should first explain how `ed25519-dalek` -handles generation of _deterministic randomness_. This mode is disabled by -default due to a tiny-but-not-nonexistent chance that this mode will open users -up to fault attacks, wherein an adversary who controls all of the inputs to -batch verification (i.e. the public keys, signatures, and messages) can craft -them in a specialised manner such as to induce a fault (e.g. causing a -mistakenly flipped bit in RAM, overheating a processor, etc.). In the -deterministic mode, we seed the PRNG which generates our blinding factors/nonces -by creating -[a PRNG based on the Fiat-Shamir transform of the public inputs](https://merlin.cool/transcript/rng.html). -This mode is potentially useful to protocols which require strong auditability -guarantees, as well as those which do not have access to secure system-/chip- -provided randomness. This feature can be enabled via -`--features='batch_deterministic'`. Note that we _do not_ support deterministic -signing, due to the numerous pitfalls therein, including a re-used nonce -accidentally revealing the secret key. - -In the default mode, we do as above in the fully deterministic mode, but we -ratchet the underlying keccak-f1600 function (used for the provided -transcript-based PRNG) forward additionally based on some system-/chip- provided -randomness. This provides _synthetic randomness_, that is, randomness based on -both deterministic and undeterinistic data. The reason for doing this is to -prevent badly seeded system RNGs from ruining the security of the signature -verification scheme. - # Features ## #![no_std] -This library aims to be `#![no_std]` compliant. If batch verification is -required (`--features='batch'`), please enable either of the `std` or `alloc` -features. +This library aims is fully `#![no_std]` compliant. No features need to be +enabled or disabled to suppose no-std. ## Nightly Compilers @@ -264,11 +228,3 @@ with potentially many different public keys over potentially many different messages) is available via the `batch` feature. It uses synthetic randomness, as noted above. Batch verification requires allocation, so this won't function in heapless settings. - -Batch verification is slightly faster with the `std` feature enabled, since it -permits us to use `rand::thread_rng`. - -### Deterministic Batch Signature Verification - -The same notion of batch signature verification as above, but with purely -deterministic randomness can be enabled via the `batch_deterministic` feature. diff --git a/benches/ed25519_benchmarks.rs b/benches/ed25519_benchmarks.rs index f1beeab..7c96853 100644 --- a/benches/ed25519_benchmarks.rs +++ b/benches/ed25519_benchmarks.rs @@ -47,7 +47,7 @@ mod ed25519_benches { }); } - #[cfg(any(feature = "batch", feature = "batch_deterministic"))] + #[cfg(feature = "batch")] fn verify_batch_signatures(c: &mut Criterion) { use ed25519_dalek::verify_batch; @@ -75,7 +75,7 @@ mod ed25519_benches { } // If the above function isn't defined, make a placeholder function - #[cfg(not(any(feature = "batch", feature = "batch_deterministic")))] + #[cfg(not(feature = "batch"))] fn verify_batch_signatures(_: &mut Criterion) {} fn key_generation(c: &mut Criterion) { diff --git a/src/batch.rs b/src/batch.rs index ad8a413..c312917 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -9,9 +9,6 @@ //! Batch signature verification. -#[cfg(all(feature = "batch", feature = "batch_deterministic"))] -compile_error!("`batch` and `batch_deterministic` features are mutually exclusive"); - use alloc::vec::Vec; use core::convert::TryFrom; @@ -27,7 +24,7 @@ pub use curve25519_dalek::digest::Digest; use merlin::Transcript; -use rand::Rng; +use rand_core::RngCore; use sha2::Sha512; @@ -36,59 +33,11 @@ use crate::errors::SignatureError; use crate::signature::InternalSignature; use crate::VerifyingKey; -/// Gets an RNG from the system, or the zero RNG if we're in deterministic mode. If available, we -/// prefer `thread_rng`, since it's faster than `OsRng`. -fn get_rng() -> impl rand_core::CryptoRngCore { - #[cfg(all(feature = "batch_deterministic", not(feature = "batch")))] - return ZeroRng; - - #[cfg(all(feature = "batch", feature = "std"))] - return rand::thread_rng(); - - #[cfg(all(feature = "batch", not(feature = "std")))] - return rand::rngs::OsRng; -} - -trait BatchTranscript { - fn append_scalars(&mut self, scalars: &Vec); - fn append_message_lengths(&mut self, message_lengths: &Vec); -} - -impl BatchTranscript for Transcript { - /// Append some `scalars` to this batch verification sigma protocol transcript. - /// - /// For ed25519 batch verification, we include the following as scalars: - /// - /// * All of the computed `H(R||A||M)`s to the protocol transcript, and - /// * All of the `s` components of each signature. - /// - /// Each is also prefixed with their index in the vector. - fn append_scalars(&mut self, scalars: &Vec) { - for (i, scalar) in scalars.iter().enumerate() { - self.append_u64(b"", i as u64); - self.append_message(b"hram", scalar.as_bytes()); - } - } - - /// Append the lengths of the messages into the transcript. - /// - /// This is done out of an (potential over-)abundance of caution, to guard against the unlikely - /// event of collisions. However, a nicer way to do this would be to append the message length - /// before the message, but this is messy w.r.t. the calculations of the `H(R||A||M)`s above. - fn append_message_lengths(&mut self, message_lengths: &Vec) { - for (i, len) in message_lengths.iter().enumerate() { - self.append_u64(b"", i as u64); - self.append_u64(b"mlen", *len as u64); - } - } -} - -/// An implementation of `rand_core::RngCore` which does nothing, to provide purely deterministic -/// transcript-based nonces, rather than synthetically random nonces. -#[cfg(feature = "batch_deterministic")] +/// An implementation of `rand_core::RngCore` which does nothing. This is necessary because merlin +/// demands an `Rng` as input to `TranscriptRngBuilder::finalize()`. Using this with `finalize()` +/// yields a PRG whose input is the hashed transcript. struct ZeroRng; -#[cfg(feature = "batch_deterministic")] impl rand_core::RngCore for ZeroRng { fn next_u32(&mut self) -> u32 { rand_core::impls::next_u32_via_fill(self) @@ -114,9 +63,16 @@ impl rand_core::RngCore for ZeroRng { } } -#[cfg(feature = "batch_deterministic")] +// `TranscriptRngBuilder::finalize()` requires a `CryptoRng` impl rand_core::CryptoRng for ZeroRng {} +// We write our own gen() function so we don't need to pull in the rand crate +fn gen_u128(rng: &mut R) -> u128 { + let mut buf = [0u8; 16]; + rng.fill_bytes(&mut buf); + u128::from_le_bytes(buf) +} + /// Verify a batch of `signatures` on `messages` with their respective `verifying_keys`. /// /// # Inputs @@ -131,84 +87,49 @@ impl rand_core::CryptoRng for ZeroRng {} /// `SignatureError` containing a description of the internal error which /// occured. /// -/// # Notes on Nonce Generation & Malleability -/// -/// ## On Synthetic Nonces -/// -/// This library defaults to using what is called "synthetic" nonces, which -/// means that a mixture of deterministic (per any unique set of inputs to this -/// function) data and system randomness is used to seed the CSPRNG for nonce -/// generation. For more of the background theory on why many cryptographers -/// currently believe this to be superior to either purely deterministic -/// generation or purely relying on the system's randomness, see [this section -/// of the Merlin design](https://merlin.cool/transcript/rng.html) by Henry de -/// Valence, isis lovecruft, and Oleg Andreev, as well as Trevor Perrin's -/// [designs for generalised -/// EdDSA](https://moderncrypto.org/mail-archive/curves/2017/000925.html). -/// /// ## On Deterministic Nonces /// -/// In order to be ammenable to protocols which require stricter third-party -/// auditability trails, such as in some financial cryptographic settings, this -/// library also supports a `--features=batch_deterministic` setting, where the -/// nonces for batch signature verification are derived purely from the inputs -/// to this function themselves. -/// -/// **This is not recommended for use unless you have several cryptographers on -/// staff who can advise you in its usage and all the horrible, terrible, -/// awful ways it can go horribly, terribly, awfully wrong.** +/// The nonces for batch signature verification are derived purely from the inputs to this function +/// themselves. /// /// In any sigma protocol it is wise to include as much context pertaining /// to the public state in the protocol as possible, to avoid malleability /// attacks where an adversary alters publics in an algebraic manner that /// manages to satisfy the equations for the protocol in question. /// -/// For ed25519 batch verification (both with synthetic and deterministic nonce -/// generation), we include the following as scalars in the protocol transcript: +/// For ed25519 batch verification we include the following as scalars in the protocol transcript: /// /// * All of the computed `H(R||A||M)`s to the protocol transcript, and /// * All of the `s` components of each signature. /// -/// Each is also prefixed with their index in the vector. -/// /// The former, while not quite as elegant as adding the `R`s, `A`s, and /// `M`s separately, saves us a bit of context hashing since the /// `H(R||A||M)`s need to be computed for the verification equation anyway. /// -/// The latter prevents a malleability attack only found in deterministic batch -/// signature verification (i.e. only when compiling `ed25519-dalek` with -/// `--features batch_deterministic`) wherein an adversary, without access +/// The latter prevents a malleability attack wherein an adversary, without access /// to the signing key(s), can take any valid signature, `(s,R)`, and swap -/// `s` with `s' = -z1`. This doesn't contitute a signature forgery, merely +/// `s` with `s' = -z1`. This doesn't constitute a signature forgery, merely /// a vulnerability, as the resulting signature will not pass single /// signature verification. (Thanks to Github users @real_or_random and /// @jonasnick for pointing out this malleability issue.) /// -/// For an additional way in which signatures can be made to probablistically -/// falsely "pass" the synthethic batch verification equation *for the same -/// inputs*, but *only some crafted inputs* will pass the deterministic batch -/// single, and neither of these will ever pass single signature verification, -/// see the documentation for [`VerifyingKey.validate()`]. -/// /// # Examples /// /// ``` -/// use ed25519_dalek::verify_batch; -/// use ed25519_dalek::SigningKey; -/// use ed25519_dalek::VerifyingKey; -/// use ed25519_dalek::Signer; -/// use ed25519_dalek::Signature; +/// use ed25519_dalek::{ +/// verify_batch, SigningKey, VerifyingKey, Signer, Signature, +/// }; /// use rand::rngs::OsRng; /// /// # fn main() { /// let mut csprng = OsRng; /// let signing_keys: Vec<_> = (0..64).map(|_| SigningKey::generate(&mut csprng)).collect(); /// let msg: &[u8] = b"They're good dogs Brant"; -/// let messages: Vec<&[u8]> = (0..64).map(|_| msg).collect(); -/// let signatures: Vec = signing_keys.iter().map(|key| key.sign(&msg)).collect(); -/// let verifying_keys: Vec = signing_keys.iter().map(|key| key.verifying_key()).collect(); +/// let messages: Vec<_> = (0..64).map(|_| msg).collect(); +/// let signatures: Vec<_> = signing_keys.iter().map(|key| key.sign(&msg)).collect(); +/// let verifying_keys: Vec<_> = signing_keys.iter().map(|key| key.verifying_key()).collect(); /// -/// let result = verify_batch(&messages[..], &signatures[..], &verifying_keys[..]); +/// let result = verify_batch(&messages, &signatures, &verifying_keys); /// assert!(result.is_ok()); /// # } /// ``` @@ -234,43 +155,61 @@ pub fn verify_batch( .into()); } + // Make a transcript which logs all inputs to this function + let mut transcript: Transcript = Transcript::new(b"ed25519 batch verification"); + + // We make one optimization in the transcript: since we will end up computing H(R || A || M) + // for each (R, A, M) triplet, we will feed _that_ into our transcript rather than each R, A, M + // individually. Since R and A are fixed-length, this modification is secure so long as SHA-512 + // is collision-resistant. + // It suffices to take `verifying_keys[i].as_bytes()` even though a `VerifyingKey` has two + // fields, and `as_bytes()` only returns the bytes of the first. This is because of an + // invariant guaranteed by `VerifyingKey`: the second field is always the (unique) + // decompression of the first. Thus, the serialized first field is a unique representation of + // the entire `VerifyingKey`. + let hrams: Vec<[u8; 64]> = (0..signatures.len()) + .map(|i| { + // Compute H(R || A || M), where + // R = sig.R + // A = verifying key + // M = msg + let mut h: Sha512 = Sha512::default(); + h.update(signatures[i].r_bytes()); + h.update(verifying_keys[i].as_bytes()); + h.update(&messages[i]); + h.finalize().try_into().unwrap() + }) + .collect(); + + // Update transcript with the hashes above. This covers verifying_keys, messages, and the R + // half of signatures + for hram in hrams.iter() { + transcript.append_message(b"hram", hram); + } + // Update transcript with the rest of the data. This covers the s half of the signatures + for sig in signatures { + transcript.append_message(b"sig.s", sig.s_bytes()); + } + + // All function inputs have now been hashed into the transcript. Finalize it and use it as + // randomness for the batch verification. + let mut rng = transcript.build_rng().finalize(&mut ZeroRng); + // Convert all signatures to `InternalSignature` let signatures = signatures .iter() .map(InternalSignature::try_from) .collect::, _>>()?; - - // Compute H(R || A || M) for each (signature, public_key, message) triplet - let hrams: Vec = (0..signatures.len()) - .map(|i| { - let mut h: Sha512 = Sha512::default(); - h.update(signatures[i].R.as_bytes()); - h.update(verifying_keys[i].as_bytes()); - h.update(&messages[i]); - Scalar::from_hash(h) - }) + // Convert the H(R || A || M) values into scalars + let hrams: Vec = hrams + .iter() + .map(Scalar::from_bytes_mod_order_wide) .collect(); - // Collect the message lengths and the scalar portions of the signatures, and add them into the - // transcript. - let message_lengths: Vec = messages.iter().map(|i| i.len()).collect(); - let scalars: Vec = signatures.iter().map(|i| i.s).collect(); - - // Build a PRNG based on a transcript of the H(R || A || M)s seen thus far. This provides - // synthethic randomness in the default configuration, and purely deterministic in the case of - // compiling with the "batch_deterministic" feature. - let mut transcript: Transcript = Transcript::new(b"ed25519 batch verification"); - - transcript.append_scalars(&hrams); - transcript.append_message_lengths(&message_lengths); - transcript.append_scalars(&scalars); - - let mut prng = transcript.build_rng().finalize(&mut get_rng()); - // Select a random 128-bit scalar for each signature. let zs: Vec = signatures .iter() - .map(|_| Scalar::from(prng.gen::())) + .map(|_| Scalar::from(gen_u128(&mut rng))) .collect(); // Compute the basepoint coefficient, ∑ s[i]z[i] (mod l) diff --git a/src/errors.rs b/src/errors.rs index 257399b..aa4e5aa 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -38,7 +38,7 @@ pub(crate) enum InternalError { Verify, /// Two arrays did not match in size, making the called signature /// verification method impossible. - #[cfg(any(feature = "batch", feature = "batch_deterministic"))] + #[cfg(feature = "batch")] ArrayLength { name_a: &'static str, length_a: usize, @@ -62,7 +62,7 @@ impl Display for InternalError { write!(f, "{} must be {} bytes in length", n, l) } InternalError::Verify => write!(f, "Verification equation was not satisfied"), - #[cfg(any(feature = "batch", feature = "batch_deterministic"))] + #[cfg(feature = "batch")] InternalError::ArrayLength { name_a: na, length_a: la, diff --git a/src/lib.rs b/src/lib.rs index edb5b98..817e954 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,8 +18,8 @@ //! secure pseudorandom number generator (CSPRNG). For this example, we'll use //! the operating system's builtin PRNG: //! -#![cfg_attr(feature = "rand", doc = "```")] -#![cfg_attr(not(feature = "rand"), doc = "```ignore")] +#![cfg_attr(feature = "rand_core", doc = "```")] +#![cfg_attr(not(feature = "rand_core"), doc = "```ignore")] //! # fn main() { //! use rand::rngs::OsRng; //! use ed25519_dalek::SigningKey; @@ -32,8 +32,8 @@ //! //! We can now use this `signing_key` to sign a message: //! -#![cfg_attr(feature = "rand", doc = "```")] -#![cfg_attr(not(feature = "rand"), doc = "```ignore")] +#![cfg_attr(feature = "rand_core", doc = "```")] +#![cfg_attr(not(feature = "rand_core"), doc = "```ignore")] //! # fn main() { //! # use rand::rngs::OsRng; //! # use ed25519_dalek::SigningKey; @@ -48,8 +48,8 @@ //! As well as to verify that this is, indeed, a valid signature on //! that `message`: //! -#![cfg_attr(feature = "rand", doc = "```")] -#![cfg_attr(not(feature = "rand"), doc = "```ignore")] +#![cfg_attr(feature = "rand_core", doc = "```")] +#![cfg_attr(not(feature = "rand_core"), doc = "```ignore")] //! # fn main() { //! # use rand::rngs::OsRng; //! # use ed25519_dalek::{SigningKey, Signature, Signer}; @@ -65,8 +65,8 @@ //! Anyone else, given the `public` half of the `signing_key` can also easily //! verify this signature: //! -#![cfg_attr(feature = "rand", doc = "```")] -#![cfg_attr(not(feature = "rand"), doc = "```ignore")] +#![cfg_attr(feature = "rand_core", doc = "```")] +#![cfg_attr(not(feature = "rand_core"), doc = "```ignore")] //! # fn main() { //! # use rand::rngs::OsRng; //! # use ed25519_dalek::SigningKey; @@ -91,8 +91,8 @@ //! secret key to anyone else, since they will only need the public key to //! verify your signatures!) //! -#![cfg_attr(feature = "rand", doc = "```")] -#![cfg_attr(not(feature = "rand"), doc = "```ignore")] +#![cfg_attr(feature = "rand_core", doc = "```")] +#![cfg_attr(not(feature = "rand_core"), doc = "```ignore")] //! # fn main() { //! # use rand::rngs::OsRng; //! # use ed25519_dalek::{SigningKey, Signature, Signer, VerifyingKey}; @@ -111,8 +111,8 @@ //! //! And similarly, decoded from bytes with `::from_bytes()`: //! -#![cfg_attr(feature = "rand", doc = "```")] -#![cfg_attr(not(feature = "rand"), doc = "```ignore")] +#![cfg_attr(feature = "rand_core", doc = "```")] +#![cfg_attr(not(feature = "rand_core"), doc = "```ignore")] //! # use std::convert::TryFrom; //! # use rand::rngs::OsRng; //! # use std::convert::TryInto; @@ -189,8 +189,8 @@ //! They can be then serialised into any of the wire formats which serde supports. //! For example, using [bincode](https://github.com/TyOverby/bincode): //! -#![cfg_attr(all(feature = "rand", feature = "serde"), doc = "```")] -#![cfg_attr(not(all(feature = "rand", feature = "serde")), doc = "```ignore")] +#![cfg_attr(all(feature = "rand_core", feature = "serde"), doc = "```")] +#![cfg_attr(not(all(feature = "rand_core", feature = "serde")), doc = "```ignore")] //! # fn main() { //! # use rand::rngs::OsRng; //! # use ed25519_dalek::{SigningKey, Signature, Signer, Verifier, VerifyingKey}; @@ -210,8 +210,8 @@ //! After sending the `encoded_verifying_key` and `encoded_signature`, the //! recipient may deserialise them and verify: //! -#![cfg_attr(all(feature = "rand", feature = "serde"), doc = "```")] -#![cfg_attr(not(all(feature = "rand", feature = "serde")), doc = "```ignore")] +#![cfg_attr(all(feature = "rand_core", feature = "serde"), doc = "```")] +#![cfg_attr(not(all(feature = "rand_core", feature = "serde")), doc = "```ignore")] //! # fn main() { //! # use rand::rngs::OsRng; //! # use ed25519_dalek::{SigningKey, Signature, Signer, Verifier, VerifyingKey}; @@ -245,7 +245,7 @@ #![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg, doc_cfg_hide))] #![cfg_attr(docsrs, doc(cfg_hide(docsrs)))] -#[cfg(any(feature = "batch", feature = "batch_deterministic"))] +#[cfg(feature = "batch")] extern crate alloc; #[cfg(any(feature = "std", test))] @@ -254,7 +254,7 @@ extern crate std; pub use ed25519; -#[cfg(any(feature = "batch", feature = "batch_deterministic"))] +#[cfg(feature = "batch")] mod batch; mod constants; mod errors; @@ -264,7 +264,7 @@ mod verifying; pub use curve25519_dalek::digest::Digest; -#[cfg(any(feature = "batch", feature = "batch_deterministic"))] +#[cfg(feature = "batch")] pub use crate::batch::*; pub use crate::constants::*; pub use crate::errors::*; diff --git a/src/signature.rs b/src/signature.rs index fdf1700..99aa553 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -160,7 +160,7 @@ impl InternalSignature { /// /// However, by the time this was standardised, most libraries in use were /// only checking the most significant three bits. (See also the - /// documentation for `PublicKey.verify_strict`.) + /// documentation for [`crate::VerifyingKey::verify_strict`].) #[inline] pub fn from_bytes(bytes: &[u8; SIGNATURE_LENGTH]) -> Result { // TODO: Use bytes.split_array_ref once it’s in MSRV. diff --git a/src/signing.rs b/src/signing.rs index 95c0041..7a43452 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -12,7 +12,7 @@ #[cfg(feature = "pkcs8")] use ed25519::pkcs8::{self, DecodePrivateKey}; -#[cfg(feature = "rand")] +#[cfg(feature = "rand_core")] use rand_core::CryptoRngCore; #[cfg(feature = "serde")] @@ -183,7 +183,7 @@ impl SigningKey { /// The standard hash function used for most ed25519 libraries is SHA-512, /// which is available with `use sha2::Sha512` as in the example above. /// Other suitable hash functions include Keccak-512 and Blake2b-512. - #[cfg(feature = "rand")] + #[cfg(feature = "rand_core")] pub fn generate(csprng: &mut R) -> SigningKey { let mut secret = SecretKey::default(); csprng.fill_bytes(&mut secret); @@ -252,7 +252,8 @@ impl SigningKey { /// Let's add a context for good measure (remember, you'll want to choose /// your own!): /// - /// ``` + #[cfg_attr(feature = "rand_core", doc = "```")] + #[cfg_attr(not(feature = "rand_core"), doc = "```ignore")] /// # use ed25519_dalek::Digest; /// # use ed25519_dalek::SigningKey; /// # use ed25519_dalek::Signature; @@ -325,7 +326,8 @@ impl SigningKey { /// /// # Examples /// - /// ``` + #[cfg_attr(feature = "rand_core", doc = "```")] + #[cfg_attr(not(feature = "rand_core"), doc = "```ignore")] /// use ed25519_dalek::Digest; /// use ed25519_dalek::SigningKey; /// use ed25519_dalek::Signature; @@ -655,21 +657,6 @@ impl Drop for ExpandedSecretKey { } impl From<&SecretKey> for ExpandedSecretKey { - /// Construct an `ExpandedSecretKey` from a `SecretKey`. - /// - /// # Examples - /// - /// ```ignore - /// # fn main() { - /// # - /// use rand::rngs::OsRng; - /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; - /// - /// let mut csprng = OsRng{}; - /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); - /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key); - /// # } - /// ``` fn from(secret_key: &SecretKey) -> ExpandedSecretKey { let mut h: Sha512 = Sha512::default(); let mut hash: [u8; 64] = [0u8; 64]; diff --git a/src/verifying.rs b/src/verifying.rs index 2a07b28..b700bac 100644 --- a/src/verifying.rs +++ b/src/verifying.rs @@ -38,6 +38,7 @@ use crate::signature::*; use crate::signing::*; /// An ed25519 public key. +// Invariant: VerifyingKey.1 is always the decompression of VerifyingKey.0 #[derive(Copy, Clone, Default, Eq, PartialEq)] pub struct VerifyingKey(pub(crate) CompressedEdwardsY, pub(crate) EdwardsPoint); @@ -121,6 +122,7 @@ impl VerifyingKey { .decompress() .ok_or(InternalError::PointDecompression)?; + // Invariant: VerifyingKey.1 is always the decompression of VerifyingKey.0 Ok(VerifyingKey(compressed, point)) } @@ -138,6 +140,7 @@ impl VerifyingKey { let point = EdwardsPoint::mul_base(&scalar); let compressed = point.compress(); + // Invariant: VerifyingKey.1 is always the decompression of VerifyingKey.0 VerifyingKey(compressed, point) } diff --git a/tests/ed25519.rs b/tests/ed25519.rs index 1a65d90..f98b1bd 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -16,7 +16,7 @@ use ed25519_dalek::*; use hex::FromHex; use hex_literal::hex; -#[cfg(feature = "rand")] +#[cfg(feature = "rand_core")] use sha2::Sha512; #[cfg(test)] @@ -281,7 +281,7 @@ mod vectors { } } -#[cfg(feature = "rand")] +#[cfg(feature = "rand_core")] mod integrations { use super::*; use rand::rngs::OsRng; @@ -425,7 +425,7 @@ mod integrations { let verifying_keys: Vec = signing_keys.iter().map(|key| key.verifying_key()).collect(); - let result = verify_batch(&messages, &signatures[..], &verifying_keys[..]); + let result = verify_batch(&messages, &signatures, &verifying_keys); assert!(result.is_ok()); } From 8c455f58ae41fcc63f5d2d4ef6b7fd1daa5f08df Mon Sep 17 00:00:00 2001 From: "pinkforest(she/her)" <36498018+pinkforest@users.noreply.github.com> Date: Mon, 16 Jan 2023 11:13:33 +1100 Subject: [PATCH 319/351] Make `rand_core` optional (#262) * Make rand_core optional * Bench requires features rand_core --- .github/workflows/rust.yml | 1 + Cargo.toml | 6 ++++-- src/signing.rs | 7 ++++--- tests/ed25519.rs | 4 +--- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 17f056d..2fd296a 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -30,6 +30,7 @@ jobs: - run: cargo test --target ${{ matrix.target }} --no-default-features --features alloc --lib - run: cargo test --target ${{ matrix.target }} - run: cargo test --target ${{ matrix.target }} --features batch + - run: cargo test --target ${{ matrix.target }} --features rand_core - run: cargo test --target ${{ matrix.target }} --features serde - run: cargo test --target ${{ matrix.target }} --features pem diff --git a/Cargo.toml b/Cargo.toml index a814730..99d51bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ sha2 = { version = "0.10", default-features = false } zeroize = { version = "1.5", default-features = false, optional = true } [dev-dependencies] +curve25519-dalek = { version = "=4.0.0-pre.5", default-features = false, features = ["digest", "rand_core"] } hex = "0.4" bincode = "1.0" serde_json = "1.0" @@ -43,14 +44,14 @@ rand = "0.8" rand_core = { version = "0.6.4", default-features = false } serde = { version = "1.0", features = ["derive"] } toml = { version = "0.5" } -curve25519-dalek = { version = "=4.0.0-pre.5", default-features = false, features = ["digest", "rand_core"] } [[bench]] name = "ed25519_benchmarks" harness = false +required-features = ["rand_core"] [features] -default = ["std", "rand_core", "zeroize"] +default = ["std", "zeroize"] alloc = ["curve25519-dalek/alloc", "ed25519/alloc", "serde?/alloc", "zeroize/alloc"] std = ["alloc", "ed25519/std", "serde?/std", "sha2/std"] @@ -60,6 +61,7 @@ batch = ["alloc", "merlin", "rand_core"] legacy_compatibility = [] pkcs8 = ["ed25519/pkcs8"] pem = ["alloc", "ed25519/pem", "pkcs8"] +rand_core = ["dep:rand_core"] serde = ["dep:serde", "serde_bytes", "ed25519/serde"] zeroize = ["dep:zeroize", "curve25519-dalek/zeroize"] diff --git a/src/signing.rs b/src/signing.rs index 7a43452..df828a6 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -12,7 +12,7 @@ #[cfg(feature = "pkcs8")] use ed25519::pkcs8::{self, DecodePrivateKey}; -#[cfg(feature = "rand_core")] +#[cfg(any(test, feature = "rand_core"))] use rand_core::CryptoRngCore; #[cfg(feature = "serde")] @@ -183,7 +183,7 @@ impl SigningKey { /// The standard hash function used for most ed25519 libraries is SHA-512, /// which is available with `use sha2::Sha512` as in the example above. /// Other suitable hash functions include Keccak-512 and Blake2b-512. - #[cfg(feature = "rand_core")] + #[cfg(any(test, feature = "rand_core"))] pub fn generate(csprng: &mut R) -> SigningKey { let mut secret = SecretKey::default(); csprng.fill_bytes(&mut secret); @@ -208,7 +208,8 @@ impl SigningKey { /// /// # Examples /// - /// ``` + #[cfg_attr(feature = "rand_core", doc = "```")] + #[cfg_attr(not(feature = "rand_core"), doc = "```ignore")] /// use ed25519_dalek::Digest; /// use ed25519_dalek::SigningKey; /// use ed25519_dalek::Sha512; diff --git a/tests/ed25519.rs b/tests/ed25519.rs index f98b1bd..03597d6 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -16,9 +16,6 @@ use ed25519_dalek::*; use hex::FromHex; use hex_literal::hex; -#[cfg(feature = "rand_core")] -use sha2::Sha512; - #[cfg(test)] mod vectors { use super::*; @@ -285,6 +282,7 @@ mod vectors { mod integrations { use super::*; use rand::rngs::OsRng; + use sha2::Sha512; #[test] fn sign_verify() { From 6d9bbd323edfce04f600427571e90afd86f52939 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Mon, 16 Jan 2023 19:38:57 -0700 Subject: [PATCH 320/351] Bump `ed25519` dependency to v2 (#266) Release notes: https://github.com/RustCrypto/signatures/pull/622 --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bf843ff..877eb1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -275,9 +275,9 @@ dependencies = [ [[package]] name = "ed25519" -version = "2.0.0-rc.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24a753d68e68a75b72508fa3d37255ae8a6f7492715e61f3a14f3769859b2fb3" +checksum = "a3af5919f6d605315213c36abdd435562224665993b274912dee0d9a0e2fed8a" dependencies = [ "pkcs8", "serde", @@ -746,9 +746,9 @@ dependencies = [ [[package]] name = "signature" -version = "2.0.0-rc.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51659052c3c82a3cb69d911c1c1d8cb5d383012b7ec537918d5ecc5f42870d2d" +checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d" [[package]] name = "spki" diff --git a/Cargo.toml b/Cargo.toml index 99d51bd..5bfa0ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ features = ["nightly", "batch", "pkcs8"] [dependencies] curve25519-dalek = { version = "=4.0.0-pre.5", default-features = false, features = ["digest"] } -ed25519 = { version = "=2.0.0-rc.0", default-features = false } +ed25519 = { version = "2", default-features = false } merlin = { version = "3", default-features = false, optional = true } rand_core = { version = "0.6.4", default-features = false, optional = true } serde = { version = "1.0", default-features = false, optional = true } From e1d4ef313ea4a5afd6df66819b1b721416c841db Mon Sep 17 00:00:00 2001 From: Linus Karl Date: Tue, 17 Jan 2023 04:43:05 +0100 Subject: [PATCH 321/351] Implement Hash trait for VerifyingKey (#265) * Added and cleaned up some verification docs Co-authored-by: Michael Rosenberg --- src/verifying.rs | 36 +++++++++++++++++++++++++++++------- tests/ed25519.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/src/verifying.rs b/src/verifying.rs index b700bac..89e1b65 100644 --- a/src/verifying.rs +++ b/src/verifying.rs @@ -11,6 +11,7 @@ use core::convert::TryFrom; use core::fmt::Debug; +use core::hash::{Hash, Hasher}; use curve25519_dalek::digest::generic_array::typenum::U64; use curve25519_dalek::digest::Digest; @@ -38,8 +39,19 @@ use crate::signature::*; use crate::signing::*; /// An ed25519 public key. +/// +/// # Note +/// +/// The `Eq` and `Hash` impls here use the compressed Edwards y encoding, _not_ the algebraic +/// representation. This means if this `VerifyingKey` is non-canonically encoded, it will be +/// considered unequal to the other equivalent encoding, despite the two representing the same +/// point. More encoding details can be found +/// [here](https://hdevalence.ca/blog/2020-10-04-its-25519am). +/// +/// If you don't care and/or don't want to deal with this, just make sure to use the +/// [`VerifyingKey::verify_strict`] function. // Invariant: VerifyingKey.1 is always the decompression of VerifyingKey.0 -#[derive(Copy, Clone, Default, Eq, PartialEq)] +#[derive(Copy, Clone, Default, Eq)] pub struct VerifyingKey(pub(crate) CompressedEdwardsY, pub(crate) EdwardsPoint); impl Debug for VerifyingKey { @@ -54,6 +66,18 @@ impl AsRef<[u8]> for VerifyingKey { } } +impl Hash for VerifyingKey { + fn hash(&self, state: &mut H) { + self.as_bytes().hash(state); + } +} + +impl PartialEq for VerifyingKey { + fn eq(&self, other: &VerifyingKey) -> bool { + self.as_bytes() == other.as_bytes() + } +} + impl From<&ExpandedSecretKey> for VerifyingKey { /// Derive this public key from its corresponding `ExpandedSecretKey`. fn from(expanded_secret_key: &ExpandedSecretKey) -> VerifyingKey { @@ -114,7 +138,7 @@ impl VerifyingKey { /// # Returns /// /// A `Result` whose okay value is an EdDSA `VerifyingKey` or whose error value - /// is an `SignatureError` describing the error that occurred. + /// is a `SignatureError` describing the error that occurred. #[inline] pub fn from_bytes(bytes: &[u8; PUBLIC_KEY_LENGTH]) -> Result { let compressed = CompressedEdwardsY(*bytes); @@ -176,14 +200,12 @@ impl VerifyingKey { /// * `context` is an optional context string, up to 255 bytes inclusive, /// which may be used to provide additional domain separation. If not /// set, this will default to an empty string. - /// * `signature` is a purported Ed25519ph [`Signature`] on the `prehashed_message`. + /// * `signature` is a purported Ed25519ph signature on the `prehashed_message`. /// /// # Returns /// /// Returns `true` if the `signature` was a valid signature created by this /// `Keypair` on the `prehashed_message`. - /// - /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 #[allow(non_snake_case)] pub fn verify_prehashed( &self, @@ -229,7 +251,7 @@ impl VerifyingKey { /// 1. Scalar Malleability /// /// The authors of the RFC explicitly stated that verification of an ed25519 - /// signature must fail if the scalar `s` is not properly reduced mod \ell: + /// signature must fail if the scalar `s` is not properly reduced mod $\ell$: /// /// > To verify a signature on a message M using public key A, with F /// > being 0 for Ed25519ctx, 1 for Ed25519ph, and if Ed25519ctx or @@ -322,7 +344,7 @@ impl VerifyingKey { /// * `context` is an optional context string, up to 255 bytes inclusive, /// which may be used to provide additional domain separation. If not /// set, this will default to an empty string. - /// * `signature` is a purported Ed25519ph [`Signature`] on the `prehashed_message`. + /// * `signature` is a purported Ed25519ph signature on the `prehashed_message`. /// /// # Returns /// diff --git a/tests/ed25519.rs b/tests/ed25519.rs index 03597d6..25c3520 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -283,6 +283,7 @@ mod integrations { use super::*; use rand::rngs::OsRng; use sha2::Sha512; + use std::collections::HashMap; #[test] fn sign_verify() { @@ -427,6 +428,33 @@ mod integrations { assert!(result.is_ok()); } + + #[test] + fn public_key_hash_trait_check() { + let mut csprng = OsRng {}; + let secret: SigningKey = SigningKey::generate(&mut csprng); + let public_from_secret: VerifyingKey = (&secret).into(); + + let mut m = HashMap::new(); + m.insert(public_from_secret, "Example_Public_Key"); + + m.insert(public_from_secret, "Updated Value"); + + let (k, v) = m.get_key_value(&public_from_secret).unwrap(); + assert_eq!(k, &public_from_secret); + assert_eq!(v.clone(), "Updated Value"); + assert_eq!(m.len(), 1usize); + + let second_secret: SigningKey = SigningKey::generate(&mut csprng); + let public_from_second_secret: VerifyingKey = (&second_secret).into(); + assert_ne!(public_from_secret, public_from_second_secret); + m.insert(public_from_second_secret, "Second public key"); + + let (k, v) = m.get_key_value(&public_from_second_secret).unwrap(); + assert_eq!(k, &public_from_second_secret); + assert_eq!(v.clone(), "Second public key"); + assert_eq!(m.len(), 2usize); + } } #[cfg(all(test, feature = "serde"))] From 431e69959d3922deba961eedbd26efb8eb40f831 Mon Sep 17 00:00:00 2001 From: "pinkforest(she/her)" <36498018+pinkforest@users.noreply.github.com> Date: Thu, 19 Jan 2023 18:59:43 +1100 Subject: [PATCH 322/351] Make digest optional (#268) digest isn't yet stable but we have use it in the public API. This makes the digest API optional to use in opt-in basis by feature gating this via an optional digest feature. API items now feature-gated: - `pub use ed25519_dalek::Digest` - `SigningKey::sign_prehashed(D: prehashed_message, ..)` - `SigningKey::verify_prehashed(D: prehahed_message, ..)` - `VerifyingKey::verify_prehashed(D: prehashed_message, ..)` - `VerifyingKey::verify_prehashed_strict(D: prehashed_message, ..)` Also no longer re-exporting `sha2::Sha512` --- .github/workflows/rust.yml | 2 +- Cargo.toml | 1 + src/errors.rs | 2 ++ src/lib.rs | 1 + src/signing.rs | 31 ++++++++++++++++++++++--------- src/verifying.rs | 5 ++++- tests/ed25519.rs | 7 ++++++- 7 files changed, 37 insertions(+), 12 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 2fd296a..d1094a6 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -30,7 +30,7 @@ jobs: - run: cargo test --target ${{ matrix.target }} --no-default-features --features alloc --lib - run: cargo test --target ${{ matrix.target }} - run: cargo test --target ${{ matrix.target }} --features batch - - run: cargo test --target ${{ matrix.target }} --features rand_core + - run: cargo test --target ${{ matrix.target }} --features "digest rand_core" - run: cargo test --target ${{ matrix.target }} --features serde - run: cargo test --target ${{ matrix.target }} --features pem diff --git a/Cargo.toml b/Cargo.toml index 5bfa0ed..e41609d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,7 @@ std = ["alloc", "ed25519/std", "serde?/std", "sha2/std"] asm = ["sha2/asm"] batch = ["alloc", "merlin", "rand_core"] +digest = [] # This features turns off stricter checking for scalar malleability in signatures legacy_compatibility = [] pkcs8 = ["ed25519/pkcs8"] diff --git a/src/errors.rs b/src/errors.rs index aa4e5aa..7cba06d 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -48,6 +48,7 @@ pub(crate) enum InternalError { length_c: usize, }, /// An ed25519ph signature can only take up to 255 octets of context. + #[cfg(feature = "digest")] PrehashedContextLength, /// A mismatched (public, secret) key pair. MismatchedKeypair, @@ -76,6 +77,7 @@ impl Display for InternalError { {} has length {}, {} has length {}.", na, la, nb, lb, nc, lc ), + #[cfg(feature = "digest")] InternalError::PrehashedContextLength => write!( f, "An ed25519ph signature can only take up to 255 octets of context" diff --git a/src/lib.rs b/src/lib.rs index 817e954..84cb275 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -262,6 +262,7 @@ mod signature; mod signing; mod verifying; +#[cfg(feature = "digest")] pub use curve25519_dalek::digest::Digest; #[cfg(feature = "batch")] diff --git a/src/signing.rs b/src/signing.rs index df828a6..a2adffa 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -24,6 +24,7 @@ use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes}; use sha2::Sha512; +#[cfg(feature = "digest")] use curve25519_dalek::digest::generic_array::typenum::U64; use curve25519_dalek::digest::Digest; use curve25519_dalek::edwards::CompressedEdwardsY; @@ -208,12 +209,15 @@ impl SigningKey { /// /// # Examples /// - #[cfg_attr(feature = "rand_core", doc = "```")] - #[cfg_attr(not(feature = "rand_core"), doc = "```ignore")] + #[cfg_attr(all(feature = "rand_core", feature = "digest"), doc = "```")] + #[cfg_attr( + any(not(feature = "rand_core"), not(feature = "digest")), + doc = "```ignore" + )] /// use ed25519_dalek::Digest; /// use ed25519_dalek::SigningKey; - /// use ed25519_dalek::Sha512; /// use ed25519_dalek::Signature; + /// use sha2::Sha512; /// use rand::rngs::OsRng; /// /// # #[cfg(feature = "std")] @@ -253,13 +257,16 @@ impl SigningKey { /// Let's add a context for good measure (remember, you'll want to choose /// your own!): /// - #[cfg_attr(feature = "rand_core", doc = "```")] - #[cfg_attr(not(feature = "rand_core"), doc = "```ignore")] + #[cfg_attr(all(feature = "rand_core", feature = "digest"), doc = "```")] + #[cfg_attr( + any(not(feature = "rand_core"), not(feature = "digest")), + doc = "```ignore" + )] /// # use ed25519_dalek::Digest; /// # use ed25519_dalek::SigningKey; /// # use ed25519_dalek::Signature; /// # use ed25519_dalek::SignatureError; - /// # use ed25519_dalek::Sha512; + /// # use sha2::Sha512; /// # use rand::rngs::OsRng; /// # /// # fn do_test() -> Result { @@ -286,6 +293,7 @@ impl SigningKey { /// /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 /// [terrible_idea]: https://github.com/isislovecruft/scripts/blob/master/gpgkey2bc.py + #[cfg(feature = "digest")] pub fn sign_prehashed( &self, prehashed_message: D, @@ -327,13 +335,16 @@ impl SigningKey { /// /// # Examples /// - #[cfg_attr(feature = "rand_core", doc = "```")] - #[cfg_attr(not(feature = "rand_core"), doc = "```ignore")] + #[cfg_attr(all(feature = "rand_core", feature = "digest"), doc = "```")] + #[cfg_attr( + any(not(feature = "rand_core"), not(feature = "digest")), + doc = "```ignore" + )] /// use ed25519_dalek::Digest; /// use ed25519_dalek::SigningKey; /// use ed25519_dalek::Signature; /// use ed25519_dalek::SignatureError; - /// use ed25519_dalek::Sha512; + /// use sha2::Sha512; /// use rand::rngs::OsRng; /// /// # fn do_test() -> Result<(), SignatureError> { @@ -369,6 +380,7 @@ impl SigningKey { /// ``` /// /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 + #[cfg(feature = "digest")] pub fn verify_prehashed( &self, prehashed_message: D, @@ -724,6 +736,7 @@ impl ExpandedSecretKey { /// a `SignatureError`. /// /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 + #[cfg(feature = "digest")] #[allow(non_snake_case)] pub(crate) fn sign_prehashed<'a, D>( &self, diff --git a/src/verifying.rs b/src/verifying.rs index 89e1b65..7879e20 100644 --- a/src/verifying.rs +++ b/src/verifying.rs @@ -13,6 +13,7 @@ use core::convert::TryFrom; use core::fmt::Debug; use core::hash::{Hash, Hasher}; +#[cfg(feature = "digest")] use curve25519_dalek::digest::generic_array::typenum::U64; use curve25519_dalek::digest::Digest; use curve25519_dalek::edwards::CompressedEdwardsY; @@ -21,7 +22,7 @@ use curve25519_dalek::scalar::Scalar; use ed25519::signature::Verifier; -pub use sha2::Sha512; +use sha2::Sha512; #[cfg(feature = "pkcs8")] use ed25519::pkcs8::{self, DecodePublicKey}; @@ -206,6 +207,7 @@ impl VerifyingKey { /// /// Returns `true` if the `signature` was a valid signature created by this /// `Keypair` on the `prehashed_message`. + #[cfg(feature = "digest")] #[allow(non_snake_case)] pub fn verify_prehashed( &self, @@ -350,6 +352,7 @@ impl VerifyingKey { /// /// Returns `true` if the `signature` was a valid signature created by this /// `Keypair` on the `prehashed_message`. + #[cfg(feature = "digest")] #[allow(non_snake_case)] pub fn verify_prehashed_strict( &self, diff --git a/tests/ed25519.rs b/tests/ed25519.rs index 25c3520..6775573 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -14,6 +14,7 @@ use curve25519_dalek; use ed25519_dalek::*; use hex::FromHex; +#[cfg(feature = "digest")] use hex_literal::hex; #[cfg(test)] @@ -96,8 +97,9 @@ mod vectors { } // From https://tools.ietf.org/html/rfc8032#section-7.3 + #[cfg(feature = "digest")] #[test] - fn ed25519ph_rf8032_test_vector() { + fn ed25519ph_rf8032_test_vector_prehash() { let sec_bytes = hex!("833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42"); let pub_bytes = hex!("ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf"); let msg_bytes = hex!("616263"); @@ -234,6 +236,7 @@ mod vectors { // Identical to repudiation() above, but testing verify_prehashed against // verify_prehashed_strict. See comments above for a description of what's happening. + #[cfg(feature = "digest")] #[test] fn repudiation_prehash() { let message1 = Sha512::new().chain_update(b"Send 100 USD to Alice"); @@ -282,6 +285,7 @@ mod vectors { mod integrations { use super::*; use rand::rngs::OsRng; + #[cfg(feature = "digest")] use sha2::Sha512; use std::collections::HashMap; @@ -328,6 +332,7 @@ mod integrations { ); } + #[cfg(feature = "digest")] #[test] fn ed25519ph_sign_verify() { let signing_key: SigningKey; From f61e9dcf9ba331db1575e96ea54338856a569d2a Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Fri, 20 Jan 2023 13:46:17 -0700 Subject: [PATCH 323/351] Add on-by-default `fast` crate feature for gating basepoint tables (#251) * Add on-by-default `fast` crate feature Disabling the feature reduces overall code size at the cost of performance, which is useful for e.g. embedded users. This feature transitively enables the `basepoint-tables` feature in `curve25519-dalek` where the basepoint tables are actually defined. * Consolidated a lot of verification code * Bump `curve25519-dalek`; use `precomputed-tables` feature The feature name changed in dalek-cryptography/curve25519-dalek#499 Co-authored-by: Michael Rosenberg --- .github/workflows/rust.yml | 10 +++++-- Cargo.lock | 2 +- Cargo.toml | 3 +- src/verifying.rs | 60 ++++++++++++++++++-------------------- 4 files changed, 39 insertions(+), 36 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index d1094a6..befdb39 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -26,13 +26,19 @@ jobs: - uses: dtolnay/rust-toolchain@stable - run: rustup target add ${{ matrix.target }} - run: ${{ matrix.deps }} - - run: cargo test --target ${{ matrix.target }} --no-default-features --lib + - run: cargo test --target ${{ matrix.target }} --no-default-features --lib --tests - run: cargo test --target ${{ matrix.target }} --no-default-features --features alloc --lib + - run: cargo test --target ${{ matrix.target }} --no-default-features --features fast --lib + - run: cargo test --target ${{ matrix.target }} --no-default-features --features rand_core --lib --tests + - run: cargo test --target ${{ matrix.target }} --no-default-features --features alloc,rand_core --lib --tests + - run: cargo test --target ${{ matrix.target }} --no-default-features --features fast,rand_core --lib --tests + - run: cargo test --target ${{ matrix.target }} --no-default-features --features alloc,fast,rand_core --lib --tests - run: cargo test --target ${{ matrix.target }} - run: cargo test --target ${{ matrix.target }} --features batch - - run: cargo test --target ${{ matrix.target }} --features "digest rand_core" + - run: cargo test --target ${{ matrix.target }} --features digest,rand_core - run: cargo test --target ${{ matrix.target }} --features serde - run: cargo test --target ${{ matrix.target }} --features pem + - run: cargo test --target ${{ matrix.target }} --all-features build-simd: name: Test simd backend (nightly) diff --git a/Cargo.lock b/Cargo.lock index 877eb1a..207bfff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -240,7 +240,7 @@ dependencies = [ [[package]] name = "curve25519-dalek" version = "4.0.0-pre.5" -source = "git+https://github.com/dalek-cryptography/curve25519-dalek.git#83f6b149d33c37b8997316cb7a87d8d247b75c3e" +source = "git+https://github.com/dalek-cryptography/curve25519-dalek.git#3effd73307a606e44469a425974f0b7b0eb85899" dependencies = [ "cfg-if", "digest", diff --git a/Cargo.toml b/Cargo.toml index e41609d..064be79 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,12 +51,13 @@ harness = false required-features = ["rand_core"] [features] -default = ["std", "zeroize"] +default = ["fast", "std", "zeroize"] alloc = ["curve25519-dalek/alloc", "ed25519/alloc", "serde?/alloc", "zeroize/alloc"] std = ["alloc", "ed25519/std", "serde?/std", "sha2/std"] asm = ["sha2/asm"] batch = ["alloc", "merlin", "rand_core"] +fast = ["curve25519-dalek/precomputed-tables"] digest = [] # This features turns off stricter checking for scalar malleability in signatures legacy_compatibility = [] diff --git a/src/verifying.rs b/src/verifying.rs index 7879e20..e11d47e 100644 --- a/src/verifying.rs +++ b/src/verifying.rs @@ -169,7 +169,8 @@ impl VerifyingKey { VerifyingKey(compressed, point) } - // A helper function that computes H(R || A || M) as well as its prehashed version + // A helper function that computes H(R || A || M). If `context.is_some()`, this does the + // prehashed variant of the computation using its contents. #[allow(non_snake_case)] fn compute_challenge( context: Option<&[u8]>, @@ -191,6 +192,22 @@ impl VerifyingKey { Scalar::from_hash(h) } + // Helper function for verification. Computes the _expected_ R component of the signature. The + // caller compares this to the real R component. If `context.is_some()`, this does the + // prehashed variant of the computation using its contents. + #[allow(non_snake_case)] + fn recompute_r( + &self, + context: Option<&[u8]>, + signature: &InternalSignature, + M: &[u8], + ) -> EdwardsPoint { + let k = Self::compute_challenge(context, &signature.R, &self.0, M); + let minus_A: EdwardsPoint = -self.1; + // Recall the (non-batched) verification equation: -[k]A + [s]B = R + EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s) + } + /// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm. /// /// # Inputs @@ -226,17 +243,10 @@ impl VerifyingKey { "The context must not be longer than 255 octets." ); - let minus_A: EdwardsPoint = -self.1; - let k = Self::compute_challenge( - Some(ctx), - &signature.R, - &self.0, - prehashed_message.finalize().as_slice(), - ); - let R: EdwardsPoint = - EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); + let message = prehashed_message.finalize(); + let expected_R = self.recompute_r(Some(ctx), &signature, &message); - if R.compress() == signature.R { + if expected_R.compress() == signature.R { Ok(()) } else { Err(InternalError::Verify.into()) @@ -323,12 +333,8 @@ impl VerifyingKey { return Err(InternalError::Verify.into()); } - let minus_A: EdwardsPoint = -self.1; - let k = Self::compute_challenge(None, &signature.R, &self.0, message); - let R: EdwardsPoint = - EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); - - if R == signature_R { + let expected_R = self.recompute_r(None, &signature, message); + if expected_R == signature_R { Ok(()) } else { Err(InternalError::Verify.into()) @@ -381,16 +387,10 @@ impl VerifyingKey { return Err(InternalError::Verify.into()); } - let minus_A: EdwardsPoint = -self.1; - let k = Self::compute_challenge( - Some(ctx), - &signature.R, - &self.0, - prehashed_message.finalize().as_slice(), - ); - let R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); + let message = prehashed_message.finalize(); + let expected_R = self.recompute_r(Some(ctx), &signature, &message); - if R == signature_R { + if expected_R == signature_R { Ok(()) } else { Err(InternalError::Verify.into()) @@ -408,12 +408,8 @@ impl Verifier for VerifyingKey { fn verify(&self, message: &[u8], signature: &ed25519::Signature) -> Result<(), SignatureError> { let signature = InternalSignature::try_from(signature)?; - let minus_A: EdwardsPoint = -self.1; - let k = Self::compute_challenge(None, &signature.R, &self.0, message); - let R: EdwardsPoint = - EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); - - if R.compress() == signature.R { + let expected_R = self.recompute_r(None, &signature, message); + if expected_R.compress() == signature.R { Ok(()) } else { Err(InternalError::Verify.into()) From ba765a5988e5216b889e54aeb3c1f3869cd98a65 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Fri, 20 Jan 2023 22:02:27 -0700 Subject: [PATCH 324/351] Impl `signature::Digest*` traits for Ed25519ph (#270) * Impl `signature::Digest*` traits for Ed25519ph Adds the following trait impls: - impl DigestSigner for SigningKey - impl DigestVerifier for VerifyingKey These traits can be used to create and verify Ed25519 signatures, thunking to `SigningKey::sign_prehashed` and `VerifyingKey::verify_prehashed` respectively. * Add rustdoc comments for trait impls --- Cargo.lock | 4 ++++ Cargo.toml | 7 +++++-- src/signing.rs | 14 ++++++++++++++ src/verifying.rs | 18 ++++++++++++++++++ 4 files changed, 41 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 207bfff..0fae5cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -301,6 +301,7 @@ dependencies = [ "serde_bytes", "serde_json", "sha2", + "signature", "toml", "zeroize", ] @@ -749,6 +750,9 @@ name = "signature" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d" +dependencies = [ + "digest", +] [[package]] name = "spki" diff --git a/Cargo.toml b/Cargo.toml index 064be79..b8c25b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,11 +26,14 @@ features = ["nightly", "batch", "pkcs8"] [dependencies] curve25519-dalek = { version = "=4.0.0-pre.5", default-features = false, features = ["digest"] } ed25519 = { version = "2", default-features = false } +signature = { version = ">=2.0, <2.1", optional = true, default-features = false } +sha2 = { version = "0.10", default-features = false } + +# optional features merlin = { version = "3", default-features = false, optional = true } rand_core = { version = "0.6.4", default-features = false, optional = true } serde = { version = "1.0", default-features = false, optional = true } serde_bytes = { version = "0.11", optional = true } -sha2 = { version = "0.10", default-features = false } zeroize = { version = "1.5", default-features = false, optional = true } [dev-dependencies] @@ -58,7 +61,7 @@ std = ["alloc", "ed25519/std", "serde?/std", "sha2/std"] asm = ["sha2/asm"] batch = ["alloc", "merlin", "rand_core"] fast = ["curve25519-dalek/precomputed-tables"] -digest = [] +digest = ["signature/digest"] # This features turns off stricter checking for scalar malleability in signatures legacy_compatibility = [] pkcs8 = ["ed25519/pkcs8"] diff --git a/src/signing.rs b/src/signing.rs index a2adffa..d376485 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -33,6 +33,9 @@ use curve25519_dalek::scalar::Scalar; use ed25519::signature::{KeypairRef, Signer, Verifier}; +#[cfg(feature = "digest")] +use signature::DigestSigner; + #[cfg(feature = "zeroize")] use zeroize::{Zeroize, ZeroizeOnDrop}; @@ -484,6 +487,17 @@ impl Signer for SigningKey { } } +/// Equivalent to [`SigningKey::sign_prehashed`] with `context` set to [`None`]. +#[cfg(feature = "digest")] +impl DigestSigner for SigningKey +where + D: Digest, +{ + fn try_sign_digest(&self, msg_digest: D) -> Result { + self.sign_prehashed(msg_digest, None) + } +} + impl Verifier for SigningKey { /// Verify a signature on a message with this signing key's public key. fn verify(&self, message: &[u8], signature: &ed25519::Signature) -> Result<(), SignatureError> { diff --git a/src/verifying.rs b/src/verifying.rs index e11d47e..726d971 100644 --- a/src/verifying.rs +++ b/src/verifying.rs @@ -34,6 +34,9 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[cfg(feature = "serde")] use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes}; +#[cfg(feature = "digest")] +use signature::DigestVerifier; + use crate::constants::*; use crate::errors::*; use crate::signature::*; @@ -417,6 +420,21 @@ impl Verifier for VerifyingKey { } } +/// Equivalent to [`VerifyingKey::verify_prehashed`] with `context` set to [`None`]. +#[cfg(feature = "digest")] +impl DigestVerifier for VerifyingKey +where + D: Digest, +{ + fn verify_digest( + &self, + msg_digest: D, + signature: &ed25519::Signature, + ) -> Result<(), SignatureError> { + self.verify_prehashed(msg_digest, None, signature) + } +} + impl TryFrom<&[u8]> for VerifyingKey { type Error = SignatureError; From 7d255cd85a6524f17a3ea81d0d92245f853cd365 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Fri, 20 Jan 2023 22:21:35 -0700 Subject: [PATCH 325/351] CI: test `cargo doc` build (#271) * CI: test `cargo doc` build Ensure it's free of warnings * Fix rustdoc build --- .github/workflows/rust.yml | 12 ++++++++++++ src/lib.rs | 4 ++-- src/signing.rs | 29 +++++++++++++---------------- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index befdb39..87b40c8 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -9,6 +9,7 @@ on: env: CARGO_TERM_COLOR: always RUSTFLAGS: '-D warnings' + RUSTDOCFLAGS: '-D warnings' jobs: test: @@ -94,3 +95,14 @@ jobs: with: components: clippy - run: cargo clippy + + doc: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + override: true + profile: minimal + - run: cargo doc --all-features diff --git a/src/lib.rs b/src/lib.rs index 84cb275..d6118a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -157,8 +157,8 @@ //! //! - [`pkcs8::DecodePrivateKey`]: decode private keys from PKCS#8 //! - [`pkcs8::EncodePrivateKey`]: encode private keys to PKCS#8 -//! - [`pkcs8::DecodeVerifyingKey`]: decode public keys from PKCS#8 -//! - [`pkcs8::EncodeVerifyingKey`]: encode public keys to PKCS#8 +//! - [`pkcs8::DecodePublicKey`]: decode public keys from PKCS#8 +//! - [`pkcs8::EncodePublicKey`]: encode public keys to PKCS#8 //! //! #### Example //! diff --git a/src/signing.rs b/src/signing.rs index d376485..814ecda 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -43,6 +43,7 @@ use crate::constants::*; use crate::errors::*; use crate::signature::*; use crate::verifying::*; +use crate::Signature; /// ed25519 secret key as defined in [RFC8032 § 5.1.5]: /// @@ -301,7 +302,7 @@ impl SigningKey { &self, prehashed_message: D, context: Option<&[u8]>, - ) -> Result + ) -> Result where D: Digest, { @@ -311,11 +312,7 @@ impl SigningKey { } /// Verify a signature on a message with this signing key's public key. - pub fn verify( - &self, - message: &[u8], - signature: &ed25519::Signature, - ) -> Result<(), SignatureError> { + pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> { self.verifying_key.verify(message, signature) } @@ -388,7 +385,7 @@ impl SigningKey { &self, prehashed_message: D, context: Option<&[u8]>, - signature: &ed25519::Signature, + signature: &Signature, ) -> Result<(), SignatureError> where D: Digest, @@ -463,7 +460,7 @@ impl SigningKey { pub fn verify_strict( &self, message: &[u8], - signature: &ed25519::Signature, + signature: &Signature, ) -> Result<(), SignatureError> { self.verifying_key.verify_strict(message, signature) } @@ -479,9 +476,9 @@ impl KeypairRef for SigningKey { type VerifyingKey = VerifyingKey; } -impl Signer for SigningKey { +impl Signer for SigningKey { /// Sign a message with this signing key's secret key. - fn try_sign(&self, message: &[u8]) -> Result { + fn try_sign(&self, message: &[u8]) -> Result { let expanded: ExpandedSecretKey = (&self.secret_key).into(); Ok(expanded.sign(message, &self.verifying_key)) } @@ -489,18 +486,18 @@ impl Signer for SigningKey { /// Equivalent to [`SigningKey::sign_prehashed`] with `context` set to [`None`]. #[cfg(feature = "digest")] -impl DigestSigner for SigningKey +impl DigestSigner for SigningKey where D: Digest, { - fn try_sign_digest(&self, msg_digest: D) -> Result { + fn try_sign_digest(&self, msg_digest: D) -> Result { self.sign_prehashed(msg_digest, None) } } -impl Verifier for SigningKey { +impl Verifier for SigningKey { /// Verify a signature on a message with this signing key's public key. - fn verify(&self, message: &[u8], signature: &ed25519::Signature) -> Result<(), SignatureError> { + fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> { self.verifying_key.verify(message, signature) } } @@ -710,7 +707,7 @@ impl From<&SecretKey> for ExpandedSecretKey { impl ExpandedSecretKey { /// Sign a message with this `ExpandedSecretKey`. #[allow(non_snake_case)] - pub(crate) fn sign(&self, message: &[u8], verifying_key: &VerifyingKey) -> ed25519::Signature { + pub(crate) fn sign(&self, message: &[u8], verifying_key: &VerifyingKey) -> Signature { let mut h: Sha512 = Sha512::new(); h.update(self.nonce); @@ -757,7 +754,7 @@ impl ExpandedSecretKey { prehashed_message: D, verifying_key: &VerifyingKey, context: Option<&'a [u8]>, - ) -> Result + ) -> Result where D: Digest, { From c2b8978927e95f380fa5630cdb874bdfc8f97b01 Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Sat, 21 Jan 2023 01:05:54 -0500 Subject: [PATCH 326/351] Do byte comparison in all `verify_*` functions (#269) * Made all signature R comparisons byte-wise * Use Scalar::from_bits_clamped rather than manually clamping * Added clippy lints and comments for use of unwrap() * Clarify use of unused --- src/lib.rs | 1 + src/signature.rs | 4 ++++ src/signing.rs | 23 +++++++---------------- src/verifying.rs | 40 ++++++++++++++++++---------------------- 4 files changed, 30 insertions(+), 38 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d6118a4..ead3d29 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -241,6 +241,7 @@ #![no_std] #![warn(future_incompatible, rust_2018_idioms)] #![deny(missing_docs)] // refuse to compile if documentation is missing +#![deny(clippy::unwrap_used)] // don't allow unwrap #![cfg_attr(not(test), forbid(unsafe_code))] #![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg, doc_cfg_hide))] #![cfg_attr(docsrs, doc(cfg_hide(docsrs)))] diff --git a/src/signature.rs b/src/signature.rs index 99aa553..c78779f 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -162,6 +162,7 @@ impl InternalSignature { /// only checking the most significant three bits. (See also the /// documentation for [`crate::VerifyingKey::verify_strict`].) #[inline] + #[allow(clippy::unwrap_used)] pub fn from_bytes(bytes: &[u8; SIGNATURE_LENGTH]) -> Result { // TODO: Use bytes.split_array_ref once it’s in MSRV. let (lower, upper) = bytes.split_at(32); @@ -181,7 +182,10 @@ impl TryFrom<&ed25519::Signature> for InternalSignature { } impl From for ed25519::Signature { + #[allow(clippy::unwrap_used)] fn from(sig: InternalSignature) -> ed25519::Signature { + // This function only fails if the s half of the parsed input exceeds the scalar modulus. + // Since the bytes are coming straight from a Scalar, this is impossible. ed25519::Signature::from_bytes(&sig.as_bytes()).unwrap() } } diff --git a/src/signing.rs b/src/signing.rs index 814ecda..ad299c1 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -681,25 +681,16 @@ impl Drop for ExpandedSecretKey { } impl From<&SecretKey> for ExpandedSecretKey { + #[allow(clippy::unwrap_used)] fn from(secret_key: &SecretKey) -> ExpandedSecretKey { - let mut h: Sha512 = Sha512::default(); - let mut hash: [u8; 64] = [0u8; 64]; - let mut lower: [u8; 32] = [0u8; 32]; - let mut upper: [u8; 32] = [0u8; 32]; - - h.update(secret_key); - hash.copy_from_slice(h.finalize().as_slice()); - - lower.copy_from_slice(&hash[00..32]); - upper.copy_from_slice(&hash[32..64]); - - lower[0] &= 248; - lower[31] &= 63; - lower[31] |= 64; + let hash = Sha512::default().chain_update(secret_key).finalize(); + // TODO: Use bytes.split_array_ref once it’s in MSRV. + let (lower, upper) = hash.split_at(32); + // The try_into here converts to fixed-size array ExpandedSecretKey { - key: Scalar::from_bits(lower), - nonce: upper, + key: Scalar::from_bits_clamped(lower.try_into().unwrap()), + nonce: upper.try_into().unwrap(), } } } diff --git a/src/verifying.rs b/src/verifying.rs index 726d971..48f8769 100644 --- a/src/verifying.rs +++ b/src/verifying.rs @@ -51,9 +51,8 @@ use crate::signing::*; /// considered unequal to the other equivalent encoding, despite the two representing the same /// point. More encoding details can be found /// [here](https://hdevalence.ca/blog/2020-10-04-its-25519am). -/// -/// If you don't care and/or don't want to deal with this, just make sure to use the -/// [`VerifyingKey::verify_strict`] function. +/// If you want to make sure that signatures produced with respect to those sorts of public keys +/// are rejected, use [`VerifyingKey::verify_strict`]. // Invariant: VerifyingKey.1 is always the decompression of VerifyingKey.0 #[derive(Copy, Clone, Default, Eq)] pub struct VerifyingKey(pub(crate) CompressedEdwardsY, pub(crate) EdwardsPoint); @@ -85,8 +84,8 @@ impl PartialEq for VerifyingKey { impl From<&ExpandedSecretKey> for VerifyingKey { /// Derive this public key from its corresponding `ExpandedSecretKey`. fn from(expanded_secret_key: &ExpandedSecretKey) -> VerifyingKey { - let mut bits: [u8; 32] = expanded_secret_key.key.to_bytes(); - VerifyingKey::mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(&mut bits) + let bits: [u8; 32] = expanded_secret_key.key.to_bytes(); + VerifyingKey::clamp_and_mul_base(bits) } } @@ -154,17 +153,10 @@ impl VerifyingKey { Ok(VerifyingKey(compressed, point)) } - /// Internal utility function for mangling the bits of a (formerly - /// mathematically well-defined) "scalar" and multiplying it to produce a - /// public key. - fn mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key( - bits: &mut [u8; 32], - ) -> VerifyingKey { - bits[0] &= 248; - bits[31] &= 127; - bits[31] |= 64; - - let scalar = Scalar::from_bits(*bits); + /// Internal utility function for clamping a scalar representation and multiplying by the + /// basepont to produce a public key. + fn clamp_and_mul_base(bits: [u8; 32]) -> VerifyingKey { + let scalar = Scalar::from_bits_clamped(bits); let point = EdwardsPoint::mul_base(&scalar); let compressed = point.compress(); @@ -198,17 +190,21 @@ impl VerifyingKey { // Helper function for verification. Computes the _expected_ R component of the signature. The // caller compares this to the real R component. If `context.is_some()`, this does the // prehashed variant of the computation using its contents. + // Note that this returns the compressed form of R and the caller does a byte comparison. This + // means that all our verification functions do not accept non-canonically encoded R values. + // See the validation criteria blog post for more details: + // https://hdevalence.ca/blog/2020-10-04-its-25519am #[allow(non_snake_case)] fn recompute_r( &self, context: Option<&[u8]>, signature: &InternalSignature, M: &[u8], - ) -> EdwardsPoint { + ) -> CompressedEdwardsY { let k = Self::compute_challenge(context, &signature.R, &self.0, M); let minus_A: EdwardsPoint = -self.1; // Recall the (non-batched) verification equation: -[k]A + [s]B = R - EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s) + EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s).compress() } /// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm. @@ -249,7 +245,7 @@ impl VerifyingKey { let message = prehashed_message.finalize(); let expected_R = self.recompute_r(Some(ctx), &signature, &message); - if expected_R.compress() == signature.R { + if expected_R == signature.R { Ok(()) } else { Err(InternalError::Verify.into()) @@ -337,7 +333,7 @@ impl VerifyingKey { } let expected_R = self.recompute_r(None, &signature, message); - if expected_R == signature_R { + if expected_R == signature.R { Ok(()) } else { Err(InternalError::Verify.into()) @@ -393,7 +389,7 @@ impl VerifyingKey { let message = prehashed_message.finalize(); let expected_R = self.recompute_r(Some(ctx), &signature, &message); - if expected_R == signature_R { + if expected_R == signature.R { Ok(()) } else { Err(InternalError::Verify.into()) @@ -412,7 +408,7 @@ impl Verifier for VerifyingKey { let signature = InternalSignature::try_from(signature)?; let expected_R = self.recompute_r(None, &signature, message); - if expected_R.compress() == signature.R { + if expected_R == signature.R { Ok(()) } else { Err(InternalError::Verify.into()) From 27ba9dd614c933220d7b4d2c286600f23d4a704a Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Sat, 21 Jan 2023 15:59:11 -0700 Subject: [PATCH 327/351] Bump `ed25519` crate dependency to v2.1 (#272) The original v2.0.0 release has been yanked. This release includes a different infallible parsing API which can be used to eliminate some usages of `unwrap()`. --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- src/signature.rs | 15 +-------------- tests/ed25519.rs | 6 +++--- tests/validation_criteria.rs | 2 +- 5 files changed, 8 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0fae5cc..6363a0b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -275,9 +275,9 @@ dependencies = [ [[package]] name = "ed25519" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3af5919f6d605315213c36abdd435562224665993b274912dee0d9a0e2fed8a" +checksum = "3cf420a7ec85d98495b0c34aa4a58ca117f982ffbece111aeb545160148d7010" dependencies = [ "pkcs8", "serde", diff --git a/Cargo.toml b/Cargo.toml index b8c25b6..49bf6ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ features = ["nightly", "batch", "pkcs8"] [dependencies] curve25519-dalek = { version = "=4.0.0-pre.5", default-features = false, features = ["digest"] } -ed25519 = { version = "2", default-features = false } +ed25519 = { version = "2.1", default-features = false } signature = { version = ">=2.0, <2.1", optional = true, default-features = false } sha2 = { version = "0.10", default-features = false } diff --git a/src/signature.rs b/src/signature.rs index c78779f..72b7b0e 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -101,16 +101,6 @@ fn check_scalar(bytes: [u8; 32]) -> Result { } impl InternalSignature { - /// Convert this `Signature` to a byte array. - #[inline] - pub fn as_bytes(&self) -> [u8; SIGNATURE_LENGTH] { - let mut signature_bytes: [u8; SIGNATURE_LENGTH] = [0u8; SIGNATURE_LENGTH]; - - signature_bytes[..32].copy_from_slice(&self.R.as_bytes()[..]); - signature_bytes[32..].copy_from_slice(&self.s.as_bytes()[..]); - signature_bytes - } - /// Construct a `Signature` from a slice of bytes. /// /// # Scalar Malleability Checking @@ -182,10 +172,7 @@ impl TryFrom<&ed25519::Signature> for InternalSignature { } impl From for ed25519::Signature { - #[allow(clippy::unwrap_used)] fn from(sig: InternalSignature) -> ed25519::Signature { - // This function only fails if the s half of the parsed input exceeds the scalar modulus. - // Since the bytes are coming straight from a Scalar, this is impossible. - ed25519::Signature::from_bytes(&sig.as_bytes()).unwrap() + ed25519::Signature::from_components(*sig.R.as_bytes(), *sig.s.as_bytes()) } } diff --git a/tests/ed25519.rs b/tests/ed25519.rs index 6775573..4ed0f72 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -496,7 +496,7 @@ mod serialisation { #[test] fn serialize_deserialize_signature_bincode() { - let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES).unwrap(); + let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES); let encoded_signature: Vec = bincode::serialize(&signature).unwrap(); let decoded_signature: Signature = bincode::deserialize(&encoded_signature).unwrap(); @@ -505,7 +505,7 @@ mod serialisation { #[test] fn serialize_deserialize_signature_json() { - let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES).unwrap(); + let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES); let encoded_signature = serde_json::to_string(&signature).unwrap(); let decoded_signature: Signature = serde_json::from_str(&encoded_signature).unwrap(); @@ -582,7 +582,7 @@ mod serialisation { #[test] fn serialize_signature_size() { - let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES).unwrap(); + let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES); assert_eq!( bincode::serialized_size(&signature).unwrap() as usize, SIGNATURE_LENGTH diff --git a/tests/validation_criteria.rs b/tests/validation_criteria.rs index 69cdad1..881108e 100644 --- a/tests/validation_criteria.rs +++ b/tests/validation_criteria.rs @@ -84,7 +84,7 @@ impl From for TestVector { let sig = { let mut buf = [0u8; 64]; buf.copy_from_slice(&tv.sig); - Signature::from_bytes(&buf).unwrap() + Signature::from_bytes(&buf) }; let msg = tv.msg.as_bytes().to_vec(); From 861784f57e8569e94018cfd07f7836f921c7fc33 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Thu, 26 Jan 2023 13:41:20 -0700 Subject: [PATCH 328/351] Add `Context` type (#273) * Add `Context` type Adds a generic type which can be used with `SigningKey` and `VerifyingKey` for storing a context string value along with the key for use with `DigestSigner` and `DigestVerifier`. * Added Context tests, docs, and re-exports * Added docs about SHA-512 for prehashing; re-re-exported Sha512 Co-authored-by: Tony Arcieri Co-authored-by: Michael Rosenberg --- src/context.rs | 107 +++++++++++++++++++++++++++++++++++++++++++++++ src/errors.rs | 2 - src/lib.rs | 10 ++++- src/signing.rs | 51 +++++++++++++++++++--- src/verifying.rs | 27 ++++++++++++ 5 files changed, 187 insertions(+), 10 deletions(-) create mode 100644 src/context.rs diff --git a/src/context.rs b/src/context.rs new file mode 100644 index 0000000..c026be5 --- /dev/null +++ b/src/context.rs @@ -0,0 +1,107 @@ +use crate::{InternalError, SignatureError}; + +/// Ed25519 contexts as used by Ed25519ph. +/// +/// Contexts are domain separator strings that can be used to isolate uses of +/// the algorithm between different protocols (which is very hard to reliably do +/// otherwise) and between different uses within the same protocol. +/// +/// To create a context, call either of the following: +/// +/// - [`SigningKey::with_context`](crate::SigningKey::with_context) +/// - [`VerifyingKey::with_context`](crate::VerifyingKey::with_context) +/// +/// For more information, see [RFC8032 § 8.3](https://www.rfc-editor.org/rfc/rfc8032#section-8.3). +/// +/// # Example +/// +#[cfg_attr(feature = "digest", doc = "```")] +#[cfg_attr(not(feature = "digest"), doc = "```ignore")] +/// # fn main() { +/// use ed25519_dalek::{Signature, SigningKey, VerifyingKey, Sha512}; +/// # use curve25519_dalek::digest::Digest; +/// # use rand::rngs::OsRng; +/// use ed25519_dalek::{DigestSigner, DigestVerifier}; +/// +/// # let mut csprng = OsRng; +/// # let signing_key = SigningKey::generate(&mut csprng); +/// # let verifying_key = signing_key.verifying_key(); +/// let context_str = b"Local Channel 3"; +/// let prehashed_message = Sha512::default().chain_update(b"Stay tuned for more news at 7"); +/// +/// // Signer +/// let signing_context = signing_key.with_context(context_str).unwrap(); +/// let signature = signing_context.sign_digest(prehashed_message.clone()); +/// +/// // Verifier +/// let verifying_context = verifying_key.with_context(context_str).unwrap(); +/// let verified: bool = verifying_context +/// .verify_digest(prehashed_message, &signature) +/// .is_ok(); +/// +/// # assert!(verified); +/// # } +/// ``` +#[derive(Clone, Debug)] +pub struct Context<'k, 'v, K> { + /// Key this context is being used with. + key: &'k K, + + /// Context value: a bytestring no longer than 255 octets. + value: &'v [u8], +} + +impl<'k, 'v, K> Context<'k, 'v, K> { + /// Maximum length of the context value in octets. + pub const MAX_LENGTH: usize = 255; + + /// Create a new Ed25519ph context. + pub(crate) fn new(key: &'k K, value: &'v [u8]) -> Result { + if value.len() <= Self::MAX_LENGTH { + Ok(Self { key, value }) + } else { + Err(SignatureError::from(InternalError::PrehashedContextLength)) + } + } + + /// Borrow the key. + pub fn key(&self) -> &'k K { + self.key + } + + /// Borrow the context string value. + pub fn value(&self) -> &'v [u8] { + self.value + } +} + +#[cfg(all(test, feature = "digest"))] +mod test { + use crate::{Signature, SigningKey, VerifyingKey}; + use curve25519_dalek::digest::Digest; + use ed25519::signature::{DigestSigner, DigestVerifier}; + use rand::rngs::OsRng; + use sha2::Sha512; + + #[test] + fn context_correctness() { + let mut csprng = OsRng; + let signing_key: SigningKey = SigningKey::generate(&mut csprng); + let verifying_key: VerifyingKey = signing_key.verifying_key(); + + let context_str = b"Local Channel 3"; + let prehashed_message = Sha512::default().chain_update(b"Stay tuned for more news at 7"); + + // Signer + let signing_context = signing_key.with_context(context_str).unwrap(); + let signature: Signature = signing_context.sign_digest(prehashed_message.clone()); + + // Verifier + let verifying_context = verifying_key.with_context(context_str).unwrap(); + let verified: bool = verifying_context + .verify_digest(prehashed_message, &signature) + .is_ok(); + + assert!(verified); + } +} diff --git a/src/errors.rs b/src/errors.rs index 7cba06d..aa4e5aa 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -48,7 +48,6 @@ pub(crate) enum InternalError { length_c: usize, }, /// An ed25519ph signature can only take up to 255 octets of context. - #[cfg(feature = "digest")] PrehashedContextLength, /// A mismatched (public, secret) key pair. MismatchedKeypair, @@ -77,7 +76,6 @@ impl Display for InternalError { {} has length {}, {} has length {}.", na, la, nb, lb, nc, lc ), - #[cfg(feature = "digest")] InternalError::PrehashedContextLength => write!( f, "An ed25519ph signature can only take up to 255 octets of context" diff --git a/src/lib.rs b/src/lib.rs index ead3d29..906ad05 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -73,7 +73,7 @@ //! # use ed25519_dalek::Signature; //! # use ed25519_dalek::Signer; //! use ed25519_dalek::{VerifyingKey, Verifier}; -//! # let mut csprng = OsRng{}; +//! # let mut csprng = OsRng; //! # let signing_key: SigningKey = SigningKey::generate(&mut csprng); //! # let message: &[u8] = b"This is a test of the tsunami alert system."; //! # let signature: Signature = signing_key.sign(message); @@ -97,7 +97,7 @@ //! # use rand::rngs::OsRng; //! # use ed25519_dalek::{SigningKey, Signature, Signer, VerifyingKey}; //! use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; -//! # let mut csprng = OsRng{}; +//! # let mut csprng = OsRng; //! # let signing_key: SigningKey = SigningKey::generate(&mut csprng); //! # let message: &[u8] = b"This is a test of the tsunami alert system."; //! # let signature: Signature = signing_key.sign(message); @@ -258,6 +258,7 @@ pub use ed25519; #[cfg(feature = "batch")] mod batch; mod constants; +mod context; mod errors; mod signature; mod signing; @@ -265,15 +266,20 @@ mod verifying; #[cfg(feature = "digest")] pub use curve25519_dalek::digest::Digest; +#[cfg(feature = "digest")] +pub use sha2::Sha512; #[cfg(feature = "batch")] pub use crate::batch::*; pub use crate::constants::*; +pub use crate::context::Context; pub use crate::errors::*; pub use crate::signing::*; pub use crate::verifying::*; // Re-export the `Signer` and `Verifier` traits from the `signature` crate +#[cfg(feature = "digest")] +pub use ed25519::signature::{DigestSigner, DigestVerifier}; pub use ed25519::signature::{Signer, Verifier}; pub use ed25519::Signature; diff --git a/src/signing.rs b/src/signing.rs index ad299c1..b6326a7 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -40,6 +40,7 @@ use signature::DigestSigner; use zeroize::{Zeroize, ZeroizeOnDrop}; use crate::constants::*; +use crate::context::Context; use crate::errors::*; use crate::signature::*; use crate::verifying::*; @@ -158,6 +159,15 @@ impl SigningKey { self.verifying_key } + /// Create a signing context that can be used for Ed25519ph with + /// [`DigestSigner`]. + pub fn with_context<'k, 'v>( + &'k self, + context_value: &'v [u8], + ) -> Result, SignatureError> { + Context::new(self, context_value) + } + /// Generate an ed25519 signing key. /// /// # Example @@ -200,9 +210,7 @@ impl SigningKey { /// /// # Inputs /// - /// * `prehashed_message` is an instantiated hash digest with 512-bits of - /// output which has had the message to be signed previously fed into its - /// state. + /// * `prehashed_message` is an instantiated SHA-512 digest of the message /// * `context` is an optional context string, up to 255 bytes inclusive, /// which may be used to provide additional domain separation. If not /// set, this will default to an empty string. @@ -211,6 +219,13 @@ impl SigningKey { /// /// An Ed25519ph [`Signature`] on the `prehashed_message`. /// + /// # Note + /// + /// The RFC only permits SHA-512 to be used for prehashing. This function technically works, + /// and is probably safe to use, with any secure hash function with 512-bit digests, but + /// anything outside of SHA-512 is NOT specification-compliant. We expose [`crate::Sha512`] for + /// user convenience. + /// /// # Examples /// #[cfg_attr(all(feature = "rand_core", feature = "digest"), doc = "```")] @@ -226,7 +241,7 @@ impl SigningKey { /// /// # #[cfg(feature = "std")] /// # fn main() { - /// let mut csprng = OsRng{}; + /// let mut csprng = OsRng; /// let signing_key: SigningKey = SigningKey::generate(&mut csprng); /// let message: &[u8] = b"All I want is to pet all of the dogs."; /// @@ -274,7 +289,7 @@ impl SigningKey { /// # use rand::rngs::OsRng; /// # /// # fn do_test() -> Result { - /// # let mut csprng = OsRng{}; + /// # let mut csprng = OsRng; /// # let signing_key: SigningKey = SigningKey::generate(&mut csprng); /// # let message: &[u8] = b"All I want is to pet all of the dogs."; /// # let mut prehashed: Sha512 = Sha512::new(); @@ -348,7 +363,7 @@ impl SigningKey { /// use rand::rngs::OsRng; /// /// # fn do_test() -> Result<(), SignatureError> { - /// let mut csprng = OsRng{}; + /// let mut csprng = OsRng; /// let signing_key: SigningKey = SigningKey::generate(&mut csprng); /// let message: &[u8] = b"All I want is to pet all of the dogs."; /// @@ -485,6 +500,12 @@ impl Signer for SigningKey { } /// Equivalent to [`SigningKey::sign_prehashed`] with `context` set to [`None`]. +/// +/// # Note +/// +/// The RFC only permits SHA-512 to be used for prehashing. This function technically works, and is +/// probably safe to use, with any secure hash function with 512-bit digests, but anything outside +/// of SHA-512 is NOT specification-compliant. We expose [`crate::Sha512`] for user convenience. #[cfg(feature = "digest")] impl DigestSigner for SigningKey where @@ -495,6 +516,24 @@ where } } +/// Equivalent to [`SigningKey::sign_prehashed`] with `context` set to [`Some`] +/// containing `self.value()`. +/// +/// # Note +/// +/// The RFC only permits SHA-512 to be used for prehashing. This function technically works, and is +/// probably safe to use, with any secure hash function with 512-bit digests, but anything outside +/// of SHA-512 is NOT specification-compliant. We expose [`crate::Sha512`] for user convenience. +#[cfg(feature = "digest")] +impl DigestSigner for Context<'_, '_, SigningKey> +where + D: Digest, +{ + fn try_sign_digest(&self, msg_digest: D) -> Result { + self.key().sign_prehashed(msg_digest, Some(self.value())) + } +} + impl Verifier for SigningKey { /// Verify a signature on a message with this signing key's public key. fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> { diff --git a/src/verifying.rs b/src/verifying.rs index 48f8769..e57f2e9 100644 --- a/src/verifying.rs +++ b/src/verifying.rs @@ -38,6 +38,7 @@ use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes}; use signature::DigestVerifier; use crate::constants::*; +use crate::context::Context; use crate::errors::*; use crate::signature::*; use crate::signing::*; @@ -153,6 +154,15 @@ impl VerifyingKey { Ok(VerifyingKey(compressed, point)) } + /// Create a verifying context that can be used for Ed25519ph with + /// [`DigestVerifier`]. + pub fn with_context<'k, 'v>( + &'k self, + context_value: &'v [u8], + ) -> Result, SignatureError> { + Context::new(self, context_value) + } + /// Internal utility function for clamping a scalar representation and multiplying by the /// basepont to produce a public key. fn clamp_and_mul_base(bits: [u8; 32]) -> VerifyingKey { @@ -431,6 +441,23 @@ where } } +/// Equivalent to [`VerifyingKey::verify_prehashed`] with `context` set to [`Some`] +/// containing `self.value()`. +#[cfg(feature = "digest")] +impl DigestVerifier for Context<'_, '_, VerifyingKey> +where + D: Digest, +{ + fn verify_digest( + &self, + msg_digest: D, + signature: &ed25519::Signature, + ) -> Result<(), SignatureError> { + self.key() + .verify_prehashed(msg_digest, Some(self.value()), signature) + } +} + impl TryFrom<&[u8]> for VerifyingKey { type Error = SignatureError; From 928d6d15f8cdce9e2fa6d3ab9f63361e663f245b Mon Sep 17 00:00:00 2001 From: "pinkforest(she/her)" <36498018+pinkforest@users.noreply.github.com> Date: Fri, 27 Jan 2023 17:06:24 +1100 Subject: [PATCH 329/351] Docs.rs + README changes for 2.x (#241) --- Cargo.toml | 10 +- README.md | 223 +- {res => docs/assets}/ed25519-malleability.png | Bin docs/assets/rustdoc-include-katex-header.html | 12 + res/batch-violin-benchmark.svg | 4251 ----------------- 5 files changed, 121 insertions(+), 4375 deletions(-) rename {res => docs/assets}/ed25519-malleability.png (100%) create mode 100644 docs/assets/rustdoc-include-katex-header.html delete mode 100644 res/batch-violin-benchmark.svg diff --git a/Cargo.toml b/Cargo.toml index 49bf6ca..ced06f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,13 +14,11 @@ description = "Fast and efficient ed25519 EdDSA key generations, signing, and ve exclude = [ ".gitignore", "TESTVECTORS", "VALIDATIONVECTORS", "res/*" ] rust-version = "1.60" -[badges] -travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master"} - [package.metadata.docs.rs] -# Disabled for now since this is borked; tracking https://github.com/rust-lang/docs.rs/issues/302 -# rustdoc-args = ["--html-in-header", ".cargo/registry/src/github.com-1ecc6299db9ec823/curve25519-dalek-0.13.2/rustdoc-include-katex-header.html"] -rustdoc-args = ["--cfg", "docsrs"] +rustdoc-args = [ + "--html-in-header", "docs/assets/rustdoc-include-katex-header.html", + "--cfg", "docsrs", +] features = ["nightly", "batch", "pkcs8"] [dependencies] diff --git a/README.md b/README.md index d9b6d78..44123d1 100644 --- a/README.md +++ b/README.md @@ -3,117 +3,148 @@ Fast and efficient Rust implementation of ed25519 key generation, signing, and verification in Rust. -# Documentation +# Use -Documentation is available [here](https://docs.rs/ed25519-dalek). - -# Installation - -To install, add the following to your project's `Cargo.toml`: +To use, add the following to your project's `Cargo.toml`: ```toml [dependencies.ed25519-dalek] version = "1" ``` -# Minimum Supported Rust Version +# Feature Flags -This crate requires Rust 1.60.0 at a minimum. Older 1.x releases of this crate supported an MSRV of 1.41. +This crate is `#[no_std]` compatible with `default-features = false` -In the future, MSRV changes will be accompanied by a minor version bump. +| Feature | Default? | Description | +| :--- | :--- | :--- | +| `alloc` | ✓ | Enables features that require dynamic heap allocation | +| `std` | ✓ | std::error::Error types | +| `zeroize` | ✓ | Enables `Zeroize` for `SigningKey` | +| `asm` | | Assembly implementation of SHA-2 compression functions | +| `batch` | | Batch verification. Requires `alloc` | +| `digest` | | TODO | +| `legacy_compatibility` | | See: A Note on Signature Malleability | +| `pkcs8` | | PKCS#8 Support | +| `pem` | | PEM Support | +| `rand_core` | | TODO | -# Changelog +# Major Changes See [CHANGELOG.md](CHANGELOG.md) for a list of changes made in past version of this crate. -# Benchmarks +## 2.0.0 Breaking Changes -On an Intel Skylake i9-7900X running at 3.30 GHz, without TurboBoost, this code achieves -the following performance benchmarks: +* Update the MSRV from 1.41 to 1.60 +* `batch` is now `batch_deterministic` +* Removed `ExpandedSecretKey` API +* [curve25519-backend selection] is more automatic - ∃!isisⒶmistakenot:(master *=)~/code/rust/ed25519-dalek ∴ cargo bench - Compiling ed25519-dalek v0.7.0 (file:///home/isis/code/rust/ed25519-dalek) - Finished release [optimized] target(s) in 3.11s - Running target/release/deps/ed25519_benchmarks-721332beed423bce +[curve25519-backend selection]: https://github.com/dalek-cryptography/curve25519-dalek/#backends - Ed25519 signing time: [15.617 us 15.630 us 15.647 us] - Ed25519 signature verification time: [45.930 us 45.968 us 46.011 us] - Ed25519 keypair generation time: [15.440 us 15.465 us 15.492 us] +# Documentation -By enabling the avx2 backend (on machines with compatible microarchitectures), -the performance for signature verification is greatly improved: +Documentation is available [here](https://docs.rs/ed25519-dalek). - ∃!isisⒶmistakenot:(master *=)~/code/rust/ed25519-dalek ∴ export RUSTFLAGS=-Ctarget_cpu=native - ∃!isisⒶmistakenot:(master *=)~/code/rust/ed25519-dalek ∴ cargo bench --features=avx2_backend - Compiling ed25519-dalek v0.7.0 (file:///home/isis/code/rust/ed25519-dalek) - Finished release [optimized] target(s) in 4.28s - Running target/release/deps/ed25519_benchmarks-e4866664de39c84d - Ed25519 signing time: [15.923 us 15.945 us 15.967 us] - Ed25519 signature verification time: [33.382 us 33.411 us 33.445 us] - Ed25519 keypair generation time: [15.246 us 15.260 us 15.275 us] +# Policies -In comparison, the equivalent package in Golang performs as follows: +All on-by-default features of this library are covered by semantic versioning (SemVer) - ∃!isisⒶmistakenot:(master *=)~/code/go/src/github.com/agl/ed25519 ∴ go test -bench . - BenchmarkKeyGeneration 30000 47007 ns/op - BenchmarkSigning 30000 48820 ns/op - BenchmarkVerification 10000 119701 ns/op - ok github.com/agl/ed25519 5.775s +SemVer exemptions are outlined below for MSRV and public API. -Making key generation and signing a rough average of 2x faster, and -verification 2.5-3x faster depending on the availability of avx2. Of course, this -is just my machine, and these results—nowhere near rigorous—should be taken -with a handful of salt. +## Minimum Supported Rust Version -Translating to a rough cycle count: we multiply by a factor of 3.3 to convert -nanoseconds to cycles per second on a 3300 Mhz CPU, that's 110256 cycles for -verification and 52618 for signing, which is competitive with hand-optimised -assembly implementations. +| Releases | MSRV | +| :--- | :--- | +| 2.x | 1.60 | +| 1.x | 1.41 | + +MSRV changes will be accompanied by a minor version bump. + +## Public API SemVer Exemptions + +Breaking changes to SemVer exempted components affecting the public API will be accompanied by some version bump. + +Below are the specific policies: + +| Releases | Public API Component(s) | Policy | +| :--- | :--- | :--- | +| 2.x | Dependencies `digest`, `pkcs8` and `rand_core` | Minor SemVer bump | + +## Safety + +This crate does not require any unsafe and forbids all unsafe in-crate outside tests. + +# Performance + +Performance is a secondary goal behind correctness, safety, and clarity, but we +aim to be competitive with other implementations. + +## Benchmarks + +Benchmarks are run using [criterion.rs](https://github.com/japaric/criterion.rs): + +```sh +cargo bench --features "batch" +# Uses avx2 or ifma only if compiled for an appropriate target. +export RUSTFLAGS='--cfg curve25519_dalek_backend="simd" -C target_cpu=native' +cargo +nightly bench --features "batch" +``` + +On an Intel 10700K running at stock comparing between the `curve25519-dalek` backends. + +| Benchmark | u64 | simd +avx2 | fiat | +| :--- | :---- | :--- | :--- | +| signing | 15.017 µs | 13.906 µs -7.3967% | 15.877 µs +14.188% | +| signature verification | 40.144 µs | 25.963 µs -35.603% | 42.118 µs +62.758% | +| strict signature verification | 41.334 µs | 27.874 µs -32.660% | 43.985 µs +57.763% | +| batch signature verification/4 | 109.44 µs | 81.778 µs -25.079% | 117.80 µs +43.629% | +| batch signature verification/8 | 182.75 µs | 138.40 µs -23.871% | 195.86 µs +40.665% | +| batch signature verification/16 | 328.67 µs | 251.39 µs -23.744% | 351.55 µs +39.901% | +| batch signature verification/32 | 619.49 µs | 477.36 µs -23.053% | 669.41 µs +39.966% | +| batch signature verification/64 | 1.2136 ms | 936.85 µs -22.543% | 1.3028 ms +38.808% | +| batch signature verification/96 | 1.8677 ms | 1.2357 ms -33.936% | 2.0552 ms +66.439% | +| batch signature verification/128| 2.3281 ms | 1.5795 ms -31.996% | 2.5596 ms +61.678% | +| batch signature verification/256| 4.1868 ms | 2.8864 ms -31.061% | 4.6494 ms +61.081% | +| keypair generation | 13.973 µs | 13.108 µs -6.5062% | 15.099 µs +15.407% | Additionally, if you're using a CSPRNG from the `rand` crate, the `nightly` feature will enable `u128`/`i128` features there, resulting in potentially faster performance. -If your protocol or application is able to batch signatures for verification, -the `verify_batch()` function has greatly improved performance. On the -aforementioned Intel Skylake i9-7900X, verifying a batch of 96 signatures takes -1.7673ms. That's 18.4094us, or roughly 60750 cycles, per signature verification, -more than double the speed of batch verification given in the original paper -(this is likely not a fair comparison as that was a Nehalem machine). -The numbers after the `/` in the test name refer to the size of the batch: +## Batch Performance - ∃!isisⒶmistakenot:(master *=)~/code/rust/ed25519-dalek ∴ export RUSTFLAGS=-Ctarget_cpu=native - ∃!isisⒶmistakenot:(master *=)~/code/rust/ed25519-dalek ∴ cargo bench --features=avx2_backend batch - Compiling ed25519-dalek v0.8.0 (file:///home/isis/code/rust/ed25519-dalek) - Finished release [optimized] target(s) in 34.16s - Running target/release/deps/ed25519_benchmarks-cf0daf7d68fc71b6 - Ed25519 batch signature verification/4 time: [105.20 us 106.04 us 106.99 us] - Ed25519 batch signature verification/8 time: [178.66 us 179.01 us 179.39 us] - Ed25519 batch signature verification/16 time: [325.65 us 326.67 us 327.90 us] - Ed25519 batch signature verification/32 time: [617.96 us 620.74 us 624.12 us] - Ed25519 batch signature verification/64 time: [1.1862 ms 1.1900 ms 1.1943 ms] - Ed25519 batch signature verification/96 time: [1.7611 ms 1.7673 ms 1.7742 ms] - Ed25519 batch signature verification/128 time: [2.3320 ms 2.3376 ms 2.3446 ms] - Ed25519 batch signature verification/256 time: [5.0124 ms 5.0290 ms 5.0491 ms] +If your protocol or application is able to batch signatures for verification, +the `verify_batch()` function has greatly improved performance. As you can see, there's an optimal batch size for each machine, so you'll likely -want to test the benchmarks on your target CPU to discover the best size. For -this machine, around 100 signatures per batch is the optimum: +want to test the benchmarks on your target CPU to discover the best size. -![](https://github.com/dalek-cryptography/ed25519-dalek/blob/master/res/batch-violin-benchmark.svg) +## (Micro)Architecture Specific Backends -Additionally, thanks to Rust, this implementation has both type and memory -safety. It's also easily readable by a much larger set of people than those who -can read qhasm, making it more readily and more easily auditable. We're of -the opinion that, ultimately, these features—combined with speed—are more -valuable than simply cycle counts alone. +`ed25519-dalek` uses the backends from the `curve25519-dalek` crate. + +By default the serial backend is used and depending on the target +platform either the 32 bit or the 64 bit serial formula is automatically used. + +To address variety of usage scenarios various backends are available that +include hardware optimisations as well as a formally verified fiat crypto +backend that does not use any hardware optimisations. + +These backends can be overriden with various configuration predicates (cfg) + +Please see the [curve25519_dalek backend documentation](https://docs.rs/curve25519-dalek/latest/curve25519_dalek). + +# Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md) # A Note on Signature Malleability The signatures produced by this library are malleable, as discussed in [the original paper](https://ed25519.cr.yp.to/ed25519-20110926.pdf): -![](https://github.com/dalek-cryptography/ed25519-dalek/blob/master/res/ed25519-malleability.png) +![](https://cdn.jsdelivr.net/gh/dalek-cryptography/ed25519-dalek/docs/assets/ed25519-malleability.png) While the scalar component of our `Signature` struct is strictly *not* malleable, because reduction checks are put in place upon `Signature` @@ -175,51 +206,7 @@ prime order, but having a small cofactor of 8. If you wish to also eliminate this source of signature malleability, please review the -[documentation for the `verify_strict()` function](https://doc.dalek.rs/ed25519_dalek/struct.PublicKey.html#method.verify_strict). - -# Features - -## #![no_std] - -This library aims is fully `#![no_std]` compliant. No features need to be -enabled or disabled to suppose no-std. - -## Nightly Compilers - -To cause your application to build `ed25519-dalek` with the nightly feature -enabled by default, instead do: - -```toml -[dependencies.ed25519-dalek] -version = "1" -features = ["nightly"] -``` - -To cause your application to instead build with the nightly feature enabled -when someone builds with `cargo build --features="nightly"` add the following -to the `Cargo.toml`: - -```toml -[features] -nightly = ["ed25519-dalek/nightly"] -``` - -## Serde - -To enable [serde](https://serde.rs) support, build `ed25519-dalek` with the -`serde` feature. - -## (Micro)Architecture Specific Backends - -By default, `ed25519-dalek` builds against `curve25519-dalek`'s `u64_backend` -feature, which uses Rust's `i128` feature to achieve roughly double the speed as -the `u32_backend` feature. When targetting 32-bit systems, however, you'll -likely want to compile with `cargo build --no-default-features ---features="u32_backend"`. If you're building for a machine with avx2 -instructions, there's also the experimental `simd_backend`s, currently -comprising either avx2 or avx512 backends. To use them, compile with -`RUSTFLAGS="-C target_cpu=native" cargo build --no-default-features ---features="simd_backend"` +[documentation for the `verify_strict()` function](https://docs.rs/ed25519-dalek/latest/ed25519_dalek/struct.PublicKey.html#method.verify_strict). ## Batch Signature Verification diff --git a/res/ed25519-malleability.png b/docs/assets/ed25519-malleability.png similarity index 100% rename from res/ed25519-malleability.png rename to docs/assets/ed25519-malleability.png diff --git a/docs/assets/rustdoc-include-katex-header.html b/docs/assets/rustdoc-include-katex-header.html new file mode 100644 index 0000000..d240432 --- /dev/null +++ b/docs/assets/rustdoc-include-katex-header.html @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/res/batch-violin-benchmark.svg b/res/batch-violin-benchmark.svg deleted file mode 100644 index 418fa1d..0000000 --- a/res/batch-violin-benchmark.svg +++ /dev/null @@ -1,4251 +0,0 @@ - - - -Gnuplot -Produced by GNUPLOT 5.0 patchlevel 6 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Ed25519 batch signature verification/256 - - - - - Ed25519 batch signature verification/128 - - - - - Ed25519 batch signature verification/96 - - - - - Ed25519 batch signature verification/64 - - - - - Ed25519 batch signature verification/32 - - - - - Ed25519 batch signature verification/16 - - - - - Ed25519 batch signature verification/8 - - - - - Ed25519 batch signature verification/4 - - - - - - - - - - - - - 0 - - - - - - - - - - - - - 1 - - - - - - - - - - - - - 2 - - - - - - - - - - - - - 3 - - - - - - - - - - - - - 4 - - - - - - - - - - - - - 5 - - - - - - - - - - - - - 6 - - - - - - - - - Input - - - - - Average time (ms) - - - - - Ed25519 batch signature verification: Violin plot - - - PDF - - - PDF - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - gnuplot_plot_2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - gnuplot_plot_3 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - gnuplot_plot_4 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - gnuplot_plot_5 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - gnuplot_plot_6 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - gnuplot_plot_7 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - gnuplot_plot_8 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 1b86ff1d3eff72cac648a9024e1f54c5063cc415 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Sat, 28 Jan 2023 16:56:35 -0700 Subject: [PATCH 330/351] Bump `curve25519-dalek` to v4.0.0-rc.0 (#276) Eliminates the `patch.crates-io` directive by using the latest RC release of `curve25519-dalek` on crates.io --- Cargo.lock | 5 +++-- Cargo.toml | 7 ++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6363a0b..c05130c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -239,8 +239,9 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.0.0-pre.5" -source = "git+https://github.com/dalek-cryptography/curve25519-dalek.git#3effd73307a606e44469a425974f0b7b0eb85899" +version = "4.0.0-rc.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da00a7a9a4eb92a0a0f8e75660926d48f0d0f3c537e455c457bcdaa1e16b1ac" dependencies = [ "cfg-if", "digest", diff --git a/Cargo.toml b/Cargo.toml index ced06f8..28002c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ rustdoc-args = [ features = ["nightly", "batch", "pkcs8"] [dependencies] -curve25519-dalek = { version = "=4.0.0-pre.5", default-features = false, features = ["digest"] } +curve25519-dalek = { version = "=4.0.0-rc.0", default-features = false, features = ["digest"] } ed25519 = { version = "2.1", default-features = false } signature = { version = ">=2.0, <2.1", optional = true, default-features = false } sha2 = { version = "0.10", default-features = false } @@ -35,7 +35,7 @@ serde_bytes = { version = "0.11", optional = true } zeroize = { version = "1.5", default-features = false, optional = true } [dev-dependencies] -curve25519-dalek = { version = "=4.0.0-pre.5", default-features = false, features = ["digest", "rand_core"] } +curve25519-dalek = { version = "=4.0.0-rc.0", default-features = false, features = ["digest", "rand_core"] } hex = "0.4" bincode = "1.0" serde_json = "1.0" @@ -67,6 +67,3 @@ pem = ["alloc", "ed25519/pem", "pkcs8"] rand_core = ["dep:rand_core"] serde = ["dep:serde", "serde_bytes", "ed25519/serde"] zeroize = ["dep:zeroize", "curve25519-dalek/zeroize"] - -[patch.crates-io.curve25519-dalek] -git = "https://github.com/dalek-cryptography/curve25519-dalek.git" From 5190ad6df87ca3520042e1e5469ec9f0a6552a1b Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Tue, 31 Jan 2023 16:23:38 -0500 Subject: [PATCH 331/351] Impl `VerifyingKey::is_weak` (#277) * Implemented VerifyingKey::is_weak * Added unit test for VerifyingKey::is_weak --- src/verifying.rs | 9 +++++++++ tests/ed25519.rs | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/src/verifying.rs b/src/verifying.rs index e57f2e9..2f207fe 100644 --- a/src/verifying.rs +++ b/src/verifying.rs @@ -163,6 +163,15 @@ impl VerifyingKey { Context::new(self, context_value) } + /// Returns whether this is a _weak_ public key, i.e., if this public key has low order. + /// + /// A weak public key can be used to generate a siganture that's valid for almost every + /// message. [`Self::verify_strict`] denies weak keys, but if you want to check for this + /// property before verification, then use this method. + pub fn is_weak(&self) -> bool { + self.1.is_small_order() + } + /// Internal utility function for clamping a scalar representation and multiplying by the /// basepont to produce a public key. fn clamp_and_mul_base(bits: [u8; 32]) -> VerifyingKey { diff --git a/tests/ed25519.rs b/tests/ed25519.rs index 4ed0f72..a3a7ebc 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -228,6 +228,9 @@ mod vectors { assert!(vk.verify(message1, &sig).is_ok()); assert!(vk.verify(message2, &sig).is_ok()); + // Check that this public key appears as weak + assert!(vk.is_weak()); + // Now check that the sigs fail under verify_strict. This is because verify_strict rejects // small order pubkeys. assert!(vk.verify_strict(message1, &sig).is_err()); @@ -306,6 +309,9 @@ mod integrations { good_sig = signing_key.sign(&good); bad_sig = signing_key.sign(&bad); + // Check that an honestly generated public key is not weak + assert!(!verifying_key.is_weak()); + assert!( signing_key.verify(&good, &good_sig).is_ok(), "Verification of a valid signature failed!" From 783b6e81c4bb882507259ca71bd0f759e16b4fcc Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Thu, 2 Feb 2023 17:07:56 -0500 Subject: [PATCH 332/351] README changes for 2.0 (#275) * Added items to changelog for 2.0 release * Removed unnecessary uses of std in doctests * Gated `Context` behind `digest` * Fixed noncompiling doctest when only `digest` is enabled * README feature flag list mostly done * Copied changelog to readme * Redid the malleability section in README * Added CONTRIBUTING.md * Bumped version number to 2.0.0-pre.0; small changes to README * Updated changelog for #277 * Added pem feature description Co-authored-by: pinkforest(she/her) <36498018+pinkforest@users.noreply.github.com> --- CHANGELOG.md | 34 +++++++-- CONTRIBUTING.md | 19 +++++ Cargo.lock | 2 +- Cargo.toml | 9 ++- README.md | 181 ++++++++++++++++++++--------------------------- src/context.rs | 7 +- src/errors.rs | 2 + src/lib.rs | 5 +- src/signing.rs | 28 ++------ src/verifying.rs | 4 +- 10 files changed, 149 insertions(+), 142 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/CHANGELOG.md b/CHANGELOG.md index efae846..da1d2bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,33 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +Entries are listed in reverse chronological order per undeprecated major series. -### Changes -* Bumped MSRV from 1.41 to 1.60.0 -* Removed `ExpandedSecretKey` API ((#205)[https://github.com/dalek-cryptography/ed25519-dalek/pull/205]) -* Implemented `Clone` for `SigningKey` +# 2.x series + +## 2.0.0 + +### Breaking changes + +* Bump MSRV from 1.41 to 1.60.0 +* Bump Rust edition +* Bump `signature` dependency to 2.0 +* Make [curve25519-backend selection](https://github.com/dalek-cryptography/curve25519-dalek/#backends) more automatic +* Make `digest` an optional dependency +* Make `zeroize` an optional dependency +* Make `rand_core` an optional dependency +* Make all batch verification deterministic remove `batch_deterministic` ([#256](https://github.com/dalek-cryptography/ed25519-dalek/pull/256)) +* Remove `ExpandedSecretKey` API ((#205)[https://github.com/dalek-cryptography/ed25519-dalek/pull/205]) +* Rename `Keypair` → `SigningKey` and `PublicKey` → `VerifyingKey` + +### Other changes + +* Add `Context` type for prehashed signing +* Add `VerifyingKey::{verify_prehash_strict, is_weak}` +* Add `pkcs` feature to support PKCS #8 (de)serialization of `SigningKey` and `VerifyingKey` +* Add `fast` feature to include basepoint tables +* Add tests for validation criteria +* Impl `DigestSigner`/`DigestVerifier` for `SigningKey`/`VerifyingKey`, respectively +* Impl `Hash` for `VerifyingKey` +* Impl `Clone`, `Drop`, and `ZeroizeOnDrop` for `SigningKey` +* Remove `rand` dependency diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..0092a5e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,19 @@ +# Contributing to ed25519-dalek + +If you have questions or comments, please feel free to email the +authors. + +For feature requests, suggestions, and bug reports, please open an issue on +[our Github](https://github.com/dalek-cryptography/ed25519-dalek). (Or, send us +an email if you're opposed to using Github for whatever reason.) + +Patches are welcomed as pull requests on +[our Github](https://github.com/dalek-cryptography/ed25519-dalek), as well as by +email (preferably sent to all of the authors listed in `Cargo.toml`). + +All issues on ed25519-dalek are mentored, if you want help with a bug just +ask @tarcieri or @rozbb. + +Some issues are easier than others. The `easy` label can be used to find the +easy issues. If you want to work on an issue, please leave a comment so that we +can assign it to you! diff --git a/Cargo.lock b/Cargo.lock index c05130c..24f3c57 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -287,7 +287,7 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "1.0.1" +version = "2.0.0-pre.0" dependencies = [ "bincode", "criterion", diff --git a/Cargo.toml b/Cargo.toml index 28002c5..682c01e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,15 @@ [package] name = "ed25519-dalek" -version = "1.0.1" +version = "2.0.0-pre.0" edition = "2021" -authors = ["isis lovecruft "] +authors = [ + "isis lovecruft ", + "Tony Arcieri ", + "Michael Rosenberg " +] readme = "README.md" license = "BSD-3-Clause" repository = "https://github.com/dalek-cryptography/ed25519-dalek" -homepage = "https://dalek.rs" documentation = "https://docs.rs/ed25519-dalek" keywords = ["cryptography", "ed25519", "curve25519", "signature", "ECC"] categories = ["cryptography", "no-std"] diff --git a/README.md b/README.md index 44123d1..a0acd3f 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,67 @@ -# ed25519-dalek [![](https://img.shields.io/crates/v/ed25519-dalek.svg)](https://crates.io/crates/ed25519-dalek) [![](https://docs.rs/ed25519-dalek/badge.svg)](https://docs.rs/ed25519-dalek) [![](https://travis-ci.org/dalek-cryptography/ed25519-dalek.svg?branch=master)](https://travis-ci.org/dalek-cryptography/ed25519-dalek?branch=master) +# ed25519-dalek [![](https://img.shields.io/crates/v/ed25519-dalek.svg)](https://crates.io/crates/ed25519-dalek) [![](https://docs.rs/ed25519-dalek/badge.svg)](https://docs.rs/ed25519-dalek) [![Rust](https://github.com/dalek-cryptography/ed25519-dalek/actions/workflows/rust.yml/badge.svg?branch=main)](https://github.com/dalek-cryptography/ed25519-dalek/actions/workflows/rust.yml) Fast and efficient Rust implementation of ed25519 key generation, signing, and -verification in Rust. +verification. # Use -To use, add the following to your project's `Cargo.toml`: +## Stable +To import `ed25519-dalek`, add the following to the dependencies section of +your project's `Cargo.toml`: ```toml -[dependencies.ed25519-dalek] -version = "1" +ed25519-dalek = "1" +``` + +## Beta + +To use the latest prerelease (see changes [below](#breaking-changes-in-200)), +use the following line in your project's `Cargo.toml`: +```toml +ed25519-dalek = "2.0.0-pre.0" ``` # Feature Flags -This crate is `#[no_std]` compatible with `default-features = false` +This crate is `#[no_std]` compatible with `default-features = false`. | Feature | Default? | Description | | :--- | :--- | :--- | -| `alloc` | ✓ | Enables features that require dynamic heap allocation | -| `std` | ✓ | std::error::Error types | -| `zeroize` | ✓ | Enables `Zeroize` for `SigningKey` | -| `asm` | | Assembly implementation of SHA-2 compression functions | -| `batch` | | Batch verification. Requires `alloc` | -| `digest` | | TODO | -| `legacy_compatibility` | | See: A Note on Signature Malleability | -| `pkcs8` | | PKCS#8 Support | -| `pem` | | PEM Support | -| `rand_core` | | TODO | +| `alloc` | ✓ | When `pkcs8` is enabled, implements `EncodePrivateKey`/`EncodePublicKey` for `SigningKey`/`VerifyingKey`, respectively. | +| `std` | ✓ | Implements `std::error::Error` for `SignatureError`. Also enables `alloc`. | +| `zeroize` | ✓ | Implements `Zeroize` and `ZeroizeOnDrop` for `SigningKey` | +| `rand_core` | | Enables `SigningKey::generate` | +| `batch` | | Enables `verify_batch` for verifying many signatures quickly. Also enables `rand_core`. | +| `digest` | | Enables `Context`, `SigningKey::{with_context, sign_prehashed}` and `VerifyingKey::{with_context, verify_prehashed, verify_prehashed_strict}` for Ed25519ph prehashed signatures | +| `asm` | | Enables assembly optimizations in the SHA-512 compression functions | +| `pkcs8` | | Enables [PKCS#8](https://en.wikipedia.org/wiki/PKCS_8) serialization/deserialization for `SigningKey` and `VerifyingKey` | +| `pem` | | Enables PEM serialization support for PKCS#8 private keys and SPKI public keys. Also enables `alloc`. | +| `legacy_compatibility` | | **Unsafe:** Disables certain signature checks. See [below](#malleability-and-the-legacy_compatibility-feature) | # Major Changes See [CHANGELOG.md](CHANGELOG.md) for a list of changes made in past version of this crate. -## 2.0.0 Breaking Changes +## Breaking Changes in 2.0.0 -* Update the MSRV from 1.41 to 1.60 -* `batch` is now `batch_deterministic` -* Removed `ExpandedSecretKey` API -* [curve25519-backend selection] is more automatic - -[curve25519-backend selection]: https://github.com/dalek-cryptography/curve25519-dalek/#backends +* Bump MSRV from 1.41 to 1.60.0 +* Bump Rust edition +* Bump `signature` dependency to 2.0 +* Make [curve25519-backend selection](https://github.com/dalek-cryptography/curve25519-dalek/#backends) more automatic +* Make `digest` an optional dependency +* Make `zeroize` an optional dependency +* Make `rand_core` an optional dependency +* Make all batch verification deterministic remove `batch_deterministic` ([#256](https://github.com/dalek-cryptography/ed25519-dalek/pull/256)) +* Remove `ExpandedSecretKey` API ((#205)[https://github.com/dalek-cryptography/ed25519-dalek/pull/205]) +* Rename `Keypair` → `SigningKey` and `PublicKey` → `VerifyingKey` # Documentation Documentation is available [here](https://docs.rs/ed25519-dalek). -# Policies - -All on-by-default features of this library are covered by semantic versioning (SemVer) +# Compatibility Policies +All on-by-default features of this library are covered by [semantic versioning](https://semver.org/spec/v2.0.0.html) (SemVer). SemVer exemptions are outlined below for MSRV and public API. ## Minimum Supported Rust Version @@ -59,11 +71,11 @@ SemVer exemptions are outlined below for MSRV and public API. | 2.x | 1.60 | | 1.x | 1.41 | -MSRV changes will be accompanied by a minor version bump. +From 2.x and on, MSRV changes will be accompanied by a minor version bump. ## Public API SemVer Exemptions -Breaking changes to SemVer exempted components affecting the public API will be accompanied by some version bump. +Breaking changes to SemVer-exempted components affecting the public API will be accompanied by some version bump. Below are the specific policies: @@ -71,9 +83,11 @@ Below are the specific policies: | :--- | :--- | :--- | | 2.x | Dependencies `digest`, `pkcs8` and `rand_core` | Minor SemVer bump | -## Safety +# Safety -This crate does not require any unsafe and forbids all unsafe in-crate outside tests. +`ed25519-dalek` is designed to prevent misuse. Signing is constant-time, all signing keys are zeroed when they go out of scope (unless `zeroize` is disabled), detached public keys [cannot](https://github.com/MystenLabs/ed25519-unsafe-libs/blob/main/README.md) be used for signing, and extra functions like [`VerifyingKey::verify_strict`](#weak-key-forgery-and-verify_strict) are made available to avoid known gotchas. + +Further, this crate has no—and in fact forbids—unsafe code. You can opt in to using some highly optimized unsafe code that resides in `curve25519-dalek`, though. See [below](#microarchitecture-specific-backends) for more information on backend selection. # Performance @@ -108,110 +122,65 @@ On an Intel 10700K running at stock comparing between the `curve25519-dalek` bac | batch signature verification/256| 4.1868 ms | 2.8864 ms -31.061% | 4.6494 ms +61.081% | | keypair generation | 13.973 µs | 13.108 µs -6.5062% | 15.099 µs +15.407% | -Additionally, if you're using a CSPRNG from the `rand` crate, the `nightly` -feature will enable `u128`/`i128` features there, resulting in potentially -faster performance. - ## Batch Performance If your protocol or application is able to batch signatures for verification, -the `verify_batch()` function has greatly improved performance. +the [`verify_batch`][func_verify_batch] function has greatly improved performance. As you can see, there's an optimal batch size for each machine, so you'll likely want to test the benchmarks on your target CPU to discover the best size. ## (Micro)Architecture Specific Backends -`ed25519-dalek` uses the backends from the `curve25519-dalek` crate. +A _backend_ refers to an implementation of elliptic curve and scalar arithmetic. Different backends have different use cases. For example, if you demand formally verified code, you want to use the `fiat` backend (as it was generated from [Fiat Crypto][fiat]). If you want the highest performance possible, you probably want the `simd` backend. -By default the serial backend is used and depending on the target -platform either the 32 bit or the 64 bit serial formula is automatically used. - -To address variety of usage scenarios various backends are available that -include hardware optimisations as well as a formally verified fiat crypto -backend that does not use any hardware optimisations. - -These backends can be overriden with various configuration predicates (cfg) - -Please see the [curve25519_dalek backend documentation](https://docs.rs/curve25519-dalek/latest/curve25519_dalek). +Backend selection details and instructions can be found in the [curve25519-dalek docs](https://github.com/dalek-cryptography/curve25519-dalek#backends). # Contributing See [CONTRIBUTING.md](CONTRIBUTING.md) -# A Note on Signature Malleability +# Batch Signature Verification -The signatures produced by this library are malleable, as discussed in -[the original paper](https://ed25519.cr.yp.to/ed25519-20110926.pdf): +The standard variants of batch signature verification (i.e. many signatures made with potentially many different public keys over potentially many different messages) is available via the `batch` feature. It uses deterministic randomness, i.e., it hashes the inputs (using [`merlin`](https://merlin.cool/), which handles transcript item separation) and uses the result to generate random coefficients. Batch verification requires allocation, so this won't function in heapless settings. -![](https://cdn.jsdelivr.net/gh/dalek-cryptography/ed25519-dalek/docs/assets/ed25519-malleability.png) +# Validation Criteria -While the scalar component of our `Signature` struct is strictly *not* -malleable, because reduction checks are put in place upon `Signature` -deserialisation from bytes, for all types of signatures in this crate, -there is still the question of potential malleability due to the group -element components. +The _validation criteria_ of a signature scheme are the criteria that signatures and public keys must satisfy in order to be accepted. Unfortunately, Ed25519 has some underspecified parts, leading to different validation criteria across implementations. For a very good overview of this, see [Henry's post][validation]. -We could eliminate the latter malleability property by multiplying by the curve -cofactor, however, this would cause our implementation to *not* match the -behaviour of every other implementation in existence. As of this writing, -[RFC 8032](https://tools.ietf.org/html/rfc8032), "Edwards-Curve Digital -Signature Algorithm (EdDSA)," advises that the stronger check should be done. -While we agree that the stronger check should be done, it is our opinion that -one shouldn't get to change the definition of "ed25519 verification" a decade -after the fact, breaking compatibility with every other implementation. +In this section, we mention some specific details about our validation criteria, and how to navigate them. -However, if you require this, please see the documentation for the -`verify_strict()` function, which does the full checks for the group elements. -This functionality is available by default. +## Malleability and the `legacy_compatibility` Feature -If for some reason—although we strongly advise you not to—you need to conform -to the original specification of ed25519 signatures as in the excerpt from the -paper above, you can disable scalar malleability checking via -`--features='legacy_compatibility'`. **WE STRONGLY ADVISE AGAINST THIS.** +A signature scheme is considered to produce _malleable signatures_ if a passive attacker with knowledge of a public key _A_, message _m_, and valid signature _σ'_ can produce a distinct _σ'_ such that _σ'_ is a valid signature of _m_ with respect to _A_. A scheme is only malleable if the attacker can do this _without_ knowledge of the private key corresponding to _A_. -## The `legacy_compatibility` Feature +`ed25519-dalek` is not a malleable signature scheme. -By default, this library performs a stricter check for malleability in the -scalar component of a signature, upon signature deserialisation. This stricter -check, that `s < \ell` where `\ell` is the order of the basepoint, is -[mandated by RFC8032](https://tools.ietf.org/html/rfc8032#section-5.1.7). -However, that RFC was standardised a decade after the original paper, which, as -described above, (usually, falsely) stated that malleability was inconsequential. +Some other Ed25519 implementations are malleable, though, such as [libsodium with `ED25519_COMPAT` enabled](https://github.com/jedisct1/libsodium/blob/24211d370a9335373f0715664271dfe203c7c2cd/src/libsodium/crypto_sign/ed25519/ref10/open.c#L30), [ed25519-donna](https://github.com/floodyberry/ed25519-donna/blob/8757bd4cd209cb032853ece0ce413f122eef212c/ed25519.c#L100), [NaCl's ref10 impl](https://github.com/floodyberry/ed25519-donna/blob/8757bd4cd209cb032853ece0ce413f122eef212c/fuzz/ed25519-ref10.c#L4627), and probably a lot more. +If you need to interoperate with such implementations and accept otherwise invalid signatures, you can enable the `legacy_compatibility` flag. **Do not enable `legacy_compatibility`** if you don't have to, because it will make your signatures malleable. -Because of this, most ed25519 implementations only perform a limited, hackier -check that the most significant three bits of the scalar are unset. If you need -compatibility with legacy implementations, including: +Note: [CIRCL](https://github.com/cloudflare/circl/blob/fa6e0cca79a443d7be18ed241e779adf9ed2a301/sign/ed25519/ed25519.go#L358) has no scalar range check at all. We do not have a feature flag for interoperating with the larger set of RFC-disallowed signatures that CIRCL accepts. -* ed25519-donna -* Golang's /x/crypto ed25519 -* libsodium (only when built with `-DED25519_COMPAT`) -* NaCl's "ref" implementation -* probably a bunch of others +## Weak key Forgery and `verify_strict()` -then enable `ed25519-dalek`'s `legacy_compatibility` feature. Please note and -be forewarned that doing so allows for signature malleability, meaning that -there may be two different and "valid" signatures with the same key for the same -message, which is obviously incredibly dangerous in a number of contexts, -including—but not limited to—identification protocols and cryptocurrency -transactions. +A _signature forgery_ is what it sounds like: it's when an attacker, given a public key _A_, creates a signature _σ_ and message _m_ such that _σ_ is a valid signature of _m_ with respect to _A_. Since this is the core security definition of any signature scheme, Ed25519 signatures cannot be forged. -## The `verify_strict()` Function +However, there's a much looser kind of forgery that Ed25519 permits, which we call _weak key forgery_. An attacker can produce a special public key _A_ (which we call a _weak_ public key) and a signature _σ_ such that _σ_ is a valid signature of _any_ message _m_, with respect to _A_, with high probability. This attack is acknowledged in the [Ed25519 paper](https://ed25519.cr.yp.to/ed25519-20110926.pdf), and caused an exploitable bug in the Scuttlebutt protocol ([paper](https://eprint.iacr.org/2019/526.pdf), section 7.1). The [`VerifyingKey::verify()`][method_verify] function permits weak keys. -The scalar component of a signature is not the only source of signature -malleability, however. Both the public key used for signature verification and -the group element component of the signature are malleable, as they may contain -a small torsion component as a consequence of the curve25519 group not being of -prime order, but having a small cofactor of 8. +We provide [`VerifyingKey::verify_strict`][method_verify_strict] (and [`verify_strict_prehashed`][method_verify_strict_ph]) to help users avoid these scenarios. These functions perform an extra check on _A_, ensuring it's not a weak public key. In addition, we provide the [`VerifyingKey::is_weak`][method_is_weak] to allow users to perform this check before attempting signature verification. -If you wish to also eliminate this source of signature malleability, please -review the -[documentation for the `verify_strict()` function](https://docs.rs/ed25519-dalek/latest/ed25519_dalek/struct.PublicKey.html#method.verify_strict). +## Batch verification -## Batch Signature Verification +As mentioned above, weak public keys can be used to produce signatures for unknown messages with high probability. This means that sometimes a weak forgery attempt will fail. In fact, it can fail up to 7/8 of the time. If you call `verify()` twice on the same failed forgery, it will return an error both times, as expected. However, if you call `verify_batch()` twice on two distinct otherwise-valid batches, both of which contain the failed forgery, there's a 21% chance that one fails and the other succeeds. -The standard variants of batch signature verification (i.e. many signatures made -with potentially many different public keys over potentially many different -messages) is available via the `batch` feature. It uses synthetic randomness, as -noted above. Batch verification requires allocation, so this won't function in -heapless settings. +Why is this? It's because `verify_batch()` does not do the weak key testing of `verify_strict()`, and it multiplies each verification equation by some random coefficient. If the failed forgery gets multiplied by 8, then the weak key (which is a low-order point) becomes 0, and the verification equation on the attempted forgery will succeed. + +Since `verify_batch()` is intended to be high-throughput, we think it's best not to put weak key checks in it. If you want to prevent weird behavior due to weak public keys in your batches, you should call [`VerifyingKey::is_weak`][method_is_weak] on the inputs in advance. + +[fiat]: https://github.com/mit-plv/fiat-crypto +[validation]: https://hdevalence.ca/blog/2020-10-04-its-25519am +[func_verify_batch]: https://docs.rs/ed25519-dalek/latest/ed25519_dalek/fn.verify_batch.html +[method_verify]: https://docs.rs/ed25519-dalek/latest/ed25519_dalek/struct.VerifyingKey.html#method.verify +[method_verify_strict]: https://docs.rs/ed25519-dalek/latest/ed25519_dalek/struct.VerifyingKey.html#method.verify_strict +[method_verify_strict_ph]: https://docs.rs/ed25519-dalek/latest/ed25519_dalek/struct.VerifyingKey.html#method.verify_strict_prehashed +[method_is_weak]: https://docs.rs/ed25519-dalek/latest/ed25519_dalek/struct.VerifyingKey.html#method.is_weak diff --git a/src/context.rs b/src/context.rs index c026be5..afc6437 100644 --- a/src/context.rs +++ b/src/context.rs @@ -15,8 +15,11 @@ use crate::{InternalError, SignatureError}; /// /// # Example /// -#[cfg_attr(feature = "digest", doc = "```")] -#[cfg_attr(not(feature = "digest"), doc = "```ignore")] +#[cfg_attr(all(feature = "digest", feature = "rand_core"), doc = "```")] +#[cfg_attr( + any(not(feature = "digest"), not(feature = "rand_core")), + doc = "```ignore" +)] /// # fn main() { /// use ed25519_dalek::{Signature, SigningKey, VerifyingKey, Sha512}; /// # use curve25519_dalek::digest::Digest; diff --git a/src/errors.rs b/src/errors.rs index aa4e5aa..7cba06d 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -48,6 +48,7 @@ pub(crate) enum InternalError { length_c: usize, }, /// An ed25519ph signature can only take up to 255 octets of context. + #[cfg(feature = "digest")] PrehashedContextLength, /// A mismatched (public, secret) key pair. MismatchedKeypair, @@ -76,6 +77,7 @@ impl Display for InternalError { {} has length {}, {} has length {}.", na, la, nb, lb, nc, lc ), + #[cfg(feature = "digest")] InternalError::PrehashedContextLength => write!( f, "An ed25519ph signature can only take up to 255 octets of context" diff --git a/src/lib.rs b/src/lib.rs index 906ad05..b7e52f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -113,9 +113,8 @@ //! #![cfg_attr(feature = "rand_core", doc = "```")] #![cfg_attr(not(feature = "rand_core"), doc = "```ignore")] -//! # use std::convert::TryFrom; +//! # use core::convert::{TryFrom, TryInto}; //! # use rand::rngs::OsRng; -//! # use std::convert::TryInto; //! # use ed25519_dalek::{SigningKey, Signature, Signer, VerifyingKey, SecretKey, SignatureError}; //! # use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; //! # fn do_test() -> Result<(SigningKey, VerifyingKey, Signature), SignatureError> { @@ -258,6 +257,7 @@ pub use ed25519; #[cfg(feature = "batch")] mod batch; mod constants; +#[cfg(feature = "digest")] mod context; mod errors; mod signature; @@ -272,6 +272,7 @@ pub use sha2::Sha512; #[cfg(feature = "batch")] pub use crate::batch::*; pub use crate::constants::*; +#[cfg(feature = "digest")] pub use crate::context::Context; pub use crate::errors::*; pub use crate::signing::*; diff --git a/src/signing.rs b/src/signing.rs index b6326a7..5985a67 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -33,6 +33,8 @@ use curve25519_dalek::scalar::Scalar; use ed25519::signature::{KeypairRef, Signer, Verifier}; +#[cfg(feature = "digest")] +use crate::context::Context; #[cfg(feature = "digest")] use signature::DigestSigner; @@ -40,7 +42,6 @@ use signature::DigestSigner; use zeroize::{Zeroize, ZeroizeOnDrop}; use crate::constants::*; -use crate::context::Context; use crate::errors::*; use crate::signature::*; use crate::verifying::*; @@ -161,6 +162,7 @@ impl SigningKey { /// Create a signing context that can be used for Ed25519ph with /// [`DigestSigner`]. + #[cfg(feature = "digest")] pub fn with_context<'k, 'v>( &'k self, context_value: &'v [u8], @@ -172,21 +174,15 @@ impl SigningKey { /// /// # Example /// - /// ``` - /// # #[cfg(feature = "std")] + #[cfg_attr(feature = "rand_core", doc = "```")] + #[cfg_attr(not(feature = "rand_core"), doc = "```ignore")] /// # fn main() { - /// /// use rand::rngs::OsRng; - /// use ed25519_dalek::SigningKey; - /// use ed25519_dalek::Signature; + /// use ed25519_dalek::{Signature, SigningKey}; /// /// let mut csprng = OsRng; /// let signing_key: SigningKey = SigningKey::generate(&mut csprng); - /// /// # } - /// # - /// # #[cfg(not(feature = "std"))] - /// # fn main() { } /// ``` /// /// # Input @@ -239,7 +235,6 @@ impl SigningKey { /// use sha2::Sha512; /// use rand::rngs::OsRng; /// - /// # #[cfg(feature = "std")] /// # fn main() { /// let mut csprng = OsRng; /// let signing_key: SigningKey = SigningKey::generate(&mut csprng); @@ -250,9 +245,6 @@ impl SigningKey { /// /// prehashed.update(message); /// # } - /// # - /// # #[cfg(not(feature = "std"))] - /// # fn main() { } /// ``` /// /// If you want, you can optionally pass a "context". It is generally a @@ -301,13 +293,9 @@ impl SigningKey { /// # /// # Ok(sig) /// # } - /// # #[cfg(feature = "std")] /// # fn main() { /// # do_test(); /// # } - /// # - /// # #[cfg(not(feature = "std"))] - /// # fn main() { } /// ``` /// /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 @@ -385,13 +373,9 @@ impl SigningKey { /// # verified /// # } /// # - /// # #[cfg(feature = "std")] /// # fn main() { /// # do_test(); /// # } - /// # - /// # #[cfg(not(feature = "std"))] - /// # fn main() { } /// ``` /// /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 diff --git a/src/verifying.rs b/src/verifying.rs index 2f207fe..a97b31c 100644 --- a/src/verifying.rs +++ b/src/verifying.rs @@ -34,11 +34,12 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[cfg(feature = "serde")] use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes}; +#[cfg(feature = "digest")] +use crate::context::Context; #[cfg(feature = "digest")] use signature::DigestVerifier; use crate::constants::*; -use crate::context::Context; use crate::errors::*; use crate::signature::*; use crate::signing::*; @@ -156,6 +157,7 @@ impl VerifyingKey { /// Create a verifying context that can be used for Ed25519ph with /// [`DigestVerifier`]. + #[cfg(feature = "digest")] pub fn with_context<'k, 'v>( &'k self, context_value: &'v [u8], From 57a8add0fd0710f53c6fcdf582480155c50bf345 Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Thu, 2 Feb 2023 17:18:47 -0500 Subject: [PATCH 333/351] Removed vestigial `nightly` feature from docsrs instructions --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 682c01e..f8cee6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ rustdoc-args = [ "--html-in-header", "docs/assets/rustdoc-include-katex-header.html", "--cfg", "docsrs", ] -features = ["nightly", "batch", "pkcs8"] +features = ["batch", "pkcs8"] [dependencies] curve25519-dalek = { version = "=4.0.0-rc.0", default-features = false, features = ["digest"] } From b77fa515690db7ff0df84cea497f2f7e467adc29 Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Sat, 4 Feb 2023 03:21:36 -0500 Subject: [PATCH 334/351] Bump curve25519-dalek dep to rc.1 --- Cargo.lock | 60 +++++++++++++++++++++++++++--------------------------- Cargo.toml | 4 ++-- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 24f3c57..43e27bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -57,9 +57,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "byteorder" @@ -75,9 +75,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" @@ -239,9 +239,9 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.0.0-rc.0" +version = "4.0.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da00a7a9a4eb92a0a0f8e75660926d48f0d0f3c537e455c457bcdaa1e16b1ac" +checksum = "8d4ba9852b42210c7538b75484f9daa0655e9a3ac04f693747bb0f02cf3cfe16" dependencies = [ "cfg-if", "digest", @@ -309,9 +309,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "fiat-crypto" @@ -409,9 +409,9 @@ checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] @@ -581,9 +581,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" dependencies = [ "unicode-ident", ] @@ -639,9 +639,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.10.1" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" +checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -651,9 +651,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "regex-syntax", ] @@ -812,9 +812,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.10" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "serde", ] @@ -862,9 +862,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -872,9 +872,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", @@ -887,9 +887,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -897,9 +897,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", @@ -910,15 +910,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/Cargo.toml b/Cargo.toml index f8cee6f..2e811d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ rustdoc-args = [ features = ["batch", "pkcs8"] [dependencies] -curve25519-dalek = { version = "=4.0.0-rc.0", default-features = false, features = ["digest"] } +curve25519-dalek = { version = "=4.0.0-rc.1", default-features = false, features = ["digest"] } ed25519 = { version = "2.1", default-features = false } signature = { version = ">=2.0, <2.1", optional = true, default-features = false } sha2 = { version = "0.10", default-features = false } @@ -38,7 +38,7 @@ serde_bytes = { version = "0.11", optional = true } zeroize = { version = "1.5", default-features = false, optional = true } [dev-dependencies] -curve25519-dalek = { version = "=4.0.0-rc.0", default-features = false, features = ["digest", "rand_core"] } +curve25519-dalek = { version = "=4.0.0-rc.1", default-features = false, features = ["digest", "rand_core"] } hex = "0.4" bincode = "1.0" serde_json = "1.0" From 0b04124175edb43f47699fe8a1bcc1771114ad14 Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Sat, 4 Feb 2023 03:24:09 -0500 Subject: [PATCH 335/351] Fixed MSRV build --- .github/workflows/rust.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 87b40c8..7dda5f3 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -59,7 +59,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - # First run `cargo +nightly -Z minimal-verisons check` in order to get a + # First delete the checked-in `Cargo.lock`. We're going to regenerate it + - run: rm Cargo.lock + # Now run `cargo +nightly -Z minimal-verisons check` in order to get a # Cargo.lock with the oldest possible deps - uses: dtolnay/rust-toolchain@nightly - run: cargo -Z minimal-versions check --no-default-features --features serde From 4686ade1b55e176c72c170fca4aed59de844776f Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Tue, 7 Mar 2023 00:16:19 -0700 Subject: [PATCH 336/351] Use named fields for `struct VerifyingKey` (#284) Previously it was a 2-tuple containing a `CompressedEdwardsY` serialization and a decompressed `EdwardsPoint`, however using `.0` and `.1` for these respectively makes the code hard to read. This commit changes them to `compressed` and `point`, which as it were are the names of the local variables used when constructing a `VerifyingKey`, which improves clarity. --- src/batch.rs | 2 +- src/verifying.rs | 28 +++++++++++++++++----------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/batch.rs b/src/batch.rs index c312917..0ca98d4 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -224,7 +224,7 @@ pub fn verify_batch( let zhrams = hrams.iter().zip(zs.iter()).map(|(hram, z)| hram * z); let Rs = signatures.iter().map(|sig| sig.R.decompress()); - let As = verifying_keys.iter().map(|pk| Some(pk.1)); + let As = verifying_keys.iter().map(|pk| Some(pk.point)); let B = once(Some(constants::ED25519_BASEPOINT_POINT)); // Compute (-∑ z[i]s[i] (mod l)) B + ∑ z[i]R[i] + ∑ (z[i]H(R||A||M)[i] (mod l)) A[i] = 0 diff --git a/src/verifying.rs b/src/verifying.rs index a97b31c..4c9730b 100644 --- a/src/verifying.rs +++ b/src/verifying.rs @@ -57,11 +57,17 @@ use crate::signing::*; /// are rejected, use [`VerifyingKey::verify_strict`]. // Invariant: VerifyingKey.1 is always the decompression of VerifyingKey.0 #[derive(Copy, Clone, Default, Eq)] -pub struct VerifyingKey(pub(crate) CompressedEdwardsY, pub(crate) EdwardsPoint); +pub struct VerifyingKey { + /// Serialized compressed Edwards-y point. + pub(crate) compressed: CompressedEdwardsY, + + /// Decompressed Edwards point used for curve arithmetic operations. + pub(crate) point: EdwardsPoint, +} impl Debug for VerifyingKey { fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - write!(f, "VerifyingKey({:?}), {:?})", self.0, self.1) + write!(f, "VerifyingKey({:?}), {:?})", self.compressed, self.point) } } @@ -101,13 +107,13 @@ impl VerifyingKey { /// Convert this public key to a byte array. #[inline] pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_LENGTH] { - self.0.to_bytes() + self.compressed.to_bytes() } /// View this public key as a byte array. #[inline] pub fn as_bytes(&self) -> &[u8; PUBLIC_KEY_LENGTH] { - &(self.0).0 + &(self.compressed).0 } /// Construct a `VerifyingKey` from a slice of bytes. @@ -152,7 +158,7 @@ impl VerifyingKey { .ok_or(InternalError::PointDecompression)?; // Invariant: VerifyingKey.1 is always the decompression of VerifyingKey.0 - Ok(VerifyingKey(compressed, point)) + Ok(VerifyingKey { compressed, point }) } /// Create a verifying context that can be used for Ed25519ph with @@ -171,7 +177,7 @@ impl VerifyingKey { /// message. [`Self::verify_strict`] denies weak keys, but if you want to check for this /// property before verification, then use this method. pub fn is_weak(&self) -> bool { - self.1.is_small_order() + self.point.is_small_order() } /// Internal utility function for clamping a scalar representation and multiplying by the @@ -182,7 +188,7 @@ impl VerifyingKey { let compressed = point.compress(); // Invariant: VerifyingKey.1 is always the decompression of VerifyingKey.0 - VerifyingKey(compressed, point) + VerifyingKey { compressed, point } } // A helper function that computes H(R || A || M). If `context.is_some()`, this does the @@ -222,8 +228,8 @@ impl VerifyingKey { signature: &InternalSignature, M: &[u8], ) -> CompressedEdwardsY { - let k = Self::compute_challenge(context, &signature.R, &self.0, M); - let minus_A: EdwardsPoint = -self.1; + let k = Self::compute_challenge(context, &signature.R, &self.compressed, M); + let minus_A: EdwardsPoint = -self.point; // Recall the (non-batched) verification equation: -[k]A + [s]B = R EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s).compress() } @@ -349,7 +355,7 @@ impl VerifyingKey { .ok_or_else(|| SignatureError::from(InternalError::Verify))?; // Logical OR is fine here as we're not trying to be constant time. - if signature_R.is_small_order() || self.1.is_small_order() { + if signature_R.is_small_order() || self.point.is_small_order() { return Err(InternalError::Verify.into()); } @@ -403,7 +409,7 @@ impl VerifyingKey { .ok_or_else(|| SignatureError::from(InternalError::Verify))?; // Logical OR is fine here as we're not trying to be constant time. - if signature_R.is_small_order() || self.1.is_small_order() { + if signature_R.is_small_order() || self.point.is_small_order() { return Err(InternalError::Verify.into()); } From e0e02cfcf4dcb4de8af9e168d45af4508d8b902e Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Tue, 7 Mar 2023 00:20:09 -0700 Subject: [PATCH 337/351] Bump `ed25519` to v2.2; `pkcs8` to v0.10 (#285) The `ed25519` v2.2.0 crate bumps the `pkcs8` dependency to v0.10. This updates `ed25519` to the latest version and updates the PKCS#8 support to use the new API. --- Cargo.lock | 24 ++++++++++++------------ Cargo.toml | 2 +- src/signing.rs | 5 +---- src/verifying.rs | 9 +++------ 4 files changed, 17 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 43e27bc..e80fe13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -135,9 +135,9 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b" +checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" [[package]] name = "cpufeatures" @@ -255,9 +255,9 @@ dependencies = [ [[package]] name = "der" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +checksum = "bc302fd9b18d66834a6f092d10ea85489c0ca8ad6b7304092135fab171d853cd" dependencies = [ "const-oid", "pem-rfc7468", @@ -276,9 +276,9 @@ dependencies = [ [[package]] name = "ed25519" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf420a7ec85d98495b0c34aa4a58ca117f982ffbece111aeb545160148d7010" +checksum = "be522bee13fa6d8059f4903a4084aa3bd50725e18150202f0238deb615cd6371" dependencies = [ "pkcs8", "serde", @@ -522,18 +522,18 @@ dependencies = [ [[package]] name = "pem-rfc7468" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" dependencies = [ "base64ct", ] [[package]] name = "pkcs8" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +checksum = "e34154ec92c136238e7c210443538e64350962b8e2788cadcf5f781a6da70c36" dependencies = [ "der", "spki", @@ -757,9 +757,9 @@ dependencies = [ [[package]] name = "spki" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +checksum = "c0445c905640145c7ea8c1993555957f65e7c46d0535b91ba501bc9bfc85522f" dependencies = [ "base64ct", "der", diff --git a/Cargo.toml b/Cargo.toml index 2e811d2..57bc2da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ features = ["batch", "pkcs8"] [dependencies] curve25519-dalek = { version = "=4.0.0-rc.1", default-features = false, features = ["digest"] } -ed25519 = { version = "2.1", default-features = false } +ed25519 = { version = ">=2.2, <2.3", default-features = false } signature = { version = ">=2.0, <2.1", optional = true, default-features = false } sha2 = { version = "0.10", default-features = false } diff --git a/src/signing.rs b/src/signing.rs index 5985a67..28f7346 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -10,7 +10,7 @@ //! ed25519 signing keys. #[cfg(feature = "pkcs8")] -use ed25519::pkcs8::{self, DecodePrivateKey}; +use ed25519::pkcs8; #[cfg(any(test, feature = "rand_core"))] use rand_core::CryptoRngCore; @@ -565,9 +565,6 @@ impl Drop for SigningKey { #[cfg(feature = "zeroize")] impl ZeroizeOnDrop for SigningKey {} -#[cfg(feature = "pkcs8")] -impl DecodePrivateKey for SigningKey {} - #[cfg(all(feature = "alloc", feature = "pkcs8"))] impl pkcs8::EncodePrivateKey for SigningKey { fn to_pkcs8_der(&self) -> pkcs8::Result { diff --git a/src/verifying.rs b/src/verifying.rs index 4c9730b..1ea9332 100644 --- a/src/verifying.rs +++ b/src/verifying.rs @@ -25,7 +25,7 @@ use ed25519::signature::Verifier; use sha2::Sha512; #[cfg(feature = "pkcs8")] -use ed25519::pkcs8::{self, DecodePublicKey}; +use ed25519::pkcs8; #[cfg(feature = "serde")] use serde::de::Error as SerdeError; @@ -488,9 +488,6 @@ impl TryFrom<&[u8]> for VerifyingKey { } } -#[cfg(feature = "pkcs8")] -impl DecodePublicKey for VerifyingKey {} - #[cfg(all(feature = "alloc", feature = "pkcs8"))] impl pkcs8::EncodePublicKey for VerifyingKey { fn to_public_key_der(&self) -> pkcs8::spki::Result { @@ -531,10 +528,10 @@ impl From<&VerifyingKey> for pkcs8::PublicKeyBytes { } #[cfg(feature = "pkcs8")] -impl TryFrom> for VerifyingKey { +impl TryFrom> for VerifyingKey { type Error = pkcs8::spki::Error; - fn try_from(public_key: pkcs8::spki::SubjectPublicKeyInfo<'_>) -> pkcs8::spki::Result { + fn try_from(public_key: pkcs8::spki::SubjectPublicKeyInfoRef<'_>) -> pkcs8::spki::Result { pkcs8::PublicKeyBytes::try_from(public_key)?.try_into() } } From 3efde345b61089b99b81a80c1d2bc01ca3888a75 Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Tue, 7 Mar 2023 08:35:10 +0100 Subject: [PATCH 338/351] Remove invalid input fields from CI action for Rust setup (#283) Neither `override` nor `profile` are valid inputs for the `dtolnay/rust-toolchain` action. It always uses the minimal profile anyways. --- .github/workflows/rust.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 7dda5f3..2e24b32 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -105,6 +105,4 @@ jobs: - uses: dtolnay/rust-toolchain@stable with: toolchain: stable - override: true - profile: minimal - run: cargo doc --all-features From c33b49bf5a2ed45a820eaeee337a1a12d7eb0234 Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Tue, 7 Mar 2023 08:44:55 +0100 Subject: [PATCH 339/351] Update actions/checkout in GitHub Actions workflow to v3 (#282) --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 2e24b32..502b290 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -45,7 +45,7 @@ jobs: name: Test simd backend (nightly) runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@nightly - env: RUSTFLAGS: '--cfg curve25519_dalek_backend="simd" -C target_feature=+avx2' From 64b26ad07448637d0116951e70dcffcbd5816e3d Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Tue, 7 Mar 2023 08:54:30 +0100 Subject: [PATCH 340/351] Fix a few typos (#281) --- src/batch.rs | 4 ++-- src/lib.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/batch.rs b/src/batch.rs index 0ca98d4..d94008d 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -83,9 +83,9 @@ fn gen_u128(rng: &mut R) -> u128 { /// /// # Returns /// -/// * A `Result` whose `Ok` value is an emtpy tuple and whose `Err` value is a +/// * A `Result` whose `Ok` value is an empty tuple and whose `Err` value is a /// `SignatureError` containing a description of the internal error which -/// occured. +/// occurred. /// /// ## On Deterministic Nonces /// diff --git a/src/lib.rs b/src/lib.rs index b7e52f6..225d871 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,7 +86,7 @@ //! ## Serialisation //! //! `VerifyingKey`s, `SecretKey`s, `SigningKey`s, and `Signature`s can be serialised -//! into byte-arrays by calling `.to_bytes()`. It's perfectly acceptible and +//! into byte-arrays by calling `.to_bytes()`. It's perfectly acceptable and //! safe to transfer and/or store those bytes. (Of course, never transfer your //! secret key to anyone else, since they will only need the public key to //! verify your signatures!) From 7dc1bbd85527306e67bc4741968bd79ea1ae25b1 Mon Sep 17 00:00:00 2001 From: Samuel Moelius <35515885+smoelius@users.noreply.github.com> Date: Sat, 18 Mar 2023 11:22:15 -0400 Subject: [PATCH 341/351] Remove two unnecessary `into_iter` (#290) --- tests/validation_criteria.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/validation_criteria.rs b/tests/validation_criteria.rs index 881108e..b7ae838 100644 --- a/tests/validation_criteria.rs +++ b/tests/validation_criteria.rs @@ -132,9 +132,8 @@ fn get_test_vectors() -> impl Iterator { /// VERIFY_STRICT_ALLOWED_EDGECASES, respectively #[test] fn check_validation_criteria() { - let verify_allowed_edgecases = Set::from_iter(VERIFY_ALLOWED_EDGECASES.to_vec().into_iter()); - let verify_strict_allowed_edgecases = - Set::from_iter(VERIFY_STRICT_ALLOWED_EDGECASES.to_vec().into_iter()); + let verify_allowed_edgecases = Set::from_iter(VERIFY_ALLOWED_EDGECASES.to_vec()); + let verify_strict_allowed_edgecases = Set::from_iter(VERIFY_STRICT_ALLOWED_EDGECASES.to_vec()); for TestVector { number, From 9577d1e3225297a9bad91dbc19b43fd5e3256281 Mon Sep 17 00:00:00 2001 From: "pinkforest(she/her)" <36498018+pinkforest@users.noreply.github.com> Date: Tue, 21 Mar 2023 16:46:43 +1100 Subject: [PATCH 342/351] Add no_std to CI (#289) * Add no_std to CI * Add serde to no_std feature test * Try out cargo hack * No serde - expect success * Add build for no-default-features * Exclude default --- .github/workflows/rust.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 502b290..543f0ec 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -70,6 +70,21 @@ jobs: - uses: dtolnay/rust-toolchain@1.60.0 - run: cargo build + build-nostd: + name: Build on no_std target (thumbv7em-none-eabi) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + targets: thumbv7em-none-eabi + - uses: taiki-e/install-action@cargo-hack + # No default features build + - run: cargo build --target thumbv7em-none-eabi --release --no-default-features + # TODO: serde pending PR#288 + - run: cargo hack build --target thumbv7em-none-eabi --release --each-feature --exclude-features default,std,serde + bench: name: Check that benchmarks compile runs-on: ubuntu-latest From 2931c688eb11341a1145e257bc41d8ecbe36277c Mon Sep 17 00:00:00 2001 From: ryan <120750323+ryan-mob@users.noreply.github.com> Date: Wed, 22 Mar 2023 08:45:33 +1300 Subject: [PATCH 343/351] Fix `serde` / `no_std` incompatibility Co-authored-by: ryan kurte Co-authored-by: Vlad Semenov --- .github/workflows/rust.yml | 3 +-- Cargo.lock | 10 ---------- Cargo.toml | 3 +-- src/signing.rs | 39 +++++++++++++++++++++++++++++++------- src/verifying.rs | 39 +++++++++++++++++++++++++++++++------- 5 files changed, 66 insertions(+), 28 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 543f0ec..a70fef0 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -82,8 +82,7 @@ jobs: - uses: taiki-e/install-action@cargo-hack # No default features build - run: cargo build --target thumbv7em-none-eabi --release --no-default-features - # TODO: serde pending PR#288 - - run: cargo hack build --target thumbv7em-none-eabi --release --each-feature --exclude-features default,std,serde + - run: cargo hack build --target thumbv7em-none-eabi --release --each-feature --exclude-features default,std bench: name: Check that benchmarks compile diff --git a/Cargo.lock b/Cargo.lock index e80fe13..6c11e18 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -299,7 +299,6 @@ dependencies = [ "rand", "rand_core", "serde", - "serde_bytes", "serde_json", "sha2", "signature", @@ -694,15 +693,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde_bytes" -version = "0.11.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718dc5fff5b36f99093fc49b280cfc96ce6fc824317783bff5a1fed0c7a64819" -dependencies = [ - "serde", -] - [[package]] name = "serde_derive" version = "1.0.152" diff --git a/Cargo.toml b/Cargo.toml index 57bc2da..3e8a439 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,6 @@ sha2 = { version = "0.10", default-features = false } merlin = { version = "3", default-features = false, optional = true } rand_core = { version = "0.6.4", default-features = false, optional = true } serde = { version = "1.0", default-features = false, optional = true } -serde_bytes = { version = "0.11", optional = true } zeroize = { version = "1.5", default-features = false, optional = true } [dev-dependencies] @@ -68,5 +67,5 @@ legacy_compatibility = [] pkcs8 = ["ed25519/pkcs8"] pem = ["alloc", "ed25519/pem", "pkcs8"] rand_core = ["dep:rand_core"] -serde = ["dep:serde", "serde_bytes", "ed25519/serde"] +serde = ["dep:serde", "ed25519/serde"] zeroize = ["dep:zeroize", "curve25519-dalek/zeroize"] diff --git a/src/signing.rs b/src/signing.rs index 28f7346..16f4ac6 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -15,12 +15,8 @@ use ed25519::pkcs8; #[cfg(any(test, feature = "rand_core"))] use rand_core::CryptoRngCore; -#[cfg(feature = "serde")] -use serde::de::Error as SerdeError; #[cfg(feature = "serde")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; -#[cfg(feature = "serde")] -use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes}; use sha2::Sha512; @@ -634,7 +630,7 @@ impl Serialize for SigningKey { where S: Serializer, { - SerdeBytes::new(&self.secret_key).serialize(serializer) + serializer.serialize_bytes(&self.secret_key) } } @@ -644,8 +640,37 @@ impl<'d> Deserialize<'d> for SigningKey { where D: Deserializer<'d>, { - let bytes = ::deserialize(deserializer)?; - Self::try_from(bytes.as_ref()).map_err(SerdeError::custom) + struct SigningKeyVisitor; + + impl<'de> serde::de::Visitor<'de> for SigningKeyVisitor { + type Value = SigningKey; + + fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + write!(formatter, concat!("An ed25519 signing (private) key")) + } + + fn visit_borrowed_bytes( + self, + bytes: &'de [u8], + ) -> Result { + SigningKey::try_from(bytes.as_ref()).map_err(E::custom) + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let mut bytes = [0u8; 32]; + for i in 0..32 { + bytes[i] = seq + .next_element()? + .ok_or_else(|| serde::de::Error::invalid_length(i, &"expected 32 bytes"))?; + } + SigningKey::try_from(bytes).map_err(serde::de::Error::custom) + } + } + + deserializer.deserialize_bytes(SigningKeyVisitor) } } diff --git a/src/verifying.rs b/src/verifying.rs index 1ea9332..8816fec 100644 --- a/src/verifying.rs +++ b/src/verifying.rs @@ -27,12 +27,8 @@ use sha2::Sha512; #[cfg(feature = "pkcs8")] use ed25519::pkcs8; -#[cfg(feature = "serde")] -use serde::de::Error as SerdeError; #[cfg(feature = "serde")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; -#[cfg(feature = "serde")] -use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes}; #[cfg(feature = "digest")] use crate::context::Context; @@ -542,7 +538,7 @@ impl Serialize for VerifyingKey { where S: Serializer, { - SerdeBytes::new(self.as_bytes()).serialize(serializer) + serializer.serialize_bytes(&self.as_bytes()[..]) } } @@ -552,7 +548,36 @@ impl<'d> Deserialize<'d> for VerifyingKey { where D: Deserializer<'d>, { - let bytes = ::deserialize(deserializer)?; - VerifyingKey::try_from(bytes.as_ref()).map_err(SerdeError::custom) + struct VerifyingKeyVisitor; + + impl<'de> serde::de::Visitor<'de> for VerifyingKeyVisitor { + type Value = VerifyingKey; + + fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + write!(formatter, concat!("An ed25519 verifying (public) key")) + } + + fn visit_borrowed_bytes( + self, + bytes: &'de [u8], + ) -> Result { + VerifyingKey::try_from(bytes.as_ref()).map_err(E::custom) + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let mut bytes = [0u8; 32]; + for i in 0..32 { + bytes[i] = seq + .next_element()? + .ok_or_else(|| serde::de::Error::invalid_length(i, &"expected 32 bytes"))?; + } + VerifyingKey::try_from(&bytes[..]).map_err(serde::de::Error::custom) + } + } + + deserializer.deserialize_bytes(VerifyingKeyVisitor) } } From 7901b21e065ecdbd275a285d7eb51f1d3ce3dcdd Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 26 Mar 2023 09:11:23 +0100 Subject: [PATCH 344/351] Improve diagnostics when key being deserializing is too long (#294) --- src/signing.rs | 13 ++++++++++++ src/verifying.rs | 14 +++++++++++++ tests/ed25519.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) diff --git a/src/signing.rs b/src/signing.rs index 16f4ac6..fd59deb 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -666,6 +666,19 @@ impl<'d> Deserialize<'d> for SigningKey { .next_element()? .ok_or_else(|| serde::de::Error::invalid_length(i, &"expected 32 bytes"))?; } + + let remaining = (0..) + .map(|_| seq.next_element::()) + .take_while(|el| matches!(el, Ok(Some(_)))) + .count(); + + if remaining > 0 { + return Err(serde::de::Error::invalid_length( + 32 + remaining, + &"expected 32 bytes", + )); + } + SigningKey::try_from(bytes).map_err(serde::de::Error::custom) } } diff --git a/src/verifying.rs b/src/verifying.rs index 8816fec..6b0ad49 100644 --- a/src/verifying.rs +++ b/src/verifying.rs @@ -569,11 +569,25 @@ impl<'d> Deserialize<'d> for VerifyingKey { A: serde::de::SeqAccess<'de>, { let mut bytes = [0u8; 32]; + for i in 0..32 { bytes[i] = seq .next_element()? .ok_or_else(|| serde::de::Error::invalid_length(i, &"expected 32 bytes"))?; } + + let remaining = (0..) + .map(|_| seq.next_element::()) + .take_while(|el| matches!(el, Ok(Some(_)))) + .count(); + + if remaining > 0 { + return Err(serde::de::Error::invalid_length( + 32 + remaining, + &"expected 32 bytes", + )); + } + VerifyingKey::try_from(&bytes[..]).map_err(serde::de::Error::custom) } } diff --git a/tests/ed25519.rs b/tests/ed25519.rs index a3a7ebc..6632f01 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -542,6 +542,33 @@ mod serialisation { assert_eq!(verifying_key, decoded_verifying_key); } + #[test] + fn serialize_deserialize_verifying_key_json_too_long() { + // derived from `serialize_deserialize_verifying_key_json` test + // trailing zero elements makes key too long (34 bytes) + let encoded_verifying_key_too_long = "[130,39,155,15,62,76,188,63,124,122,26,251,233,253,225,220,14,41,166,120,108,35,254,77,160,83,172,58,219,42,86,120,0,0]"; + let de_err = serde_json::from_str::(&encoded_verifying_key_too_long) + .unwrap_err() + .to_string(); + assert!( + de_err.contains("invalid length 34"), + "expected invalid length error, got: {de_err}", + ); + } + + #[test] + fn serialize_deserialize_verifying_key_json_too_short() { + // derived from `serialize_deserialize_verifying_key_json` test + let encoded_verifying_key_too_long = "[130,39,155,15]"; + let de_err = serde_json::from_str::(&encoded_verifying_key_too_long) + .unwrap_err() + .to_string(); + assert!( + de_err.contains("invalid length 4"), + "expected invalid length error, got: {de_err}" + ); + } + #[test] fn serialize_deserialize_signing_key_bincode() { let signing_key = SigningKey::from_bytes(&SECRET_KEY_BYTES); @@ -564,6 +591,33 @@ mod serialisation { } } + #[test] + fn serialize_deserialize_signing_key_json_too_long() { + // derived from `serialize_deserialize_signing_key_json` test + // trailing zero elements makes key too long (34 bytes) + let encoded_signing_key_too_long = "[62,70,27,163,92,182,11,3,77,234,98,4,11,127,79,228,243,187,150,73,201,137,76,22,85,251,152,2,241,42,72,54,0,0]"; + let de_err = serde_json::from_str::(&encoded_signing_key_too_long) + .unwrap_err() + .to_string(); + assert!( + de_err.contains("invalid length 34"), + "expected invalid length error, got: {de_err}", + ); + } + + #[test] + fn serialize_deserialize_signing_key_json_too_short() { + // derived from `serialize_deserialize_signing_key_json` test + let encoded_signing_key_too_long = "[62,70,27,163]"; + let de_err = serde_json::from_str::(&encoded_signing_key_too_long) + .unwrap_err() + .to_string(); + assert!( + de_err.contains("invalid length 4"), + "expected invalid length error, got: {de_err}" + ); + } + #[test] fn serialize_deserialize_signing_key_toml() { let demo = Demo { From 5014c91270cb7abcf147663f7ae0bd1971c11d75 Mon Sep 17 00:00:00 2001 From: "pinkforest(she/her)" <36498018+pinkforest@users.noreply.github.com> Date: Mon, 27 Mar 2023 02:23:14 +1100 Subject: [PATCH 345/351] chore: Release 2.0.0-rc.2 (#295) Co-authored-by: Michael Rosenberg --- CHANGELOG.md | 3 ++- Cargo.toml | 6 +++--- README.md | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da1d2bf..40ddabe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,10 +15,10 @@ Entries are listed in reverse chronological order per undeprecated major series. * Bump MSRV from 1.41 to 1.60.0 * Bump Rust edition * Bump `signature` dependency to 2.0 -* Make [curve25519-backend selection](https://github.com/dalek-cryptography/curve25519-dalek/#backends) more automatic * Make `digest` an optional dependency * Make `zeroize` an optional dependency * Make `rand_core` an optional dependency +* Adopt [curve25519-backend selection](https://github.com/dalek-cryptography/curve25519-dalek/#backends) over features * Make all batch verification deterministic remove `batch_deterministic` ([#256](https://github.com/dalek-cryptography/ed25519-dalek/pull/256)) * Remove `ExpandedSecretKey` API ((#205)[https://github.com/dalek-cryptography/ed25519-dalek/pull/205]) * Rename `Keypair` → `SigningKey` and `PublicKey` → `VerifyingKey` @@ -34,3 +34,4 @@ Entries are listed in reverse chronological order per undeprecated major series. * Impl `Hash` for `VerifyingKey` * Impl `Clone`, `Drop`, and `ZeroizeOnDrop` for `SigningKey` * Remove `rand` dependency +* Improve key deserialization diagnostics diff --git a/Cargo.toml b/Cargo.toml index 3e8a439..5c73858 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "2.0.0-pre.0" +version = "2.0.0-rc.2" edition = "2021" authors = [ "isis lovecruft ", @@ -25,7 +25,7 @@ rustdoc-args = [ features = ["batch", "pkcs8"] [dependencies] -curve25519-dalek = { version = "=4.0.0-rc.1", default-features = false, features = ["digest"] } +curve25519-dalek = { version = "=4.0.0-rc.2", default-features = false, features = ["digest"] } ed25519 = { version = ">=2.2, <2.3", default-features = false } signature = { version = ">=2.0, <2.1", optional = true, default-features = false } sha2 = { version = "0.10", default-features = false } @@ -37,7 +37,7 @@ serde = { version = "1.0", default-features = false, optional = true } zeroize = { version = "1.5", default-features = false, optional = true } [dev-dependencies] -curve25519-dalek = { version = "=4.0.0-rc.1", default-features = false, features = ["digest", "rand_core"] } +curve25519-dalek = { version = "=4.0.0-rc.2", default-features = false, features = ["digest", "rand_core"] } hex = "0.4" bincode = "1.0" serde_json = "1.0" diff --git a/README.md b/README.md index a0acd3f..0d6ba03 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ ed25519-dalek = "1" To use the latest prerelease (see changes [below](#breaking-changes-in-200)), use the following line in your project's `Cargo.toml`: ```toml -ed25519-dalek = "2.0.0-pre.0" +ed25519-dalek = "2.0.0-rc.2" ``` # Feature Flags @@ -47,10 +47,10 @@ See [CHANGELOG.md](CHANGELOG.md) for a list of changes made in past version of t * Bump MSRV from 1.41 to 1.60.0 * Bump Rust edition * Bump `signature` dependency to 2.0 -* Make [curve25519-backend selection](https://github.com/dalek-cryptography/curve25519-dalek/#backends) more automatic * Make `digest` an optional dependency * Make `zeroize` an optional dependency * Make `rand_core` an optional dependency +* Adopt [curve25519-backend selection](https://github.com/dalek-cryptography/curve25519-dalek/#backends) over features * Make all batch verification deterministic remove `batch_deterministic` ([#256](https://github.com/dalek-cryptography/ed25519-dalek/pull/256)) * Remove `ExpandedSecretKey` API ((#205)[https://github.com/dalek-cryptography/ed25519-dalek/pull/205]) * Rename `Keypair` → `SigningKey` and `PublicKey` → `VerifyingKey` From c8c9f2998916fca4761b0b64a8aec0c1ce120c37 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Thu, 30 Mar 2023 11:29:36 -0600 Subject: [PATCH 346/351] Add `Scalar` and `MontgomeryPoint` conversions (#296) * Add `Scalar` and `MontgomeryPoint` conversions - Adds `SigningKey::to_scalar` to extract the private scalar - Adds `VerifyingKey::to_montgomery` to map the verifying key's `EdwardsPoint` to a `MontgomeryPoint` - Also adds corresponding `From<&T>` impls which call the inherent methods. This is useful for systems which are keyed using Ed25519 keys which would like to use X25519 for D-H. Having inherent methods means it's possible to call these methods without having to import `Scalar` and `MontgomeryPoint` from `curve25519-dalek`. This is of course a bit circuitous: we could just multiply `Scalar` by `EdwardsPoint` and use the resulting `EdwardsPoint` as the D-H shared secret, however it seems many protocols have adopted this approach of mapping to `MontgomeryPoint` and using that for the shared secret, since X25519 is traditionally used for ECDH with Curve25519. * Add reference to eprint 2021/509 * Basic X25519 Diffie-Hellman test --- Cargo.lock | 10 ++++----- src/signing.rs | 26 ++++++++++++++++------- src/verifying.rs | 18 +++++++++++++++- tests/x25519.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 14 deletions(-) create mode 100644 tests/x25519.rs diff --git a/Cargo.lock b/Cargo.lock index 6c11e18..1eb6a42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -239,9 +239,9 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.0.0-rc.1" +version = "4.0.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d4ba9852b42210c7538b75484f9daa0655e9a3ac04f693747bb0f02cf3cfe16" +checksum = "03d928d978dbec61a1167414f5ec534f24bea0d7a0d24dd9b6233d3d8223e585" dependencies = [ "cfg-if", "digest", @@ -287,7 +287,7 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "2.0.0-pre.0" +version = "2.0.0-rc.2" dependencies = [ "bincode", "criterion", @@ -314,9 +314,9 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "fiat-crypto" -version = "0.1.17" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a214f5bb88731d436478f3ae1f8a277b62124089ba9fb67f4f93fb100ef73c90" +checksum = "93ace6ec7cc19c8ed33a32eaa9ea692d7faea05006b5356b9e2b668ec4bc3955" [[package]] name = "generic-array" diff --git a/src/signing.rs b/src/signing.rs index fd59deb..93084b8 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -305,9 +305,11 @@ impl SigningKey { where D: Digest, { - let expanded: ExpandedSecretKey = (&self.secret_key).into(); // xxx thanks i hate this - - expanded.sign_prehashed(prehashed_message, &self.verifying_key, context) + ExpandedSecretKey::from(&self.secret_key).sign_prehashed( + prehashed_message, + &self.verifying_key, + context, + ) } /// Verify a signature on a message with this signing key's public key. @@ -459,6 +461,14 @@ impl SigningKey { ) -> Result<(), SignatureError> { self.verifying_key.verify_strict(message, signature) } + + /// Convert this signing key into a Curve25519 scalar. + /// + /// This is useful for e.g. performing X25519 Diffie-Hellman using + /// Ed25519 keys. + pub fn to_scalar(&self) -> Scalar { + ExpandedSecretKey::from(&self.secret_key).scalar + } } impl AsRef for SigningKey { @@ -726,14 +736,14 @@ impl<'d> Deserialize<'d> for SigningKey { // better-designed, Schnorr-based signature scheme, see Trevor Perrin's work on // "generalised EdDSA" and "VXEdDSA". pub(crate) struct ExpandedSecretKey { - pub(crate) key: Scalar, + pub(crate) scalar: Scalar, pub(crate) nonce: [u8; 32], } #[cfg(feature = "zeroize")] impl Drop for ExpandedSecretKey { fn drop(&mut self) { - self.key.zeroize(); + self.scalar.zeroize(); self.nonce.zeroize() } } @@ -747,7 +757,7 @@ impl From<&SecretKey> for ExpandedSecretKey { // The try_into here converts to fixed-size array ExpandedSecretKey { - key: Scalar::from_bits_clamped(lower.try_into().unwrap()), + scalar: Scalar::from_bits_clamped(lower.try_into().unwrap()), nonce: upper.try_into().unwrap(), } } @@ -771,7 +781,7 @@ impl ExpandedSecretKey { h.update(message); let k = Scalar::from_hash(h); - let s: Scalar = (k * self.key) + r; + let s: Scalar = (k * self.scalar) + r; InternalSignature { R, s }.into() } @@ -854,7 +864,7 @@ impl ExpandedSecretKey { .chain_update(&prehash[..]); let k = Scalar::from_hash(h); - let s: Scalar = (k * self.key) + r; + let s: Scalar = (k * self.scalar) + r; Ok(InternalSignature { R, s }.into()) } diff --git a/src/verifying.rs b/src/verifying.rs index 6b0ad49..7de4fd1 100644 --- a/src/verifying.rs +++ b/src/verifying.rs @@ -18,6 +18,7 @@ use curve25519_dalek::digest::generic_array::typenum::U64; use curve25519_dalek::digest::Digest; use curve25519_dalek::edwards::CompressedEdwardsY; use curve25519_dalek::edwards::EdwardsPoint; +use curve25519_dalek::montgomery::MontgomeryPoint; use curve25519_dalek::scalar::Scalar; use ed25519::signature::Verifier; @@ -88,7 +89,7 @@ impl PartialEq for VerifyingKey { impl From<&ExpandedSecretKey> for VerifyingKey { /// Derive this public key from its corresponding `ExpandedSecretKey`. fn from(expanded_secret_key: &ExpandedSecretKey) -> VerifyingKey { - let bits: [u8; 32] = expanded_secret_key.key.to_bytes(); + let bits: [u8; 32] = expanded_secret_key.scalar.to_bytes(); VerifyingKey::clamp_and_mul_base(bits) } } @@ -418,6 +419,21 @@ impl VerifyingKey { Err(InternalError::Verify.into()) } } + + /// Convert this verifying key into Montgomery form. + /// + /// This is useful for systems which perform X25519 Diffie-Hellman using + /// Ed25519 keys. + /// + /// When possible, it's recommended to use separate keys for signing and + /// Diffie-Hellman. + /// + /// For more information on the security of systems which use the same keys + /// for both signing and Diffie-Hellman, see the paper + /// [On using the same key pair for Ed25519 and an X25519 based KEM](https://eprint.iacr.org/2021/509.pdf). + pub fn to_montgomery(&self) -> MontgomeryPoint { + self.point.to_montgomery() + } } impl Verifier for VerifyingKey { diff --git a/tests/x25519.rs b/tests/x25519.rs new file mode 100644 index 0000000..bb588f7 --- /dev/null +++ b/tests/x25519.rs @@ -0,0 +1,54 @@ +//! Tests for converting Ed25519 keys into X25519 (Montgomery form) keys. + +use ed25519_dalek::SigningKey; +use hex_literal::hex; + +/// Tests that X25519 Diffie-Hellman works when using keys converted from Ed25519. +// TODO: generate test vectors using another implementation of Ed25519->X25519 +#[test] +fn ed25519_to_x25519_dh() { + // Keys from RFC8032 test vectors (from section 7.1) + let ed25519_secret_key_a = + hex!("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60"); + let ed25519_secret_key_b = + hex!("4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb"); + + let ed25519_signing_key_a = SigningKey::from_bytes(&ed25519_secret_key_a); + let ed25519_signing_key_b = SigningKey::from_bytes(&ed25519_secret_key_b); + + let scalar_a = ed25519_signing_key_a.to_scalar(); + let scalar_b = ed25519_signing_key_b.to_scalar(); + + assert_eq!( + scalar_a.to_bytes(), + hex!("307c83864f2833cb427a2ef1c00a013cfdff2768d980c0a3a520f006904de94f") + ); + assert_eq!( + scalar_b.to_bytes(), + hex!("68bd9ed75882d52815a97585caf4790a7f6c6b3b7f821c5e259a24b02e502e51") + ); + + let x25519_public_key_a = ed25519_signing_key_a.verifying_key().to_montgomery(); + let x25519_public_key_b = ed25519_signing_key_b.verifying_key().to_montgomery(); + + assert_eq!( + x25519_public_key_a.to_bytes(), + hex!("d85e07ec22b0ad881537c2f44d662d1a143cf830c57aca4305d85c7a90f6b62e") + ); + assert_eq!( + x25519_public_key_b.to_bytes(), + hex!("25c704c594b88afc00a76b69d1ed2b984d7e22550f3ed0802d04fbcd07d38d47") + ); + + let expected_shared_secret = + hex!("5166f24a6918368e2af831a4affadd97af0ac326bdf143596c045967cc00230e"); + + assert_eq!( + (x25519_public_key_a * scalar_b).to_bytes(), + expected_shared_secret + ); + assert_eq!( + (x25519_public_key_b * scalar_a).to_bytes(), + expected_shared_secret + ); +} From 80aac08c1ca4a4a14912707650413b59c989e79a Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Thu, 30 Mar 2023 15:00:52 -0400 Subject: [PATCH 347/351] Fixed repoerted speedup/slowdown percentages in README benchmarks (#297) --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 0d6ba03..feedee8 100644 --- a/README.md +++ b/README.md @@ -109,18 +109,18 @@ On an Intel 10700K running at stock comparing between the `curve25519-dalek` bac | Benchmark | u64 | simd +avx2 | fiat | | :--- | :---- | :--- | :--- | -| signing | 15.017 µs | 13.906 µs -7.3967% | 15.877 µs +14.188% | -| signature verification | 40.144 µs | 25.963 µs -35.603% | 42.118 µs +62.758% | -| strict signature verification | 41.334 µs | 27.874 µs -32.660% | 43.985 µs +57.763% | -| batch signature verification/4 | 109.44 µs | 81.778 µs -25.079% | 117.80 µs +43.629% | -| batch signature verification/8 | 182.75 µs | 138.40 µs -23.871% | 195.86 µs +40.665% | -| batch signature verification/16 | 328.67 µs | 251.39 µs -23.744% | 351.55 µs +39.901% | -| batch signature verification/32 | 619.49 µs | 477.36 µs -23.053% | 669.41 µs +39.966% | -| batch signature verification/64 | 1.2136 ms | 936.85 µs -22.543% | 1.3028 ms +38.808% | -| batch signature verification/96 | 1.8677 ms | 1.2357 ms -33.936% | 2.0552 ms +66.439% | -| batch signature verification/128| 2.3281 ms | 1.5795 ms -31.996% | 2.5596 ms +61.678% | -| batch signature verification/256| 4.1868 ms | 2.8864 ms -31.061% | 4.6494 ms +61.081% | -| keypair generation | 13.973 µs | 13.108 µs -6.5062% | 15.099 µs +15.407% | +| signing | 15.017 µs | 13.906 µs -7.3967% | 15.877 μs +5.7268% | +| signature verification | 40.144 µs | 25.963 µs -35.603% | 42.118 μs +4.9173% | +| strict signature verification | 41.334 µs | 27.874 µs -32.660% | 43.985 μs +6.4136% | +| batch signature verification/4 | 109.44 µs | 81.778 µs -25.079% | 117.80 μs +7.6389% | +| batch signature verification/8 | 182.75 µs | 138.40 µs -23.871% | 195.86 μs +7.1737% | +| batch signature verification/16 | 328.67 µs | 251.39 µs -23.744% | 351.55 μs +6.9614% | +| batch signature verification/32 | 619.49 µs | 477.36 µs -23.053% | 669.41 μs +8.0582% | +| batch signature verification/64 | 1.2136 ms | 936.85 µs -22.543% | 1.3028 ms +7.3500% | +| batch signature verification/96 | 1.8677 ms | 1.2357 ms -33.936% | 2.0552 ms +10.039% | +| batch signature verification/128| 2.3281 ms | 1.5795 ms -31.996% | 2.5596 ms +9.9437% | +| batch signature verification/256| 4.1868 ms | 2.8864 ms -31.061% | 4.6494 μs +11.049% | +| keypair generation | 13.973 µs | 13.108 µs -6.5062% | 15.099 μs +8.0584% | ## Batch Performance From 90f10ed0965ce3b5292700481351b40d9135c428 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 11 Apr 2023 19:19:36 +0200 Subject: [PATCH 348/351] Fix a typo (#300) --- src/verifying.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/verifying.rs b/src/verifying.rs index 7de4fd1..5dbbefc 100644 --- a/src/verifying.rs +++ b/src/verifying.rs @@ -170,7 +170,7 @@ impl VerifyingKey { /// Returns whether this is a _weak_ public key, i.e., if this public key has low order. /// - /// A weak public key can be used to generate a siganture that's valid for almost every + /// A weak public key can be used to generate a signature that's valid for almost every /// message. [`Self::verify_strict`] denies weak keys, but if you want to check for this /// property before verification, then use this method. pub fn is_weak(&self) -> bool { From 4afbf09e1cb15bedc6f79c25cec388b5cd436f0d Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Mon, 15 May 2023 00:50:38 -0400 Subject: [PATCH 349/351] Add `hazmat` module with `ExpandedSecretKey`, `raw_sign`, `raw_sign_prehashed` (#299) * Added raw_sign() and raw_sign_prehashed() functions * Renamed `nonce` to `hash_prefix` in signing because it's really not a nonce * Moved raw signing to hazmat module * impl From for VerifyingKey * Brought back ExpandedSecretKey; made raw_* functions take it as input * Added remaining features to docs.rs feature set * Removed redundant ExpandedSecretKey def; made raw signing use a generic CtxDigest * Implemented raw_verify with generic CtxDigest * Implemented raw_verify_prehashed with generic MsgDigest and CtxDigest * Wrote hazmat tests; fixed errors; switched ordering of MsgDigest and CtxDigest * Updated changelog * ExpandedSecretKey::from_bytes takes an array and is now infallible * Add TODO comment for split_array_ref * Added from_slice and TryFrom<&[u8]> for ExpandedSecretKey --------- Co-authored-by: Tony Arcieri --- CHANGELOG.md | 1 + Cargo.lock | 22 ++++ Cargo.toml | 8 +- README.md | 2 + src/hazmat.rs | 280 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 5 + src/signing.rs | 188 +++++++++++++------------------ src/verifying.rs | 191 ++++++++++++++++++++++---------- 8 files changed, 526 insertions(+), 171 deletions(-) create mode 100644 src/hazmat.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 40ddabe..3657c20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ Entries are listed in reverse chronological order per undeprecated major series. * Make all batch verification deterministic remove `batch_deterministic` ([#256](https://github.com/dalek-cryptography/ed25519-dalek/pull/256)) * Remove `ExpandedSecretKey` API ((#205)[https://github.com/dalek-cryptography/ed25519-dalek/pull/205]) * Rename `Keypair` → `SigningKey` and `PublicKey` → `VerifyingKey` +* Make `hazmat` feature to expose, `ExpandedSecretKey`, `raw_sign()`, `raw_sign_prehashed()`, `raw_verify()`, and `raw_verify_prehashed()` ### Other changes diff --git a/Cargo.lock b/Cargo.lock index 1eb6a42..5ef6955 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,6 +46,15 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + [[package]] name = "block-buffer" version = "0.10.3" @@ -272,6 +281,7 @@ checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] [[package]] @@ -290,6 +300,7 @@ name = "ed25519-dalek" version = "2.0.0-rc.2" dependencies = [ "bincode", + "blake2", "criterion", "curve25519-dalek", "ed25519", @@ -301,6 +312,7 @@ dependencies = [ "serde", "serde_json", "sha2", + "sha3", "signature", "toml", "zeroize", @@ -736,6 +748,16 @@ dependencies = [ "cc", ] +[[package]] +name = "sha3" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54c2bb1a323307527314a36bfb73f24febb08ce2b8a554bf4ffd6f51ad15198c" +dependencies = [ + "digest", + "keccak", +] + [[package]] name = "signature" version = "2.0.0" diff --git a/Cargo.toml b/Cargo.toml index 5c73858..cdbe10a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ rustdoc-args = [ "--html-in-header", "docs/assets/rustdoc-include-katex-header.html", "--cfg", "docsrs", ] -features = ["batch", "pkcs8"] +features = ["batch", "digest", "hazmat", "pem", "serde"] [dependencies] curve25519-dalek = { version = "=4.0.0-rc.2", default-features = false, features = ["digest"] } @@ -38,6 +38,8 @@ zeroize = { version = "1.5", default-features = false, optional = true } [dev-dependencies] curve25519-dalek = { version = "=4.0.0-rc.2", default-features = false, features = ["digest", "rand_core"] } +blake2 = "0.10" +sha3 = "0.10" hex = "0.4" bincode = "1.0" serde_json = "1.0" @@ -62,7 +64,9 @@ asm = ["sha2/asm"] batch = ["alloc", "merlin", "rand_core"] fast = ["curve25519-dalek/precomputed-tables"] digest = ["signature/digest"] -# This features turns off stricter checking for scalar malleability in signatures +# Exposes the hazmat module +hazmat = [] +# Turns off stricter checking for scalar malleability in signatures legacy_compatibility = [] pkcs8 = ["ed25519/pkcs8"] pem = ["alloc", "ed25519/pem", "pkcs8"] diff --git a/README.md b/README.md index feedee8..c5c279f 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ This crate is `#[no_std]` compatible with `default-features = false`. | `pkcs8` | | Enables [PKCS#8](https://en.wikipedia.org/wiki/PKCS_8) serialization/deserialization for `SigningKey` and `VerifyingKey` | | `pem` | | Enables PEM serialization support for PKCS#8 private keys and SPKI public keys. Also enables `alloc`. | | `legacy_compatibility` | | **Unsafe:** Disables certain signature checks. See [below](#malleability-and-the-legacy_compatibility-feature) | +| `hazmat` | | **Unsafe:** Exposes the `hazmat` module for raw signing/verifying. Misuse of these functions will expose the private key, as in the [signing oracle attack](https://github.com/MystenLabs/ed25519-unsafe-libs). | # Major Changes @@ -54,6 +55,7 @@ See [CHANGELOG.md](CHANGELOG.md) for a list of changes made in past version of t * Make all batch verification deterministic remove `batch_deterministic` ([#256](https://github.com/dalek-cryptography/ed25519-dalek/pull/256)) * Remove `ExpandedSecretKey` API ((#205)[https://github.com/dalek-cryptography/ed25519-dalek/pull/205]) * Rename `Keypair` → `SigningKey` and `PublicKey` → `VerifyingKey` +* Make `hazmat` feature to expose, `ExpandedSecretKey`, `raw_sign()`, `raw_sign_prehashed()`, `raw_verify()`, and `raw_verify_prehashed()` # Documentation diff --git a/src/hazmat.rs b/src/hazmat.rs new file mode 100644 index 0000000..5f16d3a --- /dev/null +++ b/src/hazmat.rs @@ -0,0 +1,280 @@ +//! Low-level interfaces to ed25519 functions +//! +//! # ⚠️ Warning: Hazmat +//! +//! These primitives are easy-to-misuse low-level interfaces. +//! +//! If you are an end user / non-expert in cryptography, **do not use any of these functions**. +//! Failure to use them correctly can lead to catastrophic failures including **full private key +//! recovery.** + +// Permit dead code because 1) this module is only public when the `hazmat` feature is set, and 2) +// even without `hazmat` we still need this module because this is where `ExpandedSecretKey` is +// defined. +#![allow(dead_code)] + +use crate::{InternalError, SignatureError}; + +use curve25519_dalek::Scalar; + +#[cfg(feature = "zeroize")] +use zeroize::{Zeroize, ZeroizeOnDrop}; + +// These are used in the functions that are made public when the hazmat feature is set +use crate::{Signature, VerifyingKey}; +use curve25519_dalek::digest::{generic_array::typenum::U64, Digest}; + +/// Contains the secret scalar and domain separator used for generating signatures. +/// +/// This is used internally for signing. +/// +/// In the usual Ed25519 signing algorithm, `scalar` and `hash_prefix` are defined such that +/// `scalar || hash_prefix = H(sk)` where `sk` is the signing key and `H` is SHA-512. +/// **WARNING:** Deriving the values for these fields in any other way can lead to full key +/// recovery, as documented in [`raw_sign`] and [`raw_sign_prehashed`]. +/// +/// Instances of this secret are automatically overwritten with zeroes when they fall out of scope. +pub struct ExpandedSecretKey { + /// The secret scalar used for signing + pub scalar: Scalar, + /// The domain separator used when hashing the message to generate the pseudorandom `r` value + pub hash_prefix: [u8; 32], +} + +#[cfg(feature = "zeroize")] +impl Drop for ExpandedSecretKey { + fn drop(&mut self) { + self.scalar.zeroize(); + self.hash_prefix.zeroize() + } +} + +#[cfg(feature = "zeroize")] +impl ZeroizeOnDrop for ExpandedSecretKey {} + +// Some conversion methods for `ExpandedSecretKey`. The signing methods are defined in +// `signing.rs`, since we need them even when `not(feature = "hazmat")` +impl ExpandedSecretKey { + /// Convert this `ExpandedSecretKey` into an array of 64 bytes. + pub fn to_bytes(&self) -> [u8; 64] { + let mut bytes: [u8; 64] = [0u8; 64]; + + bytes[..32].copy_from_slice(self.scalar.as_bytes()); + bytes[32..].copy_from_slice(&self.hash_prefix[..]); + bytes + } + + /// Construct an `ExpandedSecretKey` from an array of 64 bytes. + pub fn from_bytes(bytes: &[u8; 64]) -> Self { + // TODO: Use bytes.split_array_ref once it’s in MSRV. + let mut lower: [u8; 32] = [0u8; 32]; + let mut upper: [u8; 32] = [0u8; 32]; + + lower.copy_from_slice(&bytes[00..32]); + upper.copy_from_slice(&bytes[32..64]); + + ExpandedSecretKey { + scalar: Scalar::from_bytes_mod_order(lower), + hash_prefix: upper, + } + } + + /// Construct an `ExpandedSecretKey` from a slice of 64 bytes. + /// + /// # Returns + /// + /// A `Result` whose okay value is an EdDSA `ExpandedSecretKey` or whose error value is an + /// `SignatureError` describing the error that occurred, namely that the given slice's length + /// is not 64. + #[allow(clippy::unwrap_used)] + pub fn from_slice(bytes: &[u8]) -> Result { + if bytes.len() != 64 { + Err(InternalError::BytesLength { + name: "ExpandedSecretKey", + length: 64, + } + .into()) + } else { + // If the input is 64 bytes long, coerce it to a 64-byte array + Ok(Self::from_bytes(bytes.try_into().unwrap())) + } + } +} + +impl TryFrom<&[u8]> for ExpandedSecretKey { + type Error = SignatureError; + + fn try_from(bytes: &[u8]) -> Result { + Self::from_slice(bytes) + } +} + +/// Compute an ordinary Ed25519 signature over the given message. `CtxDigest` is the digest used to +/// calculate the pseudorandomness needed for signing. According to the Ed25519 spec, `CtxDigest = +/// Sha512`. +/// +/// # ⚠️ Unsafe +/// +/// Do NOT use this function unless you absolutely must. Using the wrong values in +/// `ExpandedSecretKey` can leak your signing key. See +/// [here](https://github.com/MystenLabs/ed25519-unsafe-libs) for more details on this attack. +pub fn raw_sign( + esk: &ExpandedSecretKey, + message: &[u8], + verifying_key: &VerifyingKey, +) -> Signature +where + CtxDigest: Digest, +{ + esk.raw_sign::(message, verifying_key) +} + +/// Compute a signature over the given prehashed message, the Ed25519ph algorithm defined in +/// [RFC8032 §5.1][rfc8032]. `MsgDigest` is the digest function used to hash the signed message. +/// `CtxDigest` is the digest function used to calculate the pseudorandomness needed for signing. +/// According to the Ed25519 spec, `MsgDigest = CtxDigest = Sha512`. +/// +/// # ⚠️ Unsafe +// +/// Do NOT use this function unless you absolutely must. Using the wrong values in +/// `ExpandedSecretKey` can leak your signing key. See +/// [here](https://github.com/MystenLabs/ed25519-unsafe-libs) for more details on this attack. +/// +/// # Inputs +/// +/// * `esk` is the [`ExpandedSecretKey`] being used for signing +/// * `prehashed_message` is an instantiated hash digest with 512-bits of +/// output which has had the message to be signed previously fed into its +/// state. +/// * `verifying_key` is a [`VerifyingKey`] which corresponds to this secret key. +/// * `context` is an optional context string, up to 255 bytes inclusive, +/// which may be used to provide additional domain separation. If not +/// set, this will default to an empty string. +/// +/// `scalar` and `hash_prefix` are usually selected such that `scalar || hash_prefix = H(sk)` where +/// `sk` is the signing key +/// +/// # Returns +/// +/// A `Result` whose `Ok` value is an Ed25519ph [`Signature`] on the +/// `prehashed_message` if the context was 255 bytes or less, otherwise +/// a `SignatureError`. +/// +/// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 +#[cfg(feature = "digest")] +#[allow(non_snake_case)] +pub fn raw_sign_prehashed<'a, CtxDigest, MsgDigest>( + esk: &ExpandedSecretKey, + prehashed_message: MsgDigest, + verifying_key: &VerifyingKey, + context: Option<&'a [u8]>, +) -> Result +where + MsgDigest: Digest, + CtxDigest: Digest, +{ + esk.raw_sign_prehashed::(prehashed_message, verifying_key, context) +} + +/// The ordinary non-batched Ed25519 verification check, rejecting non-canonical R +/// values.`CtxDigest` is the digest used to calculate the pseudorandomness needed for signing. +/// According to the Ed25519 spec, `CtxDigest = Sha512`. +pub fn raw_verify( + vk: &VerifyingKey, + message: &[u8], + signature: &ed25519::Signature, +) -> Result<(), SignatureError> +where + CtxDigest: Digest, +{ + vk.raw_verify::(message, signature) +} + +/// The batched Ed25519 verification check, rejecting non-canonical R values. `MsgDigest` is the +/// digest used to hash the signed message. `CtxDigest` is the digest used to calculate the +/// pseudorandomness needed for signing. According to the Ed25519 spec, `MsgDigest = CtxDigest = +/// Sha512`. +#[cfg(feature = "digest")] +#[allow(non_snake_case)] +pub fn raw_verify_prehashed( + vk: &VerifyingKey, + prehashed_message: MsgDigest, + context: Option<&[u8]>, + signature: &ed25519::Signature, +) -> Result<(), SignatureError> +where + MsgDigest: Digest, + CtxDigest: Digest, +{ + vk.raw_verify_prehashed::(prehashed_message, context, signature) +} + +#[cfg(test)] +mod test { + use super::*; + + use curve25519_dalek::Scalar; + use rand::{rngs::OsRng, CryptoRng, RngCore}; + + // Pick distinct, non-spec 512-bit hash functions for message and sig-context hashing + type CtxDigest = blake2::Blake2b512; + type MsgDigest = sha3::Sha3_512; + + impl ExpandedSecretKey { + // Make a random expanded secret key for testing purposes. This is NOT how you generate + // expanded secret keys IRL. They're the hash of a seed. + fn random(mut rng: R) -> Self { + // The usual signing algorithm clamps its scalars + let scalar_bytes = [0u8; 32]; + let scalar = Scalar::from_bits_clamped(scalar_bytes); + + let mut hash_prefix = [0u8; 32]; + rng.fill_bytes(&mut hash_prefix); + + ExpandedSecretKey { + scalar, + hash_prefix, + } + } + } + + // Check that raw_sign and raw_verify work when a non-spec CtxDigest is used + #[test] + fn sign_verify_nonspec() { + // Generate the keypair + let mut rng = OsRng; + let esk = ExpandedSecretKey::random(&mut rng); + let vk = VerifyingKey::from(&esk); + + let msg = b"Then one day, a piano fell on my head"; + + // Sign and verify + let sig = raw_sign::(&esk, msg, &vk); + raw_verify::(&vk, msg, &sig).unwrap(); + } + + // Check that raw_sign_prehashed and raw_verify_prehashed work when distinct, non-spec + // MsgDigest and CtxDigest are used + #[cfg(feature = "digest")] + #[test] + fn sign_verify_prehashed_nonspec() { + use curve25519_dalek::digest::Digest; + + // Generate the keypair + let mut rng = OsRng; + let esk = ExpandedSecretKey::random(&mut rng); + let vk = VerifyingKey::from(&esk); + + // Hash the message + let msg = b"And then I got trampled by a herd of buffalo"; + let mut h = MsgDigest::new(); + h.update(msg); + + let ctx_str = &b"consequences"[..]; + + // Sign and verify prehashed + let sig = raw_sign_prehashed::(&esk, h.clone(), &vk, Some(ctx_str)) + .unwrap(); + raw_verify_prehashed::(&vk, h, Some(ctx_str), &sig).unwrap(); + } +} diff --git a/src/lib.rs b/src/lib.rs index 225d871..a7cfac4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -264,6 +264,11 @@ mod signature; mod signing; mod verifying; +#[cfg(feature = "hazmat")] +pub mod hazmat; +#[cfg(not(feature = "hazmat"))] +mod hazmat; + #[cfg(feature = "digest")] pub use curve25519_dalek::digest::Digest; #[cfg(feature = "digest")] diff --git a/src/signing.rs b/src/signing.rs index 93084b8..500f8b5 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -20,12 +20,11 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use sha2::Sha512; -#[cfg(feature = "digest")] -use curve25519_dalek::digest::generic_array::typenum::U64; -use curve25519_dalek::digest::Digest; -use curve25519_dalek::edwards::CompressedEdwardsY; -use curve25519_dalek::edwards::EdwardsPoint; -use curve25519_dalek::scalar::Scalar; +use curve25519_dalek::{ + digest::{generic_array::typenum::U64, Digest}, + edwards::{CompressedEdwardsY, EdwardsPoint}, + scalar::Scalar, +}; use ed25519::signature::{KeypairRef, Signer, Verifier}; @@ -37,11 +36,14 @@ use signature::DigestSigner; #[cfg(feature = "zeroize")] use zeroize::{Zeroize, ZeroizeOnDrop}; -use crate::constants::*; -use crate::errors::*; -use crate::signature::*; -use crate::verifying::*; -use crate::Signature; +use crate::{ + constants::{KEYPAIR_LENGTH, SECRET_KEY_LENGTH}, + errors::{InternalError, SignatureError}, + hazmat::ExpandedSecretKey, + signature::InternalSignature, + verifying::VerifyingKey, + Signature, +}; /// ed25519 secret key as defined in [RFC8032 § 5.1.5]: /// @@ -202,7 +204,9 @@ impl SigningKey { /// /// # Inputs /// - /// * `prehashed_message` is an instantiated SHA-512 digest of the message + /// * `prehashed_message` is an instantiated hash digest with 512-bits of + /// output which has had the message to be signed previously fed into its + /// state. /// * `context` is an optional context string, up to 255 bytes inclusive, /// which may be used to provide additional domain separation. If not /// set, this will default to an empty string. @@ -213,10 +217,10 @@ impl SigningKey { /// /// # Note /// - /// The RFC only permits SHA-512 to be used for prehashing. This function technically works, - /// and is probably safe to use, with any secure hash function with 512-bit digests, but - /// anything outside of SHA-512 is NOT specification-compliant. We expose [`crate::Sha512`] for - /// user convenience. + /// The RFC only permits SHA-512 to be used for prehashing, i.e., `MsgDigest = Sha512`. This + /// function technically works, and is probably safe to use, with any secure hash function with + /// 512-bit digests, but anything outside of SHA-512 is NOT specification-compliant. We expose + /// [`crate::Sha512`] for user convenience. /// /// # Examples /// @@ -297,15 +301,15 @@ impl SigningKey { /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 /// [terrible_idea]: https://github.com/isislovecruft/scripts/blob/master/gpgkey2bc.py #[cfg(feature = "digest")] - pub fn sign_prehashed( + pub fn sign_prehashed( &self, - prehashed_message: D, + prehashed_message: MsgDigest, context: Option<&[u8]>, ) -> Result where - D: Digest, + MsgDigest: Digest, { - ExpandedSecretKey::from(&self.secret_key).sign_prehashed( + ExpandedSecretKey::from(&self.secret_key).raw_sign_prehashed::( prehashed_message, &self.verifying_key, context, @@ -334,6 +338,13 @@ impl SigningKey { /// Returns `true` if the `signature` was a valid signature created by this /// [`SigningKey`] on the `prehashed_message`. /// + /// # Note + /// + /// The RFC only permits SHA-512 to be used for prehashing, i.e., `MsgDigest = Sha512`. This + /// function technically works, and is probably safe to use, with any secure hash function with + /// 512-bit digests, but anything outside of SHA-512 is NOT specification-compliant. We expose + /// [`crate::Sha512`] for user convenience. + /// /// # Examples /// #[cfg_attr(all(feature = "rand_core", feature = "digest"), doc = "```")] @@ -378,14 +389,14 @@ impl SigningKey { /// /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 #[cfg(feature = "digest")] - pub fn verify_prehashed( + pub fn verify_prehashed( &self, - prehashed_message: D, + prehashed_message: MsgDigest, context: Option<&[u8]>, signature: &Signature, ) -> Result<(), SignatureError> where - D: Digest, + MsgDigest: Digest, { self.verifying_key .verify_prehashed(prehashed_message, context, signature) @@ -485,7 +496,7 @@ impl Signer for SigningKey { /// Sign a message with this signing key's secret key. fn try_sign(&self, message: &[u8]) -> Result { let expanded: ExpandedSecretKey = (&self.secret_key).into(); - Ok(expanded.sign(message, &self.verifying_key)) + Ok(expanded.raw_sign::(message, &self.verifying_key)) } } @@ -697,57 +708,9 @@ impl<'d> Deserialize<'d> for SigningKey { } } -/// An "expanded" secret key. -/// -/// This is produced by using an hash function with 512-bits output to digest a -/// `SecretKey`. The output digest is then split in half, the lower half being -/// the actual `key` used to sign messages, after twiddling with some bits.¹ The -/// upper half is used a sort of half-baked, ill-designed² pseudo-domain-separation -/// "nonce"-like thing, which is used during signature production by -/// concatenating it with the message to be signed before the message is hashed. -/// -/// Instances of this secret are automatically overwritten with zeroes when they -/// fall out of scope. -// -// ¹ This results in a slight bias towards non-uniformity at one spectrum of -// the range of valid keys. Oh well: not my idea; not my problem. -// -// ² It is the author's view (specifically, isis agora lovecruft, in the event -// you'd like to complain about me, again) that this is "ill-designed" because -// this doesn't actually provide true hash domain separation, in that in many -// real-world applications a user wishes to have one key which is used in -// several contexts (such as within tor, which does domain separation -// manually by pre-concatenating static strings to messages to achieve more -// robust domain separation). In other real-world applications, such as -// bitcoind, a user might wish to have one master keypair from which others are -// derived (à la BIP32) and different domain separators between keys derived at -// different levels (and similarly for tree-based key derivation constructions, -// such as hash-based signatures). Leaving the domain separation to -// application designers, who thus far have produced incompatible, -// slightly-differing, ad hoc domain separation (at least those application -// designers who knew enough cryptographic theory to do so!), is therefore a -// bad design choice on the part of the cryptographer designing primitives -// which should be simple and as foolproof as possible to use for -// non-cryptographers. Further, later in the ed25519 signature scheme, as -// specified in RFC8032, the public key is added into *another* hash digest -// (along with the message, again); it is unclear to this author why there's -// not only one but two poorly-thought-out attempts at domain separation in the -// same signature scheme, and which both fail in exactly the same way. For a -// better-designed, Schnorr-based signature scheme, see Trevor Perrin's work on -// "generalised EdDSA" and "VXEdDSA". -pub(crate) struct ExpandedSecretKey { - pub(crate) scalar: Scalar, - pub(crate) nonce: [u8; 32], -} - -#[cfg(feature = "zeroize")] -impl Drop for ExpandedSecretKey { - fn drop(&mut self) { - self.scalar.zeroize(); - self.nonce.zeroize() - } -} - +/// The spec-compliant way to define an expanded secret key. This computes `SHA512(sk)`, clamps the +/// first 32 bytes and uses it as a scalar, and uses the second 32 bytes as a domain separator for +/// hashing. impl From<&SecretKey> for ExpandedSecretKey { #[allow(clippy::unwrap_used)] fn from(secret_key: &SecretKey) -> ExpandedSecretKey { @@ -758,24 +721,42 @@ impl From<&SecretKey> for ExpandedSecretKey { // The try_into here converts to fixed-size array ExpandedSecretKey { scalar: Scalar::from_bits_clamped(lower.try_into().unwrap()), - nonce: upper.try_into().unwrap(), + hash_prefix: upper.try_into().unwrap(), } } } -impl ExpandedSecretKey { - /// Sign a message with this `ExpandedSecretKey`. - #[allow(non_snake_case)] - pub(crate) fn sign(&self, message: &[u8], verifying_key: &VerifyingKey) -> Signature { - let mut h: Sha512 = Sha512::new(); +// +// Signing functions. These are pub(crate) so that the `hazmat` module can use them +// - h.update(self.nonce); +impl ExpandedSecretKey { + /// The plain, non-prehashed, signing function for Ed25519. `CtxDigest` is the digest used to + /// calculate the pseudorandomness needed for signing. According to the spec, `CtxDigest = + /// Sha512`, and `self` is derived via the method defined in `impl From<&SigningKey> for + /// ExpandedSecretKey`. + /// + /// This definition is loose in its parameters so that end-users of the `hazmat` module can + /// change how the `ExpandedSecretKey` is calculated and which hash function to use. + #[allow(non_snake_case)] + #[inline(always)] + pub(crate) fn raw_sign( + &self, + message: &[u8], + verifying_key: &VerifyingKey, + ) -> Signature + where + CtxDigest: Digest, + { + let mut h = CtxDigest::new(); + + h.update(self.hash_prefix); h.update(message); let r = Scalar::from_hash(h); let R: CompressedEdwardsY = EdwardsPoint::mul_base(&r).compress(); - h = Sha512::new(); + h = CtxDigest::new(); h.update(R.as_bytes()); h.update(verifying_key.as_bytes()); h.update(message); @@ -786,38 +767,27 @@ impl ExpandedSecretKey { InternalSignature { R, s }.into() } - /// Sign a `prehashed_message` with this `ExpandedSecretKey` using the - /// Ed25519ph algorithm defined in [RFC8032 §5.1][rfc8032]. + /// The prehashed signing function for Ed25519 (i.e., Ed25519ph). `CtxDigest` is the digest + /// function used to calculate the pseudorandomness needed for signing. `MsgDigest` is the + /// digest function used to hash the signed message. According to the spec, `MsgDigest = + /// CtxDigest = Sha512`, and `self` is derived via the method defined in `impl + /// From<&SigningKey> for ExpandedSecretKey`. /// - /// # Inputs - /// - /// * `prehashed_message` is an instantiated hash digest with 512-bits of - /// output which has had the message to be signed previously fed into its - /// state. - /// * `verifying_key` is a [`VerifyingKey`] which corresponds to this secret key. - /// * `context` is an optional context string, up to 255 bytes inclusive, - /// which may be used to provide additional domain separation. If not - /// set, this will default to an empty string. - /// - /// # Returns - /// - /// A `Result` whose `Ok` value is an Ed25519ph [`Signature`] on the - /// `prehashed_message` if the context was 255 bytes or less, otherwise - /// a `SignatureError`. - /// - /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 + /// This definition is loose in its parameters so that end-users of the `hazmat` module can + /// change how the `ExpandedSecretKey` is calculated and which `CtxDigest` function to use. #[cfg(feature = "digest")] #[allow(non_snake_case)] - pub(crate) fn sign_prehashed<'a, D>( + #[inline(always)] + pub(crate) fn raw_sign_prehashed<'a, CtxDigest, MsgDigest>( &self, - prehashed_message: D, + prehashed_message: MsgDigest, verifying_key: &VerifyingKey, context: Option<&'a [u8]>, ) -> Result where - D: Digest, + CtxDigest: Digest, + MsgDigest: Digest, { - let mut h: Sha512; let mut prehash: [u8; 64] = [0u8; 64]; let ctx: &[u8] = context.unwrap_or(b""); // By default, the context is an empty string. @@ -843,18 +813,18 @@ impl ExpandedSecretKey { // // This is a really fucking stupid bandaid, and the damned scheme is // still bleeding from malleability, for fuck's sake. - h = Sha512::new() + let mut h = CtxDigest::new() .chain_update(b"SigEd25519 no Ed25519 collisions") .chain_update([1]) // Ed25519ph .chain_update([ctx_len]) .chain_update(ctx) - .chain_update(self.nonce) + .chain_update(self.hash_prefix) .chain_update(&prehash[..]); let r = Scalar::from_hash(h); let R: CompressedEdwardsY = EdwardsPoint::mul_base(&r).compress(); - h = Sha512::new() + h = CtxDigest::new() .chain_update(b"SigEd25519 no Ed25519 collisions") .chain_update([1]) // Ed25519ph .chain_update([ctx_len]) diff --git a/src/verifying.rs b/src/verifying.rs index 5dbbefc..e97e203 100644 --- a/src/verifying.rs +++ b/src/verifying.rs @@ -13,13 +13,12 @@ use core::convert::TryFrom; use core::fmt::Debug; use core::hash::{Hash, Hasher}; -#[cfg(feature = "digest")] -use curve25519_dalek::digest::generic_array::typenum::U64; -use curve25519_dalek::digest::Digest; -use curve25519_dalek::edwards::CompressedEdwardsY; -use curve25519_dalek::edwards::EdwardsPoint; -use curve25519_dalek::montgomery::MontgomeryPoint; -use curve25519_dalek::scalar::Scalar; +use curve25519_dalek::{ + digest::{generic_array::typenum::U64, Digest}, + edwards::{CompressedEdwardsY, EdwardsPoint}, + montgomery::MontgomeryPoint, + scalar::Scalar, +}; use ed25519::signature::Verifier; @@ -36,10 +35,13 @@ use crate::context::Context; #[cfg(feature = "digest")] use signature::DigestVerifier; -use crate::constants::*; -use crate::errors::*; -use crate::signature::*; -use crate::signing::*; +use crate::{ + constants::PUBLIC_KEY_LENGTH, + errors::{InternalError, SignatureError}, + hazmat::ExpandedSecretKey, + signature::InternalSignature, + signing::SigningKey, +}; /// An ed25519 public key. /// @@ -100,6 +102,15 @@ impl From<&SigningKey> for VerifyingKey { } } +impl From for VerifyingKey { + fn from(point: EdwardsPoint) -> VerifyingKey { + VerifyingKey { + point, + compressed: point.compress(), + } + } +} + impl VerifyingKey { /// Convert this public key to a byte array. #[inline] @@ -188,16 +199,20 @@ impl VerifyingKey { VerifyingKey { compressed, point } } - // A helper function that computes H(R || A || M). If `context.is_some()`, this does the - // prehashed variant of the computation using its contents. + // A helper function that computes `H(R || A || M)` where `H` is the 512-bit hash function + // given by `CtxDigest` (this is SHA-512 in spec-compliant Ed25519). If `context.is_some()`, + // this does the prehashed variant of the computation using its contents. #[allow(non_snake_case)] - fn compute_challenge( + fn compute_challenge( context: Option<&[u8]>, R: &CompressedEdwardsY, A: &CompressedEdwardsY, M: &[u8], - ) -> Scalar { - let mut h = Sha512::new(); + ) -> Scalar + where + CtxDigest: Digest, + { + let mut h = CtxDigest::new(); if let Some(c) = context { h.update(b"SigEd25519 no Ed25519 collisions"); h.update([1]); // Ed25519ph @@ -219,18 +234,83 @@ impl VerifyingKey { // See the validation criteria blog post for more details: // https://hdevalence.ca/blog/2020-10-04-its-25519am #[allow(non_snake_case)] - fn recompute_r( + fn recompute_R( &self, context: Option<&[u8]>, signature: &InternalSignature, M: &[u8], - ) -> CompressedEdwardsY { - let k = Self::compute_challenge(context, &signature.R, &self.compressed, M); + ) -> CompressedEdwardsY + where + CtxDigest: Digest, + { + let k = Self::compute_challenge::(context, &signature.R, &self.compressed, M); let minus_A: EdwardsPoint = -self.point; // Recall the (non-batched) verification equation: -[k]A + [s]B = R EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s).compress() } + /// The ordinary non-batched Ed25519 verification check, rejecting non-canonical R values. (see + /// [`Self::recompute_R`]). `CtxDigest` is the digest used to calculate the pseudorandomness + /// needed for signing. According to the spec, `CtxDigest = Sha512`. + /// + /// This definition is loose in its parameters so that end-users of the `hazmat` module can + /// change how the `ExpandedSecretKey` is calculated and which hash function to use. + #[allow(non_snake_case)] + pub(crate) fn raw_verify( + &self, + message: &[u8], + signature: &ed25519::Signature, + ) -> Result<(), SignatureError> + where + CtxDigest: Digest, + { + let signature = InternalSignature::try_from(signature)?; + + let expected_R = self.recompute_R::(None, &signature, message); + if expected_R == signature.R { + Ok(()) + } else { + Err(InternalError::Verify.into()) + } + } + + /// The prehashed non-batched Ed25519 verification check, rejecting non-canonical R values. + /// (see [`Self::recompute_R`]). `CtxDigest` is the digest used to calculate the + /// pseudorandomness needed for signing. `MsgDigest` is the digest used to hash the signed + /// message. According to the spec, `MsgDigest = CtxDigest = Sha512`. + /// + /// This definition is loose in its parameters so that end-users of the `hazmat` module can + /// change how the `ExpandedSecretKey` is calculated and which hash function to use. + #[cfg(feature = "digest")] + #[allow(non_snake_case)] + pub(crate) fn raw_verify_prehashed( + &self, + prehashed_message: MsgDigest, + context: Option<&[u8]>, + signature: &ed25519::Signature, + ) -> Result<(), SignatureError> + where + CtxDigest: Digest, + MsgDigest: Digest, + { + let signature = InternalSignature::try_from(signature)?; + + let ctx: &[u8] = context.unwrap_or(b""); + debug_assert!( + ctx.len() <= 255, + "The context must not be longer than 255 octets." + ); + + let message = prehashed_message.finalize(); + let expected_R = self.recompute_R::(Some(ctx), &signature, &message); + + if expected_R == signature.R { + Ok(()) + } else { + Err(InternalError::Verify.into()) + } + } + /// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm. /// /// # Inputs @@ -246,34 +326,26 @@ impl VerifyingKey { /// # Returns /// /// Returns `true` if the `signature` was a valid signature created by this - /// `Keypair` on the `prehashed_message`. + /// [`SigningKey`] on the `prehashed_message`. + /// + /// # Note + /// + /// The RFC only permits SHA-512 to be used for prehashing, i.e., `MsgDigest = Sha512`. This + /// function technically works, and is probably safe to use, with any secure hash function with + /// 512-bit digests, but anything outside of SHA-512 is NOT specification-compliant. We expose + /// [`crate::Sha512`] for user convenience. #[cfg(feature = "digest")] #[allow(non_snake_case)] - pub fn verify_prehashed( + pub fn verify_prehashed( &self, - prehashed_message: D, + prehashed_message: MsgDigest, context: Option<&[u8]>, signature: &ed25519::Signature, ) -> Result<(), SignatureError> where - D: Digest, + MsgDigest: Digest, { - let signature = InternalSignature::try_from(signature)?; - - let ctx: &[u8] = context.unwrap_or(b""); - debug_assert!( - ctx.len() <= 255, - "The context must not be longer than 255 octets." - ); - - let message = prehashed_message.finalize(); - let expected_R = self.recompute_r(Some(ctx), &signature, &message); - - if expected_R == signature.R { - Ok(()) - } else { - Err(InternalError::Verify.into()) - } + self.raw_verify_prehashed::(prehashed_message, context, signature) } /// Strictly verify a signature on a message with this keypair's public key. @@ -356,7 +428,7 @@ impl VerifyingKey { return Err(InternalError::Verify.into()); } - let expected_R = self.recompute_r(None, &signature, message); + let expected_R = self.recompute_R::(None, &signature, message); if expected_R == signature.R { Ok(()) } else { @@ -380,17 +452,24 @@ impl VerifyingKey { /// # Returns /// /// Returns `true` if the `signature` was a valid signature created by this - /// `Keypair` on the `prehashed_message`. + /// [`SigningKey`] on the `prehashed_message`. + /// + /// # Note + /// + /// The RFC only permits SHA-512 to be used for prehashing, i.e., `MsgDigest = Sha512`. This + /// function technically works, and is probably safe to use, with any secure hash function with + /// 512-bit digests, but anything outside of SHA-512 is NOT specification-compliant. We expose + /// [`crate::Sha512`] for user convenience. #[cfg(feature = "digest")] #[allow(non_snake_case)] - pub fn verify_prehashed_strict( + pub fn verify_prehashed_strict( &self, - prehashed_message: D, + prehashed_message: MsgDigest, context: Option<&[u8]>, signature: &ed25519::Signature, ) -> Result<(), SignatureError> where - D: Digest, + MsgDigest: Digest, { let signature = InternalSignature::try_from(signature)?; @@ -411,7 +490,7 @@ impl VerifyingKey { } let message = prehashed_message.finalize(); - let expected_R = self.recompute_r(Some(ctx), &signature, &message); + let expected_R = self.recompute_R::(Some(ctx), &signature, &message); if expected_R == signature.R { Ok(()) @@ -442,28 +521,20 @@ impl Verifier for VerifyingKey { /// # Return /// /// Returns `Ok(())` if the signature is valid, and `Err` otherwise. - #[allow(non_snake_case)] fn verify(&self, message: &[u8], signature: &ed25519::Signature) -> Result<(), SignatureError> { - let signature = InternalSignature::try_from(signature)?; - - let expected_R = self.recompute_r(None, &signature, message); - if expected_R == signature.R { - Ok(()) - } else { - Err(InternalError::Verify.into()) - } + self.raw_verify::(message, signature) } } /// Equivalent to [`VerifyingKey::verify_prehashed`] with `context` set to [`None`]. #[cfg(feature = "digest")] -impl DigestVerifier for VerifyingKey +impl DigestVerifier for VerifyingKey where - D: Digest, + MsgDigest: Digest, { fn verify_digest( &self, - msg_digest: D, + msg_digest: MsgDigest, signature: &ed25519::Signature, ) -> Result<(), SignatureError> { self.verify_prehashed(msg_digest, None, signature) @@ -473,13 +544,13 @@ where /// Equivalent to [`VerifyingKey::verify_prehashed`] with `context` set to [`Some`] /// containing `self.value()`. #[cfg(feature = "digest")] -impl DigestVerifier for Context<'_, '_, VerifyingKey> +impl DigestVerifier for Context<'_, '_, VerifyingKey> where - D: Digest, + MsgDigest: Digest, { fn verify_digest( &self, - msg_digest: D, + msg_digest: MsgDigest, signature: &ed25519::Signature, ) -> Result<(), SignatureError> { self.key() From 9b166b75e0bb0c22bd782665f63638efef72556a Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Mon, 12 Jun 2023 00:06:00 -0400 Subject: [PATCH 350/351] Update to new `Scalar` API (#293) * Updated to new curve25519 scalar API * Made ExpandedSecretKey.scalar_bytes unclamped; clamping occurs in all scalar-point multiplication * Added legacy compat deprecation notice * Removed deprecation notice on check_scalar * Removed unnecessary unwraps --- Cargo.lock | 3 +-- Cargo.toml | 6 +++++- src/batch.rs | 2 +- src/hazmat.rs | 54 +++++++++++++++++++++++------------------------- src/signature.rs | 32 ++++++++++++++-------------- src/signing.rs | 30 +++++++++++++++------------ src/verifying.rs | 26 ++++++++++++----------- tests/x25519.rs | 16 +++++++------- 8 files changed, 88 insertions(+), 81 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5ef6955..fe13ccc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -249,8 +249,7 @@ dependencies = [ [[package]] name = "curve25519-dalek" version = "4.0.0-rc.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d928d978dbec61a1167414f5ec534f24bea0d7a0d24dd9b6233d3d8223e585" +source = "git+https://github.com/dalek-cryptography/curve25519-dalek.git?rev=f460ae149b0000695205cc78f560d74a2d3918eb#f460ae149b0000695205cc78f560d74a2d3918eb" dependencies = [ "cfg-if", "digest", diff --git a/Cargo.toml b/Cargo.toml index cdbe10a..ec28d59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,9 +67,13 @@ digest = ["signature/digest"] # Exposes the hazmat module hazmat = [] # Turns off stricter checking for scalar malleability in signatures -legacy_compatibility = [] +legacy_compatibility = ["curve25519-dalek/legacy_compatibility"] pkcs8 = ["ed25519/pkcs8"] pem = ["alloc", "ed25519/pem", "pkcs8"] rand_core = ["dep:rand_core"] serde = ["dep:serde", "ed25519/serde"] zeroize = ["dep:zeroize", "curve25519-dalek/zeroize"] + +[patch.crates-io.curve25519-dalek] +git = "https://github.com/dalek-cryptography/curve25519-dalek.git" +rev = "f460ae149b0000695205cc78f560d74a2d3918eb" diff --git a/src/batch.rs b/src/batch.rs index d94008d..d5d1746 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -177,7 +177,7 @@ pub fn verify_batch( h.update(signatures[i].r_bytes()); h.update(verifying_keys[i].as_bytes()); h.update(&messages[i]); - h.finalize().try_into().unwrap() + *h.finalize().as_ref() }) .collect(); diff --git a/src/hazmat.rs b/src/hazmat.rs index 5f16d3a..4414a84 100644 --- a/src/hazmat.rs +++ b/src/hazmat.rs @@ -15,7 +15,7 @@ use crate::{InternalError, SignatureError}; -use curve25519_dalek::Scalar; +use curve25519_dalek::scalar::{clamp_integer, Scalar}; #[cfg(feature = "zeroize")] use zeroize::{Zeroize, ZeroizeOnDrop}; @@ -35,6 +35,10 @@ use curve25519_dalek::digest::{generic_array::typenum::U64, Digest}; /// /// Instances of this secret are automatically overwritten with zeroes when they fall out of scope. pub struct ExpandedSecretKey { + // `scalar_bytes` and `scalar` are separate, because the public key is computed as an unreduced + // scalar multiplication (ie `mul_base_clamped`), whereas the signing operations are done + // modulo l. + pub(crate) scalar_bytes: [u8; 32], /// The secret scalar used for signing pub scalar: Scalar, /// The domain separator used when hashing the message to generate the pseudorandom `r` value @@ -64,18 +68,24 @@ impl ExpandedSecretKey { bytes } - /// Construct an `ExpandedSecretKey` from an array of 64 bytes. + /// Construct an `ExpandedSecretKey` from an array of 64 bytes. In the spec, the bytes are the + /// output of a SHA-512 hash. This clamps the first 32 bytes and uses it as a scalar, and uses + /// the second 32 bytes as a domain separator for hashing. pub fn from_bytes(bytes: &[u8; 64]) -> Self { // TODO: Use bytes.split_array_ref once it’s in MSRV. - let mut lower: [u8; 32] = [0u8; 32]; - let mut upper: [u8; 32] = [0u8; 32]; + let mut scalar_bytes: [u8; 32] = [0u8; 32]; + let mut hash_prefix: [u8; 32] = [0u8; 32]; + scalar_bytes.copy_from_slice(&bytes[00..32]); + hash_prefix.copy_from_slice(&bytes[32..64]); - lower.copy_from_slice(&bytes[00..32]); - upper.copy_from_slice(&bytes[32..64]); + // For signing, we'll need the integer, clamped, and converted to a Scalar. See + // PureEdDSA.keygen in RFC 8032 Appendix A. + let scalar = Scalar::from_bytes_mod_order(clamp_integer(scalar_bytes)); ExpandedSecretKey { - scalar: Scalar::from_bytes_mod_order(lower), - hash_prefix: upper, + scalar_bytes, + scalar, + hash_prefix, } } @@ -86,18 +96,15 @@ impl ExpandedSecretKey { /// A `Result` whose okay value is an EdDSA `ExpandedSecretKey` or whose error value is an /// `SignatureError` describing the error that occurred, namely that the given slice's length /// is not 64. - #[allow(clippy::unwrap_used)] pub fn from_slice(bytes: &[u8]) -> Result { - if bytes.len() != 64 { - Err(InternalError::BytesLength { + // Try to coerce bytes to a [u8; 64] + bytes.try_into().map(Self::from_bytes).map_err(|_| { + InternalError::BytesLength { name: "ExpandedSecretKey", length: 64, } - .into()) - } else { - // If the input is 64 bytes long, coerce it to a 64-byte array - Ok(Self::from_bytes(bytes.try_into().unwrap())) - } + .into() + }) } } @@ -213,7 +220,6 @@ where mod test { use super::*; - use curve25519_dalek::Scalar; use rand::{rngs::OsRng, CryptoRng, RngCore}; // Pick distinct, non-spec 512-bit hash functions for message and sig-context hashing @@ -224,17 +230,9 @@ mod test { // Make a random expanded secret key for testing purposes. This is NOT how you generate // expanded secret keys IRL. They're the hash of a seed. fn random(mut rng: R) -> Self { - // The usual signing algorithm clamps its scalars - let scalar_bytes = [0u8; 32]; - let scalar = Scalar::from_bits_clamped(scalar_bytes); - - let mut hash_prefix = [0u8; 32]; - rng.fill_bytes(&mut hash_prefix); - - ExpandedSecretKey { - scalar, - hash_prefix, - } + let mut bytes = [0u8; 64]; + rng.fill_bytes(&mut bytes); + ExpandedSecretKey::from_bytes(&bytes) } } diff --git a/src/signature.rs b/src/signature.rs index 72b7b0e..36174c8 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -63,6 +63,9 @@ impl Debug for InternalSignature { } } +/// Ensures that the scalar `s` of a signature is within the bounds [0, 2^253). +/// +/// **Unsafe**: This version of `check_scalar` permits signature malleability. See README. #[cfg(feature = "legacy_compatibility")] #[inline(always)] fn check_scalar(bytes: [u8; 32]) -> Result { @@ -76,24 +79,17 @@ fn check_scalar(bytes: [u8; 32]) -> Result { return Err(InternalError::ScalarFormat.into()); } + // You cannot do arithmetic with scalars construct with Scalar::from_bits. We only use this + // scalar for EdwardsPoint::vartime_double_scalar_mul_basepoint, which is an accepted usecase. + // The `from_bits` method is deprecated because it's unsafe. We know this. + #[allow(deprecated)] Ok(Scalar::from_bits(bytes)) } +/// Ensures that the scalar `s` of a signature is within the bounds [0, ℓ) #[cfg(not(feature = "legacy_compatibility"))] #[inline(always)] fn check_scalar(bytes: [u8; 32]) -> Result { - // Since this is only used in signature deserialisation (i.e. upon - // verification), we can do a "succeed fast" trick by checking that the most - // significant 4 bits are unset. If they are unset, we can succeed fast - // because we are guaranteed that the scalar is fully reduced. However, if - // the 4th most significant bit is set, we must do the full reduction check, - // as the order of the basepoint is roughly a 2^(252.5) bit number. - // - // This succeed-fast trick should succeed for roughly half of all scalars. - if bytes[31] & 240 == 0 { - return Ok(Scalar::from_bits(bytes)); - } - match Scalar::from_canonical_bytes(bytes).into() { None => Err(InternalError::ScalarFormat.into()), Some(x) => Ok(x), @@ -152,13 +148,17 @@ impl InternalSignature { /// only checking the most significant three bits. (See also the /// documentation for [`crate::VerifyingKey::verify_strict`].) #[inline] - #[allow(clippy::unwrap_used)] + #[allow(non_snake_case)] pub fn from_bytes(bytes: &[u8; SIGNATURE_LENGTH]) -> Result { // TODO: Use bytes.split_array_ref once it’s in MSRV. - let (lower, upper) = bytes.split_at(32); + let mut R_bytes: [u8; 32] = [0u8; 32]; + let mut s_bytes: [u8; 32] = [0u8; 32]; + R_bytes.copy_from_slice(&bytes[00..32]); + s_bytes.copy_from_slice(&bytes[32..64]); + Ok(InternalSignature { - R: CompressedEdwardsY(lower.try_into().unwrap()), - s: check_scalar(upper.try_into().unwrap())?, + R: CompressedEdwardsY(R_bytes), + s: check_scalar(s_bytes)?, }) } } diff --git a/src/signing.rs b/src/signing.rs index 500f8b5..b0f0b49 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -473,12 +473,23 @@ impl SigningKey { self.verifying_key.verify_strict(message, signature) } - /// Convert this signing key into a Curve25519 scalar. + /// Convert this signing key into a byte representation of a(n) (unreduced) Curve25519 scalar. /// - /// This is useful for e.g. performing X25519 Diffie-Hellman using - /// Ed25519 keys. - pub fn to_scalar(&self) -> Scalar { - ExpandedSecretKey::from(&self.secret_key).scalar + /// This can be used for performing X25519 Diffie-Hellman using Ed25519 keys. The bytes output + /// by this function are a valid secret key for the X25519 public key given by + /// `self.verifying_key().to_montgomery()`. + /// + /// # Note + /// + /// We do NOT recommend this usage of a signing/verifying key. Signing keys are usually + /// long-term keys, while keys used for key exchange should rather be ephemeral. If you can + /// help it, use a separate key for encryption. + /// + /// For more information on the security of systems which use the same keys for both signing + /// and Diffie-Hellman, see the paper + /// [On using the same key pair for Ed25519 and an X25519 based KEM](https://eprint.iacr.org/2021/509). + pub fn to_scalar_bytes(&self) -> [u8; 32] { + ExpandedSecretKey::from(&self.secret_key).scalar_bytes } } @@ -715,14 +726,7 @@ impl From<&SecretKey> for ExpandedSecretKey { #[allow(clippy::unwrap_used)] fn from(secret_key: &SecretKey) -> ExpandedSecretKey { let hash = Sha512::default().chain_update(secret_key).finalize(); - // TODO: Use bytes.split_array_ref once it’s in MSRV. - let (lower, upper) = hash.split_at(32); - - // The try_into here converts to fixed-size array - ExpandedSecretKey { - scalar: Scalar::from_bits_clamped(lower.try_into().unwrap()), - hash_prefix: upper.try_into().unwrap(), - } + ExpandedSecretKey::from_bytes(hash.as_ref()) } } diff --git a/src/verifying.rs b/src/verifying.rs index e97e203..1d25f38 100644 --- a/src/verifying.rs +++ b/src/verifying.rs @@ -65,7 +65,7 @@ pub struct VerifyingKey { } impl Debug for VerifyingKey { - fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "VerifyingKey({:?}), {:?})", self.compressed, self.point) } } @@ -91,8 +91,7 @@ impl PartialEq for VerifyingKey { impl From<&ExpandedSecretKey> for VerifyingKey { /// Derive this public key from its corresponding `ExpandedSecretKey`. fn from(expanded_secret_key: &ExpandedSecretKey) -> VerifyingKey { - let bits: [u8; 32] = expanded_secret_key.scalar.to_bytes(); - VerifyingKey::clamp_and_mul_base(bits) + VerifyingKey::clamp_and_mul_base(expanded_secret_key.scalar_bytes) } } @@ -191,8 +190,7 @@ impl VerifyingKey { /// Internal utility function for clamping a scalar representation and multiplying by the /// basepont to produce a public key. fn clamp_and_mul_base(bits: [u8; 32]) -> VerifyingKey { - let scalar = Scalar::from_bits_clamped(bits); - let point = EdwardsPoint::mul_base(&scalar); + let point = EdwardsPoint::mul_base_clamped(bits); let compressed = point.compress(); // Invariant: VerifyingKey.1 is always the decompression of VerifyingKey.0 @@ -501,15 +499,19 @@ impl VerifyingKey { /// Convert this verifying key into Montgomery form. /// - /// This is useful for systems which perform X25519 Diffie-Hellman using - /// Ed25519 keys. + /// This can be used for performing X25519 Diffie-Hellman using Ed25519 keys. The output of + /// this function is a valid X25519 public key whose secret key is `sk.to_scalar_bytes()`, + /// where `sk` is a valid signing key for this `VerifyingKey`. /// - /// When possible, it's recommended to use separate keys for signing and - /// Diffie-Hellman. + /// # Note /// - /// For more information on the security of systems which use the same keys - /// for both signing and Diffie-Hellman, see the paper - /// [On using the same key pair for Ed25519 and an X25519 based KEM](https://eprint.iacr.org/2021/509.pdf). + /// We do NOT recommend this usage of a signing/verifying key. Signing keys are usually + /// long-term keys, while keys used for key exchange should rather be ephemeral. If you can + /// help it, use a separate key for encryption. + /// + /// For more information on the security of systems which use the same keys for both signing + /// and Diffie-Hellman, see the paper + /// [On using the same key pair for Ed25519 and an X25519 based KEM](https://eprint.iacr.org/2021/509). pub fn to_montgomery(&self) -> MontgomeryPoint { self.point.to_montgomery() } diff --git a/tests/x25519.rs b/tests/x25519.rs index bb588f7..18ae502 100644 --- a/tests/x25519.rs +++ b/tests/x25519.rs @@ -16,16 +16,16 @@ fn ed25519_to_x25519_dh() { let ed25519_signing_key_a = SigningKey::from_bytes(&ed25519_secret_key_a); let ed25519_signing_key_b = SigningKey::from_bytes(&ed25519_secret_key_b); - let scalar_a = ed25519_signing_key_a.to_scalar(); - let scalar_b = ed25519_signing_key_b.to_scalar(); + let scalar_a_bytes = ed25519_signing_key_a.to_scalar_bytes(); + let scalar_b_bytes = ed25519_signing_key_b.to_scalar_bytes(); assert_eq!( - scalar_a.to_bytes(), - hex!("307c83864f2833cb427a2ef1c00a013cfdff2768d980c0a3a520f006904de94f") + scalar_a_bytes, + hex!("357c83864f2833cb427a2ef1c00a013cfdff2768d980c0a3a520f006904de90f") ); assert_eq!( - scalar_b.to_bytes(), - hex!("68bd9ed75882d52815a97585caf4790a7f6c6b3b7f821c5e259a24b02e502e51") + scalar_b_bytes, + hex!("6ebd9ed75882d52815a97585caf4790a7f6c6b3b7f821c5e259a24b02e502e11") ); let x25519_public_key_a = ed25519_signing_key_a.verifying_key().to_montgomery(); @@ -44,11 +44,11 @@ fn ed25519_to_x25519_dh() { hex!("5166f24a6918368e2af831a4affadd97af0ac326bdf143596c045967cc00230e"); assert_eq!( - (x25519_public_key_a * scalar_b).to_bytes(), + x25519_public_key_a.mul_clamped(scalar_b_bytes).to_bytes(), expected_shared_secret ); assert_eq!( - (x25519_public_key_b * scalar_a).to_bytes(), + x25519_public_key_b.mul_clamped(scalar_a_bytes).to_bytes(), expected_shared_secret ); } From 58a967f6fb28806a21180c880bbec4fdeb907aef Mon Sep 17 00:00:00 2001 From: "pinkforest(she/her)" <36498018+pinkforest@users.noreply.github.com> Date: Sat, 24 Jun 2023 03:53:10 +0000 Subject: [PATCH 351/351] chore: Release 2.0.0-rc.3 (#307) * chore: Release 2.0.0-rc.3 * cargo update -p curve25519-dalek * Removed some old backend selection prose and env vars --------- Co-authored-by: Michael Rosenberg --- CHANGELOG.md | 11 ++++--- Cargo.lock | 86 +++++++++++++++++++++++++++++++++------------------- Cargo.toml | 10 ++---- README.md | 6 ++-- 4 files changed, 68 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3657c20..c3fc94a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,11 +18,14 @@ Entries are listed in reverse chronological order per undeprecated major series. * Make `digest` an optional dependency * Make `zeroize` an optional dependency * Make `rand_core` an optional dependency -* Adopt [curve25519-backend selection](https://github.com/dalek-cryptography/curve25519-dalek/#backends) over features -* Make all batch verification deterministic remove `batch_deterministic` ([#256](https://github.com/dalek-cryptography/ed25519-dalek/pull/256)) -* Remove `ExpandedSecretKey` API ((#205)[https://github.com/dalek-cryptography/ed25519-dalek/pull/205]) +* [curve25519 backends] are now automatically selected +* [curve25519 backends] are now overridable via cfg instead of using additive features +* Make all batch verification deterministic remove `batch_deterministic` (PR [#256](https://github.com/dalek-cryptography/ed25519-dalek/pull/256)) * Rename `Keypair` → `SigningKey` and `PublicKey` → `VerifyingKey` -* Make `hazmat` feature to expose, `ExpandedSecretKey`, `raw_sign()`, `raw_sign_prehashed()`, `raw_verify()`, and `raw_verify_prehashed()` +* Remove default-public `ExpandedSecretKey` API (PR [#205](https://github.com/dalek-cryptography/ed25519-dalek/pull/205)) +* Make `hazmat` feature to expose `ExpandedSecretKey`, `raw_sign()`, `raw_sign_prehashed()`, `raw_verify()`, and `raw_verify_prehashed()` + +[curve25519 backends]: https://github.com/dalek-cryptography/curve25519-dalek/#backends ### Other changes diff --git a/Cargo.lock b/Cargo.lock index fe13ccc..17f94f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -150,9 +150,9 @@ checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" dependencies = [ "libc", ] @@ -248,19 +248,33 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.0.0-rc.2" -source = "git+https://github.com/dalek-cryptography/curve25519-dalek.git?rev=f460ae149b0000695205cc78f560d74a2d3918eb#f460ae149b0000695205cc78f560d74a2d3918eb" +version = "4.0.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436ace70fc06e06f7f689d2624dc4e2f0ea666efb5aa704215f7249ae6e047a7" dependencies = [ "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", "digest", "fiat-crypto", - "packed_simd_2", "platforms", "rand_core", + "rustc_version", "subtle", "zeroize", ] +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + [[package]] name = "der" version = "0.7.0" @@ -296,7 +310,7 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "2.0.0-rc.2" +version = "2.0.0-rc.3" dependencies = [ "bincode", "blake2", @@ -447,12 +461,6 @@ version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" -[[package]] -name = "libm" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" - [[package]] name = "log" version = "0.4.17" @@ -520,16 +528,6 @@ version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" -[[package]] -name = "packed_simd_2" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282" -dependencies = [ - "cfg-if", - "libm", -] - [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -591,18 +589,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.50" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" +checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.23" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] @@ -674,6 +672,15 @@ version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "ryu" version = "1.0.12" @@ -695,6 +702,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + [[package]] name = "serde" version = "1.0.152" @@ -712,7 +725,7 @@ checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -793,6 +806,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "synstructure" version = "0.12.6" @@ -801,7 +825,7 @@ checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", "unicode-xid", ] @@ -892,7 +916,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.107", "wasm-bindgen-shared", ] @@ -914,7 +938,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -983,6 +1007,6 @@ checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", "synstructure", ] diff --git a/Cargo.toml b/Cargo.toml index ec28d59..f37d5b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "2.0.0-rc.2" +version = "2.0.0-rc.3" edition = "2021" authors = [ "isis lovecruft ", @@ -25,7 +25,7 @@ rustdoc-args = [ features = ["batch", "digest", "hazmat", "pem", "serde"] [dependencies] -curve25519-dalek = { version = "=4.0.0-rc.2", default-features = false, features = ["digest"] } +curve25519-dalek = { version = "=4.0.0-rc.3", default-features = false, features = ["digest"] } ed25519 = { version = ">=2.2, <2.3", default-features = false } signature = { version = ">=2.0, <2.1", optional = true, default-features = false } sha2 = { version = "0.10", default-features = false } @@ -37,7 +37,7 @@ serde = { version = "1.0", default-features = false, optional = true } zeroize = { version = "1.5", default-features = false, optional = true } [dev-dependencies] -curve25519-dalek = { version = "=4.0.0-rc.2", default-features = false, features = ["digest", "rand_core"] } +curve25519-dalek = { version = "=4.0.0-rc.3", default-features = false, features = ["digest", "rand_core"] } blake2 = "0.10" sha3 = "0.10" hex = "0.4" @@ -73,7 +73,3 @@ pem = ["alloc", "ed25519/pem", "pkcs8"] rand_core = ["dep:rand_core"] serde = ["dep:serde", "ed25519/serde"] zeroize = ["dep:zeroize", "curve25519-dalek/zeroize"] - -[patch.crates-io.curve25519-dalek] -git = "https://github.com/dalek-cryptography/curve25519-dalek.git" -rev = "f460ae149b0000695205cc78f560d74a2d3918eb" diff --git a/README.md b/README.md index c5c279f..4d1e7b0 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ ed25519-dalek = "1" To use the latest prerelease (see changes [below](#breaking-changes-in-200)), use the following line in your project's `Cargo.toml`: ```toml -ed25519-dalek = "2.0.0-rc.2" +ed25519-dalek = "2.0.0-rc.3" ``` # Feature Flags @@ -103,7 +103,7 @@ Benchmarks are run using [criterion.rs](https://github.com/japaric/criterion.rs) ```sh cargo bench --features "batch" # Uses avx2 or ifma only if compiled for an appropriate target. -export RUSTFLAGS='--cfg curve25519_dalek_backend="simd" -C target_cpu=native' +export RUSTFLAGS='-C target_cpu=native' cargo +nightly bench --features "batch" ``` @@ -134,7 +134,7 @@ want to test the benchmarks on your target CPU to discover the best size. ## (Micro)Architecture Specific Backends -A _backend_ refers to an implementation of elliptic curve and scalar arithmetic. Different backends have different use cases. For example, if you demand formally verified code, you want to use the `fiat` backend (as it was generated from [Fiat Crypto][fiat]). If you want the highest performance possible, you probably want the `simd` backend. +A _backend_ refers to an implementation of elliptic curve and scalar arithmetic. Different backends have different use cases. For example, if you demand formally verified code, you want to use the `fiat` backend (as it was generated from [Fiat Crypto][fiat]). Backend selection details and instructions can be found in the [curve25519-dalek docs](https://github.com/dalek-cryptography/curve25519-dalek#backends).