Skip to content

Commit

Permalink
ssh-key: have PublicKey and its parts implment Hash (#145)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nresare authored Aug 11, 2023
1 parent 5c6c897 commit acbcc7e
Show file tree
Hide file tree
Showing 9 changed files with 24 additions and 9 deletions.
7 changes: 7 additions & 0 deletions ssh-key/src/mpint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -114,6 +115,12 @@ impl PartialEq for Mpint {
}
}

impl Hash for Mpint {
fn hash<H: Hasher>(&self, state: &mut H) {
self.inner.hash(state)
}
}

impl Decode for Mpint {
type Error = Error;

Expand Down
2 changes: 1 addition & 1 deletion ssh-key/src/public.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion ssh-key/src/public/dsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion ssh-key/src/public/ecdsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub type EcdsaNistP521PublicKey = sec1::EncodedPoint<U66>;
/// `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),
Expand Down
2 changes: 1 addition & 1 deletion ssh-key/src/public/key_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions ssh-key/src/public/opaque.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<u8>);

impl OpaquePublicKey {
Expand Down
2 changes: 1 addition & 1 deletion ssh-key/src/public/rsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions ssh-key/src/public/sk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -112,7 +112,7 @@ impl From<SkEcdsaSha2NistP256> 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,
Expand Down
8 changes: 8 additions & 0 deletions ssh-key/tests/public_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use hex_literal::hex;
use ssh_key::{Algorithm, PublicKey};
use std::collections::HashSet;

#[cfg(feature = "ecdsa")]
use ssh_key::EcdsaCurve;
Expand Down Expand Up @@ -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));
}

0 comments on commit acbcc7e

Please sign in to comment.