diff --git a/Cargo.toml b/Cargo.toml index e37ea8d..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" @@ -16,12 +16,13 @@ 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] version = "0.5" default-features = false +features = ["i128_support"] [dependencies.digest] version = "^0.7" @@ -42,6 +43,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 +60,8 @@ 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"] +alloc = ["curve25519-dalek/alloc"] +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/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/README.md b/README.md index 3ba0546..a6fa65a 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,7 @@ 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 +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 @@ -29,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 @@ -40,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 @@ -60,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 @@ -107,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: @@ -123,7 +146,7 @@ enabled by default, instead do: ```toml [dependencies.ed25519-dalek] -version = "^0.7" +version = "^0.8" features = ["nightly"] ``` @@ -140,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"] ``` @@ -152,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). diff --git a/benches/ed25519_benchmarks.rs b/benches/ed25519_benchmarks.rs index b9ac890..5db1361 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: [usize; 8] = [4, 8, 16, 32, 64, 96, 128, 256]; + + 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[..])); + }, + &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/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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ed25519.rs b/src/ed25519.rs index 919ecb4..d08a7ca 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: @@ -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)] // 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 { @@ -173,6 +177,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.clear(); + } +} + impl SecretKey { /// Expand this `SecretKey` into an `ExpandedSecretKey`. pub fn expand(&self) -> ExpandedSecretKey where D: Digest + Default { @@ -370,11 +381,20 @@ 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)] // 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(); + } +} + #[cfg(feature = "sha2")] impl<'a> From<&'a SecretKey> for ExpandedSecretKey { /// Construct an `ExpandedSecretKey` from a `SecretKey`. @@ -683,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); @@ -865,6 +885,119 @@ 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[..]); +/// 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 +{ + 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 rand::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: 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) + }); + + // 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| 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 + 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)) + } +} + #[cfg(feature = "serde")] impl Serialize for PublicKey { fn serialize(&self, serializer: S) -> Result where S: Serializer { @@ -894,7 +1027,7 @@ impl<'d> Deserialize<'d> for PublicKey { } /// An ed25519 keypair. -#[derive(Debug)] +#[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. @@ -1224,8 +1357,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 +1531,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[..]); + + assert!(result.is_ok()); + } + #[test] fn public_key_from_bytes() { // Make another function so that we can test the ? operator. @@ -1416,6 +1577,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..f815da8 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: @@ -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]