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 diff --git a/Cargo.toml b/Cargo.toml index 5402d2a..53040ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "ed25519-dalek" -version = "0.5.0" -authors = ["Isis Lovecruft "] +version = "0.6.0" +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"] @@ -15,44 +15,45 @@ 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] -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.6" optional = true +[dependencies.failure] +version = "^0.1.1" +default-features = false + [dev-dependencies] -hex = "0.2" -sha2 = "^0.6" +hex = "0.3" +sha2 = "0.6" 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/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"] diff --git a/src/ed25519.rs b/src/ed25519.rs index 6dd4099..b01077b 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::{Debug}; #[cfg(feature = "std")] use rand::Rng; @@ -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; @@ -41,10 +38,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 +53,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,15 +132,22 @@ impl Signature { /// Construct a `Signature` from a slice of bytes. #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { + pub fn from_bytes(bytes: &[u8]) -> Result { if bytes.len() != SIGNATURE_LENGTH { - return Err("Wrong length of bytes for signature! Need 64 bytes.") + return Err(DecodingError(InternalError::BytesLengthError{ + name: "Signature", length: SIGNATURE_LENGTH })); + } + let mut lower: [u8; 32] = [0u8; 32]; + let mut upper: [u8; 32] = [0u8; 32]; + + lower.copy_from_slice(&bytes[..32]); + upper.copy_from_slice(&bytes[32..]); + + if upper[31] & 224 != 0 { + return Err(DecodingError(InternalError::ScalarFormatError)); } - let lower: &[u8; 32] = array_ref!(bytes, 0, 32); - let upper: &[u8; 32] = array_ref!(bytes, 32, 32); - - Ok(Signature{ r: CompressedEdwardsY(*lower), s: Scalar(*upper) }) + Ok(Signature{ r: CompressedEdwardsY(lower), s: Scalar::from_bits(upper) }) } } @@ -199,8 +215,9 @@ impl SecretKey { /// # /// use ed25519_dalek::SecretKey; /// use ed25519_dalek::SECRET_KEY_LENGTH; + /// 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, @@ -221,13 +238,17 @@ 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 `DecodingError` wrapping the internal error that occurred. #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { + pub fn from_bytes(bytes: &[u8]) -> Result { if bytes.len() != SECRET_KEY_LENGTH { - return Err("Wrong length of bytes for creating secret key!"); + return Err(DecodingError(InternalError::BytesLengthError{ + name: "SecretKey", length: SECRET_KEY_LENGTH })); } - 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`. @@ -428,10 +449,10 @@ 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.0[..]); + bytes[..32].copy_from_slice(self.key.as_bytes()); bytes[32..].copy_from_slice(&self.nonce[..]); bytes } @@ -441,7 +462,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 `DecodingError` describing the error that occurred. /// /// # Examples /// @@ -452,9 +473,10 @@ impl ExpandedSecretKey { /// # /// use rand::{Rng, OsRng}; /// use ed25519_dalek::{SecretKey, ExpandedSecretKey}; + /// 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); @@ -475,12 +497,19 @@ 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 { + if bytes.len() != EXPANDED_SECRET_KEY_LENGTH { + return Err(DecodingError(InternalError::BytesLengthError{ + name: "ExpandedSecretKey", length: EXPANDED_SECRET_KEY_LENGTH })); } - 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`. @@ -507,26 +536,27 @@ 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 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`. 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; @@ -538,7 +568,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 +578,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 } } @@ -622,8 +652,9 @@ impl PublicKey { /// # /// use ed25519_dalek::PublicKey; /// use ed25519_dalek::PUBLIC_KEY_LENGTH; + /// 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]; @@ -641,13 +672,17 @@ 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 `DecodingError` describing the error that occurred. #[inline] - pub fn from_bytes(bytes: &[u8]) -> Result { + pub fn from_bytes(bytes: &[u8]) -> Result { if bytes.len() != PUBLIC_KEY_LENGTH { - return Err("Wrong length of bytes for creating public key!"); + return Err(DecodingError(InternalError::BytesLengthError{ + name: "PublicKey", length: PUBLIC_KEY_LENGTH })); } - 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. @@ -657,25 +692,24 @@ 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 { - 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 +721,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 +743,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 } @@ -740,7 +769,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 { @@ -753,6 +782,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, @@ -796,10 +826,11 @@ 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 { + /// is an `DecodingError` 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!"); + 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..])?; @@ -850,13 +881,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) } } @@ -1032,7 +1064,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, @@ -1149,10 +1181,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/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 78ec572..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}; +//! # 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), &'static str> { +//! # 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(); @@ -257,17 +257,16 @@ #![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; extern crate subtle; +extern crate failure; #[cfg(feature = "std")] extern crate rand; -#[cfg(test)] +#[cfg(any(feature = "std", test))] #[macro_use] extern crate std; @@ -288,5 +287,8 @@ extern crate bincode; mod ed25519; +pub mod errors; + // Export everything public in ed25519. pub use ed25519::*; +pub use errors::*;