Skip to content

Commit

Permalink
Use ed25519 + signature interop crates
Browse files Browse the repository at this point in the history
The `signature` crate provides `Signer` and `Verifier` traits generic
over signature types:

https://github.com/RustCrypto/traits/tree/master/signature

There's presently an open call to stabilize the parts of its API needed
by Ed25519 signatures and release a 1.0 version:

RustCrypto/traits#78

The `ed25519` crate, based on the `signature` crate, provides an
`ed25519::Signature` type which can be shared across multiple Ed25519
crates (e.g. it is also used by the `yubihsm` crate):

https://github.com/RustCrypto/signatures/tree/master/ed25519

This commit integrates the `ed25519::Signature` type, and changes the
existing `sign` and `verify` methods (where applicable) to use the
`Signer` and `Verifier` traits from the `signature` crate. Additionally,
it replaces `SignatureError` with the `signature` crate's error type.

This has the drawback of requiring the `Signer` and/or `Verifier` traits
are in scope in order to create and/or verify signatures, but with the
benefit of supporting interoperability with other Ed25519 crates which
also make use of these traits.
  • Loading branch information
tarcieri committed Mar 17, 2020
1 parent 3a91019 commit ff51427
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 155 deletions.
6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ features = ["nightly", "batch"]

[dependencies]
curve25519-dalek = { version = "2", default-features = false }
ed25519 = { version = "= 1.0.0-pre.4", default-features = false }
merlin = { version = "1", default-features = false, optional = true, git = "https://github.com/isislovecruft/merlin", branch = "develop" }
rand = { version = "0.7", default-features = false, optional = true }
rand_core = { version = "0.5", default-features = false, optional = true }
serde = { version = "1.0", default-features = false, optional = true }
serde_crate = { package = "serde", version = "1.0", default-features = false, optional = true }
sha2 = { version = "0.8", default-features = false }
zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] }

Expand All @@ -45,9 +46,10 @@ harness = false

[features]
default = ["std", "u64_backend"]
std = ["curve25519-dalek/std", "serde/std", "sha2/std", "rand/std"]
std = ["curve25519-dalek/std", "ed25519/std", "serde_crate/std", "sha2/std", "rand/std"]
alloc = ["curve25519-dalek/alloc", "rand/alloc", "zeroize/alloc"]
nightly = ["curve25519-dalek/nightly", "rand/nightly"]
serde = ["serde_crate", "ed25519/serde"]
batch = ["merlin", "rand"]
# This feature enables deterministic batch verification.
batch_deterministic = ["merlin", "rand", "rand_core"]
Expand Down
20 changes: 14 additions & 6 deletions src/batch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use alloc::vec::Vec;
#[cfg(feature = "std")]
use std::vec::Vec;

use core::convert::TryFrom;
use core::iter::once;

use curve25519_dalek::constants;
Expand All @@ -37,7 +38,7 @@ use sha2::Sha512;
use crate::errors::InternalError;
use crate::errors::SignatureError;
use crate::public::PublicKey;
use crate::signature::Signature;
use crate::signature::InternalSignature;

trait BatchTranscript {
fn append_hrams(&mut self, hrams: &Vec<Scalar>);
Expand Down Expand Up @@ -127,6 +128,7 @@ fn zero_rng() -> ZeroRng {
/// use ed25519_dalek::verify_batch;
/// use ed25519_dalek::Keypair;
/// use ed25519_dalek::PublicKey;
/// use ed25519_dalek::Signer;
/// use ed25519_dalek::Signature;
/// use rand::rngs::OsRng;
///
Expand All @@ -147,21 +149,27 @@ fn zero_rng() -> ZeroRng {
#[allow(non_snake_case)]
pub fn verify_batch(
messages: &[&[u8]],
signatures: &[Signature],
signatures: &[ed25519::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{
return Err(InternalError::ArrayLengthError{
name_a: "signatures", length_a: signatures.len(),
name_b: "messages", length_b: messages.len(),
name_c: "public_keys", length_c: public_keys.len(),
}));
}.into());
}

// Convert all signatures to `InternalSignature`
let signatures = signatures
.iter()
.map(InternalSignature::try_from)
.collect::<Result<Vec<_>, _>>()?;

// 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();
Expand Down Expand Up @@ -213,11 +221,11 @@ pub fn verify_batch(
let id = EdwardsPoint::optional_multiscalar_mul(
once(-B_coefficient).chain(zs.iter().cloned()).chain(zhrams),
B.chain(Rs).chain(As),
).ok_or_else(|| SignatureError(InternalError::VerifyError))?;
).ok_or(InternalError::VerifyError)?;

if id.is_identity() {
Ok(())
} else {
Err(SignatureError(InternalError::VerifyError))
Err(InternalError::VerifyError.into())
}
}
18 changes: 8 additions & 10 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,18 +80,16 @@ impl Error for InternalError { }
/// only be constructed from 255-bit integers.)
///
/// * Failure of a signature to satisfy the verification equation.
#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
pub struct SignatureError(pub(crate) InternalError);
pub type SignatureError = ed25519::signature::Error;

impl Display for SignatureError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
impl From<InternalError> for SignatureError {
#[cfg(not(feature = "std"))]
fn from(_err: InternalError) -> SignatureError {
SignatureError::new()
}
}

#[cfg(feature = "std")]
impl Error for SignatureError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(&self.0)
#[cfg(feature = "std")]
fn from(err: InternalError) -> SignatureError {
SignatureError::from_source(err)
}
}
51 changes: 28 additions & 23 deletions src/ed25519.rs → src/keypair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@

//! ed25519 keypairs.
use core::default::Default;

#[cfg(feature = "rand")]
use rand::{CryptoRng, RngCore};

Expand All @@ -26,13 +24,12 @@ pub use sha2::Sha512;
use curve25519_dalek::digest::generic_array::typenum::U64;
pub use curve25519_dalek::digest::Digest;

#[cfg(all(feature = "batch", any(feature = "std", feature = "alloc")))]
pub use crate::batch::*;
pub use crate::constants::*;
pub use crate::errors::*;
pub use crate::public::*;
pub use crate::secret::*;
pub use crate::signature::*;
use ed25519::signature::{Signer, Verifier};

use crate::constants::*;
use crate::errors::*;
use crate::public::*;
use crate::secret::*;

/// An ed25519 keypair.
#[derive(Debug)]
Expand Down Expand Up @@ -82,10 +79,10 @@ impl Keypair {
/// is an `SignatureError` describing the error that occurred.
pub fn from_bytes<'a>(bytes: &'a [u8]) -> Result<Keypair, SignatureError> {
if bytes.len() != KEYPAIR_LENGTH {
return Err(SignatureError(InternalError::BytesLengthError {
return Err(InternalError::BytesLengthError {
name: "Keypair",
length: KEYPAIR_LENGTH,
}));
}.into());
}
let secret = SecretKey::from_bytes(&bytes[..SECRET_KEY_LENGTH])?;
let public = PublicKey::from_bytes(&bytes[SECRET_KEY_LENGTH..])?;
Expand Down Expand Up @@ -136,13 +133,6 @@ impl Keypair {
Keypair{ public: pk, secret: sk }
}

/// Sign a message with this keypair's secret key.
pub fn sign(&self, message: &[u8]) -> Signature {
let expanded: ExpandedSecretKey = (&self.secret).into();

expanded.sign(&message, &self.public)
}

/// Sign a `prehashed_message` with this `Keypair` using the
/// Ed25519ph algorithm defined in [RFC8032 §5.1][rfc8032].
///
Expand Down Expand Up @@ -241,20 +231,20 @@ impl Keypair {
&self,
prehashed_message: D,
context: Option<&[u8]>,
) -> Signature
) -> ed25519::Signature
where
D: Digest<OutputSize = U64>,
{
let expanded: ExpandedSecretKey = (&self.secret).into(); // xxx thanks i hate this

expanded.sign_prehashed(prehashed_message, &self.public, context)
expanded.sign_prehashed(prehashed_message, &self.public, context).into()
}

/// Verify a signature on a message with this keypair's public key.
pub fn verify(
&self,
message: &[u8],
signature: &Signature
signature: &ed25519::Signature
) -> Result<(), SignatureError>
{
self.public.verify(message, signature)
Expand Down Expand Up @@ -320,7 +310,7 @@ impl Keypair {
&self,
prehashed_message: D,
context: Option<&[u8]>,
signature: &Signature,
signature: &ed25519::Signature,
) -> Result<(), SignatureError>
where
D: Digest<OutputSize = U64>,
Expand Down Expand Up @@ -394,13 +384,28 @@ impl Keypair {
pub fn verify_strict(
&self,
message: &[u8],
signature: &Signature,
signature: &ed25519::Signature,
) -> Result<(), SignatureError>
{
self.public.verify_strict(message, signature)
}
}

impl Signer<ed25519::Signature> for Keypair {
/// Sign a message with this keypair's secret key.
fn try_sign(&self, message: &[u8]) -> Result<ed25519::Signature, SignatureError> {
let expanded: ExpandedSecretKey = (&self.secret).into();
Ok(expanded.sign(&message, &self.public).into())
}
}

impl Verifier<ed25519::Signature> for Keypair {
/// Verify a signature on a message with this keypair's public key.
fn verify(&self, message: &[u8], signature: &ed25519::Signature) -> Result<(), SignatureError> {
self.public.verify(message, signature)
}
}

#[cfg(feature = "serde")]
impl Serialize for Keypair {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
Expand Down
47 changes: 30 additions & 17 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@
//! # fn main() {
//! # use rand::rngs::OsRng;
//! # use ed25519_dalek::Keypair;
//! # use ed25519_dalek::Signature;
//! # let mut csprng = OsRng{};
//! # let keypair: Keypair = Keypair::generate(&mut csprng);
//! use ed25519_dalek::{Signature, Signer};
//! let message: &[u8] = b"This is a test of the tsunami alert system.";
//! let signature: Signature = keypair.sign(message);
//! # }
Expand All @@ -60,12 +60,12 @@
//! # extern crate ed25519_dalek;
//! # fn main() {
//! # use rand::rngs::OsRng;
//! # use ed25519_dalek::Keypair;
//! # use ed25519_dalek::Signature;
//! # use ed25519_dalek::{Keypair, Signature, Signer};
//! # let mut csprng = OsRng{};
//! # let keypair: Keypair = Keypair::generate(&mut csprng);
//! # let message: &[u8] = b"This is a test of the tsunami alert system.";
//! # let signature: Signature = keypair.sign(message);
//! use ed25519_dalek::Verifier;
//! assert!(keypair.verify(message, &signature).is_ok());
//! # }
//! ```
Expand All @@ -80,7 +80,8 @@
//! # use rand::rngs::OsRng;
//! # use ed25519_dalek::Keypair;
//! # use ed25519_dalek::Signature;
//! use ed25519_dalek::PublicKey;
//! # use ed25519_dalek::Signer;
//! use ed25519_dalek::{PublicKey, Verifier};
//! # let mut csprng = OsRng{};
//! # let keypair: Keypair = Keypair::generate(&mut csprng);
//! # let message: &[u8] = b"This is a test of the tsunami alert system.";
Expand All @@ -104,7 +105,7 @@
//! # extern crate ed25519_dalek;
//! # fn main() {
//! # use rand::rngs::OsRng;
//! # use ed25519_dalek::{Keypair, Signature, PublicKey};
//! # use ed25519_dalek::{Keypair, Signature, Signer, PublicKey};
//! use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH};
//! # let mut csprng = OsRng{};
//! # let keypair: Keypair = Keypair::generate(&mut csprng);
Expand All @@ -124,8 +125,9 @@
//! ```
//! # extern crate rand;
//! # extern crate ed25519_dalek;
//! # use std::convert::TryFrom;
//! # use rand::rngs::OsRng;
//! # use ed25519_dalek::{Keypair, Signature, PublicKey, SecretKey, SignatureError};
//! # use ed25519_dalek::{Keypair, Signature, Signer, PublicKey, SecretKey, SignatureError};
//! # use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH};
//! # fn do_test() -> Result<(SecretKey, PublicKey, Keypair, Signature), SignatureError> {
//! # let mut csprng = OsRng{};
Expand All @@ -140,7 +142,7 @@
//! let public_key: PublicKey = PublicKey::from_bytes(&public_key_bytes)?;
//! let secret_key: SecretKey = SecretKey::from_bytes(&secret_key_bytes)?;
//! let keypair: Keypair = Keypair::from_bytes(&keypair_bytes)?;
//! let signature: Signature = Signature::from_bytes(&signature_bytes)?;
//! let signature: Signature = Signature::try_from(&signature_bytes[..])?;
//! #
//! # Ok((secret_key, public_key, keypair, signature))
//! # }
Expand All @@ -166,14 +168,14 @@
//! # extern crate rand;
//! # extern crate ed25519_dalek;
//! # #[cfg(feature = "serde")]
//! extern crate serde;
//! # extern crate serde_crate as serde;
//! # #[cfg(feature = "serde")]
//! extern crate bincode;
//! # extern crate bincode;
//!
//! # #[cfg(feature = "serde")]
//! # fn main() {
//! # use rand::rngs::OsRng;
//! # use ed25519_dalek::{Keypair, Signature, PublicKey};
//! # use ed25519_dalek::{Keypair, Signature, Signer, Verifier, PublicKey};
//! use bincode::{serialize, Infinite};
//! # let mut csprng = OsRng{};
//! # let keypair: Keypair = Keypair::generate(&mut csprng);
Expand All @@ -196,16 +198,16 @@
//! # extern crate rand;
//! # extern crate ed25519_dalek;
//! # #[cfg(feature = "serde")]
//! # extern crate serde;
//! # extern crate serde_crate as serde;
//! # #[cfg(feature = "serde")]
//! # extern crate bincode;
//! #
//! # #[cfg(feature = "serde")]
//! # fn main() {
//! # use rand::rngs::OsRng;
//! # use ed25519_dalek::{Keypair, Signature, PublicKey};
//! # use ed25519_dalek::{Keypair, Signature, Signer, Verifier, PublicKey};
//! # use bincode::{serialize, Infinite};
//! use bincode::{deserialize};
//! use bincode::deserialize;
//!
//! # let mut csprng = OsRng{};
//! # let keypair: Keypair = Keypair::generate(&mut csprng);
Expand Down Expand Up @@ -237,6 +239,8 @@
#[macro_use]
extern crate std;

pub extern crate ed25519;

#[cfg(all(feature = "alloc", not(feature = "std")))]
extern crate alloc;
extern crate curve25519_dalek;
Expand All @@ -245,20 +249,29 @@ extern crate merlin;
#[cfg(any(feature = "batch", feature = "std", feature = "alloc", test))]
extern crate rand;
#[cfg(feature = "serde")]
extern crate serde;
extern crate serde_crate as serde;
extern crate sha2;
extern crate zeroize;

#[cfg(all(any(feature = "batch", feature = "batch_deterministic"), any(feature = "std", feature = "alloc")))]
mod batch;
mod constants;
mod ed25519;
mod keypair;
mod errors;
mod public;
mod secret;
mod signature;

// Export everything public in ed25519.
pub use crate::ed25519::*;
pub use curve25519_dalek::digest::Digest;

#[cfg(all(any(feature = "batch", feature = "batch_deterministic"), any(feature = "std", feature = "alloc")))]
pub use crate::batch::*;
pub use crate::constants::*;
pub use crate::errors::*;
pub use crate::keypair::*;
pub use crate::public::*;
pub use crate::secret::*;

// Re-export the `Signer` and `Verifier` traits from the `signature` crate
pub use ed25519::signature::{Signer, Verifier};
pub use ed25519::Signature;
Loading

0 comments on commit ff51427

Please sign in to comment.