pub const HASH_ALG: ssh_key::HashAlg = ssh_key::HashAlg::Sha512;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EventProblem {
Malformed(String),
WrongKey {
signed_by: String,
offered: String,
},
NotVerified,
}
impl std::fmt::Display for EventProblem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
EventProblem::Malformed(why) => {
write!(
f,
"the event's signature is not a well-formed sshsig: {why}"
)
}
EventProblem::WrongKey { signed_by, offered } => write!(
f,
"the event says it was signed by {signed_by} and was checked against {offered}"
),
EventProblem::NotVerified => write!(f, "the event's signature does not verify"),
}
}
}
pub fn fingerprint(key: &ssh_key::PublicKey) -> String {
key.fingerprint(ssh_key::HashAlg::Sha256).to_string()
}
pub fn sign(
key: &ssh_key::PrivateKey,
namespace: &str,
signing_bytes: &[u8],
) -> Result<(String, String), ssh_key::Error> {
let signature = key.sign(namespace, HASH_ALG, signing_bytes)?;
Ok((
fingerprint(key.public_key()),
signature.to_pem(ssh_key::LineEnding::LF)?,
))
}
pub fn verify(
public: &ssh_key::PublicKey,
namespace: &str,
signing_bytes: &[u8],
key_fingerprint: &str,
signature_pem: &str,
) -> Result<(), EventProblem> {
let offered = fingerprint(public);
if offered != key_fingerprint {
return Err(EventProblem::WrongKey {
signed_by: key_fingerprint.to_owned(),
offered,
});
}
let signature = ssh_key::SshSig::from_pem(signature_pem.as_bytes())
.map_err(|err| EventProblem::Malformed(err.to_string()))?;
if signature.namespace() != namespace {
return Err(EventProblem::Malformed(format!(
"namespace is {:?}, not {namespace:?}",
signature.namespace()
)));
}
public
.verify(namespace, signing_bytes, &signature)
.map_err(|_| EventProblem::NotVerified)
}