use alloc::boxed::Box;
use alloc::vec::Vec;
use super::HostKey;
use crate::cert::Certificate;
use crate::error::{Error, Result};
pub struct CertHostKey {
inner: Box<dyn HostKey + Send + Sync>,
cert_blob: Vec<u8>,
cert_algo: &'static str,
}
impl CertHostKey {
pub fn new(
inner: Box<dyn HostKey + Send + Sync>,
cert: &Certificate,
cert_algo: &'static str,
) -> Result<Self> {
if inner.public_blob() != cert.embedded_pubkey_blob {
return Err(Error::Config(
"certificate embedded key does not match the host/identity key",
));
}
Ok(CertHostKey {
inner,
cert_blob: cert.raw.clone(),
cert_algo,
})
}
pub fn inner(&self) -> &(dyn HostKey + Send + Sync) {
self.inner.as_ref()
}
}
impl HostKey for CertHostKey {
fn algorithm(&self) -> &'static str {
self.cert_algo
}
fn public_blob(&self) -> Vec<u8> {
self.cert_blob.clone()
}
fn sign(&self, msg: &[u8]) -> Result<Vec<u8>> {
self.inner.sign(msg)
}
}
#[cfg(all(test, feature = "alloc"))]
mod tests {
use super::*;
use crate::hostkey::Ed25519HostKey;
fn load(name: &str) -> Vec<u8> {
let path = format!(
"{}/tests/fixtures/cert/{}",
env!("CARGO_MANIFEST_DIR"),
name
);
let text = std::fs::read_to_string(path).unwrap();
let b64 = text.split_whitespace().nth(1).unwrap();
crate::key::base64::decode(b64.as_bytes()).unwrap()
}
#[test]
fn mismatched_key_rejected() {
let cert = Certificate::parse(&load("h_ed25519-cert.pub")).unwrap();
let wrong = Box::new(Ed25519HostKey::from_seed([7u8; 32]));
assert!(CertHostKey::new(wrong, &cert, "ssh-ed25519-cert-v01@openssh.com").is_err());
}
#[test]
fn matched_key_presents_cert_blob() {
let cert = Certificate::parse(&load("h_ed25519-cert.pub")).unwrap();
let direct = Box::new(Ed25519HostKey::from_seed(seed_of("h_ed25519")));
let ch = CertHostKey::new(direct, &cert, "ssh-ed25519-cert-v01@openssh.com").unwrap();
assert_eq!(ch.algorithm(), "ssh-ed25519-cert-v01@openssh.com");
assert_eq!(ch.public_blob(), cert.raw);
let sig = ch.sign(b"H").unwrap();
let verifier = cert.embedded_verifier(&sig).unwrap();
verifier.verify(b"H", &sig).unwrap();
}
fn seed_of(name: &str) -> [u8; 32] {
let pem = std::fs::read_to_string(format!(
"{}/tests/fixtures/cert/{}",
env!("CARGO_MANIFEST_DIR"),
name
))
.unwrap();
let sk = crate::key::PrivateKey::parse_openssh_pem(&pem, None).unwrap();
match sk {
crate::key::PrivateKey::Ed25519 { seed, .. } => seed,
_ => panic!("not ed25519"),
}
}
}