use std::fmt::Display;
use aws_lc_rs::digest::{SHA256, SHA256_OUTPUT_LEN};
use thiserror::Error;
use crate::{cached_string_repr::CachedStringRepr, hex::colon_string, keys::PublicKey};
#[derive(Debug, Error)]
#[error("invalid key ID length {0}, expected {SHA256_OUTPUT_LEN}")]
pub struct KeyIdParseError(usize);
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct KeyId {
digest: [u8; SHA256_OUTPUT_LEN],
rendered: CachedStringRepr,
}
impl KeyId {
pub fn as_hex_str(&self) -> &str {
self.rendered.get_or_init(|| colon_string(self.as_ref()))
}
pub fn as_bytes(&self) -> &[u8] {
&self.digest
}
}
impl std::ops::Deref for KeyId {
type Target = [u8; 32];
fn deref(&self) -> &Self::Target {
&self.digest
}
}
impl From<&PublicKey<'_>> for KeyId {
fn from(key: &PublicKey) -> Self {
let info = rcgen::PublicKeyData::subject_public_key_info(key);
Self {
digest: aws_lc_rs::digest::digest(&SHA256, &info)
.as_ref()
.try_into()
.expect("sha256 digest is 32 bytes"),
rendered: Default::default(),
}
}
}
impl TryFrom<&[u8]> for KeyId {
type Error = KeyIdParseError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
Ok(Self {
digest: value.try_into().map_err(|_| KeyIdParseError(value.len()))?,
rendered: Default::default(),
})
}
}
impl Display for KeyId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_hex_str())
}
}
#[cfg(test)]
mod tests {
use std::hash::{DefaultHasher, Hash, Hasher};
use crate::keys::{PrivateKey, tests::fixture_key};
use super::*;
#[test]
fn test_ski_fixture() {
const WANT: &[u8] = &[
242, 141, 210, 92, 111, 76, 250, 141, 48, 196, 108, 210, 4, 182, 182, 128, 17, 12, 24,
54, 159, 16, 208, 42, 122, 158, 205, 152, 190, 76, 82, 160,
];
let key = fixture_key();
let ski = KeyId::from(&key.public_key());
assert_eq!(*ski, WANT);
assert_eq!(
ski.to_string(),
"f2:8d:d2:5c:6f:4c:fa:8d:30:c4:6c:d2:04:b6:b6:80:11:0c:18:36:9f:10:d0:2a:7a:9e:cd:98:be:4c:52:a0"
);
}
#[test]
fn test_deterministic_ski() {
let key = PrivateKey::new();
let public = key.public_key();
let ski = KeyId::from(&public);
assert_eq!(ski, KeyId::from(&public));
}
#[test]
fn test_eq() {
let key = fixture_key();
let a = KeyId::from(&key.public_key());
let b = KeyId::from(&key.public_key());
assert_eq!(a, b);
let _ = b.to_string();
assert_eq!(a, b);
}
#[test]
fn test_hash() {
let key = fixture_key();
let a = KeyId::from(&key.public_key());
let b = KeyId::from(&key.public_key());
fn do_hash<T: Hash>(t: &T) -> u64 {
let mut s = DefaultHasher::new();
t.hash(&mut s);
s.finish()
}
assert_eq!(do_hash(&a), do_hash(&b));
let _ = b.to_string();
assert_eq!(do_hash(&a), do_hash(&b));
}
}