Merge branch 'release/1.0.0-pre.3'

This commit is contained in:
Isis Lovecruft 2019-12-07 01:24:30 +00:00
commit 36a51acbf0
No known key found for this signature in database
GPG key ID: AB41313533E8E812
11 changed files with 741 additions and 290 deletions

View file

@ -1,6 +1,7 @@
[package]
name = "ed25519-dalek"
version = "1.0.0-pre.2"
version = "1.0.0-pre.3"
edition = "2018"
authors = ["isis lovecruft <isis@patternsinthevoid.net>"]
readme = "README.md"
license = "BSD-3-Clause"
@ -15,45 +16,25 @@ exclude = [ ".gitignore", "TESTVECTORS", "res/*" ]
[badges]
travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master"}
[dependencies.curve25519-dalek]
version = "1"
default-features = false
[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.rand_core]
version = "0.3"
default-features = false
[dependencies.rand]
version = "0.6"
features = ["i128_support"]
default-features = false
optional = true
[dependencies.rand_os]
version = "0.1"
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 = "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 }
[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,12 +45,15 @@ 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"]
batch = ["rand"]
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 = ["merlin", "rand"]
# This feature enables deterministic batch verification.
batch_deterministic = ["merlin", "rand", "rand_core"]
asm = ["sha2/asm"]
yolocrypto = ["curve25519-dalek/yolocrypto"]
# 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"]
avx2_backend = ["curve25519-dalek/avx2_backend"]
simd_backend = ["curve25519-dalek/simd_backend"]

153
README.md
View file

@ -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,30 +120,98 @@ 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. We
[plan](https://github.com/dalek-cryptography/curve25519-dalek/issues/9) to
eventually support VXEdDSA in curve25519-dalek.
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.
# Installation
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.**
To install, add the following to your project's `Cargo.toml`:
## The `legacy_compatibility` Feature
```toml
[dependencies.ed25519-dalek]
version = "1"
```
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.
Then, in your library or executable source, add:
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:
```rust
extern crate ed25519_dalek;
```
* 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).
# 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.
## Nightly Compilers
To cause your application to build `ed25519-dalek` with the nightly feature
enabled by default, instead do:
@ -147,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.

View file

@ -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,
}

225
src/batch.rs Normal file
View file

@ -0,0 +1,225 @@
// -*- mode: rust; -*-
//
// This file is part of ed25519-dalek.
// Copyright (c) 2017-2019 isis lovecruft
// See LICENSE for licensing information.
//
// Authors:
// - isis agora lovecruft <isis@patternsinthevoid.net>
//! 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;
use rand::Rng;
#[cfg(all(feature = "batch", not(feature = "batch_deterministic")))]
use rand::thread_rng;
#[cfg(all(not(feature = "batch"), feature = "batch_deterministic"))]
use rand_core;
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<Scalar>);
fn append_message_lengths(&mut self, message_lengths: &Vec<usize>);
}
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<Scalar>) {
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<usize>) {
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(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)
}
/// 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);
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<Keypair> = (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<Signature> = keypairs.iter().map(|key| key.sign(&msg)).collect();
/// let public_keys: Vec<PublicKey> = 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<Scalar> = (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();
// Collect the message lengths to add into the transcript.
let message_lengths: Vec<usize> = 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
// "batch_deterministic" feature.
let mut 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());
#[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<Scalar> = signatures
.iter()
.map(|_| Scalar::from(prng.gen::<u128>()))
.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))
}
}

View file

@ -7,151 +7,32 @@
// Authors:
// - isis agora lovecruft <isis@patternsinthevoid.net>
//! ed25519 keypairs and batch verification.
//! ed25519 keypairs.
#[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;
#[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`.
///
/// # 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_os;
///
/// use ed25519_dalek::verify_batch;
/// use ed25519_dalek::Keypair;
/// use ed25519_dalek::PublicKey;
/// use ed25519_dalek::Signature;
/// use rand_os::OsRng;
///
/// # fn main() {
/// let mut csprng: OsRng = OsRng::new().unwrap();
/// let keypairs: Vec<Keypair> = (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<Signature> = keypairs.iter().map(|key| key.sign(&msg)).collect();
/// let public_keys: Vec<PublicKey> = 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>
{
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);
#[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<Scalar> = signatures
.iter()
.map(|_| Scalar::from(thread_rng().gen::<u128>()))
.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 {
@ -216,19 +97,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 +162,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 +209,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();
@ -360,7 +239,7 @@ impl Keypair {
pub fn sign_prehashed<D>(
&self,
prehashed_message: D,
context: Option<&'static [u8]>,
context: Option<&[u8]>,
) -> Signature
where
D: Digest<OutputSize = U64>,
@ -401,17 +280,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.";
///
@ -447,6 +326,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")]

View file

@ -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)]
@ -33,6 +36,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,11 +54,17 @@ 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),
}
}
}
impl ::failure::Fail for InternalError {}
#[cfg(feature = "std")]
impl Error for InternalError { }
/// Errors which may occur while processing signatures and keypairs.
///
@ -75,8 +89,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)
}
}

View file

@ -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);
@ -247,8 +231,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))]
@ -259,16 +241,16 @@ extern crate std;
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(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(any(feature = "std", test))]
extern crate rand_os;
extern crate rand_core;
#[cfg(feature = "serde")]
extern crate serde;
extern crate sha2;
#[cfg(all(any(feature = "batch", feature = "batch_deterministic"), any(feature = "std", feature = "alloc")))]
mod batch;
mod constants;
mod ed25519;
mod errors;
@ -278,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::*;

View file

@ -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")]

View file

@ -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<ExpandedSecretKey, SignatureError> {
/// #
/// 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();
@ -454,11 +452,11 @@ impl ExpandedSecretKey {
///
/// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1
#[allow(non_snake_case)]
pub fn sign_prehashed<D>(
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<OutputSize = U64>,

View file

@ -71,6 +71,43 @@ impl Debug for Signature {
}
}
#[cfg(feature = "legacy_compatibility")]
#[inline(always)]
fn check_scalar(bytes: [u8; 32]) -> Result<Scalar, SignatureError> {
// 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<Scalar, SignatureError> {
// 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),
};
}
impl Signature {
/// Convert this `Signature` to a byte array.
#[inline]
@ -83,6 +120,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<Signature, SignatureError> {
if bytes.len() != SIGNATURE_LENGTH {
@ -97,13 +183,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,
})
}
}

View file

@ -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<Keypair> = Vec::new();
let mut signatures: Vec<Signature> = 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