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:
Linus Karl 2023-01-17 04:43:05 +01:00 committed by GitHub
parent 6d9bbd323e
commit e1d4ef313e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 57 additions and 7 deletions

View file

@ -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
///

View file

@ -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"))]