From 9ab4475b79b0aac2083cf21e6500cb732fe33536 Mon Sep 17 00:00:00 2001 From: Noa Resare Date: Wed, 2 Aug 2023 10:27:10 +0100 Subject: [PATCH] ssh-key: have PublicKey and its parts implment Hash This change derives the Hash trait for PublicKey and it's constitutent parts. Since Mpint implements its own PartialEq, an explicit Hash implmenetation delegating to the backing byte vector is added. A test verifying that a HashSet containing a public key can indeed be created and queried is also added. --- ssh-key/src/mpint.rs | 7 +++++++ ssh-key/src/public.rs | 2 +- ssh-key/src/public/dsa.rs | 2 +- ssh-key/src/public/ecdsa.rs | 2 +- ssh-key/src/public/key_data.rs | 2 +- ssh-key/src/public/opaque.rs | 4 ++-- ssh-key/src/public/rsa.rs | 2 +- ssh-key/src/public/sk.rs | 4 ++-- ssh-key/tests/public_key.rs | 8 ++++++++ 9 files changed, 24 insertions(+), 9 deletions(-) diff --git a/ssh-key/src/mpint.rs b/ssh-key/src/mpint.rs index f3b047c6..d632b44b 100644 --- a/ssh-key/src/mpint.rs +++ b/ssh-key/src/mpint.rs @@ -3,6 +3,7 @@ use crate::{Error, Result}; use alloc::vec::Vec; use core::fmt; +use core::hash::{Hash, Hasher}; use encoding::{CheckedSum, Decode, Encode, Reader, Writer}; use subtle::{Choice, ConstantTimeEq}; use zeroize::Zeroize; @@ -114,6 +115,12 @@ impl PartialEq for Mpint { } } +impl Hash for Mpint { + fn hash(&self, state: &mut H) { + self.inner.hash(state) + } +} + impl Decode for Mpint { type Error = Error; diff --git a/ssh-key/src/public.rs b/ssh-key/src/public.rs index 50d95821..28b4ee3f 100644 --- a/ssh-key/src/public.rs +++ b/ssh-key/src/public.rs @@ -78,7 +78,7 @@ use std::{fs, path::Path}; /// The serialization uses a binary encoding with binary formats like bincode /// and CBOR, and the OpenSSH string serialization when used with /// human-readable formats like JSON and TOML. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct PublicKey { /// Key data. pub(crate) key_data: KeyData, diff --git a/ssh-key/src/public/dsa.rs b/ssh-key/src/public/dsa.rs index fefe4883..b1c3bbbe 100644 --- a/ssh-key/src/public/dsa.rs +++ b/ssh-key/src/public/dsa.rs @@ -6,7 +6,7 @@ use encoding::{CheckedSum, Decode, Encode, Reader, Writer}; /// Digital Signature Algorithm (DSA) public key. /// /// Described in [FIPS 186-4 § 4.1](https://csrc.nist.gov/publications/detail/fips/186/4/final). -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct DsaPublicKey { /// Prime modulus. pub p: Mpint, diff --git a/ssh-key/src/public/ecdsa.rs b/ssh-key/src/public/ecdsa.rs index 889c7ec5..997274c7 100644 --- a/ssh-key/src/public/ecdsa.rs +++ b/ssh-key/src/public/ecdsa.rs @@ -20,7 +20,7 @@ pub type EcdsaNistP521PublicKey = sec1::EncodedPoint; /// `sec1` feature of this crate is enabled (which it is by default). /// /// Described in [FIPS 186-4](https://csrc.nist.gov/publications/detail/fips/186/4/final). -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub enum EcdsaPublicKey { /// NIST P-256 ECDSA public key. NistP256(EcdsaNistP256PublicKey), diff --git a/ssh-key/src/public/key_data.rs b/ssh-key/src/public/key_data.rs index 83b1510f..176dc99d 100644 --- a/ssh-key/src/public/key_data.rs +++ b/ssh-key/src/public/key_data.rs @@ -11,7 +11,7 @@ use super::{DsaPublicKey, OpaquePublicKey, RsaPublicKey}; use super::{EcdsaPublicKey, SkEcdsaSha2NistP256}; /// Public key data. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Hash, Ord, Eq, PartialEq, PartialOrd)] #[non_exhaustive] pub enum KeyData { /// Digital Signature Algorithm (DSA) public key data. diff --git a/ssh-key/src/public/opaque.rs b/ssh-key/src/public/opaque.rs index b0c64115..0af61b8f 100644 --- a/ssh-key/src/public/opaque.rs +++ b/ssh-key/src/public/opaque.rs @@ -16,7 +16,7 @@ use encoding::{Decode, Encode, Reader, Writer}; /// /// The encoded representation of an `OpaquePublicKey` is the encoded representation of its /// [`OpaquePublicKeyBytes`]. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct OpaquePublicKey { /// The [`Algorithm`] of this public key. pub algorithm: Algorithm, @@ -28,7 +28,7 @@ pub struct OpaquePublicKey { /// /// The encoded representation of an `OpaquePublicKeyBytes` consists of a 4-byte length prefix, /// followed by its byte representation. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct OpaquePublicKeyBytes(Vec); impl OpaquePublicKey { diff --git a/ssh-key/src/public/rsa.rs b/ssh-key/src/public/rsa.rs index 93935acc..e49d9c39 100644 --- a/ssh-key/src/public/rsa.rs +++ b/ssh-key/src/public/rsa.rs @@ -13,7 +13,7 @@ use { /// RSA public key. /// /// Described in [RFC4253 § 6.6](https://datatracker.ietf.org/doc/html/rfc4253#section-6.6). -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct RsaPublicKey { /// RSA public exponent. pub e: Mpint, diff --git a/ssh-key/src/public/sk.rs b/ssh-key/src/public/sk.rs index deb759bd..03ff5997 100644 --- a/ssh-key/src/public/sk.rs +++ b/ssh-key/src/public/sk.rs @@ -18,7 +18,7 @@ const DEFAULT_APPLICATION_STRING: &str = "ssh:"; /// Security Key (FIDO/U2F) ECDSA/NIST P-256 public key as specified in /// [PROTOCOL.u2f](https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.u2f?annotate=HEAD). #[cfg(feature = "ecdsa")] -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, Ord, Hash, PartialEq, PartialOrd)] pub struct SkEcdsaSha2NistP256 { /// Elliptic curve point representing a public key. ec_point: EcdsaNistP256PublicKey, @@ -112,7 +112,7 @@ impl From for EcdsaNistP256PublicKey { /// Security Key (FIDO/U2F) Ed25519 public key as specified in /// [PROTOCOL.u2f](https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.u2f?annotate=HEAD). -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] pub struct SkEd25519 { /// Ed25519 public key. public_key: Ed25519PublicKey, diff --git a/ssh-key/tests/public_key.rs b/ssh-key/tests/public_key.rs index 05e235f6..e536b825 100644 --- a/ssh-key/tests/public_key.rs +++ b/ssh-key/tests/public_key.rs @@ -2,6 +2,7 @@ use hex_literal::hex; use ssh_key::{Algorithm, PublicKey}; +use std::collections::HashSet; #[cfg(feature = "ecdsa")] use ssh_key::EcdsaCurve; @@ -378,3 +379,10 @@ fn encode_rsa_4096_openssh() { let key = PublicKey::from_openssh(OPENSSH_RSA_4096_EXAMPLE).unwrap(); assert_eq!(OPENSSH_RSA_4096_EXAMPLE.trim_end(), &key.to_string()); } + +#[test] +fn public_keys_are_hashable() { + let key = PublicKey::from_openssh(OPENSSH_ED25519_EXAMPLE).unwrap(); + let set = HashSet::from([&key]); + assert_eq!(true, set.contains(&key)); +}