attenuable_jwt/protocol.rs
1//! This module provides types and traits library users need for signing and verifying attenuable JWTs.
2
3use erased_serde::Serialize as ErasedSerialize;
4use serde::{de::DeserializeOwned, Deserialize, Serialize};
5
6/// Newtype struct for a string representing a signed JWT.
7#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
8#[serde(transparent)]
9pub struct SignedJWT(pub String);
10
11impl AsRef<str> for SignedJWT {
12 fn as_ref(&self) -> &str {
13 &self.0
14 }
15}
16
17/// Claims for a sealed attenuated JWT.
18/// These are the claims of the JWT produced by [crate::sign::AttenuableJWT::seal].
19#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
20pub(crate) struct SealedClaims {
21 /// Expiration.
22 pub exp: Option<SecondsSinceEpoch>,
23 /// Not before.
24 pub nbf: Option<SecondsSinceEpoch>,
25 /// Issuer.
26 pub iss: Option<Issuer>,
27 /// Audience.
28 pub aud: Option<Audience>,
29 /// Inner JWTs, starting with the root JWT and ending with the most-attenuated JWT.
30 pub jwts: Vec<SignedJWT>,
31}
32
33/// Newtype wrapper for the number of seconds elapsed since the unix epoch.
34/// Used in the `exp` and `nbf` claims of JWTs.
35#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
36#[serde(transparent)]
37pub struct SecondsSinceEpoch(pub u64);
38
39/// Newtype wrapper for issuer identifiers.
40/// Used in the `iss` claim of JWTs.
41#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
42#[serde(transparent)]
43pub struct Issuer(pub String);
44
45impl AsRef<str> for Issuer {
46 fn as_ref(&self) -> &str {
47 &self.0
48 }
49}
50
51/// Newtype wrapper for audience identifiers.
52/// Used in the `aud` claim of JWTs.
53#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
54#[serde(transparent)]
55pub struct Audience(pub String);
56
57impl AsRef<str> for Audience {
58 fn as_ref(&self) -> &str {
59 &self.0
60 }
61}
62
63/// A private key. The [ErasedSerialize] implementation must serialize to a JWK.
64pub trait PrivateKey: ErasedSerialize {
65 /// Key ID.
66 fn key_id(&self) -> &str;
67 /// Algorithm.
68 fn algorithm(&self) -> &str;
69 /// Sign the message.
70 fn sign(&self, message: &[u8]) -> crate::sign::Result<Vec<u8>>;
71}
72
73/// A public key. The [ErasedSerialize] implementation must serialize to a JWK.
74pub trait PublicKey: ErasedSerialize {
75 /// Key ID.
76 fn key_id(&self) -> &str;
77 /// Algorithm.
78 fn algorithm(&self) -> &str;
79 /// Intended use for the key.
80 fn key_use(&self) -> KeyUse;
81 /// Verify the signature of the message.
82 fn verify(&self, message: &[u8], signature: &[u8]) -> bool;
83}
84
85erased_serde::serialize_trait_object!(PublicKey);
86
87/// Key use identifiers.
88/// Used in the `use` claim of a JWK.
89#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
90pub enum KeyUse {
91 /// Encryption.
92 #[serde(rename = "enc")]
93 Encryption,
94 /// Signing.
95 #[serde(rename = "sig")]
96 Signing,
97}
98
99/// Trait containing all client-supplied information needed for verify a sealed [crate::sign::AttenuableJWT].
100pub trait VerificationKeyManager: Clone {
101 /// Type of the public key for the root JWT.
102 /// The root JWT may be signed by a different algorithm with a different type of key than the attenuated JWTs added to it.
103 /// For example, the root JWT may be signed with a secret key, whereas only asymmetric keys are suitable for attenuated JWTs.
104 type PublicRootKey: PublicKey;
105 /// Type of the public key for attenuated JWTs.
106 /// IMPORTANT: THIS MUST BE AN ASYMMETRIC KEY.
107 /// This is the public key counterpart to the [Self::PrivateAttenuationKey].
108 type PublicAttenuationKey: PublicKey;
109 /// Type of the private key for attenuated JWTs.
110 /// IMPORTANT: THIS MUST BE AN ASYMMETRIC KEY.
111 /// This is the private key counterpart to the [Self::PublicAttenuationKey].
112 type PrivateAttenuationKey: PrivateKey;
113 /// Type of the client-supplied attenuated claims.
114 /// Any type that is serializable to/from a JSON object is suitable.
115 type Claims: Serialize + DeserializeOwned;
116 /// Type of the JWK that represents a [Self::PublicAttenuationKey].
117 type JWK: Serialize + DeserializeOwned;
118
119 /// Given a `key_id` if it is present in the JWT header, return the corresponding [Self::PublicRootKey].
120 fn get_root_key(&self, key_id: &Option<String>) -> Option<Self::PublicRootKey>;
121 /// The [VerificationRequirements] to use for verifying the sealed JWT envelope.
122 fn get_envelope_verification_requirements(&self) -> VerificationRequirements;
123 /// [crate::verify::verify] performs a fold over existing and new claims for each JWT in the chain, invoking the client-provided `resolve_claims` function with the existing and new claims.
124 /// The `default_claims` are used as the initial value in that fold.
125 fn default_claims(&self) -> Self::Claims;
126 /// Given a [Self::JWK], return a [Self::PublicAttenuationKey].
127
128 // Convert a [Self::JWK] into a [Self::PublicAttenuationKey].
129 fn jwk_to_public_attenuation_key(&self, jwk: &Self::JWK) -> Option<Self::PublicAttenuationKey>;
130}
131
132/// JWT header.
133#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
134pub(crate) struct JWTHeader {
135 /// Key ID for the key used to sign this JWT.
136 #[serde(rename = "kid")]
137 pub key_id: Option<String>,
138 /// Algorithm used to sign this JWT.
139 #[serde(rename = "alg")]
140 pub algorithm: String,
141}
142
143/// Verification requirements to use when verifying a signed JWT.
144#[derive(Clone, Debug, PartialEq, Eq)]
145pub enum VerificationRequirements {
146 /// Verify the signature and provided claims for the JWT.
147 VerifyClaims {
148 /// Acceptable signing algorithms.
149 acceptable_algorithms: Vec<String>,
150 /// Acceptable issuers. None indicates any issuer is acceptable.
151 acceptable_issuers: Option<Vec<Issuer>>,
152 /// Acceptable audiences. None indicates any audience is acceptable.
153 acceptable_audiences: Option<Vec<Audience>>,
154 /// Acceptable subject. None indicates any subject is acceptable.
155 acceptable_subject: Option<String>,
156 },
157 /// Only verify the signature of the JWT but do not verify any claims.
158 VerifySignatureOnly {
159 /// Acceptable signing algorithms.
160 acceptable_algorithms: Vec<String>,
161 },
162}
163
164impl VerificationRequirements {
165 /// Acceptable signing algorithms.
166 pub fn acceptable_algorithms(&self) -> &[String] {
167 match self {
168 VerificationRequirements::VerifyClaims {
169 acceptable_algorithms,
170 ..
171 } => acceptable_algorithms,
172 VerificationRequirements::VerifySignatureOnly {
173 acceptable_algorithms,
174 } => acceptable_algorithms,
175 }
176 }
177}
178
179/// Trait handling signing and key generation for [crate::sign::AttenuableJWT].
180pub trait SigningKeyManager:
181 AttenuationKeyGenerator<Self::PublicAttenuationKey, Self::PrivateAttenuationKey> + Clone
182{
183 /// Type of the JWK for the [Self::PublicAttenuationKey].
184 type JWK: Serialize;
185 /// Type of the public key for the attenuation keys.
186 type PublicAttenuationKey: PublicKey;
187 /// Type of the private key for the attenuation keys.
188 type PrivateAttenuationKey: PrivateKey;
189 /// Type of the private key for the root JWT.
190 type PrivateRootKey: PrivateKey;
191 /// Type to represent the claims of the JWT. Any type that serializes to a map is suitable.
192 type Claims: Serialize;
193
194 /// Return a JWK representing the provided public attenuation key.
195 fn jwk_for_public_attenuation_key(
196 public_attenuation_key: &Self::PublicAttenuationKey,
197 ) -> Self::JWK;
198}
199
200/// Trait for generating new attenuation keys.
201pub trait AttenuationKeyGenerator<
202 PublicAttenuationKey: PublicKey,
203 PrivateAttenuationKey: PrivateKey,
204>
205{
206 /// Generate a new, random attenuation key.
207 fn generate_attenuation_key(
208 &self,
209 ) -> Result<(PublicAttenuationKey, PrivateAttenuationKey), crate::sign::Error>;
210}
211
212/// The full set of claims for an attenuated JWT, combining user-provided claims with the attenuation key claim.
213#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
214pub(crate) struct FullClaims<JWK, Claims> {
215 /// User-provided claims.
216 #[serde(flatten)]
217 pub user_provided_claims: Claims,
218 /// Attenuation key claim containing a JWK representing the public key of the next attenuation key.
219 pub aky: JWK,
220}
221
222impl<JWK: Serialize, Claims: Serialize> FullClaims<JWK, Claims> {
223 /// Create a FullClaims.
224 pub fn new(user_provided_claims: Claims, aky: JWK) -> Self {
225 Self {
226 user_provided_claims,
227 aky,
228 }
229 }
230}