use rand::rng;
use ssh_encoding::Decode;
use ssh_key::public::KeyData;
use ssh_key::{Algorithm, EcdsaCurve, PublicKey};
use crate::keys::Error;
pub trait PublicKeyExt {
fn decode(bytes: &[u8]) -> Result<PublicKey, Error>;
}
impl PublicKeyExt for PublicKey {
fn decode(mut bytes: &[u8]) -> Result<PublicKey, Error> {
check_cert_nesting(bytes)?;
let key = KeyData::decode(&mut bytes)?;
Ok(PublicKey::new(key, ""))
}
}
#[doc(hidden)]
pub trait Verify {
fn verify_client_auth(&self, buffer: &[u8], sig: &[u8]) -> bool;
fn verify_server_auth(&self, buffer: &[u8], sig: &[u8]) -> bool;
}
const MAX_CERT_NESTING: usize = 8;
pub(crate) fn check_cert_nesting(blob: &[u8]) -> Result<(), ssh_key::Error> {
const MARKER: &[u8] = b"-cert-v01@";
if blob.windows(MARKER.len()).filter(|w| *w == MARKER).count() > MAX_CERT_NESTING {
return Err(ssh_key::Error::CertificateFieldInvalid(
ssh_key::certificate::Field::SignatureKey,
));
}
Ok(())
}
pub fn parse_public_key(mut p: &[u8]) -> Result<PublicKey, Error> {
check_cert_nesting(p)?;
Ok(ssh_key::public::KeyData::decode(&mut p)?.into())
}
pub fn safe_rng() -> impl rand::CryptoRng {
rng()
}
mod private_key_with_hash_alg {
use std::ops::Deref;
use std::sync::Arc;
use ssh_key::Algorithm;
use crate::helpers::AlgorithmExt;
#[derive(Clone, Debug)]
pub struct PrivateKeyWithHashAlg {
key: Arc<crate::keys::PrivateKey>,
hash_alg: Option<crate::keys::HashAlg>,
}
impl PrivateKeyWithHashAlg {
pub fn new(
key: Arc<crate::keys::PrivateKey>,
mut hash_alg: Option<crate::keys::HashAlg>,
) -> Self {
if !key.algorithm().is_rsa() {
hash_alg = None;
}
Self { key, hash_alg }
}
pub fn algorithm(&self) -> Algorithm {
self.key.algorithm().with_hash_alg(self.hash_alg)
}
pub fn hash_alg(&self) -> Option<crate::keys::HashAlg> {
self.hash_alg
}
}
impl Deref for PrivateKeyWithHashAlg {
type Target = crate::keys::PrivateKey;
fn deref(&self) -> &Self::Target {
&self.key
}
}
}
pub use private_key_with_hash_alg::PrivateKeyWithHashAlg;
pub const ALL_KEY_TYPES: &[Algorithm] = &[
Algorithm::Dsa,
Algorithm::Ecdsa {
curve: EcdsaCurve::NistP256,
},
Algorithm::Ecdsa {
curve: EcdsaCurve::NistP384,
},
Algorithm::Ecdsa {
curve: EcdsaCurve::NistP521,
},
Algorithm::Ed25519,
#[cfg(feature = "rsa")]
Algorithm::Rsa { hash: None },
#[cfg(feature = "rsa")]
Algorithm::Rsa {
hash: Some(ssh_key::HashAlg::Sha256),
},
#[cfg(feature = "rsa")]
Algorithm::Rsa {
hash: Some(ssh_key::HashAlg::Sha512),
},
Algorithm::SkEcdsaSha2NistP256,
Algorithm::SkEd25519,
];
#[cfg(test)]
mod test {
fn s(v: &mut Vec<u8>, b: &[u8]) {
v.extend((b.len() as u32).to_be_bytes());
v.extend(b);
}
fn nested_cert(depth: usize) -> Vec<u8> {
let mut inner = Vec::new();
s(&mut inner, b"a@b");
s(&mut inner, b"");
for _ in 0..depth {
let mut v = Vec::new();
s(&mut v, b"a-cert-v01@x");
s(&mut v, b""); s(&mut v, b""); v.extend(0u64.to_be_bytes()); v.extend(1u32.to_be_bytes()); s(&mut v, b""); s(&mut v, b""); v.extend(0u64.to_be_bytes());
v.extend(u64::MAX.to_be_bytes());
s(&mut v, b""); s(&mut v, b""); s(&mut v, b""); s(&mut v, &inner); let mut sig = Vec::new();
s(&mut sig, b"a@b");
s(&mut sig, b"x");
s(&mut v, &sig);
inner = v;
}
inner
}
#[test]
fn deeply_nested_certificate_is_rejected() {
assert!(super::parse_public_key(&nested_cert(2)).is_ok());
let deep = nested_cert(2848);
assert!(super::parse_public_key(&deep).is_err());
assert!(crate::cert::PublicKeyOrCertificate::decode("ssh-ed25519", &deep).is_err());
assert!(
crate::cert::PublicKeyOrCertificate::decode("ssh-ed25519-cert-v01@openssh.com", &deep)
.is_err()
);
}
}