cratestack_cose/keys/
key_provider.rs1use cratestack_core::{CratestackError, KeyProvider};
13
14use super::hmac::{HmacSecret, HmacSigner, is_mac};
15use super::traits::CoseVerifierResolver;
16use super::verify_key::CoseVerifyKey;
17use crate::alg::CoseAlg;
18use crate::thumbprint::KID_LEN;
19
20#[derive(Debug, Clone)]
25pub struct KeyProviderMacKeys {
26 alg: CoseAlg,
27 entries: Vec<Entry>,
28}
29
30#[derive(Debug, Clone)]
31struct Entry {
32 name: String,
33 secret: HmacSecret,
34 key: CoseVerifyKey,
35}
36
37impl KeyProviderMacKeys {
38 pub async fn load<P, I, S>(
48 provider: &P,
49 alg: CoseAlg,
50 key_ids: I,
51 ) -> Result<Self, CratestackError>
52 where
53 P: KeyProvider + ?Sized,
54 I: IntoIterator<Item = S>,
55 S: Into<String>,
56 {
57 if !is_mac(alg) {
58 return Err(invalid("KeyProviderMacKeys needs an HMAC algorithm"));
59 }
60 let mut entries: Vec<Entry> = Vec::new();
61 for name in key_ids {
62 let name = name.into();
63 if entries.iter().any(|entry| entry.name == name) {
64 return Err(invalid(format!("key id {name:?} is listed twice")));
65 }
66 let secret =
67 HmacSecret::new(provider.resolve_signing_key(&name).await?).map_err(|_| {
68 invalid(format!("the secret for key id {name:?} is under 32 bytes"))
69 })?;
70 let key = CoseVerifyKey::from_hmac_secret(alg, secret.clone());
71 if entries
72 .iter()
73 .any(|entry| entry.key.thumbprint() == key.thumbprint())
74 {
75 return Err(invalid(format!(
76 "key id {name:?} resolves to the same secret as another id"
77 )));
78 }
79 entries.push(Entry { name, secret, key });
80 }
81 if entries.is_empty() {
82 return Err(invalid("KeyProviderMacKeys needs at least one key id"));
83 }
84 Ok(Self { alg, entries })
85 }
86
87 pub fn alg(&self) -> CoseAlg {
89 self.alg
90 }
91
92 pub fn kids(&self) -> impl Iterator<Item = (&str, [u8; KID_LEN])> {
95 self.entries
96 .iter()
97 .map(|entry| (entry.name.as_str(), entry.key.kid()))
98 }
99
100 pub fn signer(&self, key_id: &str) -> Option<HmacSigner> {
103 let entry = self.entries.iter().find(|entry| entry.name == key_id)?;
104 HmacSigner::from_secret(self.alg, entry.secret.clone()).ok()
105 }
106}
107
108fn invalid(message: impl Into<String>) -> CratestackError {
109 CratestackError::Validation(message.into())
110}
111
112#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
117#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
118impl CoseVerifierResolver for KeyProviderMacKeys {
119 async fn resolve(
120 &self,
121 kid: &[u8],
122 alg: CoseAlg,
123 ) -> Result<Vec<CoseVerifyKey>, CratestackError> {
124 Ok(self
125 .entries
126 .iter()
127 .filter(|entry| entry.key.kid().as_slice() == kid && entry.key.supports(alg))
128 .map(|entry| entry.key.clone())
129 .collect())
130 }
131}