From 7888bfe3f851cda161da3a6b7d4eaf77fe481513 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 4 Dec 2017 00:50:17 +0000 Subject: [PATCH 01/14] Fix serde expecting string. --- src/ed25519.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 6dd4099..cd9afcf 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -740,7 +740,7 @@ impl<'d> Deserialize<'d> for PublicKey { type Value = PublicKey; fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - formatter.write_str("An ed25519 signature as specified in RFC8032") + formatter.write_str("An ed25519 public key as a 32-byte compressed point, as specified in RFC8032") } fn visit_bytes(self, bytes: &[u8]) -> Result where E: SerdeError { From 65b7a210628a03660de64b48a5b302272aaa768c Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 4 Dec 2017 00:50:39 +0000 Subject: [PATCH 02/14] Make the Keypair struct inherit repr(C). --- src/ed25519.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ed25519.rs b/src/ed25519.rs index cd9afcf..2844923 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -753,6 +753,7 @@ impl<'d> Deserialize<'d> for PublicKey { /// An ed25519 keypair. #[derive(Debug)] +#[repr(C)] pub struct Keypair { /// The secret half of this keypair. pub secret: SecretKey, From 9d89e2f66079e78ea7eae5cc722415e218c7e7ad Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 4 Dec 2017 02:11:27 +0000 Subject: [PATCH 03/14] Upgrade to using curve25519-dalek-0.14.0. --- Cargo.toml | 5 +-- src/ed25519.rs | 101 +++++++++++++++++++++++++++++-------------------- src/lib.rs | 2 - 3 files changed, 62 insertions(+), 46 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5402d2a..0bbd8de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,11 +15,8 @@ exclude = [ ".gitignore", "TESTVECTORS", "res/*" ] [badges] travis-ci = { repository = "isislovecruft/ed25519-dalek", branch = "master"} -[dependencies] -arrayref = "0.3.4" - [dependencies.curve25519-dalek] -version = "^0.12" +version = "^0.14" default-features = false [dependencies.subtle] diff --git a/src/ed25519.rs b/src/ed25519.rs index 2844923..c38276e 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -128,10 +128,17 @@ impl Signature { return Err("Wrong length of bytes for signature! Need 64 bytes.") } - let lower: &[u8; 32] = array_ref!(bytes, 0, 32); - let upper: &[u8; 32] = array_ref!(bytes, 32, 32); + let mut lower: [u8; 32] = [0u8; 32]; + let mut upper: [u8; 32] = [0u8; 32]; - Ok(Signature{ r: CompressedEdwardsY(*lower), s: Scalar(*upper) }) + lower.copy_from_slice(&bytes[..32]); + upper.copy_from_slice(&bytes[32..]); + + if upper[31] & 224 != 0 { + return Err("High-bit of scalar 's' in signature must not be set.") + } + + Ok(Signature{ r: CompressedEdwardsY(lower), s: Scalar::from_bits(upper) }) } } @@ -227,7 +234,11 @@ impl SecretKey { if bytes.len() != SECRET_KEY_LENGTH { return Err("Wrong length of bytes for creating secret key!"); } - Ok(SecretKey(*array_ref!(bytes, 0, SECRET_KEY_LENGTH))) + let mut bits: [u8; 32] = [0u8; 32]; + + bits.copy_from_slice(&bytes[..32]); + + Ok(SecretKey(bits)) } /// Generate a `SecretKey` from a `csprng`. @@ -431,7 +442,7 @@ impl ExpandedSecretKey { pub fn to_bytes(&self) -> [u8; 64] { let mut bytes: [u8; 64] = [0u8; 64]; - bytes[..32].copy_from_slice(&self.key.0[..]); + bytes[..32].copy_from_slice(self.key.as_bytes()); bytes[32..].copy_from_slice(&self.nonce[..]); bytes } @@ -479,8 +490,15 @@ impl ExpandedSecretKey { if bytes.len() != 64 { return Err("Wrong length of bytes for creating expanded secret key!"); } - Ok(ExpandedSecretKey{ key: Scalar(*array_ref!(bytes, 0, 32)), - nonce: *array_ref!(bytes, 32, 32), }) + + let mut lower: [u8; 32] = [0u8; 32]; + let mut upper: [u8; 32] = [0u8; 32]; + + lower.copy_from_slice(&bytes[00..32]); + upper.copy_from_slice(&bytes[32..64]); + + Ok(ExpandedSecretKey{ key: Scalar::from_bits(lower), + nonce: upper }) } /// Construct an `ExpandedSecretKey` from a `SecretKey`, using hash function `D`. @@ -509,18 +527,21 @@ impl ExpandedSecretKey { where D: Digest + Default { let mut h: D = D::default(); - let mut hash: [u8; 64] = [0u8; 64]; - let mut expanded_key: Scalar; + let mut hash: [u8; 64] = [0u8; 64]; + let mut lower: [u8; 32] = [0u8; 32]; + let mut upper: [u8; 32] = [0u8; 32]; h.input(secret_key.as_bytes()); hash.copy_from_slice(h.fixed_result().as_slice()); - expanded_key = Scalar(*array_ref!(&hash, 0, 32)); - expanded_key[0] &= 248; - expanded_key[31] &= 63; - expanded_key[31] |= 64; + lower.copy_from_slice(&hash[00..32]); + upper.copy_from_slice(&hash[32..64]); - ExpandedSecretKey{ key: expanded_key, nonce: *array_ref!(&hash, 32, 32) } + lower[0] &= 248; + lower[31] &= 63; + lower[31] |= 64; + + ExpandedSecretKey{ key: Scalar::from_bits(lower), nonce: upper, } } /// Sign a message with this `ExpandedSecretKey`. @@ -538,7 +559,7 @@ impl ExpandedSecretKey { h.input(&message); hash.copy_from_slice(h.fixed_result().as_slice()); - mesg_digest = Scalar::reduce(&hash); + mesg_digest = Scalar::from_bytes_mod_order_wide(&hash); r = &mesg_digest * &constants::ED25519_BASEPOINT_TABLE; @@ -548,9 +569,9 @@ impl ExpandedSecretKey { h.input(&message); hash.copy_from_slice(h.fixed_result().as_slice()); - hram_digest = Scalar::reduce(&hash); + hram_digest = Scalar::from_bytes_mod_order_wide(&hash); - s = Scalar::multiply_add(&hram_digest, &self.key, &mesg_digest); + s = &(&hram_digest * &self.key) + &mesg_digest; Signature{ r: r.compress(), s: s } } @@ -647,7 +668,11 @@ impl PublicKey { if bytes.len() != PUBLIC_KEY_LENGTH { return Err("Wrong length of bytes for creating public key!"); } - Ok(PublicKey(CompressedEdwardsY(*array_ref!(bytes, 0, 32)))) + let mut bits: [u8; 32] = [0u8; 32]; + + bits.copy_from_slice(&bytes[..32]); + + Ok(PublicKey(CompressedEdwardsY(bits))) } /// Convert this public key to its underlying extended twisted Edwards coordinate. @@ -662,20 +687,20 @@ impl PublicKey { pub fn from_secret(secret_key: &SecretKey) -> PublicKey where D: Digest + Default { - let mut h: D = D::default(); - let mut hash: [u8; 64] = [0u8; 64]; - let pk: [u8; 32]; - let mut digest: &mut [u8; 32]; + let mut h: D = D::default(); + let mut hash: [u8; 64] = [0u8; 64]; + let mut digest: [u8; 32] = [0u8; 32]; + let pk: [u8; 32]; h.input(secret_key.as_bytes()); hash.copy_from_slice(h.fixed_result().as_slice()); - digest = array_mut_ref!(&mut hash, 0, 32); + digest.copy_from_slice(&hash[..32]); digest[0] &= 248; digest[31] &= 127; digest[31] |= 64; - pk = (&Scalar(*digest) * &constants::ED25519_BASEPOINT_TABLE).compress().to_bytes(); + pk = (&Scalar::from_bits(digest) * &constants::ED25519_BASEPOINT_TABLE).compress().to_bytes(); PublicKey(CompressedEdwardsY(pk)) } @@ -687,20 +712,15 @@ impl PublicKey { /// Returns true if the signature was successfully verified, and /// false otherwise. pub fn verify(&self, message: &[u8], signature: &Signature) -> bool - where D: Digest + Default { - + where D: Digest + Default + { use curve25519_dalek::edwards::vartime; let mut h: D = D::default(); let mut a: ExtendedPoint; let ao: Option; - let r: ExtendedPoint; - let digest: [u8; 64]; - let digest_reduced: Scalar; + let mut digest: [u8; 64] = [0u8; 64]; - if signature.s[31] & 224 != 0 { - return false; - } ao = self.decompress(); if ao.is_some() { @@ -714,10 +734,10 @@ impl PublicKey { h.input(self.as_bytes()); h.input(&message); - let digest_bytes = h.fixed_result(); - digest = *array_ref!(digest_bytes, 0, 64); - digest_reduced = Scalar::reduce(&digest); - r = vartime::double_scalar_mult_basepoint(&digest_reduced, &a, &signature.s); + digest.copy_from_slice(h.fixed_result().as_slice()); + + let digest_reduced: Scalar = Scalar::from_bytes_mod_order_wide(&digest); + let r: ExtendedPoint = vartime::double_scalar_mult_basepoint(&digest_reduced, &a, &signature.s); slices_equal(signature.r.as_bytes(), r.compress().as_bytes()) == 1 } @@ -1150,10 +1170,11 @@ mod bench { fn underlying_scalar_mult_basepoint(b: &mut Bencher) { use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; - let scalar: Scalar = Scalar([ 20, 130, 129, 196, 247, 182, 211, 102, - 11, 168, 169, 131, 159, 69, 126, 35, - 109, 193, 175, 54, 118, 234, 138, 81, - 60, 183, 80, 186, 92, 248, 132, 13, ]); + let scalar: Scalar = Scalar::from_bits([ + 20, 130, 129, 196, 247, 182, 211, 102, + 11, 168, 169, 131, 159, 69, 126, 35, + 109, 193, 175, 54, 118, 234, 138, 81, + 60, 183, 80, 186, 92, 248, 132, 13, ]); b.iter(| | &scalar * &ED25519_BASEPOINT_TABLE); } diff --git a/src/lib.rs b/src/lib.rs index 78ec572..617f579 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -257,8 +257,6 @@ #![allow(unused_features)] #![deny(missing_docs)] // refuse to compile if documentation is missing -#[macro_use] -extern crate arrayref; extern crate curve25519_dalek; extern crate generic_array; extern crate digest; From b5b295e414a0ee859750b9800cd912a7568530e5 Mon Sep 17 00:00:00 2001 From: Without Boats Date: Tue, 5 Dec 2017 18:13:43 -0800 Subject: [PATCH 04/14] Use a custom error type instead of &'static str. Advantages of a custom error type: - It can be more easily integrated into other error types by clients; they can implement From for their error types, or they can use a library like failure. - It is a zero-sized type, which can enable some representational optimizations. - It can be easier and more stable to test for. --- src/ed25519.rs | 86 +++++++++++++++++++++++++++++++++----------------- src/lib.rs | 6 ++-- 2 files changed, 60 insertions(+), 32 deletions(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 6dd4099..b790d91 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -10,7 +10,7 @@ //! A Rust implementation of ed25519 EdDSA key generation, signing, and //! verification. -use core::fmt::Debug; +use core::fmt::{self, Debug, Display}; #[cfg(feature = "std")] use rand::Rng; @@ -123,10 +123,8 @@ impl Signature { /// Construct a `Signature` from a slice of bytes. #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != SIGNATURE_LENGTH { - return Err("Wrong length of bytes for signature! Need 64 bytes.") - } + pub fn from_bytes(bytes: &[u8]) -> Result { + check_bytes_len(bytes, SIGNATURE_LENGTH)?; let lower: &[u8; 32] = array_ref!(bytes, 0, 32); let upper: &[u8; 32] = array_ref!(bytes, 32, 32); @@ -199,8 +197,9 @@ impl SecretKey { /// # /// use ed25519_dalek::SecretKey; /// use ed25519_dalek::SECRET_KEY_LENGTH; + /// use ed25519_dalek::FromBytesError; /// - /// # fn doctest() -> Result { + /// # fn doctest() -> Result { /// let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = [ /// 157, 097, 177, 157, 239, 253, 090, 096, /// 186, 132, 074, 244, 146, 236, 044, 196, @@ -221,12 +220,11 @@ impl SecretKey { /// # Returns /// /// A `Result` whose okay value is an EdDSA `SecretKey` or whose error value - /// is an `&'static str` describing the error that occurred. + /// is an `FromBytesError` describing the error that occurred. #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != SECRET_KEY_LENGTH { - return Err("Wrong length of bytes for creating secret key!"); - } + pub fn from_bytes(bytes: &[u8]) -> Result { + check_bytes_len(bytes, SECRET_KEY_LENGTH)?; + Ok(SecretKey(*array_ref!(bytes, 0, SECRET_KEY_LENGTH))) } @@ -441,7 +439,7 @@ impl ExpandedSecretKey { /// # Returns /// /// A `Result` whose okay value is an EdDSA `ExpandedSecretKey` or whose - /// error value is an `&'static str` describing the error that occurred. + /// error value is an `FromBytesError` describing the error that occurred. /// /// # Examples /// @@ -452,9 +450,10 @@ impl ExpandedSecretKey { /// # /// use rand::{Rng, OsRng}; /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; + /// use ed25519_dalek::FromBytesError; /// /// # #[cfg(feature = "sha2")] - /// # fn do_test() -> Result { + /// # fn do_test() -> Result { /// # /// let mut csprng: OsRng = OsRng::new().unwrap(); /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); @@ -475,10 +474,9 @@ impl ExpandedSecretKey { /// # fn main() {} /// ``` #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != 64 { - return Err("Wrong length of bytes for creating expanded secret key!"); - } + pub fn from_bytes(bytes: &[u8]) -> Result { + check_bytes_len(bytes, 64)?; + Ok(ExpandedSecretKey{ key: Scalar(*array_ref!(bytes, 0, 32)), nonce: *array_ref!(bytes, 32, 32), }) } @@ -622,8 +620,9 @@ impl PublicKey { /// # /// use ed25519_dalek::PublicKey; /// use ed25519_dalek::PUBLIC_KEY_LENGTH; + /// use ed25519_dalek::FromBytesError; /// - /// # fn doctest() -> Result { + /// # fn doctest() -> Result { /// let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = [ /// 215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, /// 14, 225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26]; @@ -641,12 +640,11 @@ impl PublicKey { /// # Returns /// /// A `Result` whose okay value is an EdDSA `PublicKey` or whose error value - /// is an `&'static str` describing the error that occurred. + /// is an `FromBytesError` describing the error that occurred. #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != PUBLIC_KEY_LENGTH { - return Err("Wrong length of bytes for creating public key!"); - } + pub fn from_bytes(bytes: &[u8]) -> Result { + check_bytes_len(bytes, PUBLIC_KEY_LENGTH)?; + Ok(PublicKey(CompressedEdwardsY(*array_ref!(bytes, 0, 32)))) } @@ -796,11 +794,10 @@ impl Keypair { /// # Returns /// /// A `Result` whose okay value is an EdDSA `Keypair` or whose error value - /// is an `&'static str` describing the error that occurred. - pub fn from_bytes<'a>(bytes: &'a [u8]) -> Result { - if bytes.len() != KEYPAIR_LENGTH { - return Err("Wrong length of bytes for creating keypair!"); - } + /// is an `FromBytesError` describing the error that occurred. + pub fn from_bytes<'a>(bytes: &'a [u8]) -> Result { + check_bytes_len(bytes, KEYPAIR_LENGTH)?; + let secret = SecretKey::from_bytes(&bytes[..SECRET_KEY_LENGTH])?; let public = PublicKey::from_bytes(&bytes[SECRET_KEY_LENGTH..])?; @@ -896,6 +893,37 @@ impl<'d> Deserialize<'d> for Keypair { } } +/// An error which occurred when using the `from_bytes` constructor. +/// +/// This error will be returned if the byte slice given was not the correct +/// length for constructing that kind of object. +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +pub struct FromBytesError { + _private: (), +} + +impl Display for FromBytesError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "wrong length of bytes when constructing ed25519 object") + } +} + +#[cfg(feature = "std")] +impl ::std::error::Error for FromBytesError { + fn description(&self) -> &str { + "wrong length of bytes when constructing ed25519 object" + } +} + +#[inline(always)] +fn check_bytes_len(bytes: &[u8], len: usize) -> Result<(), FromBytesError> { + if bytes.len() != len { + Err(FromBytesError { _private: () }) + } else { + Ok(()) + } +} + #[cfg(test)] mod test { use std::io::BufReader; @@ -1032,7 +1060,7 @@ mod test { #[test] fn public_key_from_bytes() { // Make another function so that we can test the ? operator. - fn do_the_test() -> Result { + fn do_the_test() -> Result { let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = [ 215, 090, 152, 001, 130, 177, 010, 183, 213, 075, 254, 211, 201, 100, 007, 058, diff --git a/src/lib.rs b/src/lib.rs index 78ec572..d862d23 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -143,9 +143,9 @@ //! # extern crate ed25519_dalek; //! # use rand::{Rng, OsRng}; //! # use sha2::Sha512; -//! # use ed25519_dalek::{Keypair, Signature, PublicKey, SecretKey}; +//! # use ed25519_dalek::{Keypair, Signature, PublicKey, SecretKey, FromBytesError}; //! # use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; -//! # fn do_test() -> Result<(SecretKey, PublicKey, Keypair, Signature), &'static str> { +//! # fn do_test() -> Result<(SecretKey, PublicKey, Keypair, Signature), FromBytesError> { //! # let mut cspring: OsRng = OsRng::new().unwrap(); //! # let keypair_orig: Keypair = Keypair::generate::(&mut cspring); //! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); @@ -267,7 +267,7 @@ extern crate subtle; #[cfg(feature = "std")] extern crate rand; -#[cfg(test)] +#[cfg(any(feature = "std", test))] #[macro_use] extern crate std; From 96246506c07dd9ee1017ed7a222258f1c33d6ca7 Mon Sep 17 00:00:00 2001 From: Without Boats Date: Tue, 5 Dec 2017 18:18:34 -0800 Subject: [PATCH 05/14] This is a breaking change. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 5402d2a..1de19d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed25519-dalek" -version = "0.5.0" +version = "0.6.0" authors = ["Isis Lovecruft "] readme = "README.md" license = "BSD-3-Clause" From 6c1acaca7c40877af5eca3c2eb191821baf0ab45 Mon Sep 17 00:00:00 2001 From: Without Boats Date: Wed, 6 Dec 2017 15:25:12 -0800 Subject: [PATCH 06/14] Use failure instead of std::error::Error. failure is no_std compatible, whereas std::error::Error is not. --- Cargo.toml | 6 +++++- src/ed25519.rs | 7 +------ src/lib.rs | 1 + 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1de19d7..c64887b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,10 @@ optional = true version = "^0.6" optional = true +[dependencies.failure] +version = "^0.1.1" +default-features = false + [dev-dependencies] hex = "0.2" sha2 = "^0.6" @@ -52,7 +56,7 @@ bincode = "^0.9" [features] default = ["std"] -std = ["rand", "curve25519-dalek/std"] +std = ["rand", "curve25519-dalek/std", "failure/std"] bench = [] nightly = ["curve25519-dalek/nightly"] asm = ["sha2/asm"] diff --git a/src/ed25519.rs b/src/ed25519.rs index b790d91..da8c2cb 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -908,12 +908,7 @@ impl Display for FromBytesError { } } -#[cfg(feature = "std")] -impl ::std::error::Error for FromBytesError { - fn description(&self) -> &str { - "wrong length of bytes when constructing ed25519 object" - } -} +impl ::failure::Fail for FromBytesError { } #[inline(always)] fn check_bytes_len(bytes: &[u8], len: usize) -> Result<(), FromBytesError> { diff --git a/src/lib.rs b/src/lib.rs index d862d23..4d20d9e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -263,6 +263,7 @@ extern crate curve25519_dalek; extern crate generic_array; extern crate digest; extern crate subtle; +extern crate failure; #[cfg(feature = "std")] extern crate rand; From f64b20b351fddeb802ed0eb8ed9a92cfd83ac101 Mon Sep 17 00:00:00 2001 From: Oleg Andreev Date: Tue, 12 Dec 2017 15:41:39 -0800 Subject: [PATCH 07/14] Do not require feature=std for PublicKey::from_secret --- src/ed25519.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ed25519.rs b/src/ed25519.rs index 6dd4099..5412278 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -657,7 +657,6 @@ impl PublicKey { } /// Derive this public key from its corresponding `SecretKey`. - #[cfg(feature = "std")] #[allow(unused_assignments)] pub fn from_secret(secret_key: &SecretKey) -> PublicKey where D: Digest + Default { From c7b69c656246b0ed9783afa7a95825e1616ba3bf Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 23 Dec 2017 22:33:59 +0000 Subject: [PATCH 08/14] Expand boats' error types to give more detailed reasons for failures. This code was significantly based off without boats' error types in commit 6c1acaca7c40877af5eca3c2eb191821baf0ab45, and also upon conversation with them. Please target them with praise, and blame me for whatever mistakes I might have made. --- src/ed25519.rs | 112 +++++++++++++++++++++++-------------------------- src/errors.rs | 82 ++++++++++++++++++++++++++++++++++++ src/lib.rs | 7 +++- 3 files changed, 140 insertions(+), 61 deletions(-) create mode 100644 src/errors.rs diff --git a/src/ed25519.rs b/src/ed25519.rs index 988c278..15b6c4b 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -10,7 +10,7 @@ //! A Rust implementation of ed25519 EdDSA key generation, signing, and //! verification. -use core::fmt::{self, Debug, Display}; +use core::fmt::{Debug}; #[cfg(feature = "std")] use rand::Rng; @@ -41,10 +41,13 @@ use curve25519_dalek::scalar::Scalar; use subtle::slices_equal; -/// The length of an ed25519 EdDSA `Signature`, in bytes. +use errors::DecodingError; +use errors::InternalError; + +/// The length of a curve25519 EdDSA `Signature`, in bytes. pub const SIGNATURE_LENGTH: usize = 64; -/// The length of an ed25519 EdDSA `SecretKey`, in bytes. +/// The length of a curve25519 EdDSA `SecretKey`, in bytes. pub const SECRET_KEY_LENGTH: usize = 32; /// The length of an ed25519 EdDSA `PublicKey`, in bytes. @@ -53,6 +56,15 @@ pub const PUBLIC_KEY_LENGTH: usize = 32; /// The length of an ed25519 EdDSA `Keypair`, in bytes. pub const KEYPAIR_LENGTH: usize = SECRET_KEY_LENGTH + PUBLIC_KEY_LENGTH; +/// The length of the "key" portion of an "expanded" curve25519 EdDSA secret key, in bytes. +const EXPANDED_SECRET_KEY_KEY_LENGTH: usize = 32; + +/// The length of the "nonce" portion of an "expanded" curve25519 EdDSA secret key, in bytes. +const EXPANDED_SECRET_KEY_NONCE_LENGTH: usize = 32; + +/// The length of an "expanded" curve25519 EdDSA key, `ExpandedSecretKey`, in bytes. +pub const EXPANDED_SECRET_KEY_LENGTH: usize = EXPANDED_SECRET_KEY_KEY_LENGTH + EXPANDED_SECRET_KEY_NONCE_LENGTH; + /// An EdDSA signature. /// /// # Note @@ -123,9 +135,11 @@ impl Signature { /// Construct a `Signature` from a slice of bytes. #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { - check_bytes_len(bytes, SIGNATURE_LENGTH)?; - + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != SIGNATURE_LENGTH { + return Err(DecodingError(InternalError::BytesLengthError{ + name: "Signature", length: SIGNATURE_LENGTH })); + } let mut lower: [u8; 32] = [0u8; 32]; let mut upper: [u8; 32] = [0u8; 32]; @@ -133,7 +147,7 @@ impl Signature { upper.copy_from_slice(&bytes[32..]); if upper[31] & 224 != 0 { - return Err("High-bit of scalar 's' in signature must not be set.") + return Err(DecodingError(InternalError::ScalarFormatError)); } Ok(Signature{ r: CompressedEdwardsY(lower), s: Scalar::from_bits(upper) }) @@ -204,9 +218,9 @@ impl SecretKey { /// # /// use ed25519_dalek::SecretKey; /// use ed25519_dalek::SECRET_KEY_LENGTH; - /// use ed25519_dalek::FromBytesError; + /// use ed25519_dalek::DecodingError; /// - /// # fn doctest() -> Result { + /// # fn doctest() -> Result { /// let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = [ /// 157, 097, 177, 157, 239, 253, 090, 096, /// 186, 132, 074, 244, 146, 236, 044, 196, @@ -227,13 +241,14 @@ impl SecretKey { /// # Returns /// /// A `Result` whose okay value is an EdDSA `SecretKey` or whose error value - /// is an `FromBytesError` describing the error that occurred. + /// is an `DecodingError` wrapping the internal error that occurred. #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { - check_bytes_len(bytes, SECRET_KEY_LENGTH)?; - + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != SECRET_KEY_LENGTH { + return Err(DecodingError(InternalError::BytesLengthError{ + name: "SecretKey", length: SECRET_KEY_LENGTH })); + } let mut bits: [u8; 32] = [0u8; 32]; - bits.copy_from_slice(&bytes[..32]); Ok(SecretKey(bits)) @@ -437,7 +452,7 @@ impl ExpandedSecretKey { /// # fn main() { } /// ``` #[inline] - pub fn to_bytes(&self) -> [u8; 64] { + pub fn to_bytes(&self) -> [u8; EXPANDED_SECRET_KEY_LENGTH] { let mut bytes: [u8; 64] = [0u8; 64]; bytes[..32].copy_from_slice(self.key.as_bytes()); @@ -450,7 +465,7 @@ impl ExpandedSecretKey { /// # Returns /// /// A `Result` whose okay value is an EdDSA `ExpandedSecretKey` or whose - /// error value is an `FromBytesError` describing the error that occurred. + /// error value is an `DecodingError` describing the error that occurred. /// /// # Examples /// @@ -461,10 +476,10 @@ impl ExpandedSecretKey { /// # /// use rand::{Rng, OsRng}; /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; - /// use ed25519_dalek::FromBytesError; + /// use ed25519_dalek::DecodingError; /// /// # #[cfg(feature = "sha2")] - /// # fn do_test() -> Result { + /// # fn do_test() -> Result { /// # /// let mut csprng: OsRng = OsRng::new().unwrap(); /// let secret_key: SecretKey = SecretKey::generate(&mut csprng); @@ -485,9 +500,11 @@ impl ExpandedSecretKey { /// # fn main() {} /// ``` #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { - check_bytes_len(bytes, 64)?; - + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != EXPANDED_SECRET_KEY_LENGTH { + return Err(DecodingError(InternalError::BytesLengthError{ + name: "ExpandedSecretKey", length: EXPANDED_SECRET_KEY_LENGTH })); + } let mut lower: [u8; 32] = [0u8; 32]; let mut upper: [u8; 32] = [0u8; 32]; @@ -640,9 +657,9 @@ impl PublicKey { /// # /// use ed25519_dalek::PublicKey; /// use ed25519_dalek::PUBLIC_KEY_LENGTH; - /// use ed25519_dalek::FromBytesError; + /// use ed25519_dalek::DecodingError; /// - /// # fn doctest() -> Result { + /// # fn doctest() -> Result { /// let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = [ /// 215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, /// 14, 225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26]; @@ -660,13 +677,14 @@ impl PublicKey { /// # Returns /// /// A `Result` whose okay value is an EdDSA `PublicKey` or whose error value - /// is an `FromBytesError` describing the error that occurred. + /// is an `DecodingError` describing the error that occurred. #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { - check_bytes_len(bytes, PUBLIC_KEY_LENGTH)?; - + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != PUBLIC_KEY_LENGTH { + return Err(DecodingError(InternalError::BytesLengthError{ + name: "PublicKey", length: PUBLIC_KEY_LENGTH })); + } let mut bits: [u8; 32] = [0u8; 32]; - bits.copy_from_slice(&bytes[..32]); Ok(PublicKey(CompressedEdwardsY(bits))) @@ -814,10 +832,12 @@ impl Keypair { /// # Returns /// /// A `Result` whose okay value is an EdDSA `Keypair` or whose error value - /// is an `FromBytesError` describing the error that occurred. - pub fn from_bytes<'a>(bytes: &'a [u8]) -> Result { - check_bytes_len(bytes, KEYPAIR_LENGTH)?; - + /// is an `DecodingError` describing the error that occurred. + pub fn from_bytes<'a>(bytes: &'a [u8]) -> Result { + if bytes.len() != KEYPAIR_LENGTH { + return Err(DecodingError(InternalError::BytesLengthError{ + name: "Keypair", length: KEYPAIR_LENGTH})); + } let secret = SecretKey::from_bytes(&bytes[..SECRET_KEY_LENGTH])?; let public = PublicKey::from_bytes(&bytes[SECRET_KEY_LENGTH..])?; @@ -913,32 +933,6 @@ impl<'d> Deserialize<'d> for Keypair { } } -/// An error which occurred when using the `from_bytes` constructor. -/// -/// This error will be returned if the byte slice given was not the correct -/// length for constructing that kind of object. -#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] -pub struct FromBytesError { - _private: (), -} - -impl Display for FromBytesError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "wrong length of bytes when constructing ed25519 object") - } -} - -impl ::failure::Fail for FromBytesError { } - -#[inline(always)] -fn check_bytes_len(bytes: &[u8], len: usize) -> Result<(), FromBytesError> { - if bytes.len() != len { - Err(FromBytesError { _private: () }) - } else { - Ok(()) - } -} - #[cfg(test)] mod test { use std::io::BufReader; @@ -1075,7 +1069,7 @@ mod test { #[test] fn public_key_from_bytes() { // Make another function so that we can test the ? operator. - fn do_the_test() -> Result { + fn do_the_test() -> Result { let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = [ 215, 090, 152, 001, 130, 177, 010, 183, 213, 075, 254, 211, 201, 100, 007, 058, diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 0000000..ca672ec --- /dev/null +++ b/src/errors.rs @@ -0,0 +1,82 @@ +// -*- mode: rust; -*- +// +// This file is part of ed25519-dalek. +// Copyright (c) 2017 Isis Lovecruft +// See LICENSE for licensing information. +// +// Authors: +// - Isis Agora Lovecruft + +//! Errors which may occur when parsing keys and/or signatures to or from wire formats. + +// rustc seems to think the typenames in match statements (e.g. in +// Display) should be snake cased, for some reason. +#![allow(non_snake_case)] + +use core::fmt; +use core::fmt::Display; + +/// Internal errors. Most application-level developer will likely not +/// need to pay any attention to these. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +pub (crate) enum InternalError { + PointDecompressionError, + ScalarFormatError, + /// An error in the length of bytes handed to a constructor. + /// + /// To use this, pass a string specifying the `name` of the type which is + /// returning the error, and the `length` in bytes which its constructor + /// expects. + BytesLengthError{ name: &'static str, length: usize }, +} + +impl Display for InternalError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + InternalError::PointDecompressionError + => write!(f, "Cannot decompress extended twisted edwards point"), + InternalError::ScalarFormatError + => write!(f, "Cannot use scalar with high-bit set"), + InternalError::BytesLengthError{ name: n, length: l} + => write!(f, "{} must be {} bytes in length", n, l), + } + } +} + +impl ::failure::Fail for InternalError {} + +/// Errors which may occur in the `from_bytes()` constructors of `PublicKey`, +/// `SecretKey`, `ExpandedSecretKey`, `Keypair`, and `Signature`. +/// +/// There was an internal problem due to parsing the `Signature`. +/// +/// This error may arise due to: +/// +/// * A problem decompressing `r`, a curve point, in the `Signature`, or the +/// curve point for a `PublicKey`. +/// * A problem with the format of `s`, a scalar, in the `Signature`. This +/// is only raised if the high-bit of the scalar was set. (Scalars must +/// only be constructed from 255-bit integers.) +/// * Being given bytes with a length different to what was expected. +#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)] +pub struct DecodingError(pub (crate) InternalError); + +impl Display for DecodingError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.0 { + InternalError::PointDecompressionError => write!(f, "{}", self.0), + InternalError::ScalarFormatError => write!(f, "{}", self.0), + InternalError::BytesLengthError{ name: _, length: _ } => write!(f, "{}", self.0), + } + } +} + +impl ::failure::Fail for DecodingError { + fn cause(&self) -> Option<&::failure::Fail> { + match self.0 { + InternalError::PointDecompressionError => Some(&self.0), + InternalError::ScalarFormatError => Some(&self.0), + InternalError::BytesLengthError{ name: _, length: _} => Some(&self.0), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 4e4da19..a9a34e4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -143,9 +143,9 @@ //! # extern crate ed25519_dalek; //! # use rand::{Rng, OsRng}; //! # use sha2::Sha512; -//! # use ed25519_dalek::{Keypair, Signature, PublicKey, SecretKey, FromBytesError}; +//! # use ed25519_dalek::{Keypair, Signature, PublicKey, SecretKey, DecodingError}; //! # use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH}; -//! # fn do_test() -> Result<(SecretKey, PublicKey, Keypair, Signature), FromBytesError> { +//! # fn do_test() -> Result<(SecretKey, PublicKey, Keypair, Signature), DecodingError> { //! # let mut cspring: OsRng = OsRng::new().unwrap(); //! # let keypair_orig: Keypair = Keypair::generate::(&mut cspring); //! # let message: &[u8] = "This is a test of the tsunami alert system.".as_bytes(); @@ -287,5 +287,8 @@ extern crate bincode; mod ed25519; +pub mod errors; + // Export everything public in ed25519. pub use ed25519::*; +pub use errors::*; From e356e476d89e95c81eaa536093d154baa359cf0a Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 20 Jan 2018 02:33:53 +0000 Subject: [PATCH 09/14] Enable slack notifications. --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 1828597..7a99953 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,3 +27,8 @@ matrix: script: - cargo $TEST_COMMAND $FEATURES + +notifications: + slack: + rooms: + - dalek-cryptography:Xxv9WotKYWdSoKlgKNqXiHoD#dalek-bots From 04c8574106fd954bfa5faf5c22bb40a9357eb59c Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 20 Jan 2018 02:36:42 +0000 Subject: [PATCH 10/14] Bump versions for several dependencies. --- Cargo.toml | 12 ++++++------ src/ed25519.rs | 10 +++------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 103cb9a..ad944c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,30 +16,30 @@ exclude = [ ".gitignore", "TESTVECTORS", "res/*" ] travis-ci = { repository = "isislovecruft/ed25519-dalek", branch = "master"} [dependencies.curve25519-dalek] -version = "^0.14" +version = "0.14" default-features = false [dependencies.subtle] -version = "^0.3" +version = "0.5" default-features = false [dependencies.rand] optional = true -version = "^0.3" +version = "0.4" [dependencies.digest] -version = "^0.6" +version = "0.6" [dependencies.generic-array] # same version that digest depends on -version = "^0.8" +version = "0.9" [dependencies.serde] version = "^1.0" optional = true [dependencies.sha2] -version = "^0.6" +version = "0.7" optional = true [dependencies.failure] diff --git a/src/ed25519.rs b/src/ed25519.rs index 15b6c4b..b3b2f2b 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -27,10 +27,7 @@ use serde::de::Visitor; #[cfg(feature = "sha2")] use sha2::Sha512; -use digest::BlockInput; use digest::Digest; -use digest::Input; -use digest::FixedOutput; use generic_array::typenum::U64; @@ -539,7 +536,6 @@ impl ExpandedSecretKey { /// ``` pub fn from_secret_key(secret_key: &SecretKey) -> ExpandedSecretKey where D: Digest + Default { - let mut h: D = D::default(); let mut hash: [u8; 64] = [0u8; 64]; let mut lower: [u8; 32] = [0u8; 32]; @@ -561,7 +557,6 @@ impl ExpandedSecretKey { /// Sign a message with this `ExpandedSecretKey`. pub fn sign(&self, message: &[u8], public_key: &PublicKey) -> Signature where D: Digest + Default { - let mut h: D = D::default(); let mut hash: [u8; 64] = [0u8; 64]; let mesg_digest: Scalar; @@ -887,13 +882,14 @@ impl Keypair { } /// Sign a message with this keypair's secret key. - pub fn sign(&self, message: &[u8]) -> Signature where D: Digest + Default { + pub fn sign(&self, message: &[u8]) -> Signature + where D: Digest + Default { self.secret.expand::().sign::(&message, &self.public) } /// Verify a signature on a message with this keypair's public key. pub fn verify(&self, message: &[u8], signature: &Signature) -> bool - where D: FixedOutput + BlockInput + Default + Input { + where D: Digest + Default { self.public.verify::(message, signature) } } From 6724268ea112a31f952b3548ee4b726668cb5566 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 20 Jan 2018 02:49:53 +0000 Subject: [PATCH 11/14] Update website and repo links. --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ad944c9..953ad88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "ed25519-dalek" version = "0.6.0" -authors = ["Isis Lovecruft "] +authors = ["Isis Lovecruft "] readme = "README.md" license = "BSD-3-Clause" -repository = "https://github.com/isislovecruft/ed25519-dalek" -homepage = "https://code.ciph.re/isis/ed25519-dalek" +repository = "https://github.com/dalek-cryptography/ed25519-dalek" +homepage = "https://dalek.rs" documentation = "https://docs.rs/ed25519-dalek" keywords = ["cryptography", "ed25519", "curve25519", "signature", "ECC"] categories = ["cryptography", "no-std"] From b650ae0c28cf242eaa74fa07200628efddfe9d3a Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 20 Jan 2018 02:54:42 +0000 Subject: [PATCH 12/14] Update dev dependency versions. --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 953ad88..65c8fc8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,8 +47,8 @@ version = "^0.1.1" default-features = false [dev-dependencies] -hex = "0.2" -sha2 = "^0.6" +hex = "0.3" +sha2 = "0.7" bincode = "^0.9" [features] From 4c633acaf93e5c07952bf6721d9eb7c946771b8f Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 20 Jan 2018 02:56:35 +0000 Subject: [PATCH 13/14] Bump ed25519-dalek version to 0.6.0. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5ea1c7b..b580cf8 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ eventually support VXEdDSA in curve25519-dalek. To install, add the following to your project's `Cargo.toml`: [dependencies.ed25519-dalek] - version = "^0.5" + version = "^0.6" Then, in your library or executable source, add: @@ -129,7 +129,7 @@ To cause your application to build `ed25519-dalek` with the nightly feature enabled by default, instead do: [dependencies.ed25519-dalek] - version = "^0.5" + version = "^0.6" features = ["nightly"] To cause your application to instead build with the nightly feature enabled @@ -145,7 +145,7 @@ verification. To enable [serde](https://serde.rs) support, build `ed25519-dalek` with: [dependencies.ed25519-dalek] - version = "^0.5" + version = "^0.6" features = ["serde"] From 20fd237d35d10352dd553fa766af3afb5059fc63 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 20 Jan 2018 03:00:47 +0000 Subject: [PATCH 14/14] Revert to using sha2^=0.6. --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 65c8fc8..53040ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ version = "^1.0" optional = true [dependencies.sha2] -version = "0.7" +version = "0.6" optional = true [dependencies.failure] @@ -48,7 +48,7 @@ default-features = false [dev-dependencies] hex = "0.3" -sha2 = "0.7" +sha2 = "0.6" bincode = "^0.9" [features]