mirror of
https://github.com/dalek-cryptography/ed25519-dalek.git
synced 2026-01-09 19:18:00 -05:00
Rename Keypair => SigningKey; PublicKey => VerifyingKey (#242)
* Rename `signing` and `verifying` modules Renames the following modules: - `keypair` => `signing` - `public` => `verifying` Renaming these in an individual commit preserves the commit history. This is in anticipation of renaming the following per #225: - `Keypair` => `SigningKey` - `PublicKey` => `VerifyingKey` * Rename `Keypair` => `SigningKey`; `PublicKey` => `VerifyingKey` As proposed in #225, renames key types after their roles: - `SigningKey` produces signatures - `VerifyingKey` verifies signatures The `SecretKey` type is changed to a type alias for `[u8; 32]`, which matches the RFC8032 definition: https://www.rfc-editor.org/rfc/rfc8032#section-5.1.5 > The private key is 32 octets (256 bits, corresponding to b) of > cryptographically secure random data.
This commit is contained in:
@@ -12,16 +12,16 @@ use criterion::{criterion_group, criterion_main, Criterion};
|
||||
mod ed25519_benches {
|
||||
use super::*;
|
||||
use ed25519_dalek::verify_batch;
|
||||
use ed25519_dalek::Keypair;
|
||||
use ed25519_dalek::PublicKey;
|
||||
use ed25519_dalek::Signature;
|
||||
use ed25519_dalek::Signer;
|
||||
use ed25519_dalek::SigningKey;
|
||||
use ed25519_dalek::VerifyingKey;
|
||||
use rand::prelude::ThreadRng;
|
||||
use rand::thread_rng;
|
||||
|
||||
fn sign(c: &mut Criterion) {
|
||||
let mut csprng: ThreadRng = thread_rng();
|
||||
let keypair: Keypair = Keypair::generate(&mut csprng);
|
||||
let keypair: SigningKey = SigningKey::generate(&mut csprng);
|
||||
let msg: &[u8] = b"";
|
||||
|
||||
c.bench_function("Ed25519 signing", move |b| b.iter(|| keypair.sign(msg)));
|
||||
@@ -29,7 +29,7 @@ mod ed25519_benches {
|
||||
|
||||
fn verify(c: &mut Criterion) {
|
||||
let mut csprng: ThreadRng = thread_rng();
|
||||
let keypair: Keypair = Keypair::generate(&mut csprng);
|
||||
let keypair: SigningKey = SigningKey::generate(&mut csprng);
|
||||
let msg: &[u8] = b"";
|
||||
let sig: Signature = keypair.sign(msg);
|
||||
|
||||
@@ -40,7 +40,7 @@ mod ed25519_benches {
|
||||
|
||||
fn verify_strict(c: &mut Criterion) {
|
||||
let mut csprng: ThreadRng = thread_rng();
|
||||
let keypair: Keypair = Keypair::generate(&mut csprng);
|
||||
let keypair: SigningKey = SigningKey::generate(&mut csprng);
|
||||
let msg: &[u8] = b"";
|
||||
let sig: Signature = keypair.sign(msg);
|
||||
|
||||
@@ -58,16 +58,17 @@ mod ed25519_benches {
|
||||
"Ed25519 batch signature verification",
|
||||
|b, &&size| {
|
||||
let mut csprng: ThreadRng = thread_rng();
|
||||
let keypairs: Vec<Keypair> =
|
||||
(0..size).map(|_| Keypair::generate(&mut csprng)).collect();
|
||||
let keypairs: Vec<SigningKey> = (0..size)
|
||||
.map(|_| SigningKey::generate(&mut csprng))
|
||||
.collect();
|
||||
let msg: &[u8] = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
||||
let messages: Vec<&[u8]> = (0..size).map(|_| msg).collect();
|
||||
let signatures: Vec<Signature> =
|
||||
keypairs.iter().map(|key| key.sign(&msg)).collect();
|
||||
let public_keys: Vec<PublicKey> =
|
||||
keypairs.iter().map(|key| key.public_key()).collect();
|
||||
let verifying_keys: Vec<VerifyingKey> =
|
||||
keypairs.iter().map(|key| key.verifying_key()).collect();
|
||||
|
||||
b.iter(|| verify_batch(&messages[..], &signatures[..], &public_keys[..]));
|
||||
b.iter(|| verify_batch(&messages[..], &signatures[..], &verifying_keys[..]));
|
||||
},
|
||||
&BATCH_SIZES,
|
||||
);
|
||||
@@ -77,7 +78,7 @@ mod ed25519_benches {
|
||||
let mut csprng: ThreadRng = thread_rng();
|
||||
|
||||
c.bench_function("Ed25519 keypair generation", move |b| {
|
||||
b.iter(|| Keypair::generate(&mut csprng))
|
||||
b.iter(|| SigningKey::generate(&mut csprng))
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
34
src/batch.rs
34
src/batch.rs
@@ -34,8 +34,8 @@ use sha2::Sha512;
|
||||
|
||||
use crate::errors::InternalError;
|
||||
use crate::errors::SignatureError;
|
||||
use crate::public::PublicKey;
|
||||
use crate::signature::InternalSignature;
|
||||
use crate::VerifyingKey;
|
||||
|
||||
trait BatchTranscript {
|
||||
fn append_scalars(&mut self, scalars: &Vec<Scalar>);
|
||||
@@ -112,13 +112,13 @@ fn zero_rng() -> ZeroRng {
|
||||
ZeroRng {}
|
||||
}
|
||||
|
||||
/// Verify a batch of `signatures` on `messages` with their respective `public_keys`.
|
||||
/// Verify a batch of `signatures` on `messages` with their respective `verifying_keys`.
|
||||
///
|
||||
/// # Inputs
|
||||
///
|
||||
/// * `messages` is a slice of byte slices, one per signed message.
|
||||
/// * `signatures` is a slice of `Signature`s.
|
||||
/// * `public_keys` is a slice of `PublicKey`s.
|
||||
/// * `verifying_keys` is a slice of `VerifyingKey`s.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
@@ -183,27 +183,27 @@ fn zero_rng() -> ZeroRng {
|
||||
/// falsely "pass" the synthethic batch verification equation *for the same
|
||||
/// inputs*, but *only some crafted inputs* will pass the deterministic batch
|
||||
/// single, and neither of these will ever pass single signature verification,
|
||||
/// see the documentation for [`PublicKey.validate()`].
|
||||
/// see the documentation for [`VerifyingKey.validate()`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ed25519_dalek::verify_batch;
|
||||
/// use ed25519_dalek::Keypair;
|
||||
/// use ed25519_dalek::PublicKey;
|
||||
/// use ed25519_dalek::SigningKey;
|
||||
/// use ed25519_dalek::VerifyingKey;
|
||||
/// use ed25519_dalek::Signer;
|
||||
/// use ed25519_dalek::Signature;
|
||||
/// use rand::rngs::OsRng;
|
||||
///
|
||||
/// # fn main() {
|
||||
/// let mut csprng = OsRng{};
|
||||
/// let keypairs: Vec<Keypair> = (0..64).map(|_| Keypair::generate(&mut csprng)).collect();
|
||||
/// let signing_keys: Vec<_> = (0..64).map(|_| SigningKey::generate(&mut csprng)).collect();
|
||||
/// let msg: &[u8] = b"They're good dogs Brant";
|
||||
/// let messages: Vec<&[u8]> = (0..64).map(|_| msg).collect();
|
||||
/// let signatures: Vec<Signature> = keypairs.iter().map(|key| key.sign(&msg)).collect();
|
||||
/// let public_keys: Vec<PublicKey> = keypairs.iter().map(|key| key.public_key()).collect();
|
||||
/// let signatures: Vec<Signature> = signing_keys.iter().map(|key| key.sign(&msg)).collect();
|
||||
/// let verifying_keys: Vec<VerifyingKey> = signing_keys.iter().map(|key| key.verifying_key()).collect();
|
||||
///
|
||||
/// let result = verify_batch(&messages[..], &signatures[..], &public_keys[..]);
|
||||
/// let result = verify_batch(&messages[..], &signatures[..], &verifying_keys[..]);
|
||||
/// assert!(result.is_ok());
|
||||
/// # }
|
||||
/// ```
|
||||
@@ -211,20 +211,20 @@ fn zero_rng() -> ZeroRng {
|
||||
pub fn verify_batch(
|
||||
messages: &[&[u8]],
|
||||
signatures: &[ed25519::Signature],
|
||||
public_keys: &[PublicKey],
|
||||
verifying_keys: &[VerifyingKey],
|
||||
) -> 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()
|
||||
|| signatures.len() != verifying_keys.len()
|
||||
|| verifying_keys.len() != messages.len()
|
||||
{
|
||||
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(),
|
||||
name_c: "verifying_keys",
|
||||
length_c: verifying_keys.len(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
@@ -240,7 +240,7 @@ pub fn verify_batch(
|
||||
.map(|i| {
|
||||
let mut h: Sha512 = Sha512::default();
|
||||
h.update(signatures[i].R.as_bytes());
|
||||
h.update(public_keys[i].as_bytes());
|
||||
h.update(verifying_keys[i].as_bytes());
|
||||
h.update(&messages[i]);
|
||||
Scalar::from_hash(h)
|
||||
})
|
||||
@@ -284,7 +284,7 @@ pub fn verify_batch(
|
||||
let zhrams = hrams.iter().zip(zs.iter()).map(|(hram, z)| hram * z);
|
||||
|
||||
let Rs = signatures.iter().map(|sig| sig.R.decompress());
|
||||
let As = public_keys.iter().map(|pk| Some(pk.1));
|
||||
let As = verifying_keys.iter().map(|pk| Some(pk.1));
|
||||
let B = once(Some(constants::ED25519_BASEPOINT_POINT));
|
||||
|
||||
// Compute (-∑ z[i]s[i] (mod l)) B + ∑ z[i]R[i] + ∑ (z[i]H(R||A||M)[i] (mod l)) A[i] = 0
|
||||
|
||||
534
src/keypair.rs
534
src/keypair.rs
@@ -1,534 +0,0 @@
|
||||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of ed25519-dalek.
|
||||
// Copyright (c) 2017-2019 isis lovecruft
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
// - isis agora lovecruft <isis@patternsinthevoid.net>
|
||||
|
||||
//! ed25519 keypairs.
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
use ed25519::pkcs8::{self, DecodePrivateKey};
|
||||
|
||||
#[cfg(feature = "rand")]
|
||||
use rand::{CryptoRng, RngCore};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::de::Error as SerdeError;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes};
|
||||
|
||||
pub use sha2::Sha512;
|
||||
|
||||
use curve25519_dalek::digest::generic_array::typenum::U64;
|
||||
pub use curve25519_dalek::digest::Digest;
|
||||
|
||||
use ed25519::signature::{Signer, Verifier};
|
||||
|
||||
use crate::constants::*;
|
||||
use crate::errors::*;
|
||||
use crate::public::*;
|
||||
use crate::secret::*;
|
||||
|
||||
/// 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)]
|
||||
pub struct Keypair {
|
||||
/// The secret half of this keypair.
|
||||
pub(crate) secret: SecretKey,
|
||||
/// The public half of this keypair.
|
||||
pub(crate) public: PublicKey,
|
||||
}
|
||||
|
||||
impl From<SecretKey> for Keypair {
|
||||
fn from(secret: SecretKey) -> Self {
|
||||
let public = PublicKey::from(&secret);
|
||||
Self { secret, public }
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// An array of bytes, `[u8; KEYPAIR_LENGTH]`. The first
|
||||
/// `SECRET_KEY_LENGTH` of bytes is the `SecretKey`, and the next
|
||||
/// `PUBLIC_KEY_LENGTH` bytes is the `PublicKey` (the same as other
|
||||
/// libraries, such as [Adam Langley's ed25519 Golang
|
||||
/// 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] {
|
||||
let mut bytes: [u8; KEYPAIR_LENGTH] = [0u8; KEYPAIR_LENGTH];
|
||||
|
||||
bytes[..SECRET_KEY_LENGTH].copy_from_slice(self.secret.as_bytes());
|
||||
bytes[SECRET_KEY_LENGTH..].copy_from_slice(self.public.as_bytes());
|
||||
bytes
|
||||
}
|
||||
|
||||
/// Construct a `Keypair` from the bytes of a `PublicKey` and `SecretKey`.
|
||||
///
|
||||
/// # Inputs
|
||||
///
|
||||
/// * `bytes`: an `&[u8]` of length [`KEYPAIR_LENGTH`], representing the
|
||||
/// scalar for the secret key, and a compressed Edwards-Y coordinate of a
|
||||
/// point on curve25519, both as bytes. (As obtained from
|
||||
/// [`Keypair::to_bytes`].)
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Result` whose okay value is an EdDSA `Keypair` or whose error value
|
||||
/// is an `SignatureError` describing the error that occurred.
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Keypair, SignatureError> {
|
||||
if bytes.len() != KEYPAIR_LENGTH {
|
||||
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..])?;
|
||||
|
||||
if public != (&secret).into() {
|
||||
return Err(InternalError::MismatchedKeypairError.into());
|
||||
}
|
||||
|
||||
Ok(Keypair { secret, public })
|
||||
}
|
||||
|
||||
/// Generate an ed25519 keypair.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # #[cfg(feature = "std")]
|
||||
/// # 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);
|
||||
///
|
||||
/// # }
|
||||
/// #
|
||||
/// # #[cfg(not(feature = "std"))]
|
||||
/// # fn main() { }
|
||||
/// ```
|
||||
///
|
||||
/// # Input
|
||||
///
|
||||
/// A CSPRNG with a `fill_bytes()` method, e.g. `rand_os::OsRng`.
|
||||
///
|
||||
/// The caller must also supply a hash function which implements the
|
||||
/// `Digest` and `Default` traits, and which returns 512 bits of output.
|
||||
/// The standard hash function used for most ed25519 libraries is SHA-512,
|
||||
/// which is available with `use sha2::Sha512` as in the example above.
|
||||
/// Other suitable hash functions include Keccak-512 and Blake2b-512.
|
||||
#[cfg(feature = "rand")]
|
||||
pub fn generate<R>(csprng: &mut R) -> Keypair
|
||||
where
|
||||
R: CryptoRng + RngCore,
|
||||
{
|
||||
let sk: SecretKey = SecretKey::generate(csprng);
|
||||
let pk: PublicKey = (&sk).into();
|
||||
|
||||
Keypair {
|
||||
public: pk,
|
||||
secret: sk,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sign a `prehashed_message` with this `Keypair` using the
|
||||
/// Ed25519ph algorithm defined in [RFC8032 §5.1][rfc8032].
|
||||
///
|
||||
/// # Inputs
|
||||
///
|
||||
/// * `prehashed_message` is an instantiated hash digest with 512-bits of
|
||||
/// output which has had the message to be signed previously fed into its
|
||||
/// state.
|
||||
/// * `context` is an optional context string, up to 255 bytes inclusive,
|
||||
/// which may be used to provide additional domain separation. If not
|
||||
/// set, this will default to an empty string.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// An Ed25519ph [`Signature`] on the `prehashed_message`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ed25519_dalek::Digest;
|
||||
/// use ed25519_dalek::Keypair;
|
||||
/// use ed25519_dalek::Sha512;
|
||||
/// use ed25519_dalek::Signature;
|
||||
/// use rand::rngs::OsRng;
|
||||
///
|
||||
/// # #[cfg(feature = "std")]
|
||||
/// # fn main() {
|
||||
/// let mut csprng = OsRng{};
|
||||
/// let keypair: Keypair = Keypair::generate(&mut csprng);
|
||||
/// let message: &[u8] = b"All I want is to pet all of the dogs.";
|
||||
///
|
||||
/// // Create a hash digest object which we'll feed the message into:
|
||||
/// let mut prehashed: Sha512 = Sha512::new();
|
||||
///
|
||||
/// prehashed.update(message);
|
||||
/// # }
|
||||
/// #
|
||||
/// # #[cfg(not(feature = "std"))]
|
||||
/// # fn main() { }
|
||||
/// ```
|
||||
///
|
||||
/// If you want, you can optionally pass a "context". It is generally a
|
||||
/// good idea to choose a context and try to make it unique to your project
|
||||
/// and this specific usage of signatures.
|
||||
///
|
||||
/// For example, without this, if you were to [convert your OpenPGP key
|
||||
/// to a Bitcoin key][terrible_idea] (just as an example, and also Don't
|
||||
/// Ever Do That) and someone tricked you into signing an "email" which was
|
||||
/// actually a Bitcoin transaction moving all your magic internet money to
|
||||
/// their address, it'd be a valid transaction.
|
||||
///
|
||||
/// By adding a context, this trick becomes impossible, because the context
|
||||
/// is concatenated into the hash, which is then signed. So, going with the
|
||||
/// previous example, if your bitcoin wallet used a context of
|
||||
/// "BitcoinWalletAppTxnSigning" and OpenPGP used a context (this is likely
|
||||
/// the least of their safety problems) of "GPGsCryptoIsntConstantTimeLol",
|
||||
/// then the signatures produced by both could never match the other, even
|
||||
/// if they signed the exact same message with the same key.
|
||||
///
|
||||
/// Let's add a context for good measure (remember, you'll want to choose
|
||||
/// your own!):
|
||||
///
|
||||
/// ```
|
||||
/// # use ed25519_dalek::Digest;
|
||||
/// # use ed25519_dalek::Keypair;
|
||||
/// # use ed25519_dalek::Signature;
|
||||
/// # use ed25519_dalek::SignatureError;
|
||||
/// # use ed25519_dalek::Sha512;
|
||||
/// # use rand::rngs::OsRng;
|
||||
/// #
|
||||
/// # fn do_test() -> Result<Signature, SignatureError> {
|
||||
/// # let mut csprng = OsRng{};
|
||||
/// # let keypair: Keypair = Keypair::generate(&mut csprng);
|
||||
/// # let message: &[u8] = b"All I want is to pet all of the dogs.";
|
||||
/// # let mut prehashed: Sha512 = Sha512::new();
|
||||
/// # prehashed.update(message);
|
||||
/// #
|
||||
/// let context: &[u8] = b"Ed25519DalekSignPrehashedDoctest";
|
||||
///
|
||||
/// let sig: Signature = keypair.sign_prehashed(prehashed, Some(context))?;
|
||||
/// #
|
||||
/// # Ok(sig)
|
||||
/// # }
|
||||
/// # #[cfg(feature = "std")]
|
||||
/// # fn main() {
|
||||
/// # do_test();
|
||||
/// # }
|
||||
/// #
|
||||
/// # #[cfg(not(feature = "std"))]
|
||||
/// # fn main() { }
|
||||
/// ```
|
||||
///
|
||||
/// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1
|
||||
/// [terrible_idea]: https://github.com/isislovecruft/scripts/blob/master/gpgkey2bc.py
|
||||
pub fn sign_prehashed<D>(
|
||||
&self,
|
||||
prehashed_message: D,
|
||||
context: Option<&[u8]>,
|
||||
) -> Result<ed25519::Signature, SignatureError>
|
||||
where
|
||||
D: Digest<OutputSize = U64>,
|
||||
{
|
||||
let expanded: ExpandedSecretKey = (&self.secret).into(); // xxx thanks i hate this
|
||||
|
||||
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: &ed25519::Signature,
|
||||
) -> Result<(), SignatureError> {
|
||||
self.public.verify(message, signature)
|
||||
}
|
||||
|
||||
/// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm.
|
||||
///
|
||||
/// # Inputs
|
||||
///
|
||||
/// * `prehashed_message` is an instantiated hash digest with 512-bits of
|
||||
/// output which has had the message to be signed previously fed into its
|
||||
/// state.
|
||||
/// * `context` is an optional context string, up to 255 bytes inclusive,
|
||||
/// which may be used to provide additional domain separation. If not
|
||||
/// set, this will default to an empty string.
|
||||
/// * `signature` is a purported Ed25519ph [`Signature`] on the `prehashed_message`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns `true` if the `signature` was a valid signature created by this
|
||||
/// `Keypair` on the `prehashed_message`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ed25519_dalek::Digest;
|
||||
/// use ed25519_dalek::Keypair;
|
||||
/// use ed25519_dalek::Signature;
|
||||
/// use ed25519_dalek::SignatureError;
|
||||
/// use ed25519_dalek::Sha512;
|
||||
/// use rand::rngs::OsRng;
|
||||
///
|
||||
/// # fn do_test() -> Result<(), SignatureError> {
|
||||
/// let mut csprng = OsRng{};
|
||||
/// let keypair: Keypair = Keypair::generate(&mut csprng);
|
||||
/// let message: &[u8] = b"All I want is to pet all of the dogs.";
|
||||
///
|
||||
/// let mut prehashed: Sha512 = Sha512::new();
|
||||
/// prehashed.update(message);
|
||||
///
|
||||
/// let context: &[u8] = b"Ed25519DalekSignPrehashedDoctest";
|
||||
///
|
||||
/// let sig: Signature = keypair.sign_prehashed(prehashed, Some(context))?;
|
||||
///
|
||||
/// // The sha2::Sha512 struct doesn't implement Copy, so we'll have to create a new one:
|
||||
/// let mut prehashed_again: Sha512 = Sha512::default();
|
||||
/// prehashed_again.update(message);
|
||||
///
|
||||
/// let verified = keypair.public_key().verify_prehashed(prehashed_again, Some(context), &sig);
|
||||
///
|
||||
/// assert!(verified.is_ok());
|
||||
///
|
||||
/// # verified
|
||||
/// # }
|
||||
/// #
|
||||
/// # #[cfg(feature = "std")]
|
||||
/// # fn main() {
|
||||
/// # do_test();
|
||||
/// # }
|
||||
/// #
|
||||
/// # #[cfg(not(feature = "std"))]
|
||||
/// # fn main() { }
|
||||
/// ```
|
||||
///
|
||||
/// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1
|
||||
pub fn verify_prehashed<D>(
|
||||
&self,
|
||||
prehashed_message: D,
|
||||
context: Option<&[u8]>,
|
||||
signature: &ed25519::Signature,
|
||||
) -> Result<(), SignatureError>
|
||||
where
|
||||
D: Digest<OutputSize = U64>,
|
||||
{
|
||||
self.public
|
||||
.verify_prehashed(prehashed_message, context, signature)
|
||||
}
|
||||
|
||||
/// Strictly verify a signature on a message with this keypair's public key.
|
||||
///
|
||||
/// # On The (Multiple) Sources of Malleability in Ed25519 Signatures
|
||||
///
|
||||
/// This version of verification is technically non-RFC8032 compliant. The
|
||||
/// following explains why.
|
||||
///
|
||||
/// 1. Scalar Malleability
|
||||
///
|
||||
/// The authors of the RFC explicitly stated that verification of an ed25519
|
||||
/// signature must fail if the scalar `s` is not properly reduced mod \ell:
|
||||
///
|
||||
/// > To verify a signature on a message M using public key A, with F
|
||||
/// > being 0 for Ed25519ctx, 1 for Ed25519ph, and if Ed25519ctx or
|
||||
/// > Ed25519ph is being used, C being the context, first split the
|
||||
/// > signature into two 32-octet halves. Decode the first half as a
|
||||
/// > point R, and the second half as an integer S, in the range
|
||||
/// > 0 <= s < L. Decode the public key A as point A'. If any of the
|
||||
/// > decodings fail (including S being out of range), the signature is
|
||||
/// > invalid.)
|
||||
///
|
||||
/// All `verify_*()` functions within ed25519-dalek perform this check.
|
||||
///
|
||||
/// 2. Point malleability
|
||||
///
|
||||
/// The authors of the RFC added in a malleability check to step #3 in
|
||||
/// §5.1.7, for small torsion components in the `R` value of the signature,
|
||||
/// *which is not strictly required*, as they state:
|
||||
///
|
||||
/// > Check the group equation \[8\]\[S\]B = \[8\]R + \[8\]\[k\]A'. It's
|
||||
/// > sufficient, but not required, to instead check \[S\]B = R + \[k\]A'.
|
||||
///
|
||||
/// # History of Malleability Checks
|
||||
///
|
||||
/// As originally defined (cf. the "Malleability" section in the README of
|
||||
/// this repo), ed25519 signatures didn't consider *any* form of
|
||||
/// malleability to be an issue. Later the scalar malleability was
|
||||
/// considered important. Still later, particularly with interests in
|
||||
/// cryptocurrency design and in unique identities (e.g. for Signal users,
|
||||
/// Tor onion services, etc.), the group element malleability became a
|
||||
/// concern.
|
||||
///
|
||||
/// However, libraries had already been created to conform to the original
|
||||
/// definition. One well-used library in particular even implemented the
|
||||
/// group element malleability check, *but only for batch verification*!
|
||||
/// Which meant that even using the same library, a single signature could
|
||||
/// verify fine individually, but suddenly, when verifying it with a bunch
|
||||
/// of other signatures, the whole batch would fail!
|
||||
///
|
||||
/// # "Strict" Verification
|
||||
///
|
||||
/// This method performs *both* of the above signature malleability checks.
|
||||
///
|
||||
/// It must be done as a separate method because one doesn't simply get to
|
||||
/// change the definition of a cryptographic primitive ten years
|
||||
/// after-the-fact with zero consideration for backwards compatibility in
|
||||
/// hardware and protocols which have it already have the older definition
|
||||
/// baked in.
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// Returns `Ok(())` if the signature is valid, and `Err` otherwise.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn verify_strict(
|
||||
&self,
|
||||
message: &[u8],
|
||||
signature: &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)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for Keypair {
|
||||
type Error = SignatureError;
|
||||
|
||||
fn try_from(bytes: &[u8]) -> Result<Keypair, SignatureError> {
|
||||
Keypair::from_bytes(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl DecodePrivateKey for Keypair {}
|
||||
|
||||
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
|
||||
impl pkcs8::EncodePrivateKey for Keypair {
|
||||
fn to_pkcs8_der(&self) -> pkcs8::Result<pkcs8::SecretDocument> {
|
||||
pkcs8::KeypairBytes::from(self).to_pkcs8_der()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl TryFrom<pkcs8::KeypairBytes> for Keypair {
|
||||
type Error = pkcs8::Error;
|
||||
|
||||
fn try_from(pkcs8_key: pkcs8::KeypairBytes) -> pkcs8::Result<Self> {
|
||||
Keypair::try_from(&pkcs8_key)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl TryFrom<&pkcs8::KeypairBytes> for Keypair {
|
||||
type Error = pkcs8::Error;
|
||||
|
||||
fn try_from(pkcs8_key: &pkcs8::KeypairBytes) -> pkcs8::Result<Self> {
|
||||
let secret = SecretKey::from_bytes(&pkcs8_key.secret_key)
|
||||
.map_err(|_| pkcs8::Error::KeyMalformed)?;
|
||||
|
||||
let public = PublicKey::from(&secret);
|
||||
|
||||
// Validate the public key in the PKCS#8 document if present
|
||||
if let Some(public_bytes) = pkcs8_key.public_key {
|
||||
let pk = PublicKey::from_bytes(public_bytes.as_ref())
|
||||
.map_err(|_| pkcs8::Error::KeyMalformed)?;
|
||||
|
||||
if public != pk {
|
||||
return Err(pkcs8::Error::KeyMalformed);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Keypair { secret, public })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl From<Keypair> for pkcs8::KeypairBytes {
|
||||
fn from(keypair: Keypair) -> pkcs8::KeypairBytes {
|
||||
pkcs8::KeypairBytes::from(&keypair)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl From<&Keypair> for pkcs8::KeypairBytes {
|
||||
fn from(keypair: &Keypair) -> pkcs8::KeypairBytes {
|
||||
pkcs8::KeypairBytes {
|
||||
secret_key: keypair.secret.to_bytes(),
|
||||
public_key: Some(pkcs8::PublicKeyBytes(keypair.public.to_bytes())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl TryFrom<pkcs8::PrivateKeyInfo<'_>> for Keypair {
|
||||
type Error = pkcs8::Error;
|
||||
|
||||
fn try_from(private_key: pkcs8::PrivateKeyInfo<'_>) -> pkcs8::Result<Self> {
|
||||
pkcs8::KeypairBytes::try_from(private_key)?.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for Keypair {
|
||||
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 Keypair {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'d>,
|
||||
{
|
||||
let bytes = <SerdeByteBuf>::deserialize(deserializer)?;
|
||||
Keypair::from_bytes(bytes.as_ref()).map_err(SerdeError::custom)
|
||||
}
|
||||
}
|
||||
126
src/lib.rs
126
src/lib.rs
@@ -13,7 +13,7 @@
|
||||
//!
|
||||
//! Creating an ed25519 signature on a message is simple.
|
||||
//!
|
||||
//! First, we need to generate a `Keypair`, which includes both public and
|
||||
//! First, we need to generate a `SigningKey`, which includes both public and
|
||||
//! secret halves of an asymmetric key. To do so, we need a cryptographically
|
||||
//! secure pseudorandom number generator (CSPRNG). For this example, we'll use
|
||||
//! the operating system's builtin PRNG:
|
||||
@@ -22,28 +22,28 @@
|
||||
//! # #[cfg(feature = "std")]
|
||||
//! # fn main() {
|
||||
//! use rand::rngs::OsRng;
|
||||
//! use ed25519_dalek::Keypair;
|
||||
//! use ed25519_dalek::SigningKey;
|
||||
//! use ed25519_dalek::Signature;
|
||||
//!
|
||||
//! let mut csprng = OsRng{};
|
||||
//! let keypair: Keypair = Keypair::generate(&mut csprng);
|
||||
//! let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
//! # }
|
||||
//! #
|
||||
//! # #[cfg(not(feature = "std"))]
|
||||
//! # fn main() { }
|
||||
//! ```
|
||||
//!
|
||||
//! We can now use this `keypair` to sign a message:
|
||||
//! We can now use this `signing_key` to sign a message:
|
||||
//!
|
||||
//! ```
|
||||
//! # fn main() {
|
||||
//! # use rand::rngs::OsRng;
|
||||
//! # use ed25519_dalek::Keypair;
|
||||
//! # use ed25519_dalek::SigningKey;
|
||||
//! # let mut csprng = OsRng{};
|
||||
//! # let keypair: Keypair = Keypair::generate(&mut csprng);
|
||||
//! # let signing_key: SigningKey = SigningKey::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);
|
||||
//! let signature: Signature = signing_key.sign(message);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
@@ -53,39 +53,39 @@
|
||||
//! ```
|
||||
//! # fn main() {
|
||||
//! # use rand::rngs::OsRng;
|
||||
//! # use ed25519_dalek::{Keypair, Signature, Signer};
|
||||
//! # use ed25519_dalek::{SigningKey, Signature, Signer};
|
||||
//! # let mut csprng = OsRng{};
|
||||
//! # let keypair: Keypair = Keypair::generate(&mut csprng);
|
||||
//! # let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
//! # let message: &[u8] = b"This is a test of the tsunami alert system.";
|
||||
//! # let signature: Signature = keypair.sign(message);
|
||||
//! # let signature: Signature = signing_key.sign(message);
|
||||
//! use ed25519_dalek::Verifier;
|
||||
//! assert!(keypair.verify(message, &signature).is_ok());
|
||||
//! assert!(signing_key.verify(message, &signature).is_ok());
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! Anyone else, given the `public` half of the `keypair` can also easily
|
||||
//! Anyone else, given the `public` half of the `signing_key` can also easily
|
||||
//! verify this signature:
|
||||
//!
|
||||
//! ```
|
||||
//! # fn main() {
|
||||
//! # use rand::rngs::OsRng;
|
||||
//! # use ed25519_dalek::Keypair;
|
||||
//! # use ed25519_dalek::SigningKey;
|
||||
//! # use ed25519_dalek::Signature;
|
||||
//! # use ed25519_dalek::Signer;
|
||||
//! use ed25519_dalek::{PublicKey, Verifier};
|
||||
//! use ed25519_dalek::{VerifyingKey, Verifier};
|
||||
//! # let mut csprng = OsRng{};
|
||||
//! # let keypair: Keypair = Keypair::generate(&mut csprng);
|
||||
//! # let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
//! # let message: &[u8] = b"This is a test of the tsunami alert system.";
|
||||
//! # let signature: Signature = keypair.sign(message);
|
||||
//! # let signature: Signature = signing_key.sign(message);
|
||||
//!
|
||||
//! let public_key: PublicKey = keypair.public_key();
|
||||
//! assert!(public_key.verify(message, &signature).is_ok());
|
||||
//! let verifying_key: VerifyingKey = signing_key.verifying_key();
|
||||
//! assert!(verifying_key.verify(message, &signature).is_ok());
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Serialisation
|
||||
//!
|
||||
//! `PublicKey`s, `SecretKey`s, `Keypair`s, and `Signature`s can be serialised
|
||||
//! `VerifyingKey`s, `SecretKey`s, `SigningKey`s, and `Signature`s can be serialised
|
||||
//! into byte-arrays by calling `.to_bytes()`. It's perfectly acceptible and
|
||||
//! safe to transfer and/or store those bytes. (Of course, never transfer your
|
||||
//! secret key to anyone else, since they will only need the public key to
|
||||
@@ -94,16 +94,16 @@
|
||||
//! ```
|
||||
//! # fn main() {
|
||||
//! # use rand::rngs::OsRng;
|
||||
//! # use ed25519_dalek::{Keypair, Signature, Signer, PublicKey};
|
||||
//! # use ed25519_dalek::{SigningKey, Signature, Signer, VerifyingKey};
|
||||
//! use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH};
|
||||
//! # let mut csprng = OsRng{};
|
||||
//! # let keypair: Keypair = Keypair::generate(&mut csprng);
|
||||
//! # let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
//! # let message: &[u8] = b"This is a test of the tsunami alert system.";
|
||||
//! # let signature: Signature = keypair.sign(message);
|
||||
//! # let signature: Signature = signing_key.sign(message);
|
||||
//!
|
||||
//! let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = keypair.public_key().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 verifying_key_bytes: [u8; PUBLIC_KEY_LENGTH] = signing_key.verifying_key().to_bytes();
|
||||
//! let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = signing_key.to_bytes();
|
||||
//! let signing_key_bytes: [u8; KEYPAIR_LENGTH] = signing_key.to_keypair_bytes();
|
||||
//! let signature_bytes: [u8; SIGNATURE_LENGTH] = signature.to_bytes();
|
||||
//! # }
|
||||
//! ```
|
||||
@@ -114,24 +114,22 @@
|
||||
//! # use std::convert::TryFrom;
|
||||
//! # use rand::rngs::OsRng;
|
||||
//! # use std::convert::TryInto;
|
||||
//! # use ed25519_dalek::{Keypair, Signature, Signer, PublicKey, SecretKey, SignatureError};
|
||||
//! # use ed25519_dalek::{SigningKey, Signature, Signer, VerifyingKey, SecretKey, SignatureError};
|
||||
//! # 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<(SigningKey, VerifyingKey, Signature), SignatureError> {
|
||||
//! # let mut csprng = OsRng{};
|
||||
//! # let keypair_orig: Keypair = Keypair::generate(&mut csprng);
|
||||
//! # let signing_key_orig: SigningKey = SigningKey::generate(&mut csprng);
|
||||
//! # let message: &[u8] = b"This is a test of the tsunami alert system.";
|
||||
//! # let signature_orig: Signature = keypair_orig.sign(message);
|
||||
//! # 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_key().to_bytes();
|
||||
//! # let keypair_bytes: [u8; KEYPAIR_LENGTH] = keypair_orig.to_bytes();
|
||||
//! # let signature_orig: Signature = signing_key_orig.sign(message);
|
||||
//! # let verifying_key_bytes: [u8; PUBLIC_KEY_LENGTH] = signing_key_orig.verifying_key().to_bytes();
|
||||
//! # let signing_key_bytes: [u8; SECRET_KEY_LENGTH] = signing_key_orig.to_bytes();
|
||||
//! # let signature_bytes: [u8; SIGNATURE_LENGTH] = signature_orig.to_bytes();
|
||||
//! #
|
||||
//! 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::try_from(&signature_bytes[..])?;
|
||||
//! let verifying_key: VerifyingKey = VerifyingKey::from_bytes(&verifying_key_bytes)?;
|
||||
//! let signing_key: SigningKey = SigningKey::from_bytes(&signing_key_bytes);
|
||||
//! let signature: Signature = Signature::try_from(&signature_bytes[..])?;
|
||||
//! #
|
||||
//! # Ok((secret_key, public_key, keypair, signature))
|
||||
//! # Ok((signing_key, verifying_key, signature))
|
||||
//! # }
|
||||
//! # fn main() {
|
||||
//! # do_test();
|
||||
@@ -151,14 +149,14 @@
|
||||
//!
|
||||
//! To use PKCS#8, you need to enable the `pkcs8` crate feature.
|
||||
//!
|
||||
//! The following traits can be used to decode/encode [`Keypair`] and
|
||||
//! [`PublicKey`] as PKCS#8. Note that [`pkcs8`] is re-exported from the
|
||||
//! The following traits can be used to decode/encode [`SigningKey`] and
|
||||
//! [`VerifyingKey`] as PKCS#8. Note that [`pkcs8`] is re-exported from the
|
||||
//! toplevel of the crate:
|
||||
//!
|
||||
//! - [`pkcs8::DecodePrivateKey`]: decode private keys from PKCS#8
|
||||
//! - [`pkcs8::EncodePrivateKey`]: encode private keys to PKCS#8
|
||||
//! - [`pkcs8::DecodePublicKey`]: decode public keys from PKCS#8
|
||||
//! - [`pkcs8::EncodePublicKey`]: encode public keys to PKCS#8
|
||||
//! - [`pkcs8::DecodeVerifyingKey`]: decode public keys from PKCS#8
|
||||
//! - [`pkcs8::EncodeVerifyingKey`]: encode public keys to PKCS#8
|
||||
//!
|
||||
//! #### Example
|
||||
//!
|
||||
@@ -166,13 +164,13 @@
|
||||
//!
|
||||
#![cfg_attr(feature = "pem", doc = "```")]
|
||||
#![cfg_attr(not(feature = "pem"), doc = "```ignore")]
|
||||
//! use ed25519_dalek::{PublicKey, pkcs8::DecodePublicKey};
|
||||
//! use ed25519_dalek::{VerifyingKey, pkcs8::DecodeVerifyingKey};
|
||||
//!
|
||||
//! let pem = "-----BEGIN PUBLIC KEY-----
|
||||
//! MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE=
|
||||
//! -----END PUBLIC KEY-----";
|
||||
//!
|
||||
//! let public_key = PublicKey::from_public_key_pem(pem)
|
||||
//! let verifying_key = VerifyingKey::from_verifying_key_pem(pem)
|
||||
//! .expect("invalid public key PEM");
|
||||
//! ```
|
||||
//!
|
||||
@@ -193,48 +191,48 @@
|
||||
//! # #[cfg(feature = "serde")]
|
||||
//! # fn main() {
|
||||
//! # use rand::rngs::OsRng;
|
||||
//! # use ed25519_dalek::{Keypair, Signature, Signer, Verifier, PublicKey};
|
||||
//! # use ed25519_dalek::{SigningKey, Signature, Signer, Verifier, VerifyingKey};
|
||||
//! use bincode::serialize;
|
||||
//! # let mut csprng = OsRng{};
|
||||
//! # let keypair: Keypair = Keypair::generate(&mut csprng);
|
||||
//! # let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
//! # let message: &[u8] = b"This is a test of the tsunami alert system.";
|
||||
//! # let signature: Signature = keypair.sign(message);
|
||||
//! # let public_key: PublicKey = keypair.public_key();
|
||||
//! # let verified: bool = public_key.verify(message, &signature).is_ok();
|
||||
//! # let signature: Signature = signing_key.sign(message);
|
||||
//! # let verifying_key: VerifyingKey = signing_key.verifying_key();
|
||||
//! # let verified: bool = verifying_key.verify(message, &signature).is_ok();
|
||||
//!
|
||||
//! let encoded_public_key: Vec<u8> = serialize(&public_key).unwrap();
|
||||
//! let encoded_verifying_key: Vec<u8> = serialize(&verifying_key).unwrap();
|
||||
//! let encoded_signature: Vec<u8> = serialize(&signature).unwrap();
|
||||
//! # }
|
||||
//! # #[cfg(not(feature = "serde"))]
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! After sending the `encoded_public_key` and `encoded_signature`, the
|
||||
//! After sending the `encoded_verifying_key` and `encoded_signature`, the
|
||||
//! recipient may deserialise them and verify:
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "serde")]
|
||||
//! # fn main() {
|
||||
//! # use rand::rngs::OsRng;
|
||||
//! # use ed25519_dalek::{Keypair, Signature, Signer, Verifier, PublicKey};
|
||||
//! # use ed25519_dalek::{SigningKey, Signature, Signer, Verifier, VerifyingKey};
|
||||
//! # use bincode::serialize;
|
||||
//! use bincode::deserialize;
|
||||
//!
|
||||
//! # let mut csprng = OsRng{};
|
||||
//! # let keypair: Keypair = Keypair::generate(&mut csprng);
|
||||
//! # let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
//! let message: &[u8] = b"This is a test of the tsunami alert system.";
|
||||
//! # let signature: Signature = keypair.sign(message);
|
||||
//! # let public_key: PublicKey = keypair.public_key();
|
||||
//! # let verified: bool = public_key.verify(message, &signature).is_ok();
|
||||
//! # let encoded_public_key: Vec<u8> = serialize(&public_key).unwrap();
|
||||
//! # let signature: Signature = signing_key.sign(message);
|
||||
//! # let verifying_key: VerifyingKey = signing_key.verifying_key();
|
||||
//! # let verified: bool = verifying_key.verify(message, &signature).is_ok();
|
||||
//! # let encoded_verifying_key: Vec<u8> = serialize(&verifying_key).unwrap();
|
||||
//! # let encoded_signature: Vec<u8> = serialize(&signature).unwrap();
|
||||
//! let decoded_public_key: PublicKey = deserialize(&encoded_public_key).unwrap();
|
||||
//! let decoded_verifying_key: VerifyingKey = deserialize(&encoded_verifying_key).unwrap();
|
||||
//! let decoded_signature: Signature = deserialize(&encoded_signature).unwrap();
|
||||
//!
|
||||
//! # assert_eq!(public_key, decoded_public_key);
|
||||
//! # assert_eq!(verifying_key, decoded_verifying_key);
|
||||
//! # assert_eq!(signature, decoded_signature);
|
||||
//! #
|
||||
//! let verified: bool = decoded_public_key.verify(&message, &decoded_signature).is_ok();
|
||||
//! let verified: bool = decoded_verifying_key.verify(&message, &decoded_signature).is_ok();
|
||||
//!
|
||||
//! assert!(verified);
|
||||
//! # }
|
||||
@@ -265,10 +263,9 @@ pub use ed25519;
|
||||
mod batch;
|
||||
mod constants;
|
||||
mod errors;
|
||||
mod keypair;
|
||||
mod public;
|
||||
mod secret;
|
||||
mod signature;
|
||||
mod signing;
|
||||
mod verifying;
|
||||
|
||||
pub use curve25519_dalek::digest::Digest;
|
||||
|
||||
@@ -276,9 +273,8 @@ pub use curve25519_dalek::digest::Digest;
|
||||
pub use crate::batch::*;
|
||||
pub use crate::constants::*;
|
||||
pub use crate::errors::*;
|
||||
pub use crate::keypair::*;
|
||||
pub use crate::public::*;
|
||||
pub use crate::secret::*;
|
||||
pub use crate::signing::*;
|
||||
pub use crate::verifying::*;
|
||||
|
||||
// Re-export the `Signer` and `Verifier` traits from the `signature` crate
|
||||
pub use ed25519::signature::{Signer, Verifier};
|
||||
|
||||
432
src/secret.rs
432
src/secret.rs
@@ -1,432 +0,0 @@
|
||||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of ed25519-dalek.
|
||||
// Copyright (c) 2017-2019 isis lovecruft
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
// - isis agora lovecruft <isis@patternsinthevoid.net>
|
||||
|
||||
//! ed25519 secret key types.
|
||||
|
||||
use core::fmt::Debug;
|
||||
|
||||
use curve25519_dalek::constants;
|
||||
use curve25519_dalek::digest::generic_array::typenum::U64;
|
||||
use curve25519_dalek::digest::Digest;
|
||||
use curve25519_dalek::edwards::CompressedEdwardsY;
|
||||
use curve25519_dalek::scalar::Scalar;
|
||||
|
||||
#[cfg(feature = "rand")]
|
||||
use rand::{CryptoRng, RngCore};
|
||||
|
||||
use sha2::Sha512;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::de::Error as SerdeError;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes};
|
||||
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use crate::constants::*;
|
||||
use crate::errors::*;
|
||||
use crate::public::*;
|
||||
use crate::signature::*;
|
||||
|
||||
/// An EdDSA secret key.
|
||||
///
|
||||
/// Instances of this secret are automatically overwritten with zeroes when they
|
||||
/// fall out of scope.
|
||||
pub struct SecretKey(pub(crate) [u8; SECRET_KEY_LENGTH]);
|
||||
|
||||
impl Drop for SecretKey {
|
||||
fn drop(&mut self) {
|
||||
self.0.zeroize()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for SecretKey {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
|
||||
write!(f, "SecretKey: {:?}", &self.0[..])
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for SecretKey {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl SecretKey {
|
||||
/// Convert this secret key to a byte array.
|
||||
#[inline]
|
||||
pub fn to_bytes(&self) -> [u8; SECRET_KEY_LENGTH] {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// View this secret key as a byte array.
|
||||
#[inline]
|
||||
pub fn as_bytes<'a>(&'a self) -> &'a [u8; SECRET_KEY_LENGTH] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Construct a `SecretKey` from a slice of bytes.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use ed25519_dalek::SecretKey;
|
||||
/// use ed25519_dalek::SECRET_KEY_LENGTH;
|
||||
/// use ed25519_dalek::SignatureError;
|
||||
///
|
||||
/// # fn doctest() -> Result<SecretKey, SignatureError> {
|
||||
/// let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = [
|
||||
/// 157, 097, 177, 157, 239, 253, 090, 096,
|
||||
/// 186, 132, 074, 244, 146, 236, 044, 196,
|
||||
/// 068, 073, 197, 105, 123, 050, 105, 025,
|
||||
/// 112, 059, 172, 003, 028, 174, 127, 096, ];
|
||||
///
|
||||
/// let secret_key: SecretKey = SecretKey::from_bytes(&secret_key_bytes)?;
|
||||
/// #
|
||||
/// # Ok(secret_key)
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// # let result = doctest();
|
||||
/// # assert!(result.is_ok());
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Result` whose okay value is an EdDSA `SecretKey` or whose error value
|
||||
/// is an `SignatureError` wrapping the internal error that occurred.
|
||||
#[inline]
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<SecretKey, SignatureError> {
|
||||
if bytes.len() != SECRET_KEY_LENGTH {
|
||||
return Err(InternalError::BytesLengthError {
|
||||
name: "SecretKey",
|
||||
length: SECRET_KEY_LENGTH,
|
||||
}
|
||||
.into());
|
||||
}
|
||||
let mut bits: [u8; 32] = [0u8; 32];
|
||||
bits.copy_from_slice(&bytes[..32]);
|
||||
|
||||
Ok(SecretKey(bits))
|
||||
}
|
||||
|
||||
/// Generate a `SecretKey` from a `csprng`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # #[cfg(feature = "std")]
|
||||
/// # fn main() {
|
||||
/// #
|
||||
/// use rand::rngs::OsRng;
|
||||
/// use ed25519_dalek::PublicKey;
|
||||
/// use ed25519_dalek::SecretKey;
|
||||
/// use ed25519_dalek::Signature;
|
||||
///
|
||||
/// let mut csprng = OsRng{};
|
||||
/// let secret_key: SecretKey = SecretKey::generate(&mut csprng);
|
||||
/// # }
|
||||
/// #
|
||||
/// # #[cfg(not(feature = "std"))]
|
||||
/// # fn main() { }
|
||||
/// ```
|
||||
///
|
||||
/// Afterwards, you can generate the corresponding public:
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() {
|
||||
/// #
|
||||
/// # use rand::rngs::OsRng;
|
||||
/// # use ed25519_dalek::PublicKey;
|
||||
/// # use ed25519_dalek::SecretKey;
|
||||
/// # use ed25519_dalek::Signature;
|
||||
/// #
|
||||
/// # let mut csprng = OsRng{};
|
||||
/// # let secret_key: SecretKey = SecretKey::generate(&mut csprng);
|
||||
///
|
||||
/// let public_key: PublicKey = (&secret_key).into();
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// # Input
|
||||
///
|
||||
/// A CSPRNG with a `fill_bytes()` method, e.g. `rand::OsRng`
|
||||
#[cfg(feature = "rand")]
|
||||
pub fn generate<T>(csprng: &mut T) -> SecretKey
|
||||
where
|
||||
T: CryptoRng + RngCore,
|
||||
{
|
||||
let mut sk: SecretKey = SecretKey([0u8; 32]);
|
||||
|
||||
csprng.fill_bytes(&mut sk.0);
|
||||
|
||||
sk
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for SecretKey {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
SerdeBytes::new(self.as_bytes()).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'d> Deserialize<'d> for SecretKey {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'d>,
|
||||
{
|
||||
let bytes = <SerdeByteBuf>::deserialize(deserializer)?;
|
||||
SecretKey::from_bytes(bytes.as_ref()).map_err(SerdeError::custom)
|
||||
}
|
||||
}
|
||||
|
||||
/// An "expanded" secret key.
|
||||
///
|
||||
/// This is produced by using an hash function with 512-bits output to digest a
|
||||
/// `SecretKey`. The output digest is then split in half, the lower half being
|
||||
/// the actual `key` used to sign messages, after twiddling with some bits.¹ The
|
||||
/// upper half is used a sort of half-baked, ill-designed² pseudo-domain-separation
|
||||
/// "nonce"-like thing, which is used during signature production by
|
||||
/// concatenating it with the message to be signed before the message is hashed.
|
||||
///
|
||||
/// Instances of this secret are automatically overwritten with zeroes when they
|
||||
/// fall out of scope.
|
||||
//
|
||||
// ¹ This results in a slight bias towards non-uniformity at one spectrum of
|
||||
// the range of valid keys. Oh well: not my idea; not my problem.
|
||||
//
|
||||
// ² It is the author's view (specifically, isis agora lovecruft, in the event
|
||||
// you'd like to complain about me, again) that this is "ill-designed" because
|
||||
// this doesn't actually provide true hash domain separation, in that in many
|
||||
// real-world applications a user wishes to have one key which is used in
|
||||
// several contexts (such as within tor, which does domain separation
|
||||
// manually by pre-concatenating static strings to messages to achieve more
|
||||
// robust domain separation). In other real-world applications, such as
|
||||
// bitcoind, a user might wish to have one master keypair from which others are
|
||||
// derived (à la BIP32) and different domain separators between keys derived at
|
||||
// different levels (and similarly for tree-based key derivation constructions,
|
||||
// such as hash-based signatures). Leaving the domain separation to
|
||||
// application designers, who thus far have produced incompatible,
|
||||
// slightly-differing, ad hoc domain separation (at least those application
|
||||
// designers who knew enough cryptographic theory to do so!), is therefore a
|
||||
// bad design choice on the part of the cryptographer designing primitives
|
||||
// which should be simple and as foolproof as possible to use for
|
||||
// non-cryptographers. Further, later in the ed25519 signature scheme, as
|
||||
// specified in RFC8032, the public key is added into *another* hash digest
|
||||
// (along with the message, again); it is unclear to this author why there's
|
||||
// not only one but two poorly-thought-out attempts at domain separation in the
|
||||
// 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
|
||||
// "generalised EdDSA" and "VXEdDSA".
|
||||
pub(crate) struct ExpandedSecretKey {
|
||||
pub(crate) key: Scalar,
|
||||
pub(crate) nonce: [u8; 32],
|
||||
}
|
||||
|
||||
impl Drop for ExpandedSecretKey {
|
||||
fn drop(&mut self) {
|
||||
self.key.zeroize();
|
||||
self.nonce.zeroize()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a SecretKey> for ExpandedSecretKey {
|
||||
/// Construct an `ExpandedSecretKey` from a `SecretKey`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// # fn main() {
|
||||
/// #
|
||||
/// use rand::rngs::OsRng;
|
||||
/// use ed25519_dalek::{SecretKey, ExpandedSecretKey};
|
||||
///
|
||||
/// let mut csprng = OsRng{};
|
||||
/// let secret_key: SecretKey = SecretKey::generate(&mut csprng);
|
||||
/// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key);
|
||||
/// # }
|
||||
/// ```
|
||||
fn from(secret_key: &'a SecretKey) -> ExpandedSecretKey {
|
||||
let mut h: Sha512 = Sha512::default();
|
||||
let mut hash: [u8; 64] = [0u8; 64];
|
||||
let mut lower: [u8; 32] = [0u8; 32];
|
||||
let mut upper: [u8; 32] = [0u8; 32];
|
||||
|
||||
h.update(secret_key.as_bytes());
|
||||
hash.copy_from_slice(h.finalize().as_slice());
|
||||
|
||||
lower.copy_from_slice(&hash[00..32]);
|
||||
upper.copy_from_slice(&hash[32..64]);
|
||||
|
||||
lower[0] &= 248;
|
||||
lower[31] &= 63;
|
||||
lower[31] |= 64;
|
||||
|
||||
ExpandedSecretKey {
|
||||
key: Scalar::from_bits(lower),
|
||||
nonce: upper,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExpandedSecretKey {
|
||||
/// Sign a message with this `ExpandedSecretKey`.
|
||||
#[allow(non_snake_case)]
|
||||
pub(crate) fn sign(&self, message: &[u8], public_key: &PublicKey) -> ed25519::Signature {
|
||||
let mut h: Sha512 = Sha512::new();
|
||||
let R: CompressedEdwardsY;
|
||||
let r: Scalar;
|
||||
let s: Scalar;
|
||||
let k: Scalar;
|
||||
|
||||
h.update(&self.nonce);
|
||||
h.update(&message);
|
||||
|
||||
r = Scalar::from_hash(h);
|
||||
R = (&r * &constants::ED25519_BASEPOINT_TABLE).compress();
|
||||
|
||||
h = Sha512::new();
|
||||
h.update(R.as_bytes());
|
||||
h.update(public_key.as_bytes());
|
||||
h.update(&message);
|
||||
|
||||
k = Scalar::from_hash(h);
|
||||
s = &(&k * &self.key) + &r;
|
||||
|
||||
InternalSignature { R, s }.into()
|
||||
}
|
||||
|
||||
/// Sign a `prehashed_message` with this `ExpandedSecretKey` using the
|
||||
/// Ed25519ph algorithm defined in [RFC8032 §5.1][rfc8032].
|
||||
///
|
||||
/// # Inputs
|
||||
///
|
||||
/// * `prehashed_message` is an instantiated hash digest with 512-bits of
|
||||
/// output which has had the message to be signed previously fed into its
|
||||
/// state.
|
||||
/// * `public_key` is a [`PublicKey`] which corresponds to this secret key.
|
||||
/// * `context` is an optional context string, up to 255 bytes inclusive,
|
||||
/// which may be used to provide additional domain separation. If not
|
||||
/// set, this will default to an empty string.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Result` whose `Ok` value is an Ed25519ph [`Signature`] on the
|
||||
/// `prehashed_message` if the context was 255 bytes or less, otherwise
|
||||
/// a `SignatureError`.
|
||||
///
|
||||
/// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1
|
||||
#[allow(non_snake_case)]
|
||||
pub(crate) fn sign_prehashed<'a, D>(
|
||||
&self,
|
||||
prehashed_message: D,
|
||||
public_key: &PublicKey,
|
||||
context: Option<&'a [u8]>,
|
||||
) -> Result<ed25519::Signature, SignatureError>
|
||||
where
|
||||
D: Digest<OutputSize = U64>,
|
||||
{
|
||||
let mut h: Sha512;
|
||||
let mut prehash: [u8; 64] = [0u8; 64];
|
||||
let R: CompressedEdwardsY;
|
||||
let r: Scalar;
|
||||
let s: Scalar;
|
||||
let k: Scalar;
|
||||
|
||||
let ctx: &[u8] = context.unwrap_or(b""); // By default, the context is an empty string.
|
||||
|
||||
if ctx.len() > 255 {
|
||||
return Err(SignatureError::from(
|
||||
InternalError::PrehashedContextLengthError,
|
||||
));
|
||||
}
|
||||
|
||||
let ctx_len: u8 = ctx.len() as u8;
|
||||
|
||||
// Get the result of the pre-hashed message.
|
||||
prehash.copy_from_slice(prehashed_message.finalize().as_slice());
|
||||
|
||||
// This is the dumbest, ten-years-late, non-admission of fucking up the
|
||||
// domain separation I have ever seen. Why am I still required to put
|
||||
// the upper half "prefix" of the hashed "secret key" in here? Why
|
||||
// can't the user just supply their own nonce and decide for themselves
|
||||
// whether or not they want a deterministic signature scheme? Why does
|
||||
// the message go into what's ostensibly the signature domain separation
|
||||
// hash? Why wasn't there always a way to provide a context string?
|
||||
//
|
||||
// ...
|
||||
//
|
||||
// This is a really fucking stupid bandaid, and the damned scheme is
|
||||
// still bleeding from malleability, for fuck's sake.
|
||||
h = Sha512::new()
|
||||
.chain_update(b"SigEd25519 no Ed25519 collisions")
|
||||
.chain_update(&[1]) // Ed25519ph
|
||||
.chain_update(&[ctx_len])
|
||||
.chain_update(ctx)
|
||||
.chain_update(&self.nonce)
|
||||
.chain_update(&prehash[..]);
|
||||
|
||||
r = Scalar::from_hash(h);
|
||||
R = (&r * &constants::ED25519_BASEPOINT_TABLE).compress();
|
||||
|
||||
h = Sha512::new()
|
||||
.chain_update(b"SigEd25519 no Ed25519 collisions")
|
||||
.chain_update(&[1]) // Ed25519ph
|
||||
.chain_update(&[ctx_len])
|
||||
.chain_update(ctx)
|
||||
.chain_update(R.as_bytes())
|
||||
.chain_update(public_key.as_bytes())
|
||||
.chain_update(&prehash[..]);
|
||||
|
||||
k = Scalar::from_hash(h);
|
||||
s = &(&k * &self.key) + &r;
|
||||
|
||||
Ok(InternalSignature { R, s }.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn secret_key_zeroize_on_drop() {
|
||||
let secret_ptr: *const u8;
|
||||
|
||||
{
|
||||
// scope for the secret to ensure it's been dropped
|
||||
let secret = SecretKey::from_bytes(&[0x15u8; 32][..]).unwrap();
|
||||
|
||||
secret_ptr = secret.0.as_ptr();
|
||||
}
|
||||
|
||||
let memory: &[u8] = unsafe { ::std::slice::from_raw_parts(secret_ptr, 32) };
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
818
src/signing.rs
Normal file
818
src/signing.rs
Normal file
@@ -0,0 +1,818 @@
|
||||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of ed25519-dalek.
|
||||
// Copyright (c) 2017-2019 isis lovecruft
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
// - isis agora lovecruft <isis@patternsinthevoid.net>
|
||||
|
||||
//! ed25519 signing keys.
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
use ed25519::pkcs8::{self, DecodePrivateKey};
|
||||
|
||||
#[cfg(feature = "rand")]
|
||||
use rand::{CryptoRng, RngCore};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::de::Error as SerdeError;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes};
|
||||
|
||||
use sha2::Sha512;
|
||||
|
||||
use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE;
|
||||
use curve25519_dalek::digest::generic_array::typenum::U64;
|
||||
use curve25519_dalek::digest::Digest;
|
||||
use curve25519_dalek::edwards::CompressedEdwardsY;
|
||||
use curve25519_dalek::scalar::Scalar;
|
||||
|
||||
use ed25519::signature::{KeypairRef, Signer, Verifier};
|
||||
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use crate::constants::*;
|
||||
use crate::errors::*;
|
||||
use crate::signature::*;
|
||||
use crate::verifying::*;
|
||||
|
||||
/// ed25519 secret key as defined in [RFC8032 § 5.1.5]:
|
||||
///
|
||||
/// > The private key is 32 octets (256 bits, corresponding to b) of
|
||||
/// > cryptographically secure random data.
|
||||
///
|
||||
/// [RFC8032 § 5.1.5]: https://www.rfc-editor.org/rfc/rfc8032#section-5.1.5
|
||||
pub type SecretKey = [u8; SECRET_KEY_LENGTH];
|
||||
|
||||
/// ed25519 signing key which can be used to produce signatures.
|
||||
// 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)]
|
||||
pub struct SigningKey {
|
||||
/// The secret half of this signing key.
|
||||
pub(crate) secret_key: SecretKey,
|
||||
/// The public half of this signing key.
|
||||
pub(crate) verifying_key: VerifyingKey,
|
||||
}
|
||||
|
||||
impl SigningKey {
|
||||
/// Construct a [`SigningKey`] from a slice of bytes.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate ed25519_dalek;
|
||||
/// #
|
||||
/// use ed25519_dalek::SigningKey;
|
||||
/// use ed25519_dalek::SECRET_KEY_LENGTH;
|
||||
/// use ed25519_dalek::SignatureError;
|
||||
///
|
||||
/// # fn doctest() -> Result<SigningKey, SignatureError> {
|
||||
/// let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = [
|
||||
/// 157, 097, 177, 157, 239, 253, 090, 096,
|
||||
/// 186, 132, 074, 244, 146, 236, 044, 196,
|
||||
/// 068, 073, 197, 105, 123, 050, 105, 025,
|
||||
/// 112, 059, 172, 003, 028, 174, 127, 096, ];
|
||||
///
|
||||
/// let signing_key: SigningKey = SigningKey::from_bytes(&secret_key_bytes);
|
||||
/// #
|
||||
/// # Ok(signing_key)
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// # let result = doctest();
|
||||
/// # assert!(result.is_ok());
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Result` whose okay value is an EdDSA `SecretKey` or whose error value
|
||||
/// is an `SignatureError` wrapping the internal error that occurred.
|
||||
#[inline]
|
||||
pub fn from_bytes(secret_key: &SecretKey) -> Self {
|
||||
let verifying_key = VerifyingKey::from(&ExpandedSecretKey::from(secret_key));
|
||||
Self {
|
||||
secret_key: *secret_key,
|
||||
verifying_key,
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert this secret key to a byte array.
|
||||
#[inline]
|
||||
pub fn to_bytes(&self) -> SecretKey {
|
||||
self.secret_key
|
||||
}
|
||||
|
||||
/// Construct a [`SigningKey`] from the bytes of a `VerifyingKey` and `SecretKey`.
|
||||
///
|
||||
/// # Inputs
|
||||
///
|
||||
/// * `bytes`: an `&[u8]` of length [`KEYPAIR_LENGTH`], representing the
|
||||
/// scalar for the secret key, and a compressed Edwards-Y coordinate of a
|
||||
/// point on curve25519, both as bytes. (As obtained from
|
||||
/// [`SigningKey::to_bytes`].)
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Result` whose okay value is an EdDSA [`SigningKey`] or whose error value
|
||||
/// is an `SignatureError` describing the error that occurred.
|
||||
#[inline]
|
||||
pub fn from_keypair_bytes(bytes: &[u8; 64]) -> Result<SigningKey, SignatureError> {
|
||||
if bytes.len() != KEYPAIR_LENGTH {
|
||||
return Err(InternalError::BytesLengthError {
|
||||
name: "SigningKey",
|
||||
length: KEYPAIR_LENGTH,
|
||||
}
|
||||
.into());
|
||||
}
|
||||
|
||||
let secret_key =
|
||||
SecretKey::try_from(&bytes[..SECRET_KEY_LENGTH]).map_err(|_| SignatureError::new())?;
|
||||
let verifying_key = VerifyingKey::from_bytes(&bytes[SECRET_KEY_LENGTH..])?;
|
||||
|
||||
if verifying_key != VerifyingKey::from(&secret_key) {
|
||||
return Err(InternalError::MismatchedKeypairError.into());
|
||||
}
|
||||
|
||||
Ok(SigningKey {
|
||||
secret_key,
|
||||
verifying_key,
|
||||
})
|
||||
}
|
||||
|
||||
/// Convert this signing key to bytes.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// An array of bytes, `[u8; KEYPAIR_LENGTH]`. The first
|
||||
/// `SECRET_KEY_LENGTH` of bytes is the `SecretKey`, and the next
|
||||
/// `PUBLIC_KEY_LENGTH` bytes is the `VerifyingKey` (the same as other
|
||||
/// libraries, such as [Adam Langley's ed25519 Golang
|
||||
/// 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_keypair_bytes(&self) -> [u8; KEYPAIR_LENGTH] {
|
||||
let mut bytes: [u8; KEYPAIR_LENGTH] = [0u8; KEYPAIR_LENGTH];
|
||||
|
||||
bytes[..SECRET_KEY_LENGTH].copy_from_slice(&self.secret_key);
|
||||
bytes[SECRET_KEY_LENGTH..].copy_from_slice(self.verifying_key.as_bytes());
|
||||
bytes
|
||||
}
|
||||
|
||||
/// Get the [`VerifyingKey`] for this [`SigningKey`].
|
||||
pub fn verifying_key(&self) -> VerifyingKey {
|
||||
self.verifying_key
|
||||
}
|
||||
|
||||
/// Generate an ed25519 signing key.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # #[cfg(feature = "std")]
|
||||
/// # fn main() {
|
||||
///
|
||||
/// use rand::rngs::OsRng;
|
||||
/// use ed25519_dalek::SigningKey;
|
||||
/// use ed25519_dalek::Signature;
|
||||
///
|
||||
/// let mut csprng = OsRng{};
|
||||
/// let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
///
|
||||
/// # }
|
||||
/// #
|
||||
/// # #[cfg(not(feature = "std"))]
|
||||
/// # fn main() { }
|
||||
/// ```
|
||||
///
|
||||
/// # Input
|
||||
///
|
||||
/// A CSPRNG with a `fill_bytes()` method, e.g. `rand_os::OsRng`.
|
||||
///
|
||||
/// The caller must also supply a hash function which implements the
|
||||
/// `Digest` and `Default` traits, and which returns 512 bits of output.
|
||||
/// The standard hash function used for most ed25519 libraries is SHA-512,
|
||||
/// which is available with `use sha2::Sha512` as in the example above.
|
||||
/// Other suitable hash functions include Keccak-512 and Blake2b-512.
|
||||
#[cfg(feature = "rand")]
|
||||
pub fn generate<R>(csprng: &mut R) -> SigningKey
|
||||
where
|
||||
R: CryptoRng + RngCore,
|
||||
{
|
||||
let mut secret = SecretKey::default();
|
||||
csprng.fill_bytes(&mut secret);
|
||||
Self::from_bytes(&secret)
|
||||
}
|
||||
|
||||
/// Sign a `prehashed_message` with this [`SigningKey`] using the
|
||||
/// Ed25519ph algorithm defined in [RFC8032 §5.1][rfc8032].
|
||||
///
|
||||
/// # Inputs
|
||||
///
|
||||
/// * `prehashed_message` is an instantiated hash digest with 512-bits of
|
||||
/// output which has had the message to be signed previously fed into its
|
||||
/// state.
|
||||
/// * `context` is an optional context string, up to 255 bytes inclusive,
|
||||
/// which may be used to provide additional domain separation. If not
|
||||
/// set, this will default to an empty string.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// An Ed25519ph [`Signature`] on the `prehashed_message`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ed25519_dalek::Digest;
|
||||
/// use ed25519_dalek::SigningKey;
|
||||
/// use ed25519_dalek::Sha512;
|
||||
/// use ed25519_dalek::Signature;
|
||||
/// use rand::rngs::OsRng;
|
||||
///
|
||||
/// # #[cfg(feature = "std")]
|
||||
/// # fn main() {
|
||||
/// let mut csprng = OsRng{};
|
||||
/// let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
/// let message: &[u8] = b"All I want is to pet all of the dogs.";
|
||||
///
|
||||
/// // Create a hash digest object which we'll feed the message into:
|
||||
/// let mut prehashed: Sha512 = Sha512::new();
|
||||
///
|
||||
/// prehashed.update(message);
|
||||
/// # }
|
||||
/// #
|
||||
/// # #[cfg(not(feature = "std"))]
|
||||
/// # fn main() { }
|
||||
/// ```
|
||||
///
|
||||
/// If you want, you can optionally pass a "context". It is generally a
|
||||
/// good idea to choose a context and try to make it unique to your project
|
||||
/// and this specific usage of signatures.
|
||||
///
|
||||
/// For example, without this, if you were to [convert your OpenPGP key
|
||||
/// to a Bitcoin key][terrible_idea] (just as an example, and also Don't
|
||||
/// Ever Do That) and someone tricked you into signing an "email" which was
|
||||
/// actually a Bitcoin transaction moving all your magic internet money to
|
||||
/// their address, it'd be a valid transaction.
|
||||
///
|
||||
/// By adding a context, this trick becomes impossible, because the context
|
||||
/// is concatenated into the hash, which is then signed. So, going with the
|
||||
/// previous example, if your bitcoin wallet used a context of
|
||||
/// "BitcoinWalletAppTxnSigning" and OpenPGP used a context (this is likely
|
||||
/// the least of their safety problems) of "GPGsCryptoIsntConstantTimeLol",
|
||||
/// then the signatures produced by both could never match the other, even
|
||||
/// if they signed the exact same message with the same key.
|
||||
///
|
||||
/// Let's add a context for good measure (remember, you'll want to choose
|
||||
/// your own!):
|
||||
///
|
||||
/// ```
|
||||
/// # use ed25519_dalek::Digest;
|
||||
/// # use ed25519_dalek::SigningKey;
|
||||
/// # use ed25519_dalek::Signature;
|
||||
/// # use ed25519_dalek::SignatureError;
|
||||
/// # use ed25519_dalek::Sha512;
|
||||
/// # use rand::rngs::OsRng;
|
||||
/// #
|
||||
/// # fn do_test() -> Result<Signature, SignatureError> {
|
||||
/// # let mut csprng = OsRng{};
|
||||
/// # let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
/// # let message: &[u8] = b"All I want is to pet all of the dogs.";
|
||||
/// # let mut prehashed: Sha512 = Sha512::new();
|
||||
/// # prehashed.update(message);
|
||||
/// #
|
||||
/// let context: &[u8] = b"Ed25519DalekSignPrehashedDoctest";
|
||||
///
|
||||
/// let sig: Signature = signing_key.sign_prehashed(prehashed, Some(context))?;
|
||||
/// #
|
||||
/// # Ok(sig)
|
||||
/// # }
|
||||
/// # #[cfg(feature = "std")]
|
||||
/// # fn main() {
|
||||
/// # do_test();
|
||||
/// # }
|
||||
/// #
|
||||
/// # #[cfg(not(feature = "std"))]
|
||||
/// # fn main() { }
|
||||
/// ```
|
||||
///
|
||||
/// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1
|
||||
/// [terrible_idea]: https://github.com/isislovecruft/scripts/blob/master/gpgkey2bc.py
|
||||
pub fn sign_prehashed<D>(
|
||||
&self,
|
||||
prehashed_message: D,
|
||||
context: Option<&[u8]>,
|
||||
) -> Result<ed25519::Signature, SignatureError>
|
||||
where
|
||||
D: Digest<OutputSize = U64>,
|
||||
{
|
||||
let expanded: ExpandedSecretKey = (&self.secret_key).into(); // xxx thanks i hate this
|
||||
|
||||
expanded
|
||||
.sign_prehashed(prehashed_message, &self.verifying_key, context)
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Verify a signature on a message with this signing key's public key.
|
||||
pub fn verify(
|
||||
&self,
|
||||
message: &[u8],
|
||||
signature: &ed25519::Signature,
|
||||
) -> Result<(), SignatureError> {
|
||||
self.verifying_key.verify(message, signature)
|
||||
}
|
||||
|
||||
/// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm.
|
||||
///
|
||||
/// # Inputs
|
||||
///
|
||||
/// * `prehashed_message` is an instantiated hash digest with 512-bits of
|
||||
/// output which has had the message to be signed previously fed into its
|
||||
/// state.
|
||||
/// * `context` is an optional context string, up to 255 bytes inclusive,
|
||||
/// which may be used to provide additional domain separation. If not
|
||||
/// set, this will default to an empty string.
|
||||
/// * `signature` is a purported Ed25519ph [`Signature`] on the `prehashed_message`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns `true` if the `signature` was a valid signature created by this
|
||||
/// [`SigningKey`] on the `prehashed_message`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ed25519_dalek::Digest;
|
||||
/// use ed25519_dalek::SigningKey;
|
||||
/// use ed25519_dalek::Signature;
|
||||
/// use ed25519_dalek::SignatureError;
|
||||
/// use ed25519_dalek::Sha512;
|
||||
/// use rand::rngs::OsRng;
|
||||
///
|
||||
/// # fn do_test() -> Result<(), SignatureError> {
|
||||
/// let mut csprng = OsRng{};
|
||||
/// let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
/// let message: &[u8] = b"All I want is to pet all of the dogs.";
|
||||
///
|
||||
/// let mut prehashed: Sha512 = Sha512::new();
|
||||
/// prehashed.update(message);
|
||||
///
|
||||
/// let context: &[u8] = b"Ed25519DalekSignPrehashedDoctest";
|
||||
///
|
||||
/// let sig: Signature = signing_key.sign_prehashed(prehashed, Some(context))?;
|
||||
///
|
||||
/// // The sha2::Sha512 struct doesn't implement Copy, so we'll have to create a new one:
|
||||
/// let mut prehashed_again: Sha512 = Sha512::default();
|
||||
/// prehashed_again.update(message);
|
||||
///
|
||||
/// let verified = signing_key.verifying_key().verify_prehashed(prehashed_again, Some(context), &sig);
|
||||
///
|
||||
/// assert!(verified.is_ok());
|
||||
///
|
||||
/// # verified
|
||||
/// # }
|
||||
/// #
|
||||
/// # #[cfg(feature = "std")]
|
||||
/// # fn main() {
|
||||
/// # do_test();
|
||||
/// # }
|
||||
/// #
|
||||
/// # #[cfg(not(feature = "std"))]
|
||||
/// # fn main() { }
|
||||
/// ```
|
||||
///
|
||||
/// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1
|
||||
pub fn verify_prehashed<D>(
|
||||
&self,
|
||||
prehashed_message: D,
|
||||
context: Option<&[u8]>,
|
||||
signature: &ed25519::Signature,
|
||||
) -> Result<(), SignatureError>
|
||||
where
|
||||
D: Digest<OutputSize = U64>,
|
||||
{
|
||||
self.verifying_key
|
||||
.verify_prehashed(prehashed_message, context, signature)
|
||||
}
|
||||
|
||||
/// Strictly verify a signature on a message with this signing key's public key.
|
||||
///
|
||||
/// # On The (Multiple) Sources of Malleability in Ed25519 Signatures
|
||||
///
|
||||
/// This version of verification is technically non-RFC8032 compliant. The
|
||||
/// following explains why.
|
||||
///
|
||||
/// 1. Scalar Malleability
|
||||
///
|
||||
/// The authors of the RFC explicitly stated that verification of an ed25519
|
||||
/// signature must fail if the scalar `s` is not properly reduced mod \ell:
|
||||
///
|
||||
/// > To verify a signature on a message M using public key A, with F
|
||||
/// > being 0 for Ed25519ctx, 1 for Ed25519ph, and if Ed25519ctx or
|
||||
/// > Ed25519ph is being used, C being the context, first split the
|
||||
/// > signature into two 32-octet halves. Decode the first half as a
|
||||
/// > point R, and the second half as an integer S, in the range
|
||||
/// > 0 <= s < L. Decode the public key A as point A'. If any of the
|
||||
/// > decodings fail (including S being out of range), the signature is
|
||||
/// > invalid.)
|
||||
///
|
||||
/// All `verify_*()` functions within ed25519-dalek perform this check.
|
||||
///
|
||||
/// 2. Point malleability
|
||||
///
|
||||
/// The authors of the RFC added in a malleability check to step #3 in
|
||||
/// §5.1.7, for small torsion components in the `R` value of the signature,
|
||||
/// *which is not strictly required*, as they state:
|
||||
///
|
||||
/// > Check the group equation \[8\]\[S\]B = \[8\]R + \[8\]\[k\]A'. It's
|
||||
/// > sufficient, but not required, to instead check \[S\]B = R + \[k\]A'.
|
||||
///
|
||||
/// # History of Malleability Checks
|
||||
///
|
||||
/// As originally defined (cf. the "Malleability" section in the README of
|
||||
/// this repo), ed25519 signatures didn't consider *any* form of
|
||||
/// malleability to be an issue. Later the scalar malleability was
|
||||
/// considered important. Still later, particularly with interests in
|
||||
/// cryptocurrency design and in unique identities (e.g. for Signal users,
|
||||
/// Tor onion services, etc.), the group element malleability became a
|
||||
/// concern.
|
||||
///
|
||||
/// However, libraries had already been created to conform to the original
|
||||
/// definition. One well-used library in particular even implemented the
|
||||
/// group element malleability check, *but only for batch verification*!
|
||||
/// Which meant that even using the same library, a single signature could
|
||||
/// verify fine individually, but suddenly, when verifying it with a bunch
|
||||
/// of other signatures, the whole batch would fail!
|
||||
///
|
||||
/// # "Strict" Verification
|
||||
///
|
||||
/// This method performs *both* of the above signature malleability checks.
|
||||
///
|
||||
/// It must be done as a separate method because one doesn't simply get to
|
||||
/// change the definition of a cryptographic primitive ten years
|
||||
/// after-the-fact with zero consideration for backwards compatibility in
|
||||
/// hardware and protocols which have it already have the older definition
|
||||
/// baked in.
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// Returns `Ok(())` if the signature is valid, and `Err` otherwise.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn verify_strict(
|
||||
&self,
|
||||
message: &[u8],
|
||||
signature: &ed25519::Signature,
|
||||
) -> Result<(), SignatureError> {
|
||||
self.verifying_key.verify_strict(message, signature)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<VerifyingKey> for SigningKey {
|
||||
fn as_ref(&self) -> &VerifyingKey {
|
||||
&self.verifying_key
|
||||
}
|
||||
}
|
||||
|
||||
impl KeypairRef for SigningKey {
|
||||
type VerifyingKey = VerifyingKey;
|
||||
}
|
||||
|
||||
impl Signer<ed25519::Signature> for SigningKey {
|
||||
/// Sign a message with this signing key's secret key.
|
||||
fn try_sign(&self, message: &[u8]) -> Result<ed25519::Signature, SignatureError> {
|
||||
let expanded: ExpandedSecretKey = (&self.secret_key).into();
|
||||
Ok(expanded.sign(&message, &self.verifying_key).into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Verifier<ed25519::Signature> for SigningKey {
|
||||
/// Verify a signature on a message with this signing key's public key.
|
||||
fn verify(&self, message: &[u8], signature: &ed25519::Signature) -> Result<(), SignatureError> {
|
||||
self.verifying_key.verify(message, signature)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SecretKey> for SigningKey {
|
||||
#[inline]
|
||||
fn from(secret: SecretKey) -> Self {
|
||||
Self::from_bytes(&secret)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&SecretKey> for SigningKey {
|
||||
#[inline]
|
||||
fn from(secret: &SecretKey) -> Self {
|
||||
Self::from_bytes(secret)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for SigningKey {
|
||||
type Error = SignatureError;
|
||||
|
||||
fn try_from(bytes: &[u8]) -> Result<SigningKey, SignatureError> {
|
||||
SecretKey::try_from(bytes)
|
||||
.map(|bytes| Self::from_bytes(&bytes))
|
||||
.map_err(|_| {
|
||||
InternalError::BytesLengthError {
|
||||
name: "SecretKey",
|
||||
length: SECRET_KEY_LENGTH,
|
||||
}
|
||||
.into()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl DecodePrivateKey for SigningKey {}
|
||||
|
||||
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
|
||||
impl pkcs8::EncodePrivateKey for SigningKey {
|
||||
fn to_pkcs8_der(&self) -> pkcs8::Result<pkcs8::SecretDocument> {
|
||||
pkcs8::KeypairBytes::from(self).to_pkcs8_der()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl TryFrom<pkcs8::KeypairBytes> for SigningKey {
|
||||
type Error = pkcs8::Error;
|
||||
|
||||
fn try_from(pkcs8_key: pkcs8::KeypairBytes) -> pkcs8::Result<Self> {
|
||||
SigningKey::try_from(&pkcs8_key)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl TryFrom<&pkcs8::KeypairBytes> for SigningKey {
|
||||
type Error = pkcs8::Error;
|
||||
|
||||
fn try_from(pkcs8_key: &pkcs8::KeypairBytes) -> pkcs8::Result<Self> {
|
||||
// Validate the public key in the PKCS#8 document if present
|
||||
if let Some(public_bytes) = pkcs8_key.public_key {
|
||||
let expected_verifying_key = VerifyingKey::from(&pkcs8_key.secret_key);
|
||||
|
||||
let pkcs8_verifying_key = VerifyingKey::from_bytes(public_bytes.as_ref())
|
||||
.map_err(|_| pkcs8::Error::KeyMalformed)?;
|
||||
|
||||
if expected_verifying_key != pkcs8_verifying_key {
|
||||
return Err(pkcs8::Error::KeyMalformed);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(SigningKey::from_bytes(&pkcs8_key.secret_key))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl From<SigningKey> for pkcs8::KeypairBytes {
|
||||
fn from(signing_key: SigningKey) -> pkcs8::KeypairBytes {
|
||||
pkcs8::KeypairBytes::from(&signing_key)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl From<&SigningKey> for pkcs8::KeypairBytes {
|
||||
fn from(signing_key: &SigningKey) -> pkcs8::KeypairBytes {
|
||||
pkcs8::KeypairBytes {
|
||||
secret_key: signing_key.to_bytes(),
|
||||
public_key: Some(pkcs8::PublicKeyBytes(signing_key.verifying_key.to_bytes())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl TryFrom<pkcs8::PrivateKeyInfo<'_>> for SigningKey {
|
||||
type Error = pkcs8::Error;
|
||||
|
||||
fn try_from(private_key: pkcs8::PrivateKeyInfo<'_>) -> pkcs8::Result<Self> {
|
||||
pkcs8::KeypairBytes::try_from(private_key)?.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for SigningKey {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
SerdeBytes::new(&self.secret_key).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'d> Deserialize<'d> for SigningKey {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'d>,
|
||||
{
|
||||
let bytes = <SerdeByteBuf>::deserialize(deserializer)?;
|
||||
Self::try_from(bytes.as_ref()).map_err(SerdeError::custom)
|
||||
}
|
||||
}
|
||||
|
||||
/// An "expanded" secret key.
|
||||
///
|
||||
/// This is produced by using an hash function with 512-bits output to digest a
|
||||
/// `SecretKey`. The output digest is then split in half, the lower half being
|
||||
/// the actual `key` used to sign messages, after twiddling with some bits.¹ The
|
||||
/// upper half is used a sort of half-baked, ill-designed² pseudo-domain-separation
|
||||
/// "nonce"-like thing, which is used during signature production by
|
||||
/// concatenating it with the message to be signed before the message is hashed.
|
||||
///
|
||||
/// Instances of this secret are automatically overwritten with zeroes when they
|
||||
/// fall out of scope.
|
||||
//
|
||||
// ¹ This results in a slight bias towards non-uniformity at one spectrum of
|
||||
// the range of valid keys. Oh well: not my idea; not my problem.
|
||||
//
|
||||
// ² It is the author's view (specifically, isis agora lovecruft, in the event
|
||||
// you'd like to complain about me, again) that this is "ill-designed" because
|
||||
// this doesn't actually provide true hash domain separation, in that in many
|
||||
// real-world applications a user wishes to have one key which is used in
|
||||
// several contexts (such as within tor, which does domain separation
|
||||
// manually by pre-concatenating static strings to messages to achieve more
|
||||
// robust domain separation). In other real-world applications, such as
|
||||
// bitcoind, a user might wish to have one master keypair from which others are
|
||||
// derived (à la BIP32) and different domain separators between keys derived at
|
||||
// different levels (and similarly for tree-based key derivation constructions,
|
||||
// such as hash-based signatures). Leaving the domain separation to
|
||||
// application designers, who thus far have produced incompatible,
|
||||
// slightly-differing, ad hoc domain separation (at least those application
|
||||
// designers who knew enough cryptographic theory to do so!), is therefore a
|
||||
// bad design choice on the part of the cryptographer designing primitives
|
||||
// which should be simple and as foolproof as possible to use for
|
||||
// non-cryptographers. Further, later in the ed25519 signature scheme, as
|
||||
// specified in RFC8032, the public key is added into *another* hash digest
|
||||
// (along with the message, again); it is unclear to this author why there's
|
||||
// not only one but two poorly-thought-out attempts at domain separation in the
|
||||
// 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
|
||||
// "generalised EdDSA" and "VXEdDSA".
|
||||
pub(crate) struct ExpandedSecretKey {
|
||||
pub(crate) key: Scalar,
|
||||
pub(crate) nonce: [u8; 32],
|
||||
}
|
||||
|
||||
impl Drop for ExpandedSecretKey {
|
||||
fn drop(&mut self) {
|
||||
self.key.zeroize();
|
||||
self.nonce.zeroize()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&SecretKey> for ExpandedSecretKey {
|
||||
/// Construct an `ExpandedSecretKey` from a `SecretKey`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// # fn main() {
|
||||
/// #
|
||||
/// use rand::rngs::OsRng;
|
||||
/// use ed25519_dalek::{SecretKey, ExpandedSecretKey};
|
||||
///
|
||||
/// let mut csprng = OsRng{};
|
||||
/// let secret_key: SecretKey = SecretKey::generate(&mut csprng);
|
||||
/// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key);
|
||||
/// # }
|
||||
/// ```
|
||||
fn from(secret_key: &SecretKey) -> ExpandedSecretKey {
|
||||
let mut h: Sha512 = Sha512::default();
|
||||
let mut hash: [u8; 64] = [0u8; 64];
|
||||
let mut lower: [u8; 32] = [0u8; 32];
|
||||
let mut upper: [u8; 32] = [0u8; 32];
|
||||
|
||||
h.update(secret_key);
|
||||
hash.copy_from_slice(h.finalize().as_slice());
|
||||
|
||||
lower.copy_from_slice(&hash[00..32]);
|
||||
upper.copy_from_slice(&hash[32..64]);
|
||||
|
||||
lower[0] &= 248;
|
||||
lower[31] &= 63;
|
||||
lower[31] |= 64;
|
||||
|
||||
ExpandedSecretKey {
|
||||
key: Scalar::from_bits(lower),
|
||||
nonce: upper,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExpandedSecretKey {
|
||||
/// Sign a message with this `ExpandedSecretKey`.
|
||||
#[allow(non_snake_case)]
|
||||
pub(crate) fn sign(&self, message: &[u8], verifying_key: &VerifyingKey) -> ed25519::Signature {
|
||||
let mut h: Sha512 = Sha512::new();
|
||||
let R: CompressedEdwardsY;
|
||||
let r: Scalar;
|
||||
let s: Scalar;
|
||||
let k: Scalar;
|
||||
|
||||
h.update(&self.nonce);
|
||||
h.update(&message);
|
||||
|
||||
r = Scalar::from_hash(h);
|
||||
R = (&r * &ED25519_BASEPOINT_TABLE).compress();
|
||||
|
||||
h = Sha512::new();
|
||||
h.update(R.as_bytes());
|
||||
h.update(verifying_key.as_bytes());
|
||||
h.update(&message);
|
||||
|
||||
k = Scalar::from_hash(h);
|
||||
s = &(&k * &self.key) + &r;
|
||||
|
||||
InternalSignature { R, s }.into()
|
||||
}
|
||||
|
||||
/// Sign a `prehashed_message` with this `ExpandedSecretKey` using the
|
||||
/// Ed25519ph algorithm defined in [RFC8032 §5.1][rfc8032].
|
||||
///
|
||||
/// # Inputs
|
||||
///
|
||||
/// * `prehashed_message` is an instantiated hash digest with 512-bits of
|
||||
/// output which has had the message to be signed previously fed into its
|
||||
/// state.
|
||||
/// * `verifying_key` is a [`VerifyingKey`] which corresponds to this secret key.
|
||||
/// * `context` is an optional context string, up to 255 bytes inclusive,
|
||||
/// which may be used to provide additional domain separation. If not
|
||||
/// set, this will default to an empty string.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Result` whose `Ok` value is an Ed25519ph [`Signature`] on the
|
||||
/// `prehashed_message` if the context was 255 bytes or less, otherwise
|
||||
/// a `SignatureError`.
|
||||
///
|
||||
/// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1
|
||||
#[allow(non_snake_case)]
|
||||
pub(crate) fn sign_prehashed<'a, D>(
|
||||
&self,
|
||||
prehashed_message: D,
|
||||
verifying_key: &VerifyingKey,
|
||||
context: Option<&'a [u8]>,
|
||||
) -> Result<ed25519::Signature, SignatureError>
|
||||
where
|
||||
D: Digest<OutputSize = U64>,
|
||||
{
|
||||
let mut h: Sha512;
|
||||
let mut prehash: [u8; 64] = [0u8; 64];
|
||||
let R: CompressedEdwardsY;
|
||||
let r: Scalar;
|
||||
let s: Scalar;
|
||||
let k: Scalar;
|
||||
|
||||
let ctx: &[u8] = context.unwrap_or(b""); // By default, the context is an empty string.
|
||||
|
||||
if ctx.len() > 255 {
|
||||
return Err(SignatureError::from(
|
||||
InternalError::PrehashedContextLengthError,
|
||||
));
|
||||
}
|
||||
|
||||
let ctx_len: u8 = ctx.len() as u8;
|
||||
|
||||
// Get the result of the pre-hashed message.
|
||||
prehash.copy_from_slice(prehashed_message.finalize().as_slice());
|
||||
|
||||
// This is the dumbest, ten-years-late, non-admission of fucking up the
|
||||
// domain separation I have ever seen. Why am I still required to put
|
||||
// the upper half "prefix" of the hashed "secret key" in here? Why
|
||||
// can't the user just supply their own nonce and decide for themselves
|
||||
// whether or not they want a deterministic signature scheme? Why does
|
||||
// the message go into what's ostensibly the signature domain separation
|
||||
// hash? Why wasn't there always a way to provide a context string?
|
||||
//
|
||||
// ...
|
||||
//
|
||||
// This is a really fucking stupid bandaid, and the damned scheme is
|
||||
// still bleeding from malleability, for fuck's sake.
|
||||
h = Sha512::new()
|
||||
.chain_update(b"SigEd25519 no Ed25519 collisions")
|
||||
.chain_update(&[1]) // Ed25519ph
|
||||
.chain_update(&[ctx_len])
|
||||
.chain_update(ctx)
|
||||
.chain_update(&self.nonce)
|
||||
.chain_update(&prehash[..]);
|
||||
|
||||
r = Scalar::from_hash(h);
|
||||
R = (&r * &ED25519_BASEPOINT_TABLE).compress();
|
||||
|
||||
h = Sha512::new()
|
||||
.chain_update(b"SigEd25519 no Ed25519 collisions")
|
||||
.chain_update(&[1]) // Ed25519ph
|
||||
.chain_update(&[ctx_len])
|
||||
.chain_update(ctx)
|
||||
.chain_update(R.as_bytes())
|
||||
.chain_update(verifying_key.as_bytes())
|
||||
.chain_update(&prehash[..]);
|
||||
|
||||
k = Scalar::from_hash(h);
|
||||
s = &(&k * &self.key) + &r;
|
||||
|
||||
Ok(InternalSignature { R, s }.into())
|
||||
}
|
||||
}
|
||||
@@ -35,51 +35,53 @@ use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes};
|
||||
|
||||
use crate::constants::*;
|
||||
use crate::errors::*;
|
||||
use crate::secret::*;
|
||||
use crate::signature::*;
|
||||
use crate::signing::*;
|
||||
|
||||
/// An ed25519 public key.
|
||||
#[derive(Copy, Clone, Default, Eq, PartialEq)]
|
||||
pub struct PublicKey(pub(crate) CompressedEdwardsY, pub(crate) EdwardsPoint);
|
||||
pub struct VerifyingKey(pub(crate) CompressedEdwardsY, pub(crate) EdwardsPoint);
|
||||
|
||||
impl Debug for PublicKey {
|
||||
impl Debug for VerifyingKey {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
|
||||
write!(f, "PublicKey({:?}), {:?})", self.0, self.1)
|
||||
write!(f, "VerifyingKey({:?}), {:?})", self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for PublicKey {
|
||||
impl AsRef<[u8]> for VerifyingKey {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a SecretKey> for PublicKey {
|
||||
impl From<&SecretKey> for VerifyingKey {
|
||||
/// Derive this public key from its corresponding `SecretKey`.
|
||||
fn from(secret_key: &SecretKey) -> PublicKey {
|
||||
fn from(secret_key: &SecretKey) -> VerifyingKey {
|
||||
let mut h: Sha512 = Sha512::new();
|
||||
let mut hash: [u8; 64] = [0u8; 64];
|
||||
let mut digest: [u8; 32] = [0u8; 32];
|
||||
|
||||
h.update(secret_key.as_bytes());
|
||||
h.update(secret_key);
|
||||
hash.copy_from_slice(h.finalize().as_slice());
|
||||
|
||||
digest.copy_from_slice(&hash[..32]);
|
||||
|
||||
PublicKey::mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(&mut digest)
|
||||
VerifyingKey::mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(
|
||||
&mut digest,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a ExpandedSecretKey> for PublicKey {
|
||||
impl From<&ExpandedSecretKey> for VerifyingKey {
|
||||
/// Derive this public key from its corresponding `ExpandedSecretKey`.
|
||||
fn from(expanded_secret_key: &ExpandedSecretKey) -> PublicKey {
|
||||
fn from(expanded_secret_key: &ExpandedSecretKey) -> VerifyingKey {
|
||||
let mut bits: [u8; 32] = expanded_secret_key.key.to_bytes();
|
||||
|
||||
PublicKey::mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(&mut bits)
|
||||
VerifyingKey::mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(&mut bits)
|
||||
}
|
||||
}
|
||||
|
||||
impl PublicKey {
|
||||
impl VerifyingKey {
|
||||
/// Convert this public key to a byte array.
|
||||
#[inline]
|
||||
pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_LENGTH] {
|
||||
@@ -92,7 +94,7 @@ impl PublicKey {
|
||||
&(self.0).0
|
||||
}
|
||||
|
||||
/// Construct a `PublicKey` from a slice of bytes.
|
||||
/// Construct a `VerifyingKey` from a slice of bytes.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
@@ -103,16 +105,16 @@ impl PublicKey {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use ed25519_dalek::PublicKey;
|
||||
/// use ed25519_dalek::VerifyingKey;
|
||||
/// use ed25519_dalek::PUBLIC_KEY_LENGTH;
|
||||
/// use ed25519_dalek::SignatureError;
|
||||
///
|
||||
/// # fn doctest() -> Result<PublicKey, SignatureError> {
|
||||
/// # fn doctest() -> Result<VerifyingKey, SignatureError> {
|
||||
/// 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];
|
||||
///
|
||||
/// let public_key = PublicKey::from_bytes(&public_key_bytes)?;
|
||||
/// let public_key = VerifyingKey::from_bytes(&public_key_bytes)?;
|
||||
/// #
|
||||
/// # Ok(public_key)
|
||||
/// # }
|
||||
@@ -124,13 +126,13 @@ impl PublicKey {
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Result` whose okay value is an EdDSA `PublicKey` or whose error value
|
||||
/// A `Result` whose okay value is an EdDSA `VerifyingKey` or whose error value
|
||||
/// is an `SignatureError` describing the error that occurred.
|
||||
#[inline]
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<PublicKey, SignatureError> {
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<VerifyingKey, SignatureError> {
|
||||
if bytes.len() != PUBLIC_KEY_LENGTH {
|
||||
return Err(InternalError::BytesLengthError {
|
||||
name: "PublicKey",
|
||||
name: "VerifyingKey",
|
||||
length: PUBLIC_KEY_LENGTH,
|
||||
}
|
||||
.into());
|
||||
@@ -143,7 +145,7 @@ impl PublicKey {
|
||||
.decompress()
|
||||
.ok_or(InternalError::PointDecompressionError)?;
|
||||
|
||||
Ok(PublicKey(compressed, point))
|
||||
Ok(VerifyingKey(compressed, point))
|
||||
}
|
||||
|
||||
/// Internal utility function for mangling the bits of a (formerly
|
||||
@@ -151,7 +153,7 @@ impl PublicKey {
|
||||
/// public key.
|
||||
fn mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(
|
||||
bits: &mut [u8; 32],
|
||||
) -> PublicKey {
|
||||
) -> VerifyingKey {
|
||||
bits[0] &= 248;
|
||||
bits[31] &= 127;
|
||||
bits[31] |= 64;
|
||||
@@ -159,7 +161,7 @@ impl PublicKey {
|
||||
let point = &Scalar::from_bits(*bits) * &constants::ED25519_BASEPOINT_TABLE;
|
||||
let compressed = point.compress();
|
||||
|
||||
PublicKey(compressed, point)
|
||||
VerifyingKey(compressed, point)
|
||||
}
|
||||
|
||||
/// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm.
|
||||
@@ -323,7 +325,7 @@ impl PublicKey {
|
||||
}
|
||||
}
|
||||
|
||||
impl Verifier<ed25519::Signature> for PublicKey {
|
||||
impl Verifier<ed25519::Signature> for VerifyingKey {
|
||||
/// Verify a signature on a message with this keypair's public key.
|
||||
///
|
||||
/// # Return
|
||||
@@ -353,58 +355,58 @@ impl Verifier<ed25519::Signature> for PublicKey {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for PublicKey {
|
||||
impl TryFrom<&[u8]> for VerifyingKey {
|
||||
type Error = SignatureError;
|
||||
|
||||
fn try_from(bytes: &[u8]) -> Result<PublicKey, SignatureError> {
|
||||
PublicKey::from_bytes(bytes)
|
||||
fn try_from(bytes: &[u8]) -> Result<VerifyingKey, SignatureError> {
|
||||
VerifyingKey::from_bytes(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl DecodePublicKey for PublicKey {}
|
||||
impl DecodePublicKey for VerifyingKey {}
|
||||
|
||||
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
|
||||
impl pkcs8::EncodePublicKey for PublicKey {
|
||||
impl pkcs8::EncodePublicKey for VerifyingKey {
|
||||
fn to_public_key_der(&self) -> pkcs8::spki::Result<pkcs8::Document> {
|
||||
pkcs8::PublicKeyBytes::from(self).to_public_key_der()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl TryFrom<pkcs8::PublicKeyBytes> for PublicKey {
|
||||
impl TryFrom<pkcs8::PublicKeyBytes> for VerifyingKey {
|
||||
type Error = pkcs8::spki::Error;
|
||||
|
||||
fn try_from(pkcs8_key: pkcs8::PublicKeyBytes) -> pkcs8::spki::Result<Self> {
|
||||
PublicKey::try_from(&pkcs8_key)
|
||||
VerifyingKey::try_from(&pkcs8_key)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl TryFrom<&pkcs8::PublicKeyBytes> for PublicKey {
|
||||
impl TryFrom<&pkcs8::PublicKeyBytes> for VerifyingKey {
|
||||
type Error = pkcs8::spki::Error;
|
||||
|
||||
fn try_from(pkcs8_key: &pkcs8::PublicKeyBytes) -> pkcs8::spki::Result<Self> {
|
||||
PublicKey::from_bytes(pkcs8_key.as_ref()).map_err(|_| pkcs8::spki::Error::KeyMalformed)
|
||||
VerifyingKey::from_bytes(pkcs8_key.as_ref()).map_err(|_| pkcs8::spki::Error::KeyMalformed)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl From<PublicKey> for pkcs8::PublicKeyBytes {
|
||||
fn from(public_key: PublicKey) -> pkcs8::PublicKeyBytes {
|
||||
pkcs8::PublicKeyBytes::from(&public_key)
|
||||
impl From<VerifyingKey> for pkcs8::PublicKeyBytes {
|
||||
fn from(verifying_key: VerifyingKey) -> pkcs8::PublicKeyBytes {
|
||||
pkcs8::PublicKeyBytes::from(&verifying_key)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl From<&PublicKey> for pkcs8::PublicKeyBytes {
|
||||
fn from(public_key: &PublicKey) -> pkcs8::PublicKeyBytes {
|
||||
pkcs8::PublicKeyBytes(public_key.to_bytes())
|
||||
impl From<&VerifyingKey> for pkcs8::PublicKeyBytes {
|
||||
fn from(verifying_key: &VerifyingKey) -> pkcs8::PublicKeyBytes {
|
||||
pkcs8::PublicKeyBytes(verifying_key.to_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl TryFrom<pkcs8::spki::SubjectPublicKeyInfo<'_>> for PublicKey {
|
||||
impl TryFrom<pkcs8::spki::SubjectPublicKeyInfo<'_>> for VerifyingKey {
|
||||
type Error = pkcs8::spki::Error;
|
||||
|
||||
fn try_from(public_key: pkcs8::spki::SubjectPublicKeyInfo<'_>) -> pkcs8::spki::Result<Self> {
|
||||
@@ -413,7 +415,7 @@ impl TryFrom<pkcs8::spki::SubjectPublicKeyInfo<'_>> for PublicKey {
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for PublicKey {
|
||||
impl Serialize for VerifyingKey {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
@@ -423,12 +425,12 @@ impl Serialize for PublicKey {
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'d> Deserialize<'d> for PublicKey {
|
||||
impl<'d> Deserialize<'d> for VerifyingKey {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'d>,
|
||||
{
|
||||
let bytes = <SerdeByteBuf>::deserialize(deserializer)?;
|
||||
PublicKey::from_bytes(bytes.as_ref()).map_err(SerdeError::custom)
|
||||
VerifyingKey::from_bytes(bytes.as_ref()).map_err(SerdeError::custom)
|
||||
}
|
||||
}
|
||||
179
tests/ed25519.rs
179
tests/ed25519.rs
@@ -61,20 +61,19 @@ mod vectors {
|
||||
let msg_bytes: Vec<u8> = FromHex::from_hex(&parts[2]).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 expected_public: PublicKey =
|
||||
PublicKey::from_bytes(&pub_bytes[..PUBLIC_KEY_LENGTH]).unwrap();
|
||||
let keypair: Keypair = Keypair::from(secret);
|
||||
assert_eq!(expected_public, keypair.public_key());
|
||||
let signing_key = SigningKey::try_from(&sec_bytes[..SECRET_KEY_LENGTH]).unwrap();
|
||||
let expected_verifying_key =
|
||||
VerifyingKey::from_bytes(&pub_bytes[..PUBLIC_KEY_LENGTH]).unwrap();
|
||||
assert_eq!(expected_verifying_key, signing_key.verifying_key());
|
||||
|
||||
// The signatures in the test vectors also include the message
|
||||
// at the end, but we just want R and S.
|
||||
let sig1: Signature = Signature::try_from(&sig_bytes[..64]).unwrap();
|
||||
let sig2: Signature = keypair.sign(&msg_bytes);
|
||||
let sig2: Signature = signing_key.sign(&msg_bytes);
|
||||
|
||||
assert!(sig1 == sig2, "Signature bytes not equal on line {}", lineno);
|
||||
assert!(
|
||||
keypair.verify(&msg_bytes, &sig2).is_ok(),
|
||||
signing_key.verify(&msg_bytes, &sig2).is_ok(),
|
||||
"Signature verification failed on line {}",
|
||||
lineno
|
||||
);
|
||||
@@ -85,20 +84,21 @@ mod vectors {
|
||||
#[test]
|
||||
fn ed25519ph_rf8032_test_vector() {
|
||||
let secret_key: &[u8] = b"833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42";
|
||||
let public_key: &[u8] = b"ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf";
|
||||
let verifying_key: &[u8] =
|
||||
b"ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf";
|
||||
let message: &[u8] = b"616263";
|
||||
let signature: &[u8] = b"98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406";
|
||||
|
||||
let sec_bytes: Vec<u8> = FromHex::from_hex(secret_key).unwrap();
|
||||
let pub_bytes: Vec<u8> = FromHex::from_hex(public_key).unwrap();
|
||||
let pub_bytes: Vec<u8> = FromHex::from_hex(verifying_key).unwrap();
|
||||
let msg_bytes: Vec<u8> = FromHex::from_hex(message).unwrap();
|
||||
let sig_bytes: Vec<u8> = FromHex::from_hex(signature).unwrap();
|
||||
|
||||
let secret: SecretKey = SecretKey::from_bytes(&sec_bytes[..SECRET_KEY_LENGTH]).unwrap();
|
||||
let expected_public: PublicKey =
|
||||
PublicKey::from_bytes(&pub_bytes[..PUBLIC_KEY_LENGTH]).unwrap();
|
||||
let keypair: Keypair = Keypair::from(secret);
|
||||
assert_eq!(expected_public, keypair.public_key());
|
||||
let signing_key: SigningKey =
|
||||
SigningKey::try_from(&sec_bytes[..SECRET_KEY_LENGTH]).unwrap();
|
||||
let expected_verifying_key: VerifyingKey =
|
||||
VerifyingKey::from_bytes(&pub_bytes[..PUBLIC_KEY_LENGTH]).unwrap();
|
||||
assert_eq!(expected_verifying_key, signing_key.verifying_key());
|
||||
let sig1: Signature = Signature::try_from(&sig_bytes[..]).unwrap();
|
||||
|
||||
let mut prehash_for_signing: Sha512 = Sha512::default();
|
||||
@@ -107,7 +107,9 @@ mod vectors {
|
||||
prehash_for_signing.update(&msg_bytes[..]);
|
||||
prehash_for_verifying.update(&msg_bytes[..]);
|
||||
|
||||
let sig2: Signature = keypair.sign_prehashed(prehash_for_signing, None).unwrap();
|
||||
let sig2: Signature = signing_key
|
||||
.sign_prehashed(prehash_for_signing, None)
|
||||
.unwrap();
|
||||
|
||||
assert!(
|
||||
sig1 == sig2,
|
||||
@@ -117,7 +119,7 @@ mod vectors {
|
||||
sig2
|
||||
);
|
||||
assert!(
|
||||
keypair
|
||||
signing_key
|
||||
.verify_prehashed(prehash_for_verifying, None, &sig2)
|
||||
.is_ok(),
|
||||
"Could not verify ed25519ph signature!"
|
||||
@@ -185,7 +187,7 @@ mod vectors {
|
||||
}
|
||||
|
||||
let signature = serialize_signature(&r, &s);
|
||||
let pk = PublicKey::from_bytes(&pub_key.compress().as_bytes()[..]).unwrap();
|
||||
let pk = VerifyingKey::from_bytes(&pub_key.compress().as_bytes()[..]).unwrap();
|
||||
let sig = Signature::try_from(&signature[..]).unwrap();
|
||||
// The same signature verifies for both messages
|
||||
assert!(pk.verify(message1, &sig).is_ok() && pk.verify(message2, &sig).is_ok());
|
||||
@@ -204,7 +206,7 @@ mod integrations {
|
||||
#[test]
|
||||
fn sign_verify() {
|
||||
// TestSignVerify
|
||||
let keypair: Keypair;
|
||||
let signing_key: SigningKey;
|
||||
let good_sig: Signature;
|
||||
let bad_sig: Signature;
|
||||
|
||||
@@ -213,27 +215,27 @@ mod integrations {
|
||||
|
||||
let mut csprng = OsRng {};
|
||||
|
||||
keypair = Keypair::generate(&mut csprng);
|
||||
good_sig = keypair.sign(&good);
|
||||
bad_sig = keypair.sign(&bad);
|
||||
signing_key = SigningKey::generate(&mut csprng);
|
||||
good_sig = signing_key.sign(&good);
|
||||
bad_sig = signing_key.sign(&bad);
|
||||
|
||||
assert!(
|
||||
keypair.verify(&good, &good_sig).is_ok(),
|
||||
signing_key.verify(&good, &good_sig).is_ok(),
|
||||
"Verification of a valid signature failed!"
|
||||
);
|
||||
assert!(
|
||||
keypair.verify(&good, &bad_sig).is_err(),
|
||||
signing_key.verify(&good, &bad_sig).is_err(),
|
||||
"Verification of a signature on a different message passed!"
|
||||
);
|
||||
assert!(
|
||||
keypair.verify(&bad, &good_sig).is_err(),
|
||||
signing_key.verify(&bad, &good_sig).is_err(),
|
||||
"Verification of a signature on a different message passed!"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ed25519ph_sign_verify() {
|
||||
let keypair: Keypair;
|
||||
let signing_key: SigningKey;
|
||||
let good_sig: Signature;
|
||||
let bad_sig: Signature;
|
||||
|
||||
@@ -257,28 +259,28 @@ mod integrations {
|
||||
|
||||
let context: &[u8] = b"testing testing 1 2 3";
|
||||
|
||||
keypair = Keypair::generate(&mut csprng);
|
||||
good_sig = keypair
|
||||
signing_key = SigningKey::generate(&mut csprng);
|
||||
good_sig = signing_key
|
||||
.sign_prehashed(prehashed_good1, Some(context))
|
||||
.unwrap();
|
||||
bad_sig = keypair
|
||||
bad_sig = signing_key
|
||||
.sign_prehashed(prehashed_bad1, Some(context))
|
||||
.unwrap();
|
||||
|
||||
assert!(
|
||||
keypair
|
||||
signing_key
|
||||
.verify_prehashed(prehashed_good2, Some(context), &good_sig)
|
||||
.is_ok(),
|
||||
"Verification of a valid signature failed!"
|
||||
);
|
||||
assert!(
|
||||
keypair
|
||||
signing_key
|
||||
.verify_prehashed(prehashed_good3, Some(context), &bad_sig)
|
||||
.is_err(),
|
||||
"Verification of a signature on a different message passed!"
|
||||
);
|
||||
assert!(
|
||||
keypair
|
||||
signing_key
|
||||
.verify_prehashed(prehashed_bad2, Some(context), &good_sig)
|
||||
.is_err(),
|
||||
"Verification of a signature on a different message passed!"
|
||||
@@ -297,17 +299,18 @@ mod integrations {
|
||||
b"Hey, I never cared about your bucks, so if I run up with a mask on, probably got a gas can too.",
|
||||
b"And I'm not here to fill 'er up. Nope, we came to riot, here to incite, we don't want any of your stuff.", ];
|
||||
let mut csprng = OsRng;
|
||||
let mut keypairs: Vec<Keypair> = Vec::new();
|
||||
let mut signing_keys: Vec<SigningKey> = Vec::new();
|
||||
let mut signatures: Vec<Signature> = Vec::new();
|
||||
|
||||
for i in 0..messages.len() {
|
||||
let keypair: Keypair = Keypair::generate(&mut csprng);
|
||||
signatures.push(keypair.sign(&messages[i]));
|
||||
keypairs.push(keypair);
|
||||
let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
signatures.push(signing_key.sign(&messages[i]));
|
||||
signing_keys.push(signing_key);
|
||||
}
|
||||
let public_keys: Vec<PublicKey> = keypairs.iter().map(|key| key.public_key()).collect();
|
||||
let verifying_keys: Vec<VerifyingKey> =
|
||||
signing_keys.iter().map(|key| key.verifying_key()).collect();
|
||||
|
||||
let result = verify_batch(&messages, &signatures[..], &public_keys[..]);
|
||||
let result = verify_batch(&messages, &signatures[..], &verifying_keys[..]);
|
||||
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
@@ -317,7 +320,7 @@ mod integrations {
|
||||
#[derive(Debug, serde_crate::Serialize, serde_crate::Deserialize)]
|
||||
#[serde(crate = "serde_crate")]
|
||||
struct Demo {
|
||||
keypair: Keypair,
|
||||
signing_key: SigningKey,
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "serde"))]
|
||||
@@ -337,7 +340,7 @@ mod serialisation {
|
||||
150, 073, 201, 137, 076, 022, 085, 251, 152, 002, 241, 042, 072, 054,
|
||||
];
|
||||
|
||||
/// Signature with the above keypair of a blank message.
|
||||
/// Signature with the above signing_key of a blank message.
|
||||
static SIGNATURE_BYTES: [u8; SIGNATURE_LENGTH] = [
|
||||
010, 126, 151, 143, 157, 064, 047, 001, 196, 140, 179, 058, 226, 152, 018, 102, 160, 123,
|
||||
080, 016, 210, 086, 196, 028, 053, 231, 012, 157, 169, 019, 158, 063, 045, 154, 238, 007,
|
||||
@@ -345,13 +348,6 @@ mod serialisation {
|
||||
041, 081, 063, 120, 126, 100, 092, 059, 050, 011,
|
||||
];
|
||||
|
||||
static KEYPAIR_BYTES: [u8; KEYPAIR_LENGTH] = [
|
||||
239, 085, 017, 235, 167, 103, 034, 062, 007, 010, 032, 146, 113, 039, 096, 174, 003, 219,
|
||||
232, 166, 240, 121, 167, 013, 098, 238, 122, 116, 193, 114, 215, 213, 175, 181, 075, 166,
|
||||
224, 164, 140, 146, 053, 120, 010, 037, 104, 094, 136, 225, 249, 102, 171, 160, 097, 132,
|
||||
015, 071, 035, 056, 000, 074, 130, 168, 225, 071,
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn serialize_deserialize_signature_bincode() {
|
||||
let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES).unwrap();
|
||||
@@ -371,75 +367,55 @@ mod serialisation {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_deserialize_public_key_bincode() {
|
||||
let public_key: PublicKey = PublicKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap();
|
||||
let encoded_public_key: Vec<u8> = bincode::serialize(&public_key).unwrap();
|
||||
let decoded_public_key: PublicKey = bincode::deserialize(&encoded_public_key).unwrap();
|
||||
fn serialize_deserialize_verifying_key_bincode() {
|
||||
let verifying_key: VerifyingKey = VerifyingKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap();
|
||||
let encoded_verifying_key: Vec<u8> = bincode::serialize(&verifying_key).unwrap();
|
||||
let decoded_verifying_key: VerifyingKey =
|
||||
bincode::deserialize(&encoded_verifying_key).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
&PUBLIC_KEY_BYTES[..],
|
||||
&encoded_public_key[encoded_public_key.len() - PUBLIC_KEY_LENGTH..]
|
||||
&encoded_verifying_key[encoded_verifying_key.len() - PUBLIC_KEY_LENGTH..]
|
||||
);
|
||||
assert_eq!(public_key, decoded_public_key);
|
||||
assert_eq!(verifying_key, decoded_verifying_key);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_deserialize_public_key_json() {
|
||||
let public_key: PublicKey = PublicKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap();
|
||||
let encoded_public_key = serde_json::to_string(&public_key).unwrap();
|
||||
let decoded_public_key: PublicKey = serde_json::from_str(&encoded_public_key).unwrap();
|
||||
fn serialize_deserialize_verifying_key_json() {
|
||||
let verifying_key: VerifyingKey = VerifyingKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap();
|
||||
let encoded_verifying_key = serde_json::to_string(&verifying_key).unwrap();
|
||||
let decoded_verifying_key: VerifyingKey =
|
||||
serde_json::from_str(&encoded_verifying_key).unwrap();
|
||||
|
||||
assert_eq!(public_key, decoded_public_key);
|
||||
assert_eq!(verifying_key, decoded_verifying_key);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_deserialize_secret_key_bincode() {
|
||||
let secret_key: SecretKey = SecretKey::from_bytes(&SECRET_KEY_BYTES).unwrap();
|
||||
let encoded_secret_key: Vec<u8> = bincode::serialize(&secret_key).unwrap();
|
||||
let decoded_secret_key: SecretKey = bincode::deserialize(&encoded_secret_key).unwrap();
|
||||
fn serialize_deserialize_signing_key_bincode() {
|
||||
let signing_key = SigningKey::from_bytes(&SECRET_KEY_BYTES);
|
||||
let encoded_signing_key: Vec<u8> = bincode::serialize(&signing_key).unwrap();
|
||||
let decoded_signing_key: SigningKey = bincode::deserialize(&encoded_signing_key).unwrap();
|
||||
|
||||
for i in 0..SECRET_KEY_LENGTH {
|
||||
assert_eq!(SECRET_KEY_BYTES[i], decoded_secret_key.as_bytes()[i]);
|
||||
assert_eq!(SECRET_KEY_BYTES[i], decoded_signing_key.to_bytes()[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_deserialize_secret_key_json() {
|
||||
let secret_key: SecretKey = SecretKey::from_bytes(&SECRET_KEY_BYTES).unwrap();
|
||||
let encoded_secret_key = serde_json::to_string(&secret_key).unwrap();
|
||||
let decoded_secret_key: SecretKey = serde_json::from_str(&encoded_secret_key).unwrap();
|
||||
fn serialize_deserialize_signing_key_json() {
|
||||
let signing_key = SigningKey::from_bytes(&SECRET_KEY_BYTES);
|
||||
let encoded_signing_key = serde_json::to_string(&signing_key).unwrap();
|
||||
let decoded_signing_key: SigningKey = serde_json::from_str(&encoded_signing_key).unwrap();
|
||||
|
||||
for i in 0..SECRET_KEY_LENGTH {
|
||||
assert_eq!(SECRET_KEY_BYTES[i], decoded_secret_key.as_bytes()[i]);
|
||||
assert_eq!(SECRET_KEY_BYTES[i], decoded_signing_key.to_bytes()[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_deserialize_keypair_bincode() {
|
||||
let keypair = Keypair::from_bytes(&KEYPAIR_BYTES).unwrap();
|
||||
let encoded_keypair: Vec<u8> = bincode::serialize(&keypair).unwrap();
|
||||
let decoded_keypair: Keypair = bincode::deserialize(&encoded_keypair).unwrap();
|
||||
|
||||
for i in 0..KEYPAIR_LENGTH {
|
||||
assert_eq!(KEYPAIR_BYTES[i], decoded_keypair.to_bytes()[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_deserialize_keypair_json() {
|
||||
let keypair = Keypair::from_bytes(&KEYPAIR_BYTES).unwrap();
|
||||
let encoded_keypair = serde_json::to_string(&keypair).unwrap();
|
||||
let decoded_keypair: Keypair = serde_json::from_str(&encoded_keypair).unwrap();
|
||||
|
||||
for i in 0..KEYPAIR_LENGTH {
|
||||
assert_eq!(KEYPAIR_BYTES[i], decoded_keypair.to_bytes()[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_deserialize_keypair_toml() {
|
||||
fn serialize_deserialize_signing_key_toml() {
|
||||
let demo = Demo {
|
||||
keypair: Keypair::from_bytes(&KEYPAIR_BYTES).unwrap(),
|
||||
signing_key: SigningKey::from_bytes(&SECRET_KEY_BYTES),
|
||||
};
|
||||
|
||||
println!("\n\nWrite to toml");
|
||||
@@ -450,10 +426,10 @@ mod serialisation {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_public_key_size() {
|
||||
let public_key: PublicKey = PublicKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap();
|
||||
fn serialize_verifying_key_size() {
|
||||
let verifying_key: VerifyingKey = VerifyingKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap();
|
||||
assert_eq!(
|
||||
bincode::serialized_size(&public_key).unwrap() as usize,
|
||||
bincode::serialized_size(&verifying_key).unwrap() as usize,
|
||||
BINCODE_INT_LENGTH + PUBLIC_KEY_LENGTH
|
||||
);
|
||||
}
|
||||
@@ -468,20 +444,11 @@ mod serialisation {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_secret_key_size() {
|
||||
let secret_key: SecretKey = SecretKey::from_bytes(&SECRET_KEY_BYTES).unwrap();
|
||||
fn serialize_signing_key_size() {
|
||||
let signing_key = SigningKey::from_bytes(&SECRET_KEY_BYTES);
|
||||
assert_eq!(
|
||||
bincode::serialized_size(&secret_key).unwrap() as usize,
|
||||
bincode::serialized_size(&signing_key).unwrap() as usize,
|
||||
BINCODE_INT_LENGTH + SECRET_KEY_LENGTH
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_keypair_size() {
|
||||
let keypair = Keypair::from_bytes(&KEYPAIR_BYTES).unwrap();
|
||||
assert_eq!(
|
||||
bincode::serialized_size(&keypair).unwrap() as usize,
|
||||
BINCODE_INT_LENGTH + KEYPAIR_LENGTH
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,14 +6,11 @@
|
||||
#![cfg(feature = "pkcs8")]
|
||||
|
||||
use ed25519_dalek::pkcs8::{DecodePrivateKey, DecodePublicKey};
|
||||
use ed25519_dalek::{Keypair, PublicKey};
|
||||
use ed25519_dalek::{SigningKey, VerifyingKey};
|
||||
use hex_literal::hex;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use ed25519_dalek::{
|
||||
pkcs8::{EncodePrivateKey, EncodePublicKey},
|
||||
SecretKey,
|
||||
};
|
||||
use ed25519_dalek::pkcs8::{EncodePrivateKey, EncodePublicKey};
|
||||
|
||||
/// Ed25519 PKCS#8 v1 private key encoded as ASN.1 DER.
|
||||
const PKCS8_V1_DER: &[u8] = include_bytes!("examples/pkcs8-v1.der");
|
||||
@@ -21,7 +18,7 @@ const PKCS8_V1_DER: &[u8] = include_bytes!("examples/pkcs8-v1.der");
|
||||
/// Ed25519 PKCS#8 v2 private key + public key encoded as ASN.1 DER.
|
||||
const PKCS8_V2_DER: &[u8] = include_bytes!("examples/pkcs8-v2.der");
|
||||
|
||||
/// Ed25519 SubjectPublicKeyInfo encoded as ASN.1 DER.
|
||||
/// Ed25519 SubjectVerifyingKeyInfo encoded as ASN.1 DER.
|
||||
const PUBLIC_KEY_DER: &[u8] = include_bytes!("examples/pubkey.der");
|
||||
|
||||
/// Secret key bytes.
|
||||
@@ -35,40 +32,40 @@ const PK_BYTES: [u8; 32] = hex!("19BF44096984CDFE8541BAC167DC3B96C85086AA30B6B6C
|
||||
|
||||
#[test]
|
||||
fn decode_pkcs8_v1() {
|
||||
let keypair = Keypair::from_pkcs8_der(PKCS8_V1_DER).unwrap();
|
||||
assert_eq!(SK_BYTES, keypair.secret_key().to_bytes());
|
||||
assert_eq!(PK_BYTES, keypair.public_key().to_bytes());
|
||||
let keypair = SigningKey::from_pkcs8_der(PKCS8_V1_DER).unwrap();
|
||||
assert_eq!(SK_BYTES, keypair.to_bytes());
|
||||
assert_eq!(PK_BYTES, keypair.verifying_key().to_bytes());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_pkcs8_v2() {
|
||||
let keypair = Keypair::from_pkcs8_der(PKCS8_V2_DER).unwrap();
|
||||
assert_eq!(SK_BYTES, keypair.secret_key().to_bytes());
|
||||
assert_eq!(PK_BYTES, keypair.public_key().to_bytes());
|
||||
let keypair = SigningKey::from_pkcs8_der(PKCS8_V2_DER).unwrap();
|
||||
assert_eq!(SK_BYTES, keypair.to_bytes());
|
||||
assert_eq!(PK_BYTES, keypair.verifying_key().to_bytes());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_public_key() {
|
||||
let public_key = PublicKey::from_public_key_der(PUBLIC_KEY_DER).unwrap();
|
||||
assert_eq!(PK_BYTES, public_key.to_bytes());
|
||||
fn decode_verifying_key() {
|
||||
let verifying_key = VerifyingKey::from_public_key_der(PUBLIC_KEY_DER).unwrap();
|
||||
assert_eq!(PK_BYTES, verifying_key.to_bytes());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "alloc")]
|
||||
fn encode_pkcs8() {
|
||||
let keypair = Keypair::from(SecretKey::from_bytes(&SK_BYTES).unwrap());
|
||||
let keypair = SigningKey::from_bytes(&SK_BYTES);
|
||||
let pkcs8_key = keypair.to_pkcs8_der().unwrap();
|
||||
|
||||
let keypair2 = Keypair::from_pkcs8_der(pkcs8_key.as_bytes()).unwrap();
|
||||
let keypair2 = SigningKey::from_pkcs8_der(pkcs8_key.as_bytes()).unwrap();
|
||||
assert_eq!(keypair.to_bytes(), keypair2.to_bytes());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "alloc")]
|
||||
fn encode_public_key() {
|
||||
let public_key = PublicKey::from_bytes(&PK_BYTES).unwrap();
|
||||
let public_key_der = public_key.to_public_key_der().unwrap();
|
||||
fn encode_verifying_key() {
|
||||
let verifying_key = VerifyingKey::from_bytes(&PK_BYTES).unwrap();
|
||||
let verifying_key_der = verifying_key.to_public_key_der().unwrap();
|
||||
|
||||
let public_key2 = PublicKey::from_public_key_der(public_key_der.as_bytes()).unwrap();
|
||||
assert_eq!(public_key, public_key2);
|
||||
let verifying_key2 = VerifyingKey::from_public_key_der(verifying_key_der.as_bytes()).unwrap();
|
||||
assert_eq!(verifying_key, verifying_key2);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user