1use crate::jose::create_signed_jwt;
2use crate::jose::jws::RegisteredHeader;
3use crate::jose::jwt::Claims;
4use jacquard_common::{CowStr, IntoStatic};
5use jose_jwa::{Algorithm, Signing};
6use jose_jwk::{Class, EcCurves, crypto};
7use jose_jwk::{Jwk, JwkSet, Key};
8use std::collections::HashSet;
9use thiserror::Error;
10
11#[derive(Error, Debug)]
12pub enum Error {
13 #[error("duplicate kid: {0}")]
14 DuplicateKid(String),
15 #[error("keys must not be empty")]
16 EmptyKeys,
17 #[error("key must have a `kid`")]
18 EmptyKid,
19 #[error("no signing key found for algorithms: {0:?}")]
20 NotFound(Vec<CowStr<'static>>),
21 #[error("key for signing must be a secret key")]
22 PublicKey,
23 #[error("crypto error: {0:?}")]
24 JwkCrypto(crypto::Error),
25 #[error(transparent)]
26 SerdeJson(#[from] serde_json::Error),
27}
28
29pub type Result<T> = core::result::Result<T, Error>;
30
31#[derive(Clone, Debug, Default, PartialEq, Eq)]
32pub struct Keyset(Vec<Jwk>);
33
34impl Keyset {
35 const PREFERRED_SIGNING_ALGORITHMS: [&'static str; 9] = [
36 "EdDSA", "ES256K", "ES256", "PS256", "PS384", "PS512", "HS256", "HS384", "HS512",
37 ];
38 pub fn public_jwks(&self) -> JwkSet {
39 let mut keys = Vec::with_capacity(self.0.len());
40 for mut key in self.0.clone() {
41 match key.key {
42 Key::Ec(ref mut ec) => {
43 ec.d = None;
44 }
45 _ => unimplemented!(),
46 }
47 keys.push(key);
48 }
49 JwkSet { keys }
50 }
51 pub fn create_jwt(&self, algs: &[CowStr], claims: Claims) -> Result<CowStr<'static>> {
52 let Some(jwk) = self.find_key(algs, Class::Signing) else {
53 return Err(Error::NotFound(algs.to_vec().into_static()));
54 };
55 self.create_jwt_with_key(jwk, claims)
56 }
57 fn find_key(&self, algs: &[CowStr], cls: Class) -> Option<&Jwk> {
58 let candidates = self
59 .0
60 .iter()
61 .filter_map(|key| {
62 if key.prm.cls.is_some_and(|c| c != cls) {
63 return None;
64 }
65 let alg = match &key.key {
66 Key::Ec(ec) => match ec.crv {
67 EcCurves::P256 => "ES256",
68 _ => unimplemented!(),
69 },
70 _ => unimplemented!(),
71 };
72 Some((alg, key)).filter(|(alg, _)| algs.contains(&CowStr::Borrowed(&alg)))
73 })
74 .collect::<Vec<_>>();
75 for pref_alg in Self::PREFERRED_SIGNING_ALGORITHMS {
76 for (alg, key) in &candidates {
77 if alg == &pref_alg {
78 return Some(key);
79 }
80 }
81 }
82 None
83 }
84 fn create_jwt_with_key(&self, key: &Jwk, claims: Claims) -> Result<CowStr<'static>> {
85 let kid = key.prm.kid.clone().unwrap();
86 match crypto::Key::try_from(&key.key).map_err(Error::JwkCrypto)? {
87 crypto::Key::P256(crypto::Kind::Secret(secret_key)) => {
88 let mut header = RegisteredHeader::from(Algorithm::Signing(Signing::Es256));
89 header.kid = Some(kid.into());
90 Ok(create_signed_jwt(secret_key.into(), header.into(), claims)?)
91 }
92 _ => unimplemented!(),
93 }
94 }
95}
96
97impl TryFrom<Vec<Jwk>> for Keyset {
98 type Error = Error;
99
100 fn try_from(keys: Vec<Jwk>) -> Result<Self> {
101 if keys.is_empty() {
102 return Err(Error::EmptyKeys);
103 }
104 let mut v = Vec::with_capacity(keys.len());
105 let mut hs = HashSet::with_capacity(keys.len());
106 for key in keys {
107 if let Some(kid) = key.prm.kid.clone() {
108 if hs.contains(&kid) {
109 return Err(Error::DuplicateKid(kid));
110 }
111 hs.insert(kid);
112 // ensure that the key is a secret key
113 if match crypto::Key::try_from(&key.key).map_err(Error::JwkCrypto)? {
114 crypto::Key::P256(crypto::Kind::Public(_)) => true,
115 crypto::Key::P256(crypto::Kind::Secret(_)) => false,
116 _ => unimplemented!(),
117 } {
118 return Err(Error::PublicKey);
119 }
120 v.push(key);
121 } else {
122 return Err(Error::EmptyKid);
123 }
124 }
125 Ok(Self(v))
126 }
127}