Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core/identity: Implement Hash and Ord for PublicKey #2915

Merged
merged 12 commits into from
Sep 22, 2022
6 changes: 6 additions & 0 deletions core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# 0.36.1 - [unreleased]
futpib marked this conversation as resolved.
Show resolved Hide resolved

- Added `Hash` and `Ord` instances for `PublicKey`. See [PR 2915].
futpib marked this conversation as resolved.
Show resolved Hide resolved

[PR 2915]: https://github.com/libp2p/rust-libp2p/pull/2915

# 0.36.0

- Make RSA keypair support optional. To enable RSA support, `rsa` feature should be enabled.
Expand Down
54 changes: 53 additions & 1 deletion core/src/identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ impl zeroize::Zeroize for keys_proto::PrivateKey {
}

/// The public key of a node's identity keypair.
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum PublicKey {
/// A public Ed25519 key.
Ed25519(ed25519::PublicKey),
Expand Down Expand Up @@ -379,4 +379,56 @@ mod tests {

assert_eq!(expected_peer_id, peer_id);
}

#[test]
fn public_key_hash() {
futpib marked this conversation as resolved.
Show resolved Hide resolved
use std::collections::*;

let mut keypairs = Vec::new();

for _ in 0..8 {
keypairs.push(Keypair::generate_ed25519());
keypairs.push(Keypair::generate_secp256k1());
keypairs.push(Keypair::generate_ecdsa());
}

let public_keys: Vec<_> = keypairs
.iter()
.map(|k| k.public())
.collect();

let public_keys_hash_set: HashSet<PublicKey> = HashSet::from_iter(
public_keys
.iter()
.map(|p| p.clone())
);

assert_eq!(public_keys_hash_set.len(), public_keys.len());
}

#[test]
fn public_key_ord() {
use std::collections::*;

let mut keypairs = Vec::new();

for _ in 0..8 {
keypairs.push(Keypair::generate_ed25519());
keypairs.push(Keypair::generate_secp256k1());
keypairs.push(Keypair::generate_ecdsa());
}

let public_keys: Vec<_> = keypairs
.iter()
.map(|k| k.public())
.collect();

let public_keys_b_tree_set: BTreeSet<PublicKey> = BTreeSet::from_iter(
public_keys
.iter()
.map(|p| p.clone())
);

assert_eq!(public_keys_b_tree_set.len(), public_keys.len());
}
}
9 changes: 8 additions & 1 deletion core/src/identity/ecdsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

use super::error::DecodingError;
use core::fmt;
use core::hash;
use p256::{
ecdsa::{
signature::{Signer, Verifier},
Expand Down Expand Up @@ -117,7 +118,7 @@ impl fmt::Debug for SecretKey {
}

/// An ECDSA public key.
#[derive(Clone, PartialEq, Eq)]
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct PublicKey(VerifyingKey);

impl PublicKey {
Expand Down Expand Up @@ -222,6 +223,12 @@ impl fmt::Debug for PublicKey {
}
}

impl hash::Hash for PublicKey {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.to_bytes().hash(state);
}
}
futpib marked this conversation as resolved.
Show resolved Hide resolved

#[cfg(test)]
mod tests {
use super::*;
Expand Down
22 changes: 22 additions & 0 deletions core/src/identity/ed25519.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

use super::error::DecodingError;
use core::fmt;
use core::hash;
use core::cmp;
use ed25519_dalek::{self as ed25519, Signer as _, Verifier as _};
use rand::RngCore;
use std::convert::TryFrom;
Expand Down Expand Up @@ -126,6 +128,26 @@ impl fmt::Debug for PublicKey {
}
}

// Remove (derive instead) when this PR is merged:
// https://github.com/dalek-cryptography/ed25519-dalek/pull/176
impl hash::Hash for PublicKey {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.0.as_bytes().hash(state);
}
}
futpib marked this conversation as resolved.
Show resolved Hide resolved

impl cmp::PartialOrd for PublicKey {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
self.0.as_bytes().partial_cmp(other.0.as_bytes())
}
}

impl cmp::Ord for PublicKey {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.0.as_bytes().cmp(other.0.as_bytes())
}
}

impl PublicKey {
/// Verify the Ed25519 signature on a message using the public key.
pub fn verify(&self, msg: &[u8], sig: &[u8]) -> bool {
Expand Down
20 changes: 20 additions & 0 deletions core/src/identity/secp256k1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
use super::error::{DecodingError, SigningError};
use asn1_der::typed::{DerDecodable, Sequence};
use core::fmt;
use core::hash;
use core::cmp;
use libsecp256k1::{Message, Signature};
use sha2::{Digest as ShaDigestTrait, Sha256};
use zeroize::Zeroize;
Expand Down Expand Up @@ -163,6 +165,24 @@ impl fmt::Debug for PublicKey {
}
}

impl hash::Hash for PublicKey {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.encode().hash(state);
}
}
futpib marked this conversation as resolved.
Show resolved Hide resolved

impl cmp::PartialOrd for PublicKey {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
self.encode().partial_cmp(&other.encode())
}
}

impl cmp::Ord for PublicKey {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.encode().cmp(&other.encode())
}
}

impl PublicKey {
/// Verify the Secp256k1 signature on a message using the public key.
pub fn verify(&self, msg: &[u8], sig: &[u8]) -> bool {
Expand Down