1use ct_codecs::{Base64UrlSafeNoPadding, Decoder, Encoder};
4use serde::{de::DeserializeOwned, Serialize};
5
6use crate::algorithms::jwe::content::{ContentEncryption, CEK};
7use crate::claims::*;
8use crate::common::{VerificationOptions, DEFAULT_MAX_TOKEN_LENGTH};
9use crate::error::*;
10use crate::jwe_header::JWEHeader;
11
12pub const MAX_JWE_HEADER_LENGTH: usize = 8192;
13
14pub const MAX_JWE_ENCRYPTED_KEY_LENGTH: usize = 2048;
17
18#[derive(Clone, Debug, Default)]
20pub struct EncryptionOptions {
21 pub content_encryption: ContentEncryption,
23 pub content_type: Option<String>,
25 pub key_id: Option<String>,
27}
28
29#[derive(Clone, Debug)]
31pub struct DecryptionOptions {
32 pub max_token_length: Option<usize>,
35 pub max_header_length: Option<usize>,
37 pub required_key_id: Option<String>,
39 pub claim_options: Option<VerificationOptions>,
41}
42
43impl Default for DecryptionOptions {
44 fn default() -> Self {
45 Self {
46 max_token_length: Some(DEFAULT_MAX_TOKEN_LENGTH),
47 max_header_length: None,
48 required_key_id: None,
49 claim_options: None,
50 }
51 }
52}
53
54#[derive(Debug, Clone)]
56pub struct JWETokenMetadata {
57 header: JWEHeader,
58}
59
60impl JWETokenMetadata {
61 pub fn algorithm(&self) -> &str {
63 &self.header.algorithm
64 }
65
66 pub fn encryption(&self) -> &str {
68 &self.header.encryption
69 }
70
71 pub fn key_id(&self) -> Option<&str> {
73 self.header.key_id.as_deref()
74 }
75
76 pub fn content_type(&self) -> Option<&str> {
78 self.header.content_type.as_deref()
79 }
80
81 pub fn header(&self) -> &JWEHeader {
83 &self.header
84 }
85}
86
87pub struct JWEToken;
89
90impl JWEToken {
91 pub fn build(
103 header: &JWEHeader,
104 encrypted_key: &[u8],
105 iv: &[u8],
106 ciphertext: &[u8],
107 tag: &[u8],
108 ) -> Result<String, Error> {
109 let header_json = serde_json::to_string(header)?;
110 let header_b64 = Base64UrlSafeNoPadding::encode_to_string(&header_json)?;
111 let encrypted_key_b64 = Base64UrlSafeNoPadding::encode_to_string(encrypted_key)?;
112 let iv_b64 = Base64UrlSafeNoPadding::encode_to_string(iv)?;
113 let ciphertext_b64 = Base64UrlSafeNoPadding::encode_to_string(ciphertext)?;
114 let tag_b64 = Base64UrlSafeNoPadding::encode_to_string(tag)?;
115
116 Ok(format!(
117 "{}.{}.{}.{}.{}",
118 header_b64, encrypted_key_b64, iv_b64, ciphertext_b64, tag_b64
119 ))
120 }
121
122 pub fn build_from_claims<KeyWrapFn, CustomClaims: Serialize>(
126 header: &JWEHeader,
127 claims: &JWTClaims<CustomClaims>,
128 content_encryption: ContentEncryption,
129 key_wrap_fn: KeyWrapFn,
130 ) -> Result<String, Error>
131 where
132 KeyWrapFn: FnOnce(&[u8]) -> Result<Vec<u8>, Error>,
133 {
134 let claims_json = serde_json::to_string(claims)?;
136 let plaintext = claims_json.as_bytes();
137
138 let cek = CEK::new(content_encryption.generate_cek());
140 let iv = content_encryption.generate_iv();
141
142 let encrypted_key = key_wrap_fn(cek.as_bytes())?;
144
145 let header_json = serde_json::to_string(header)?;
147 let header_b64 = Base64UrlSafeNoPadding::encode_to_string(&header_json)?;
148 let aad = header_b64.as_bytes();
149
150 let (ciphertext, tag) = content_encryption.encrypt(cek.as_bytes(), &iv, aad, plaintext)?;
152 drop(cek); let encrypted_key_b64 = Base64UrlSafeNoPadding::encode_to_string(&encrypted_key)?;
156 let iv_b64 = Base64UrlSafeNoPadding::encode_to_string(&iv)?;
157 let ciphertext_b64 = Base64UrlSafeNoPadding::encode_to_string(&ciphertext)?;
158 let tag_b64 = Base64UrlSafeNoPadding::encode_to_string(&tag)?;
159
160 Ok(format!(
161 "{}.{}.{}.{}.{}",
162 header_b64, encrypted_key_b64, iv_b64, ciphertext_b64, tag_b64
163 ))
164 }
165
166 pub fn decrypt<KeyUnwrapFn, CustomClaims: DeserializeOwned>(
171 expected_alg: &str,
172 token: &str,
173 options: Option<DecryptionOptions>,
174 key_unwrap_fn: KeyUnwrapFn,
175 ) -> Result<JWTClaims<CustomClaims>, Error>
176 where
177 KeyUnwrapFn: FnOnce(&JWEHeader, &[u8]) -> Result<Vec<u8>, Error>,
178 {
179 let options = options.unwrap_or_default();
180
181 if let Some(max_len) = options.max_token_length {
182 ensure!(token.len() <= max_len, JWTError::TokenTooLong);
183 }
184
185 let mut parts = token.split('.');
186 let header_b64 = parts.next().ok_or(JWTError::InvalidJWEFormat)?;
187 let encrypted_key_b64 = parts.next().ok_or(JWTError::InvalidJWEFormat)?;
188 let iv_b64 = parts.next().ok_or(JWTError::InvalidJWEFormat)?;
189 let ciphertext_b64 = parts.next().ok_or(JWTError::InvalidJWEFormat)?;
190 let tag_b64 = parts.next().ok_or(JWTError::InvalidJWEFormat)?;
191 ensure!(parts.next().is_none(), JWTError::InvalidJWEFormat);
192
193 let max_header_len = options.max_header_length.unwrap_or(MAX_JWE_HEADER_LENGTH);
194 ensure!(header_b64.len() <= max_header_len, JWTError::HeaderTooLarge);
195
196 let header_bytes = Base64UrlSafeNoPadding::decode_to_vec(header_b64, None)?;
197 let header: JWEHeader = serde_json::from_slice(&header_bytes)?;
198
199 if let Some(ref crit) = header.critical {
201 if !crit.is_empty() {
202 bail!(JWTError::UnknownCriticalExtension);
203 }
204 }
205
206 ensure!(
207 header.algorithm == expected_alg,
208 JWTError::AlgorithmMismatch
209 );
210
211 if let Some(required_key_id) = &options.required_key_id {
212 if let Some(key_id) = &header.key_id {
213 ensure!(key_id == required_key_id, JWTError::KeyIdentifierMismatch);
214 } else {
215 bail!(JWTError::MissingJWTKeyIdentifier);
216 }
217 }
218
219 let content_encryption = ContentEncryption::from_alg_name(&header.encryption)?;
220
221 ensure!(
224 encrypted_key_b64.len()
225 <= Base64UrlSafeNoPadding::encoded_len(MAX_JWE_ENCRYPTED_KEY_LENGTH)?,
226 JWTError::InvalidJWEFormat
227 );
228 ensure!(
229 iv_b64.len() == Base64UrlSafeNoPadding::encoded_len(content_encryption.iv_size())?,
230 JWTError::InvalidIV
231 );
232 ensure!(
233 tag_b64.len() == Base64UrlSafeNoPadding::encoded_len(content_encryption.tag_size())?,
234 JWTError::InvalidJWEAuthTag
235 );
236
237 let encrypted_key = Base64UrlSafeNoPadding::decode_to_vec(encrypted_key_b64, None)?;
239 let cek = CEK::new(key_unwrap_fn(&header, &encrypted_key)?);
240
241 let iv = Base64UrlSafeNoPadding::decode_to_vec(iv_b64, None)?;
242 let tag = Base64UrlSafeNoPadding::decode_to_vec(tag_b64, None)?;
243 let ciphertext = Base64UrlSafeNoPadding::decode_to_vec(ciphertext_b64, None)?;
244
245 let aad = header_b64.as_bytes();
247
248 let plaintext = content_encryption.decrypt(cek.as_bytes(), &iv, aad, &ciphertext, &tag)?;
249 drop(cek); let claims: JWTClaims<CustomClaims> = serde_json::from_slice(&plaintext)?;
252
253 claims.validate(&options.claim_options.unwrap_or_default())?;
254
255 Ok(claims)
256 }
257
258 pub fn decode_metadata(token: &str) -> Result<JWETokenMetadata, Error> {
263 let mut parts = token.split('.');
264 let header_b64 = parts.next().ok_or(JWTError::InvalidJWEFormat)?;
265
266 ensure!(
267 header_b64.len() <= MAX_JWE_HEADER_LENGTH,
268 JWTError::HeaderTooLarge
269 );
270
271 let header_bytes = Base64UrlSafeNoPadding::decode_to_vec(header_b64, None)?;
272 let header: JWEHeader = serde_json::from_slice(&header_bytes)?;
273
274 Ok(JWETokenMetadata { header })
275 }
276}
277
278#[test]
279fn decrypt_enforces_size_limits_before_doing_work() {
280 use crate::{prelude::*, JWTError};
281
282 const ENCRYPTED_KEY: usize = 1;
283 const IV: usize = 2;
284 const CIPHERTEXT: usize = 3;
285 const TAG: usize = 4;
286
287 let key = A256KWKey::generate();
288 let token = key
289 .encrypt(Claims::create(Duration::from_hours(1)))
290 .unwrap();
291 let segments: Vec<&str> = token.split('.').collect();
292
293 let error_for = |patches: &[(usize, &str)]| {
294 let mut segments = segments.clone();
295 for &(index, segment) in patches {
296 segments[index] = segment;
297 }
298 key.decrypt_token::<NoCustomClaims>(&segments.join("."), None)
299 .unwrap_err()
300 .downcast::<JWTError>()
301 .unwrap()
302 };
303
304 let oversized_key =
305 "A".repeat(Base64UrlSafeNoPadding::encoded_len(MAX_JWE_ENCRYPTED_KEY_LENGTH).unwrap() + 1);
306 assert!(matches!(
307 error_for(&[(ENCRYPTED_KEY, &oversized_key)]),
308 JWTError::InvalidJWEFormat
309 ));
310 assert!(matches!(
311 error_for(&[(IV, &format!("{}AAAA", segments[IV]))]),
312 JWTError::InvalidIV
313 ));
314 assert!(matches!(
315 error_for(&[(TAG, &format!("{}AAAA", segments[TAG]))]),
316 JWTError::InvalidJWEAuthTag
317 ));
318
319 let bogus_key = Base64UrlSafeNoPadding::encode_to_string([0u8; 40]).unwrap();
322 assert!(matches!(
323 error_for(&[(ENCRYPTED_KEY, &bogus_key), (CIPHERTEXT, &"!".repeat(1024))]),
324 JWTError::KeyUnwrapFailed
325 ));
326
327 let issuer = "i".repeat(1_100_000);
330 let large = key
331 .encrypt(Claims::create(Duration::from_hours(1)).with_issuer(&issuer))
332 .unwrap();
333 assert!(large.len() > DEFAULT_MAX_TOKEN_LENGTH);
334 assert!(matches!(
335 key.decrypt_token::<NoCustomClaims>(&large, None)
336 .unwrap_err()
337 .downcast::<JWTError>()
338 .unwrap(),
339 JWTError::TokenTooLong
340 ));
341
342 let options = DecryptionOptions {
343 max_token_length: None,
344 ..Default::default()
345 };
346 let claims = key
347 .decrypt_token::<NoCustomClaims>(&large, Some(options))
348 .unwrap();
349 assert_eq!(claims.issuer.unwrap(), issuer);
350}