mirror of
https://github.com/saymrwulf/curve25519-dalek-source.git
synced 2026-09-08 21:00:40 +00:00
Made ExpandedSecretKey private to avoid signing key oracle (#205)
This fix eliminates a scenario where a user misuses the `ExpandedSecretKey` API in a way that leaks the user's secret key. In short, if a user sends `ExpandedSecretKey::sign(sk, msg, pk1)` followed by `ExpandedSecretKey::sign(sk, msg, pk2)`, where `pk1 != pk2`, a passive adversary [can easily][0] derive `sk`. To mitigate this, we remove the API entirely. [0]: https://github.com/MystenLabs/ed25519-unsafe-libs
This commit is contained in:
parent
ad461f4f0f
commit
9638ab40a5
7 changed files with 99 additions and 129 deletions
|
|
@ -16,7 +16,7 @@ use criterion::Criterion;
|
||||||
|
|
||||||
mod ed25519_benches {
|
mod ed25519_benches {
|
||||||
use super::*;
|
use super::*;
|
||||||
use ed25519_dalek::ExpandedSecretKey;
|
use ed25519_dalek::verify_batch;
|
||||||
use ed25519_dalek::Keypair;
|
use ed25519_dalek::Keypair;
|
||||||
use ed25519_dalek::PublicKey;
|
use ed25519_dalek::PublicKey;
|
||||||
use ed25519_dalek::Signature;
|
use ed25519_dalek::Signature;
|
||||||
|
|
@ -30,20 +30,7 @@ mod ed25519_benches {
|
||||||
let keypair: Keypair = Keypair::generate(&mut csprng);
|
let keypair: Keypair = Keypair::generate(&mut csprng);
|
||||||
let msg: &[u8] = b"";
|
let msg: &[u8] = b"";
|
||||||
|
|
||||||
c.bench_function("Ed25519 signing", move |b| {
|
c.bench_function("Ed25519 signing", move |b| b.iter(|| keypair.sign(msg)));
|
||||||
b.iter(| | keypair.sign(msg))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sign_expanded_key(c: &mut Criterion) {
|
|
||||||
let mut csprng: ThreadRng = thread_rng();
|
|
||||||
let keypair: Keypair = Keypair::generate(&mut csprng);
|
|
||||||
let expanded: ExpandedSecretKey = (&keypair.secret).into();
|
|
||||||
let msg: &[u8] = b"";
|
|
||||||
|
|
||||||
c.bench_function("Ed25519 signing with an expanded secret key", move |b| {
|
|
||||||
b.iter(| | expanded.sign(msg, &keypair.public))
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify(c: &mut Criterion) {
|
fn verify(c: &mut Criterion) {
|
||||||
|
|
@ -78,8 +65,10 @@ mod ed25519_benches {
|
||||||
let keypairs: Vec<Keypair> = (0..size).map(|_| Keypair::generate(&mut csprng)).collect();
|
let keypairs: Vec<Keypair> = (0..size).map(|_| Keypair::generate(&mut csprng)).collect();
|
||||||
let msg: &[u8] = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
let msg: &[u8] = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
||||||
let messages: Vec<&[u8]> = (0..size).map(|_| msg).collect();
|
let messages: Vec<&[u8]> = (0..size).map(|_| msg).collect();
|
||||||
let signatures: Vec<Signature> = keypairs.iter().map(|key| key.sign(&msg)).collect();
|
let signatures: Vec<Signature> =
|
||||||
let public_keys: Vec<PublicKey> = keypairs.iter().map(|key| key.public).collect();
|
keypairs.iter().map(|key| key.sign(&msg)).collect();
|
||||||
|
let public_keys: Vec<PublicKey> =
|
||||||
|
keypairs.iter().map(|key| key.public_key()).collect();
|
||||||
|
|
||||||
b.iter(|| verify_batch(&messages[..], &signatures[..], &public_keys[..]));
|
b.iter(|| verify_batch(&messages[..], &signatures[..], &public_keys[..]));
|
||||||
},
|
},
|
||||||
|
|
@ -100,7 +89,6 @@ mod ed25519_benches {
|
||||||
config = Criterion::default();
|
config = Criterion::default();
|
||||||
targets =
|
targets =
|
||||||
sign,
|
sign,
|
||||||
sign_expanded_key,
|
|
||||||
verify,
|
verify,
|
||||||
verify_strict,
|
verify_strict,
|
||||||
verify_batch_signatures,
|
verify_batch_signatures,
|
||||||
|
|
|
||||||
|
|
@ -209,7 +209,7 @@ fn zero_rng() -> ZeroRng {
|
||||||
/// let msg: &[u8] = b"They're good dogs Brant";
|
/// let msg: &[u8] = b"They're good dogs Brant";
|
||||||
/// let messages: Vec<&[u8]> = (0..64).map(|_| msg).collect();
|
/// let messages: Vec<&[u8]> = (0..64).map(|_| msg).collect();
|
||||||
/// let signatures: Vec<Signature> = keypairs.iter().map(|key| key.sign(&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 public_keys: Vec<PublicKey> = keypairs.iter().map(|key| key.public_key()).collect();
|
||||||
///
|
///
|
||||||
/// let result = verify_batch(&messages[..], &signatures[..], &public_keys[..]);
|
/// let result = verify_batch(&messages[..], &signatures[..], &public_keys[..]);
|
||||||
/// assert!(result.is_ok());
|
/// assert!(result.is_ok());
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,8 @@ pub(crate) enum InternalError {
|
||||||
name_c: &'static str, length_c: usize, },
|
name_c: &'static str, length_c: usize, },
|
||||||
/// An ed25519ph signature can only take up to 255 octets of context.
|
/// An ed25519ph signature can only take up to 255 octets of context.
|
||||||
PrehashedContextLengthError,
|
PrehashedContextLengthError,
|
||||||
|
/// A mismatched (public, secret) key pair.
|
||||||
|
MismatchedKeypairError,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for InternalError {
|
impl Display for InternalError {
|
||||||
|
|
@ -63,6 +65,7 @@ impl Display for InternalError {
|
||||||
{} has length {}, {} has length {}.", na, la, nb, lb, nc, lc),
|
{} has length {}, {} has length {}.", na, la, nb, lb, nc, lc),
|
||||||
InternalError::PrehashedContextLengthError
|
InternalError::PrehashedContextLengthError
|
||||||
=> write!(f, "An ed25519ph signature can only take up to 255 octets of context"),
|
=> write!(f, "An ed25519ph signature can only take up to 255 octets of context"),
|
||||||
|
InternalError::MismatchedKeypairError => write!(f, "Mismatched Keypair detected"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ use serde::de::Error as SerdeError;
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use serde_bytes::{Bytes as SerdeBytes, ByteBuf as SerdeByteBuf};
|
use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes};
|
||||||
|
|
||||||
pub use sha2::Sha512;
|
pub use sha2::Sha512;
|
||||||
|
|
||||||
|
|
@ -32,15 +32,34 @@ use crate::public::*;
|
||||||
use crate::secret::*;
|
use crate::secret::*;
|
||||||
|
|
||||||
/// An ed25519 keypair.
|
/// An ed25519 keypair.
|
||||||
|
// Invariant: `public` is always the public key of `secret`. This prevents the signing function
|
||||||
|
// oracle attack described in https://github.com/MystenLabs/ed25519-unsafe-libs
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Keypair {
|
pub struct Keypair {
|
||||||
/// The secret half of this keypair.
|
/// The secret half of this keypair.
|
||||||
pub secret: SecretKey,
|
pub(crate) secret: SecretKey,
|
||||||
/// The public half of this keypair.
|
/// The public half of this keypair.
|
||||||
pub public: PublicKey,
|
pub(crate) public: PublicKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SecretKey> for Keypair {
|
||||||
|
fn from(secret: SecretKey) -> Self {
|
||||||
|
let public = PublicKey::from(&secret);
|
||||||
|
Self { secret, public }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Keypair {
|
impl Keypair {
|
||||||
|
/// Get the secret key of this keypair.
|
||||||
|
pub fn secret_key(&self) -> SecretKey {
|
||||||
|
SecretKey(self.secret.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the public key of this keypair.
|
||||||
|
pub fn public_key(&self) -> PublicKey {
|
||||||
|
self.public
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert this keypair to bytes.
|
/// Convert this keypair to bytes.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
|
|
@ -49,7 +68,8 @@ impl Keypair {
|
||||||
/// `SECRET_KEY_LENGTH` of bytes is the `SecretKey`, and the next
|
/// `SECRET_KEY_LENGTH` of bytes is the `SecretKey`, and the next
|
||||||
/// `PUBLIC_KEY_LENGTH` bytes is the `PublicKey` (the same as other
|
/// `PUBLIC_KEY_LENGTH` bytes is the `PublicKey` (the same as other
|
||||||
/// libraries, such as [Adam Langley's ed25519 Golang
|
/// libraries, such as [Adam Langley's ed25519 Golang
|
||||||
/// implementation](https://github.com/agl/ed25519/)).
|
/// implementation](https://github.com/agl/ed25519/)). It is guaranteed that
|
||||||
|
/// the encoded public key is the one derived from the encoded secret key.
|
||||||
pub fn to_bytes(&self) -> [u8; KEYPAIR_LENGTH] {
|
pub fn to_bytes(&self) -> [u8; KEYPAIR_LENGTH] {
|
||||||
let mut bytes: [u8; KEYPAIR_LENGTH] = [0u8; KEYPAIR_LENGTH];
|
let mut bytes: [u8; KEYPAIR_LENGTH] = [0u8; KEYPAIR_LENGTH];
|
||||||
|
|
||||||
|
|
@ -62,32 +82,31 @@ impl Keypair {
|
||||||
///
|
///
|
||||||
/// # Inputs
|
/// # Inputs
|
||||||
///
|
///
|
||||||
/// * `bytes`: an `&[u8]` representing the scalar for the secret key, and a
|
/// * `bytes`: an `&[u8]` of length [`KEYPAIR_LENGTH`], representing the
|
||||||
/// compressed Edwards-Y coordinate of a point on curve25519, both as bytes.
|
/// scalar for the secret key, and a compressed Edwards-Y coordinate of a
|
||||||
/// (As obtained from `Keypair::to_bytes()`.)
|
/// point on curve25519, both as bytes. (As obtained from
|
||||||
///
|
/// [`Keypair::to_bytes`].)
|
||||||
/// # Warning
|
|
||||||
///
|
|
||||||
/// Absolutely no validation is done on the key. If you give this function
|
|
||||||
/// bytes which do not represent a valid point, or which do not represent
|
|
||||||
/// corresponding parts of the key, then your `Keypair` will be broken and
|
|
||||||
/// it will be your fault.
|
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// A `Result` whose okay value is an EdDSA `Keypair` or whose error value
|
/// A `Result` whose okay value is an EdDSA `Keypair` or whose error value
|
||||||
/// is an `SignatureError` describing the error that occurred.
|
/// is an `SignatureError` describing the error that occurred.
|
||||||
pub fn from_bytes<'a>(bytes: &'a [u8]) -> Result<Keypair, SignatureError> {
|
pub fn from_bytes(bytes: &[u8]) -> Result<Keypair, SignatureError> {
|
||||||
if bytes.len() != KEYPAIR_LENGTH {
|
if bytes.len() != KEYPAIR_LENGTH {
|
||||||
return Err(InternalError::BytesLengthError {
|
return Err(InternalError::BytesLengthError {
|
||||||
name: "Keypair",
|
name: "Keypair",
|
||||||
length: KEYPAIR_LENGTH,
|
length: KEYPAIR_LENGTH,
|
||||||
}.into());
|
}
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
let secret = SecretKey::from_bytes(&bytes[..SECRET_KEY_LENGTH])?;
|
let secret = SecretKey::from_bytes(&bytes[..SECRET_KEY_LENGTH])?;
|
||||||
let public = PublicKey::from_bytes(&bytes[SECRET_KEY_LENGTH..])?;
|
let public = PublicKey::from_bytes(&bytes[SECRET_KEY_LENGTH..])?;
|
||||||
|
|
||||||
Ok(Keypair{ secret: secret, public: public })
|
if public != (&secret).into() {
|
||||||
|
return Err(InternalError::MismatchedKeypairError.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Keypair { secret, public })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate an ed25519 keypair.
|
/// Generate an ed25519 keypair.
|
||||||
|
|
@ -131,7 +150,10 @@ impl Keypair {
|
||||||
let sk: SecretKey = SecretKey::generate(csprng);
|
let sk: SecretKey = SecretKey::generate(csprng);
|
||||||
let pk: PublicKey = (&sk).into();
|
let pk: PublicKey = (&sk).into();
|
||||||
|
|
||||||
Keypair{ public: pk, secret: sk }
|
Keypair {
|
||||||
|
public: pk,
|
||||||
|
secret: sk,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sign a `prehashed_message` with this `Keypair` using the
|
/// Sign a `prehashed_message` with this `Keypair` using the
|
||||||
|
|
@ -244,16 +266,17 @@ impl Keypair {
|
||||||
{
|
{
|
||||||
let expanded: ExpandedSecretKey = (&self.secret).into(); // xxx thanks i hate this
|
let expanded: ExpandedSecretKey = (&self.secret).into(); // xxx thanks i hate this
|
||||||
|
|
||||||
expanded.sign_prehashed(prehashed_message, &self.public, context).into()
|
expanded
|
||||||
|
.sign_prehashed(prehashed_message, &self.public, context)
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify a signature on a message with this keypair's public key.
|
/// Verify a signature on a message with this keypair's public key.
|
||||||
pub fn verify(
|
pub fn verify(
|
||||||
&self,
|
&self,
|
||||||
message: &[u8],
|
message: &[u8],
|
||||||
signature: &ed25519::Signature
|
signature: &ed25519::Signature,
|
||||||
) -> Result<(), SignatureError>
|
) -> Result<(), SignatureError> {
|
||||||
{
|
|
||||||
self.public.verify(message, signature)
|
self.public.verify(message, signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -303,7 +326,7 @@ impl Keypair {
|
||||||
/// let mut prehashed_again: Sha512 = Sha512::default();
|
/// let mut prehashed_again: Sha512 = Sha512::default();
|
||||||
/// prehashed_again.update(message);
|
/// prehashed_again.update(message);
|
||||||
///
|
///
|
||||||
/// let verified = keypair.public.verify_prehashed(prehashed_again, Some(context), &sig);
|
/// let verified = keypair.public_key().verify_prehashed(prehashed_again, Some(context), &sig);
|
||||||
///
|
///
|
||||||
/// assert!(verified.is_ok());
|
/// assert!(verified.is_ok());
|
||||||
///
|
///
|
||||||
|
|
@ -329,7 +352,8 @@ impl Keypair {
|
||||||
where
|
where
|
||||||
D: Digest<OutputSize = U64>,
|
D: Digest<OutputSize = U64>,
|
||||||
{
|
{
|
||||||
self.public.verify_prehashed(prehashed_message, context, signature)
|
self.public
|
||||||
|
.verify_prehashed(prehashed_message, context, signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Strictly verify a signature on a message with this keypair's public key.
|
/// Strictly verify a signature on a message with this keypair's public key.
|
||||||
|
|
@ -399,8 +423,7 @@ impl Keypair {
|
||||||
&self,
|
&self,
|
||||||
message: &[u8],
|
message: &[u8],
|
||||||
signature: &ed25519::Signature,
|
signature: &ed25519::Signature,
|
||||||
) -> Result<(), SignatureError>
|
) -> Result<(), SignatureError> {
|
||||||
{
|
|
||||||
self.public.verify_strict(message, signature)
|
self.public.verify_strict(message, signature)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
17
src/lib.rs
17
src/lib.rs
|
|
@ -87,7 +87,7 @@
|
||||||
//! # let message: &[u8] = b"This is a test of the tsunami alert system.";
|
//! # let message: &[u8] = b"This is a test of the tsunami alert system.";
|
||||||
//! # let signature: Signature = keypair.sign(message);
|
//! # let signature: Signature = keypair.sign(message);
|
||||||
//!
|
//!
|
||||||
//! let public_key: PublicKey = keypair.public;
|
//! let public_key: PublicKey = keypair.public_key();
|
||||||
//! assert!(public_key.verify(message, &signature).is_ok());
|
//! assert!(public_key.verify(message, &signature).is_ok());
|
||||||
//! # }
|
//! # }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
@ -111,10 +111,9 @@
|
||||||
//! # let keypair: Keypair = Keypair::generate(&mut csprng);
|
//! # let keypair: Keypair = Keypair::generate(&mut csprng);
|
||||||
//! # let message: &[u8] = b"This is a test of the tsunami alert system.";
|
//! # let message: &[u8] = b"This is a test of the tsunami alert system.";
|
||||||
//! # let signature: Signature = keypair.sign(message);
|
//! # let signature: Signature = keypair.sign(message);
|
||||||
//! # let public_key: PublicKey = keypair.public;
|
|
||||||
//!
|
//!
|
||||||
//! let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = public_key.to_bytes();
|
//! let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = keypair.public_key().to_bytes();
|
||||||
//! let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = keypair.secret.to_bytes();
|
//! let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = keypair.secret_key().to_bytes();
|
||||||
//! let keypair_bytes: [u8; KEYPAIR_LENGTH] = keypair.to_bytes();
|
//! let keypair_bytes: [u8; KEYPAIR_LENGTH] = keypair.to_bytes();
|
||||||
//! let signature_bytes: [u8; SIGNATURE_LENGTH] = signature.to_bytes();
|
//! let signature_bytes: [u8; SIGNATURE_LENGTH] = signature.to_bytes();
|
||||||
//! # }
|
//! # }
|
||||||
|
|
@ -127,6 +126,7 @@
|
||||||
//! # extern crate ed25519_dalek;
|
//! # extern crate ed25519_dalek;
|
||||||
//! # use std::convert::TryFrom;
|
//! # use std::convert::TryFrom;
|
||||||
//! # use rand::rngs::OsRng;
|
//! # use rand::rngs::OsRng;
|
||||||
|
//! # use std::convert::TryInto;
|
||||||
//! # use ed25519_dalek::{Keypair, Signature, Signer, 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};
|
//! # use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH};
|
||||||
//! # fn do_test() -> Result<(SecretKey, PublicKey, Keypair, Signature), SignatureError> {
|
//! # fn do_test() -> Result<(SecretKey, PublicKey, Keypair, Signature), SignatureError> {
|
||||||
|
|
@ -134,8 +134,8 @@
|
||||||
//! # let keypair_orig: Keypair = Keypair::generate(&mut csprng);
|
//! # let keypair_orig: Keypair = Keypair::generate(&mut csprng);
|
||||||
//! # let message: &[u8] = b"This is a test of the tsunami alert system.";
|
//! # let message: &[u8] = b"This is a test of the tsunami alert system.";
|
||||||
//! # let signature_orig: Signature = keypair_orig.sign(message);
|
//! # let signature_orig: Signature = keypair_orig.sign(message);
|
||||||
//! # let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = keypair_orig.public.to_bytes();
|
//! # let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = keypair_orig.public_key().to_bytes();
|
||||||
//! # let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = keypair_orig.secret.to_bytes();
|
//! # let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = keypair_orig.secret_key().to_bytes();
|
||||||
//! # let keypair_bytes: [u8; KEYPAIR_LENGTH] = keypair_orig.to_bytes();
|
//! # let keypair_bytes: [u8; KEYPAIR_LENGTH] = keypair_orig.to_bytes();
|
||||||
//! # let signature_bytes: [u8; SIGNATURE_LENGTH] = signature_orig.to_bytes();
|
//! # let signature_bytes: [u8; SIGNATURE_LENGTH] = signature_orig.to_bytes();
|
||||||
//! #
|
//! #
|
||||||
|
|
@ -181,7 +181,7 @@
|
||||||
//! # let keypair: Keypair = Keypair::generate(&mut csprng);
|
//! # let keypair: Keypair = Keypair::generate(&mut csprng);
|
||||||
//! # let message: &[u8] = b"This is a test of the tsunami alert system.";
|
//! # let message: &[u8] = b"This is a test of the tsunami alert system.";
|
||||||
//! # let signature: Signature = keypair.sign(message);
|
//! # let signature: Signature = keypair.sign(message);
|
||||||
//! # let public_key: PublicKey = keypair.public;
|
//! # let public_key: PublicKey = keypair.public_key();
|
||||||
//! # let verified: bool = public_key.verify(message, &signature).is_ok();
|
//! # let verified: bool = public_key.verify(message, &signature).is_ok();
|
||||||
//!
|
//!
|
||||||
//! let encoded_public_key: Vec<u8> = serialize(&public_key).unwrap();
|
//! let encoded_public_key: Vec<u8> = serialize(&public_key).unwrap();
|
||||||
|
|
@ -213,7 +213,7 @@
|
||||||
//! # let keypair: Keypair = Keypair::generate(&mut csprng);
|
//! # let keypair: Keypair = Keypair::generate(&mut csprng);
|
||||||
//! let message: &[u8] = b"This is a test of the tsunami alert system.";
|
//! let message: &[u8] = b"This is a test of the tsunami alert system.";
|
||||||
//! # let signature: Signature = keypair.sign(message);
|
//! # let signature: Signature = keypair.sign(message);
|
||||||
//! # let public_key: PublicKey = keypair.public;
|
//! # let public_key: PublicKey = keypair.public_key();
|
||||||
//! # let verified: bool = public_key.verify(message, &signature).is_ok();
|
//! # let verified: bool = public_key.verify(message, &signature).is_ok();
|
||||||
//! # let encoded_public_key: Vec<u8> = serialize(&public_key).unwrap();
|
//! # let encoded_public_key: Vec<u8> = serialize(&public_key).unwrap();
|
||||||
//! # let encoded_signature: Vec<u8> = serialize(&signature).unwrap();
|
//! # let encoded_signature: Vec<u8> = serialize(&signature).unwrap();
|
||||||
|
|
@ -234,7 +234,6 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![warn(future_incompatible)]
|
#![warn(future_incompatible)]
|
||||||
#![deny(missing_docs)] // refuse to compile if documentation is missing
|
#![deny(missing_docs)] // refuse to compile if documentation is missing
|
||||||
|
|
||||||
#![cfg_attr(not(test), forbid(unsafe_code))]
|
#![cfg_attr(not(test), forbid(unsafe_code))]
|
||||||
|
|
||||||
#[cfg(any(feature = "std", test))]
|
#[cfg(any(feature = "std", test))]
|
||||||
|
|
|
||||||
|
|
@ -239,7 +239,7 @@ impl<'d> Deserialize<'d> for SecretKey {
|
||||||
// same signature scheme, and which both fail in exactly the same way. For a
|
// same signature scheme, and which both fail in exactly the same way. For a
|
||||||
// better-designed, Schnorr-based signature scheme, see Trevor Perrin's work on
|
// better-designed, Schnorr-based signature scheme, see Trevor Perrin's work on
|
||||||
// "generalised EdDSA" and "VXEdDSA".
|
// "generalised EdDSA" and "VXEdDSA".
|
||||||
pub struct ExpandedSecretKey {
|
pub(crate) struct ExpandedSecretKey {
|
||||||
pub(crate) key: Scalar,
|
pub(crate) key: Scalar,
|
||||||
pub(crate) nonce: [u8; 32],
|
pub(crate) nonce: [u8; 32],
|
||||||
}
|
}
|
||||||
|
|
@ -256,7 +256,7 @@ impl<'a> From<&'a SecretKey> for ExpandedSecretKey {
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```ignore
|
||||||
/// # extern crate rand;
|
/// # extern crate rand;
|
||||||
/// # extern crate sha2;
|
/// # extern crate sha2;
|
||||||
/// # extern crate ed25519_dalek;
|
/// # extern crate ed25519_dalek;
|
||||||
|
|
@ -302,7 +302,7 @@ impl ExpandedSecretKey {
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```ignore
|
||||||
/// # extern crate rand;
|
/// # extern crate rand;
|
||||||
/// # extern crate sha2;
|
/// # extern crate sha2;
|
||||||
/// # extern crate ed25519_dalek;
|
/// # extern crate ed25519_dalek;
|
||||||
|
|
@ -342,7 +342,7 @@ impl ExpandedSecretKey {
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```ignore
|
||||||
/// # extern crate rand;
|
/// # extern crate rand;
|
||||||
/// # extern crate sha2;
|
/// # extern crate sha2;
|
||||||
/// # extern crate ed25519_dalek;
|
/// # extern crate ed25519_dalek;
|
||||||
|
|
@ -375,12 +375,13 @@ impl ExpandedSecretKey {
|
||||||
/// # fn main() { }
|
/// # fn main() { }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Result<ExpandedSecretKey, SignatureError> {
|
pub(crate) fn from_bytes(bytes: &[u8]) -> Result<ExpandedSecretKey, SignatureError> {
|
||||||
if bytes.len() != EXPANDED_SECRET_KEY_LENGTH {
|
if bytes.len() != EXPANDED_SECRET_KEY_LENGTH {
|
||||||
return Err(InternalError::BytesLengthError {
|
return Err(InternalError::BytesLengthError {
|
||||||
name: "ExpandedSecretKey",
|
name: "ExpandedSecretKey",
|
||||||
length: EXPANDED_SECRET_KEY_LENGTH,
|
length: EXPANDED_SECRET_KEY_LENGTH,
|
||||||
}.into());
|
}
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
let mut lower: [u8; 32] = [0u8; 32];
|
let mut lower: [u8; 32] = [0u8; 32];
|
||||||
let mut upper: [u8; 32] = [0u8; 32];
|
let mut upper: [u8; 32] = [0u8; 32];
|
||||||
|
|
@ -396,7 +397,7 @@ impl ExpandedSecretKey {
|
||||||
|
|
||||||
/// Sign a message with this `ExpandedSecretKey`.
|
/// Sign a message with this `ExpandedSecretKey`.
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub fn sign(&self, message: &[u8], public_key: &PublicKey) -> ed25519::Signature {
|
pub(crate) fn sign(&self, message: &[u8], public_key: &PublicKey) -> ed25519::Signature {
|
||||||
let mut h: Sha512 = Sha512::new();
|
let mut h: Sha512 = Sha512::new();
|
||||||
let R: CompressedEdwardsY;
|
let R: CompressedEdwardsY;
|
||||||
let r: Scalar;
|
let r: Scalar;
|
||||||
|
|
@ -441,7 +442,7 @@ impl ExpandedSecretKey {
|
||||||
///
|
///
|
||||||
/// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1
|
/// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub fn sign_prehashed<'a, D>(
|
pub(crate) fn sign_prehashed<'a, D>(
|
||||||
&self,
|
&self,
|
||||||
prehashed_message: D,
|
prehashed_message: D,
|
||||||
public_key: &PublicKey,
|
public_key: &PublicKey,
|
||||||
|
|
@ -507,28 +508,6 @@ impl ExpandedSecretKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl Serialize for ExpandedSecretKey {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: Serializer,
|
|
||||||
{
|
|
||||||
let bytes = &self.to_bytes()[..];
|
|
||||||
SerdeBytes::new(bytes).serialize(serializer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl<'d> Deserialize<'d> for ExpandedSecretKey {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'d>,
|
|
||||||
{
|
|
||||||
let bytes = <SerdeByteBuf>::deserialize(deserializer)?;
|
|
||||||
ExpandedSecretKey::from_bytes(bytes.as_ref()).map_err(SerdeError::custom)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
@ -547,4 +526,15 @@ mod test {
|
||||||
|
|
||||||
assert!(!memory.contains(&0x15));
|
assert!(!memory.contains(&0x15));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pubkey_from_secret_and_expanded_secret() {
|
||||||
|
let mut csprng = rand::rngs::OsRng {};
|
||||||
|
let secret: SecretKey = SecretKey::generate(&mut csprng);
|
||||||
|
let expanded_secret: ExpandedSecretKey = (&secret).into();
|
||||||
|
let public_from_secret: PublicKey = (&secret).into(); // XXX eww
|
||||||
|
let public_from_expanded_secret: PublicKey = (&expanded_secret).into(); // XXX eww
|
||||||
|
|
||||||
|
assert!(public_from_secret == public_from_expanded_secret);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ use sha2::Sha512;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod vectors {
|
mod vectors {
|
||||||
use curve25519_dalek::{edwards::EdwardsPoint, scalar::Scalar};
|
use curve25519_dalek::{edwards::EdwardsPoint, scalar::Scalar};
|
||||||
use ed25519::signature::Signature as _;
|
|
||||||
use sha2::{digest::Digest, Sha512};
|
use sha2::{digest::Digest, Sha512};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
|
|
@ -69,8 +68,10 @@ mod vectors {
|
||||||
let sig_bytes: Vec<u8> = FromHex::from_hex(&parts[3]).unwrap();
|
let sig_bytes: Vec<u8> = FromHex::from_hex(&parts[3]).unwrap();
|
||||||
|
|
||||||
let secret: SecretKey = SecretKey::from_bytes(&sec_bytes[..SECRET_KEY_LENGTH]).unwrap();
|
let secret: SecretKey = SecretKey::from_bytes(&sec_bytes[..SECRET_KEY_LENGTH]).unwrap();
|
||||||
let public: PublicKey = PublicKey::from_bytes(&pub_bytes[..PUBLIC_KEY_LENGTH]).unwrap();
|
let expected_public: PublicKey =
|
||||||
let keypair: Keypair = Keypair{ secret: secret, public: public };
|
PublicKey::from_bytes(&pub_bytes[..PUBLIC_KEY_LENGTH]).unwrap();
|
||||||
|
let keypair: Keypair = Keypair::from(secret);
|
||||||
|
assert_eq!(expected_public, keypair.public_key());
|
||||||
|
|
||||||
// The signatures in the test vectors also include the message
|
// The signatures in the test vectors also include the message
|
||||||
// at the end, but we just want R and S.
|
// at the end, but we just want R and S.
|
||||||
|
|
@ -97,8 +98,10 @@ mod vectors {
|
||||||
let sig_bytes: Vec<u8> = FromHex::from_hex(signature).unwrap();
|
let sig_bytes: Vec<u8> = FromHex::from_hex(signature).unwrap();
|
||||||
|
|
||||||
let secret: SecretKey = SecretKey::from_bytes(&sec_bytes[..SECRET_KEY_LENGTH]).unwrap();
|
let secret: SecretKey = SecretKey::from_bytes(&sec_bytes[..SECRET_KEY_LENGTH]).unwrap();
|
||||||
let public: PublicKey = PublicKey::from_bytes(&pub_bytes[..PUBLIC_KEY_LENGTH]).unwrap();
|
let expected_public: PublicKey =
|
||||||
let keypair: Keypair = Keypair{ secret: secret, public: public };
|
PublicKey::from_bytes(&pub_bytes[..PUBLIC_KEY_LENGTH]).unwrap();
|
||||||
|
let keypair: Keypair = Keypair::from(secret);
|
||||||
|
assert_eq!(expected_public, keypair.public_key());
|
||||||
let sig1: Signature = Signature::from_bytes(&sig_bytes[..]).unwrap();
|
let sig1: Signature = Signature::from_bytes(&sig_bytes[..]).unwrap();
|
||||||
|
|
||||||
let mut prehash_for_signing: Sha512 = Sha512::default();
|
let mut prehash_for_signing: Sha512 = Sha512::default();
|
||||||
|
|
@ -280,17 +283,6 @@ mod integrations {
|
||||||
|
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn pubkey_from_secret_and_expanded_secret() {
|
|
||||||
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
|
|
||||||
let public_from_expanded_secret: PublicKey = (&expanded_secret).into(); // XXX eww
|
|
||||||
|
|
||||||
assert!(public_from_secret == public_from_expanded_secret);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[serde(crate = "serde_crate")]
|
#[serde(crate = "serde_crate")]
|
||||||
|
|
@ -401,28 +393,6 @@ mod serialisation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn serialize_deserialize_expanded_secret_key_bincode() {
|
|
||||||
let expanded_secret_key = ExpandedSecretKey::from(&SecretKey::from_bytes(&SECRET_KEY_BYTES).unwrap());
|
|
||||||
let encoded_expanded_secret_key: Vec<u8> = bincode::serialize(&expanded_secret_key).unwrap();
|
|
||||||
let decoded_expanded_secret_key: ExpandedSecretKey = bincode::deserialize(&encoded_expanded_secret_key).unwrap();
|
|
||||||
|
|
||||||
for i in 0..EXPANDED_SECRET_KEY_LENGTH {
|
|
||||||
assert_eq!(expanded_secret_key.to_bytes()[i], decoded_expanded_secret_key.to_bytes()[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn serialize_deserialize_expanded_secret_key_json() {
|
|
||||||
let expanded_secret_key = ExpandedSecretKey::from(&SecretKey::from_bytes(&SECRET_KEY_BYTES).unwrap());
|
|
||||||
let encoded_expanded_secret_key = serde_json::to_string(&expanded_secret_key).unwrap();
|
|
||||||
let decoded_expanded_secret_key: ExpandedSecretKey = serde_json::from_str(&encoded_expanded_secret_key).unwrap();
|
|
||||||
|
|
||||||
for i in 0..EXPANDED_SECRET_KEY_LENGTH {
|
|
||||||
assert_eq!(expanded_secret_key.to_bytes()[i], decoded_expanded_secret_key.to_bytes()[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize_deserialize_keypair_bincode() {
|
fn serialize_deserialize_keypair_bincode() {
|
||||||
let keypair = Keypair::from_bytes(&KEYPAIR_BYTES).unwrap();
|
let keypair = Keypair::from_bytes(&KEYPAIR_BYTES).unwrap();
|
||||||
|
|
@ -471,13 +441,10 @@ mod serialisation {
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize_secret_key_size() {
|
fn serialize_secret_key_size() {
|
||||||
let secret_key: SecretKey = SecretKey::from_bytes(&SECRET_KEY_BYTES).unwrap();
|
let secret_key: SecretKey = SecretKey::from_bytes(&SECRET_KEY_BYTES).unwrap();
|
||||||
assert_eq!(bincode::serialized_size(&secret_key).unwrap() as usize, BINCODE_INT_LENGTH + SECRET_KEY_LENGTH);
|
assert_eq!(
|
||||||
}
|
bincode::serialized_size(&secret_key).unwrap() as usize,
|
||||||
|
BINCODE_INT_LENGTH + SECRET_KEY_LENGTH
|
||||||
#[test]
|
);
|
||||||
fn serialize_expanded_secret_key_size() {
|
|
||||||
let expanded_secret_key = ExpandedSecretKey::from(&SecretKey::from_bytes(&SECRET_KEY_BYTES).unwrap());
|
|
||||||
assert_eq!(bincode::serialized_size(&expanded_secret_key).unwrap() as usize, BINCODE_INT_LENGTH + EXPANDED_SECRET_KEY_LENGTH);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue