Skip to content

Commit f49fb89

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 f49fb89

5 files changed

Lines changed: 66 additions & 15 deletions

File tree

ssh-key/src/algorithm.rs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,10 @@ mod name;
66
use crate::{Error, Result};
77
use core::{fmt, str};
88
use encoding::{Label, LabelError};
9+
use sha2::{Digest, Sha256, Sha512};
910

1011
#[cfg(feature = "alloc")]
11-
use {
12-
alloc::{borrow::ToOwned, string::String, vec::Vec},
13-
sha2::{Digest, Sha256, Sha512},
14-
};
12+
use alloc::{borrow::ToOwned, string::String, vec::Vec};
1513

1614
#[cfg(feature = "alloc")]
1715
pub use name::AlgorithmName;
@@ -88,10 +86,7 @@ const SK_ECDSA_SHA2_P256: &str = "sk-ecdsa-sha2-nistp256@openssh.com";
8886
/// U2F/FIDO security key with Ed25519
8987
const SK_SSH_ED25519: &str = "sk-ssh-ed25519@openssh.com";
9088

91-
/// SSH key algorithms.
92-
///
93-
/// This type provides a registry of supported digital signature algorithms
94-
/// used for SSH keys.
89+
/// SSH key algorithms, i.e. digital signature algorithms used with SSH private/public keys.
9590
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
9691
#[non_exhaustive]
9792
pub enum Algorithm {
@@ -471,6 +466,20 @@ impl str::FromStr for HashAlg {
471466
}
472467
}
473468

469+
/// Associate an SSH [`HashAlg`] with the given type.
470+
pub trait AssociatedHashAlg: Digest {
471+
/// Algorithm identifier for this hash.
472+
const HASH_ALG: HashAlg;
473+
}
474+
475+
impl AssociatedHashAlg for Sha256 {
476+
const HASH_ALG: HashAlg = HashAlg::Sha256;
477+
}
478+
479+
impl AssociatedHashAlg for Sha512 {
480+
const HASH_ALG: HashAlg = HashAlg::Sha512;
481+
}
482+
474483
/// Key Derivation Function (KDF) algorithms.
475484
#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
476485
#[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: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ use subtle::{Choice, ConstantTimeEq};
150150

151151
#[cfg(feature = "alloc")]
152152
use {
153+
crate::AssociatedHashAlg,
153154
alloc::{string::String, vec::Vec},
154155
zeroize::Zeroizing,
155156
};
@@ -344,7 +345,17 @@ impl PrivateKey {
344345
SshSig::sign(self, namespace, hash_alg, msg)
345346
}
346347

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

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)