1use std::fmt::Display;
16
17use aws_lc_rs::digest::{SHA256, SHA256_OUTPUT_LEN};
18use thiserror::Error;
19
20use crate::{cached_string_repr::CachedStringRepr, hex::colon_string, keys::PublicKey};
21
22#[derive(Debug, Error)]
24#[error("invalid key ID length {0}, expected {SHA256_OUTPUT_LEN}")]
25pub struct KeyIdParseError(usize);
26
27#[derive(Debug, PartialEq, Eq, Hash, Clone)]
38pub struct KeyId {
39 digest: [u8; SHA256_OUTPUT_LEN],
40
41 rendered: CachedStringRepr,
45}
46
47impl KeyId {
48 pub fn as_hex_str(&self) -> &str {
55 self.rendered.get_or_init(|| colon_string(self.as_ref()))
56 }
57
58 pub fn as_bytes(&self) -> &[u8] {
60 &self.digest
61 }
62}
63
64impl std::ops::Deref for KeyId {
65 type Target = [u8; 32];
66
67 fn deref(&self) -> &Self::Target {
68 &self.digest
69 }
70}
71
72impl From<&PublicKey<'_>> for KeyId {
73 fn from(key: &PublicKey) -> Self {
74 let info = rcgen::PublicKeyData::subject_public_key_info(key);
77
78 Self {
79 digest: aws_lc_rs::digest::digest(&SHA256, &info)
80 .as_ref()
81 .try_into()
82 .expect("sha256 digest is 32 bytes"),
83 rendered: Default::default(),
84 }
85 }
86}
87
88impl TryFrom<&[u8]> for KeyId {
89 type Error = KeyIdParseError;
90
91 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
92 Ok(Self {
93 digest: value.try_into().map_err(|_| KeyIdParseError(value.len()))?,
94 rendered: Default::default(),
95 })
96 }
97}
98
99impl Display for KeyId {
100 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101 f.write_str(self.as_hex_str())
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use std::hash::{DefaultHasher, Hash, Hasher};
108
109 use crate::keys::{PrivateKey, tests::fixture_key};
110
111 use super::*;
112
113 #[test]
116 fn test_ski_fixture() {
117 const WANT: &[u8] = &[
118 242, 141, 210, 92, 111, 76, 250, 141, 48, 196, 108, 210, 4, 182, 182, 128, 17, 12, 24,
119 54, 159, 16, 208, 42, 122, 158, 205, 152, 190, 76, 82, 160,
120 ];
121
122 let key = fixture_key();
123 let ski = KeyId::from(&key.public_key());
124
125 assert_eq!(*ski, WANT);
126 assert_eq!(
127 ski.to_string(),
128 "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"
129 );
130 }
131
132 #[test]
133 fn test_deterministic_ski() {
134 let key = PrivateKey::new();
135 let public = key.public_key();
136
137 let ski = KeyId::from(&public);
138 assert_eq!(ski, KeyId::from(&public));
139 }
140
141 #[test]
142 fn test_eq() {
143 let key = fixture_key();
144 let a = KeyId::from(&key.public_key());
145 let b = KeyId::from(&key.public_key());
146
147 assert_eq!(a, b);
148
149 let _ = b.to_string();
151 assert_eq!(a, b);
152 }
153
154 #[test]
155 fn test_hash() {
156 let key = fixture_key();
157 let a = KeyId::from(&key.public_key());
158 let b = KeyId::from(&key.public_key());
159
160 fn do_hash<T: Hash>(t: &T) -> u64 {
161 let mut s = DefaultHasher::new();
162 t.hash(&mut s);
163 s.finish()
164 }
165
166 assert_eq!(do_hash(&a), do_hash(&b));
167
168 let _ = b.to_string();
170 assert_eq!(do_hash(&a), do_hash(&b));
171 }
172}