mirror of
https://github.com/saymrwulf/risc0-curve25519-dalek-source.git
synced 2026-09-04 20:03:40 +00:00
Implement Hash trait for VerifyingKey (#265)
* Added and cleaned up some verification docs Co-authored-by: Michael Rosenberg <michael@mrosenberg.pub>
This commit is contained in:
parent
6d9bbd323e
commit
e1d4ef313e
2 changed files with 57 additions and 7 deletions
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
use core::convert::TryFrom;
|
||||
use core::fmt::Debug;
|
||||
use core::hash::{Hash, Hasher};
|
||||
|
||||
use curve25519_dalek::digest::generic_array::typenum::U64;
|
||||
use curve25519_dalek::digest::Digest;
|
||||
|
|
@ -38,8 +39,19 @@ use crate::signature::*;
|
|||
use crate::signing::*;
|
||||
|
||||
/// An ed25519 public key.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// The `Eq` and `Hash` impls here use the compressed Edwards y encoding, _not_ the algebraic
|
||||
/// representation. This means if this `VerifyingKey` is non-canonically encoded, it will be
|
||||
/// considered unequal to the other equivalent encoding, despite the two representing the same
|
||||
/// point. More encoding details can be found
|
||||
/// [here](https://hdevalence.ca/blog/2020-10-04-its-25519am).
|
||||
///
|
||||
/// If you don't care and/or don't want to deal with this, just make sure to use the
|
||||
/// [`VerifyingKey::verify_strict`] function.
|
||||
// Invariant: VerifyingKey.1 is always the decompression of VerifyingKey.0
|
||||
#[derive(Copy, Clone, Default, Eq, PartialEq)]
|
||||
#[derive(Copy, Clone, Default, Eq)]
|
||||
pub struct VerifyingKey(pub(crate) CompressedEdwardsY, pub(crate) EdwardsPoint);
|
||||
|
||||
impl Debug for VerifyingKey {
|
||||
|
|
@ -54,6 +66,18 @@ impl AsRef<[u8]> for VerifyingKey {
|
|||
}
|
||||
}
|
||||
|
||||
impl Hash for VerifyingKey {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.as_bytes().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<VerifyingKey> for VerifyingKey {
|
||||
fn eq(&self, other: &VerifyingKey) -> bool {
|
||||
self.as_bytes() == other.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ExpandedSecretKey> for VerifyingKey {
|
||||
/// Derive this public key from its corresponding `ExpandedSecretKey`.
|
||||
fn from(expanded_secret_key: &ExpandedSecretKey) -> VerifyingKey {
|
||||
|
|
@ -114,7 +138,7 @@ impl VerifyingKey {
|
|||
/// # Returns
|
||||
///
|
||||
/// A `Result` whose okay value is an EdDSA `VerifyingKey` or whose error value
|
||||
/// is an `SignatureError` describing the error that occurred.
|
||||
/// is a `SignatureError` describing the error that occurred.
|
||||
#[inline]
|
||||
pub fn from_bytes(bytes: &[u8; PUBLIC_KEY_LENGTH]) -> Result<VerifyingKey, SignatureError> {
|
||||
let compressed = CompressedEdwardsY(*bytes);
|
||||
|
|
@ -176,14 +200,12 @@ impl VerifyingKey {
|
|||
/// * `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`.
|
||||
/// * `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`.
|
||||
///
|
||||
/// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1
|
||||
#[allow(non_snake_case)]
|
||||
pub fn verify_prehashed<D>(
|
||||
&self,
|
||||
|
|
@ -229,7 +251,7 @@ impl VerifyingKey {
|
|||
/// 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:
|
||||
/// 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
|
||||
|
|
@ -322,7 +344,7 @@ impl VerifyingKey {
|
|||
/// * `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`.
|
||||
/// * `signature` is a purported Ed25519ph signature on the `prehashed_message`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
|
|
|
|||
|
|
@ -283,6 +283,7 @@ mod integrations {
|
|||
use super::*;
|
||||
use rand::rngs::OsRng;
|
||||
use sha2::Sha512;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[test]
|
||||
fn sign_verify() {
|
||||
|
|
@ -427,6 +428,33 @@ mod integrations {
|
|||
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn public_key_hash_trait_check() {
|
||||
let mut csprng = OsRng {};
|
||||
let secret: SigningKey = SigningKey::generate(&mut csprng);
|
||||
let public_from_secret: VerifyingKey = (&secret).into();
|
||||
|
||||
let mut m = HashMap::new();
|
||||
m.insert(public_from_secret, "Example_Public_Key");
|
||||
|
||||
m.insert(public_from_secret, "Updated Value");
|
||||
|
||||
let (k, v) = m.get_key_value(&public_from_secret).unwrap();
|
||||
assert_eq!(k, &public_from_secret);
|
||||
assert_eq!(v.clone(), "Updated Value");
|
||||
assert_eq!(m.len(), 1usize);
|
||||
|
||||
let second_secret: SigningKey = SigningKey::generate(&mut csprng);
|
||||
let public_from_second_secret: VerifyingKey = (&second_secret).into();
|
||||
assert_ne!(public_from_secret, public_from_second_secret);
|
||||
m.insert(public_from_second_secret, "Second public key");
|
||||
|
||||
let (k, v) = m.get_key_value(&public_from_second_secret).unwrap();
|
||||
assert_eq!(k, &public_from_second_secret);
|
||||
assert_eq!(v.clone(), "Second public key");
|
||||
assert_eq!(m.len(), 2usize);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "serde"))]
|
||||
|
|
|
|||
Loading…
Reference in a new issue