Skip to content

Commit 04c97d4

Browse files
committed
ssh-key: add SshSig support for Digest
Similar to #384, but adds an `AssociatedHashAlg` trait, impls it for `sha2::{Sha256, Sha512}`, and adds the following methods which operate on a `Digest`, inferring the `HashAlg` to use from the type: - `PrivateKey::sign_digest` - `PublicKey::verify_digest` - `SshSig::sign_digest`
1 parent 0d5202d commit 04c97d4

5 files changed

Lines changed: 67 additions & 12 deletions

File tree

ssh-key/src/algorithm.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,7 @@ const SK_ECDSA_SHA2_P256: &str = "sk-ecdsa-sha2-nistp256@openssh.com";
8888
/// U2F/FIDO security key with Ed25519
8989
const SK_SSH_ED25519: &str = "sk-ssh-ed25519@openssh.com";
9090

91-
/// SSH key algorithms.
92-
///
93-
/// This type provides a registry of supported digital signature algorithms
94-
/// used for SSH keys.
91+
/// SSH key algorithms, i.e. digital signature algorithms used with SSH private/public keys.
9592
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
9693
#[non_exhaustive]
9794
pub enum Algorithm {
@@ -471,6 +468,20 @@ impl str::FromStr for HashAlg {
471468
}
472469
}
473470

471+
/// Associate an SSH [`HashAlg`] with the given type.
472+
pub trait AssociatedHashAlg: Digest {
473+
/// Algorithm identifier for this hash.
474+
const HASH_ALG: HashAlg;
475+
}
476+
477+
impl AssociatedHashAlg for Sha256 {
478+
const HASH_ALG: HashAlg = HashAlg::Sha256;
479+
}
480+
481+
impl AssociatedHashAlg for Sha512 {
482+
const HASH_ALG: HashAlg = HashAlg::Sha512;
483+
}
484+
474485
/// Key Derivation Function (KDF) algorithms.
475486
#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
476487
#[non_exhaustive]

ssh-key/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ mod signature;
168168
mod sshsig;
169169

170170
pub use crate::{
171-
algorithm::{Algorithm, EcdsaCurve, HashAlg, KdfAlg},
171+
algorithm::{Algorithm, AssociatedHashAlg, EcdsaCurve, HashAlg, KdfAlg},
172172
authorized_keys::AuthorizedKeys,
173173
error::{Error, Result},
174174
fingerprint::Fingerprint,

ssh-key/src/private.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,10 @@ pub use self::ecdsa::{EcdsaKeypair, EcdsaPrivateKey};
139139
#[cfg(all(feature = "alloc", feature = "ecdsa"))]
140140
pub use self::sk::SkEcdsaSha2NistP256;
141141

142-
use crate::{Algorithm, Cipher, Error, Fingerprint, HashAlg, Kdf, PublicKey, Result, public};
142+
use crate::{
143+
Algorithm, AssociatedHashAlg, Cipher, Error, Fingerprint, HashAlg, Kdf, PublicKey, Result,
144+
public,
145+
};
143146
use cipher::Tag;
144147
use core::str;
145148
use encoding::{
@@ -344,7 +347,17 @@ impl PrivateKey {
344347
SshSig::sign(self, namespace, hash_alg, msg)
345348
}
346349

347-
/// Sign the given message prehash using this private key, returning an [`SshSig`].
350+
/// Sign the given message [`Digest`] using this private key, returning an [`SshSig`].
351+
///
352+
/// These signatures can be produced using `ssh-keygen -Y sign`.
353+
///
354+
/// For more information, see [`PrivateKey::sign`].
355+
#[cfg(feature = "alloc")]
356+
pub fn sign_digest<D: AssociatedHashAlg>(&self, namespace: &str, digest: D) -> Result<SshSig> {
357+
SshSig::sign_digest(self, namespace, digest)
358+
}
359+
360+
/// Sign the given raw message prehash using this private key, returning an [`SshSig`].
348361
///
349362
/// These signatures can be produced using `ssh-keygen -Y sign`.
350363
///

ssh-key/src/public.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use encoding::{Base64Reader, Decode, Reader};
3535

3636
#[cfg(feature = "alloc")]
3737
use {
38-
crate::{Comment, SshSig},
38+
crate::{AssociatedHashAlg, Comment, SshSig},
3939
alloc::{
4040
borrow::ToOwned,
4141
string::{String, ToString},
@@ -177,8 +177,7 @@ impl PublicKey {
177177
Ok(self.key_data.encode_vec()?)
178178
}
179179

180-
/// Verify the [`SshSig`] signature over the given message using this
181-
/// public key.
180+
/// Verify the [`SshSig`] signature is valid the given message using this public key.
182181
///
183182
/// These signatures can be produced using `ssh-keygen -Y sign`. They're
184183
/// encoded as PEM and begin with the following:
@@ -241,7 +240,24 @@ impl PublicKey {
241240
)
242241
}
243242

244-
/// Verify the [`SshSig`] signature over the given prehashed message digest using this
243+
/// Verify the [`SshSig`] signature is valid the given message [`Digest`] using this public key.
244+
///
245+
/// See [`PublicKey::verify`] for more information.
246+
#[cfg(feature = "alloc")]
247+
pub fn verify_digest<D: AssociatedHashAlg>(
248+
&self,
249+
namespace: &str,
250+
digest: D,
251+
signature: &SshSig,
252+
) -> Result<()> {
253+
if D::HASH_ALG != signature.hash_alg() {
254+
return Err(Error::Crypto);
255+
}
256+
257+
self.verify_prehash(namespace, digest.finalize().as_slice(), signature)
258+
}
259+
260+
/// Verify the [`SshSig`] signature matches the given prehashed message digest using this
245261
/// public key.
246262
///
247263
/// See [`PublicKey::verify`] for more information.

ssh-key/src/sshsig.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
//! `sshsig` implementation.
22
3-
use crate::{Algorithm, Error, HashAlg, Result, Signature, SigningKey, public};
3+
use crate::{Algorithm, AssociatedHashAlg, Error, HashAlg, Result, Signature, SigningKey, public};
44
use alloc::{string::String, string::ToString, vec::Vec};
55
use core::str::FromStr;
66
use encoding::{
77
CheckedSum, Decode, DecodePem, Encode, EncodePem, Reader, Writer,
88
pem::{LineEnding, PemLabel},
99
};
10+
use sha2::Digest;
1011
use signature::Verifier;
1112

1213
#[cfg(doc)]
@@ -115,6 +116,20 @@ impl SshSig {
115116
)
116117
}
117118

119+
/// Sign the given message digest with the provided signing key.
120+
pub fn sign_digest<S: SigningKey, D: AssociatedHashAlg + Digest>(
121+
signing_key: &S,
122+
namespace: &str,
123+
digest: D,
124+
) -> Result<Self> {
125+
Self::sign_prehash(
126+
signing_key,
127+
namespace,
128+
D::HASH_ALG,
129+
digest.finalize().as_slice(),
130+
)
131+
}
132+
118133
/// Sign the given prehashed message digest with the provided signing key.
119134
pub fn sign_prehash<S: SigningKey>(
120135
signing_key: &S,

0 commit comments

Comments
 (0)