mirror of
https://github.com/saymrwulf/risc0-curve25519-dalek-source.git
synced 2026-09-05 20:10:35 +00:00
Merge branch 'release/1.0.0-pre.3'
This commit is contained in:
commit
36a51acbf0
11 changed files with 741 additions and 290 deletions
68
Cargo.toml
68
Cargo.toml
|
|
@ -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
153
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):
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -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
225
src/batch.rs
Normal 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))
|
||||
}
|
||||
}
|
||||
229
src/ed25519.rs
229
src/ed25519.rs
|
|
@ -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")]
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
78
src/lib.rs
78
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);
|
||||
|
|
@ -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::*;
|
||||
|
|
|
|||
|
|
@ -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")]
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue