mirror of
https://github.com/saymrwulf/curve25519-dalek-source.git
synced 2026-09-07 20:50:39 +00:00
Add Context type (#273)
* Add `Context` type Adds a generic type which can be used with `SigningKey` and `VerifyingKey` for storing a context string value along with the key for use with `DigestSigner` and `DigestVerifier`. * Added Context tests, docs, and re-exports * Added docs about SHA-512 for prehashing; re-re-exported Sha512 Co-authored-by: Tony Arcieri <bascule@gmail.com> Co-authored-by: Michael Rosenberg <michael@mrosenberg.pub>
This commit is contained in:
parent
27ba9dd614
commit
861784f57e
5 changed files with 187 additions and 10 deletions
107
src/context.rs
Normal file
107
src/context.rs
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
use crate::{InternalError, SignatureError};
|
||||||
|
|
||||||
|
/// Ed25519 contexts as used by Ed25519ph.
|
||||||
|
///
|
||||||
|
/// Contexts are domain separator strings that can be used to isolate uses of
|
||||||
|
/// the algorithm between different protocols (which is very hard to reliably do
|
||||||
|
/// otherwise) and between different uses within the same protocol.
|
||||||
|
///
|
||||||
|
/// To create a context, call either of the following:
|
||||||
|
///
|
||||||
|
/// - [`SigningKey::with_context`](crate::SigningKey::with_context)
|
||||||
|
/// - [`VerifyingKey::with_context`](crate::VerifyingKey::with_context)
|
||||||
|
///
|
||||||
|
/// For more information, see [RFC8032 § 8.3](https://www.rfc-editor.org/rfc/rfc8032#section-8.3).
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
#[cfg_attr(feature = "digest", doc = "```")]
|
||||||
|
#[cfg_attr(not(feature = "digest"), doc = "```ignore")]
|
||||||
|
/// # fn main() {
|
||||||
|
/// use ed25519_dalek::{Signature, SigningKey, VerifyingKey, Sha512};
|
||||||
|
/// # use curve25519_dalek::digest::Digest;
|
||||||
|
/// # use rand::rngs::OsRng;
|
||||||
|
/// use ed25519_dalek::{DigestSigner, DigestVerifier};
|
||||||
|
///
|
||||||
|
/// # let mut csprng = OsRng;
|
||||||
|
/// # let signing_key = SigningKey::generate(&mut csprng);
|
||||||
|
/// # let verifying_key = signing_key.verifying_key();
|
||||||
|
/// let context_str = b"Local Channel 3";
|
||||||
|
/// let prehashed_message = Sha512::default().chain_update(b"Stay tuned for more news at 7");
|
||||||
|
///
|
||||||
|
/// // Signer
|
||||||
|
/// let signing_context = signing_key.with_context(context_str).unwrap();
|
||||||
|
/// let signature = signing_context.sign_digest(prehashed_message.clone());
|
||||||
|
///
|
||||||
|
/// // Verifier
|
||||||
|
/// let verifying_context = verifying_key.with_context(context_str).unwrap();
|
||||||
|
/// let verified: bool = verifying_context
|
||||||
|
/// .verify_digest(prehashed_message, &signature)
|
||||||
|
/// .is_ok();
|
||||||
|
///
|
||||||
|
/// # assert!(verified);
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Context<'k, 'v, K> {
|
||||||
|
/// Key this context is being used with.
|
||||||
|
key: &'k K,
|
||||||
|
|
||||||
|
/// Context value: a bytestring no longer than 255 octets.
|
||||||
|
value: &'v [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'k, 'v, K> Context<'k, 'v, K> {
|
||||||
|
/// Maximum length of the context value in octets.
|
||||||
|
pub const MAX_LENGTH: usize = 255;
|
||||||
|
|
||||||
|
/// Create a new Ed25519ph context.
|
||||||
|
pub(crate) fn new(key: &'k K, value: &'v [u8]) -> Result<Self, SignatureError> {
|
||||||
|
if value.len() <= Self::MAX_LENGTH {
|
||||||
|
Ok(Self { key, value })
|
||||||
|
} else {
|
||||||
|
Err(SignatureError::from(InternalError::PrehashedContextLength))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Borrow the key.
|
||||||
|
pub fn key(&self) -> &'k K {
|
||||||
|
self.key
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Borrow the context string value.
|
||||||
|
pub fn value(&self) -> &'v [u8] {
|
||||||
|
self.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(test, feature = "digest"))]
|
||||||
|
mod test {
|
||||||
|
use crate::{Signature, SigningKey, VerifyingKey};
|
||||||
|
use curve25519_dalek::digest::Digest;
|
||||||
|
use ed25519::signature::{DigestSigner, DigestVerifier};
|
||||||
|
use rand::rngs::OsRng;
|
||||||
|
use sha2::Sha512;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn context_correctness() {
|
||||||
|
let mut csprng = OsRng;
|
||||||
|
let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||||
|
let verifying_key: VerifyingKey = signing_key.verifying_key();
|
||||||
|
|
||||||
|
let context_str = b"Local Channel 3";
|
||||||
|
let prehashed_message = Sha512::default().chain_update(b"Stay tuned for more news at 7");
|
||||||
|
|
||||||
|
// Signer
|
||||||
|
let signing_context = signing_key.with_context(context_str).unwrap();
|
||||||
|
let signature: Signature = signing_context.sign_digest(prehashed_message.clone());
|
||||||
|
|
||||||
|
// Verifier
|
||||||
|
let verifying_context = verifying_key.with_context(context_str).unwrap();
|
||||||
|
let verified: bool = verifying_context
|
||||||
|
.verify_digest(prehashed_message, &signature)
|
||||||
|
.is_ok();
|
||||||
|
|
||||||
|
assert!(verified);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -48,7 +48,6 @@ pub(crate) enum InternalError {
|
||||||
length_c: usize,
|
length_c: usize,
|
||||||
},
|
},
|
||||||
/// An ed25519ph signature can only take up to 255 octets of context.
|
/// An ed25519ph signature can only take up to 255 octets of context.
|
||||||
#[cfg(feature = "digest")]
|
|
||||||
PrehashedContextLength,
|
PrehashedContextLength,
|
||||||
/// A mismatched (public, secret) key pair.
|
/// A mismatched (public, secret) key pair.
|
||||||
MismatchedKeypair,
|
MismatchedKeypair,
|
||||||
|
|
@ -77,7 +76,6 @@ impl Display for InternalError {
|
||||||
{} has length {}, {} has length {}.",
|
{} has length {}, {} has length {}.",
|
||||||
na, la, nb, lb, nc, lc
|
na, la, nb, lb, nc, lc
|
||||||
),
|
),
|
||||||
#[cfg(feature = "digest")]
|
|
||||||
InternalError::PrehashedContextLength => write!(
|
InternalError::PrehashedContextLength => write!(
|
||||||
f,
|
f,
|
||||||
"An ed25519ph signature can only take up to 255 octets of context"
|
"An ed25519ph signature can only take up to 255 octets of context"
|
||||||
|
|
|
||||||
10
src/lib.rs
10
src/lib.rs
|
|
@ -73,7 +73,7 @@
|
||||||
//! # use ed25519_dalek::Signature;
|
//! # use ed25519_dalek::Signature;
|
||||||
//! # use ed25519_dalek::Signer;
|
//! # use ed25519_dalek::Signer;
|
||||||
//! use ed25519_dalek::{VerifyingKey, Verifier};
|
//! use ed25519_dalek::{VerifyingKey, Verifier};
|
||||||
//! # let mut csprng = OsRng{};
|
//! # let mut csprng = OsRng;
|
||||||
//! # let signing_key: SigningKey = SigningKey::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 message: &[u8] = b"This is a test of the tsunami alert system.";
|
||||||
//! # let signature: Signature = signing_key.sign(message);
|
//! # let signature: Signature = signing_key.sign(message);
|
||||||
|
|
@ -97,7 +97,7 @@
|
||||||
//! # use rand::rngs::OsRng;
|
//! # use rand::rngs::OsRng;
|
||||||
//! # use ed25519_dalek::{SigningKey, Signature, Signer, VerifyingKey};
|
//! # use ed25519_dalek::{SigningKey, Signature, Signer, VerifyingKey};
|
||||||
//! use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH};
|
//! use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH};
|
||||||
//! # let mut csprng = OsRng{};
|
//! # let mut csprng = OsRng;
|
||||||
//! # let signing_key: SigningKey = SigningKey::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 message: &[u8] = b"This is a test of the tsunami alert system.";
|
||||||
//! # let signature: Signature = signing_key.sign(message);
|
//! # let signature: Signature = signing_key.sign(message);
|
||||||
|
|
@ -258,6 +258,7 @@ pub use ed25519;
|
||||||
#[cfg(feature = "batch")]
|
#[cfg(feature = "batch")]
|
||||||
mod batch;
|
mod batch;
|
||||||
mod constants;
|
mod constants;
|
||||||
|
mod context;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod signature;
|
mod signature;
|
||||||
mod signing;
|
mod signing;
|
||||||
|
|
@ -265,15 +266,20 @@ mod verifying;
|
||||||
|
|
||||||
#[cfg(feature = "digest")]
|
#[cfg(feature = "digest")]
|
||||||
pub use curve25519_dalek::digest::Digest;
|
pub use curve25519_dalek::digest::Digest;
|
||||||
|
#[cfg(feature = "digest")]
|
||||||
|
pub use sha2::Sha512;
|
||||||
|
|
||||||
#[cfg(feature = "batch")]
|
#[cfg(feature = "batch")]
|
||||||
pub use crate::batch::*;
|
pub use crate::batch::*;
|
||||||
pub use crate::constants::*;
|
pub use crate::constants::*;
|
||||||
|
pub use crate::context::Context;
|
||||||
pub use crate::errors::*;
|
pub use crate::errors::*;
|
||||||
pub use crate::signing::*;
|
pub use crate::signing::*;
|
||||||
pub use crate::verifying::*;
|
pub use crate::verifying::*;
|
||||||
|
|
||||||
// Re-export the `Signer` and `Verifier` traits from the `signature` crate
|
// Re-export the `Signer` and `Verifier` traits from the `signature` crate
|
||||||
|
#[cfg(feature = "digest")]
|
||||||
|
pub use ed25519::signature::{DigestSigner, DigestVerifier};
|
||||||
pub use ed25519::signature::{Signer, Verifier};
|
pub use ed25519::signature::{Signer, Verifier};
|
||||||
pub use ed25519::Signature;
|
pub use ed25519::Signature;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ use signature::DigestSigner;
|
||||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||||
|
|
||||||
use crate::constants::*;
|
use crate::constants::*;
|
||||||
|
use crate::context::Context;
|
||||||
use crate::errors::*;
|
use crate::errors::*;
|
||||||
use crate::signature::*;
|
use crate::signature::*;
|
||||||
use crate::verifying::*;
|
use crate::verifying::*;
|
||||||
|
|
@ -158,6 +159,15 @@ impl SigningKey {
|
||||||
self.verifying_key
|
self.verifying_key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a signing context that can be used for Ed25519ph with
|
||||||
|
/// [`DigestSigner`].
|
||||||
|
pub fn with_context<'k, 'v>(
|
||||||
|
&'k self,
|
||||||
|
context_value: &'v [u8],
|
||||||
|
) -> Result<Context<'k, 'v, Self>, SignatureError> {
|
||||||
|
Context::new(self, context_value)
|
||||||
|
}
|
||||||
|
|
||||||
/// Generate an ed25519 signing key.
|
/// Generate an ed25519 signing key.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
|
|
@ -200,9 +210,7 @@ impl SigningKey {
|
||||||
///
|
///
|
||||||
/// # Inputs
|
/// # Inputs
|
||||||
///
|
///
|
||||||
/// * `prehashed_message` is an instantiated hash digest with 512-bits of
|
/// * `prehashed_message` is an instantiated SHA-512 digest of the message
|
||||||
/// 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,
|
/// * `context` is an optional context string, up to 255 bytes inclusive,
|
||||||
/// which may be used to provide additional domain separation. If not
|
/// which may be used to provide additional domain separation. If not
|
||||||
/// set, this will default to an empty string.
|
/// set, this will default to an empty string.
|
||||||
|
|
@ -211,6 +219,13 @@ impl SigningKey {
|
||||||
///
|
///
|
||||||
/// An Ed25519ph [`Signature`] on the `prehashed_message`.
|
/// An Ed25519ph [`Signature`] on the `prehashed_message`.
|
||||||
///
|
///
|
||||||
|
/// # Note
|
||||||
|
///
|
||||||
|
/// The RFC only permits SHA-512 to be used for prehashing. This function technically works,
|
||||||
|
/// and is probably safe to use, with any secure hash function with 512-bit digests, but
|
||||||
|
/// anything outside of SHA-512 is NOT specification-compliant. We expose [`crate::Sha512`] for
|
||||||
|
/// user convenience.
|
||||||
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
#[cfg_attr(all(feature = "rand_core", feature = "digest"), doc = "```")]
|
#[cfg_attr(all(feature = "rand_core", feature = "digest"), doc = "```")]
|
||||||
|
|
@ -226,7 +241,7 @@ impl SigningKey {
|
||||||
///
|
///
|
||||||
/// # #[cfg(feature = "std")]
|
/// # #[cfg(feature = "std")]
|
||||||
/// # fn main() {
|
/// # fn main() {
|
||||||
/// let mut csprng = OsRng{};
|
/// let mut csprng = OsRng;
|
||||||
/// let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
/// let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||||
/// let message: &[u8] = b"All I want is to pet all of the dogs.";
|
/// let message: &[u8] = b"All I want is to pet all of the dogs.";
|
||||||
///
|
///
|
||||||
|
|
@ -274,7 +289,7 @@ impl SigningKey {
|
||||||
/// # use rand::rngs::OsRng;
|
/// # use rand::rngs::OsRng;
|
||||||
/// #
|
/// #
|
||||||
/// # fn do_test() -> Result<Signature, SignatureError> {
|
/// # fn do_test() -> Result<Signature, SignatureError> {
|
||||||
/// # let mut csprng = OsRng{};
|
/// # let mut csprng = OsRng;
|
||||||
/// # let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
/// # let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||||
/// # let message: &[u8] = b"All I want is to pet all of the dogs.";
|
/// # let message: &[u8] = b"All I want is to pet all of the dogs.";
|
||||||
/// # let mut prehashed: Sha512 = Sha512::new();
|
/// # let mut prehashed: Sha512 = Sha512::new();
|
||||||
|
|
@ -348,7 +363,7 @@ impl SigningKey {
|
||||||
/// use rand::rngs::OsRng;
|
/// use rand::rngs::OsRng;
|
||||||
///
|
///
|
||||||
/// # fn do_test() -> Result<(), SignatureError> {
|
/// # fn do_test() -> Result<(), SignatureError> {
|
||||||
/// let mut csprng = OsRng{};
|
/// let mut csprng = OsRng;
|
||||||
/// let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
/// let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||||
/// let message: &[u8] = b"All I want is to pet all of the dogs.";
|
/// let message: &[u8] = b"All I want is to pet all of the dogs.";
|
||||||
///
|
///
|
||||||
|
|
@ -485,6 +500,12 @@ impl Signer<Signature> for SigningKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Equivalent to [`SigningKey::sign_prehashed`] with `context` set to [`None`].
|
/// Equivalent to [`SigningKey::sign_prehashed`] with `context` set to [`None`].
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
///
|
||||||
|
/// The RFC only permits SHA-512 to be used for prehashing. This function technically works, and is
|
||||||
|
/// probably safe to use, with any secure hash function with 512-bit digests, but anything outside
|
||||||
|
/// of SHA-512 is NOT specification-compliant. We expose [`crate::Sha512`] for user convenience.
|
||||||
#[cfg(feature = "digest")]
|
#[cfg(feature = "digest")]
|
||||||
impl<D> DigestSigner<D, Signature> for SigningKey
|
impl<D> DigestSigner<D, Signature> for SigningKey
|
||||||
where
|
where
|
||||||
|
|
@ -495,6 +516,24 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Equivalent to [`SigningKey::sign_prehashed`] with `context` set to [`Some`]
|
||||||
|
/// containing `self.value()`.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
///
|
||||||
|
/// The RFC only permits SHA-512 to be used for prehashing. This function technically works, and is
|
||||||
|
/// probably safe to use, with any secure hash function with 512-bit digests, but anything outside
|
||||||
|
/// of SHA-512 is NOT specification-compliant. We expose [`crate::Sha512`] for user convenience.
|
||||||
|
#[cfg(feature = "digest")]
|
||||||
|
impl<D> DigestSigner<D, Signature> for Context<'_, '_, SigningKey>
|
||||||
|
where
|
||||||
|
D: Digest<OutputSize = U64>,
|
||||||
|
{
|
||||||
|
fn try_sign_digest(&self, msg_digest: D) -> Result<Signature, SignatureError> {
|
||||||
|
self.key().sign_prehashed(msg_digest, Some(self.value()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Verifier<Signature> for SigningKey {
|
impl Verifier<Signature> for SigningKey {
|
||||||
/// Verify a signature on a message with this signing key's public key.
|
/// Verify a signature on a message with this signing key's public key.
|
||||||
fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> {
|
fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> {
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes};
|
||||||
use signature::DigestVerifier;
|
use signature::DigestVerifier;
|
||||||
|
|
||||||
use crate::constants::*;
|
use crate::constants::*;
|
||||||
|
use crate::context::Context;
|
||||||
use crate::errors::*;
|
use crate::errors::*;
|
||||||
use crate::signature::*;
|
use crate::signature::*;
|
||||||
use crate::signing::*;
|
use crate::signing::*;
|
||||||
|
|
@ -153,6 +154,15 @@ impl VerifyingKey {
|
||||||
Ok(VerifyingKey(compressed, point))
|
Ok(VerifyingKey(compressed, point))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a verifying context that can be used for Ed25519ph with
|
||||||
|
/// [`DigestVerifier`].
|
||||||
|
pub fn with_context<'k, 'v>(
|
||||||
|
&'k self,
|
||||||
|
context_value: &'v [u8],
|
||||||
|
) -> Result<Context<'k, 'v, Self>, SignatureError> {
|
||||||
|
Context::new(self, context_value)
|
||||||
|
}
|
||||||
|
|
||||||
/// Internal utility function for clamping a scalar representation and multiplying by the
|
/// Internal utility function for clamping a scalar representation and multiplying by the
|
||||||
/// basepont to produce a public key.
|
/// basepont to produce a public key.
|
||||||
fn clamp_and_mul_base(bits: [u8; 32]) -> VerifyingKey {
|
fn clamp_and_mul_base(bits: [u8; 32]) -> VerifyingKey {
|
||||||
|
|
@ -431,6 +441,23 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Equivalent to [`VerifyingKey::verify_prehashed`] with `context` set to [`Some`]
|
||||||
|
/// containing `self.value()`.
|
||||||
|
#[cfg(feature = "digest")]
|
||||||
|
impl<D> DigestVerifier<D, ed25519::Signature> for Context<'_, '_, VerifyingKey>
|
||||||
|
where
|
||||||
|
D: Digest<OutputSize = U64>,
|
||||||
|
{
|
||||||
|
fn verify_digest(
|
||||||
|
&self,
|
||||||
|
msg_digest: D,
|
||||||
|
signature: &ed25519::Signature,
|
||||||
|
) -> Result<(), SignatureError> {
|
||||||
|
self.key()
|
||||||
|
.verify_prehashed(msg_digest, Some(self.value()), signature)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<&[u8]> for VerifyingKey {
|
impl TryFrom<&[u8]> for VerifyingKey {
|
||||||
type Error = SignatureError;
|
type Error = SignatureError;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue