1use anyhow::Result;
7use atproto_identity::key::{KeyData, KeyType, sign, to_public, validate};
8use base64::{Engine as _, engine::general_purpose};
9use elliptic_curve::JwkEcKey;
10use serde::{Deserialize, Serialize};
11use std::collections::BTreeMap;
12use std::time::{SystemTime, UNIX_EPOCH};
13
14use crate::encoding::ToBase64;
15use crate::errors::JWTError;
16
17#[cfg(feature = "zeroize")]
18use zeroize::{Zeroize, ZeroizeOnDrop};
19
20#[derive(Clone, Default, PartialEq, Serialize, Deserialize)]
22#[cfg_attr(debug_assertions, derive(Debug))]
23#[cfg_attr(feature = "zeroize", derive(Zeroize, ZeroizeOnDrop))]
24pub struct Header {
25 #[serde(rename = "alg", skip_serializing_if = "Option::is_none")]
27 pub algorithm: Option<String>,
28
29 #[serde(rename = "kid", skip_serializing_if = "Option::is_none")]
31 pub key_id: Option<String>,
32
33 #[serde(rename = "typ", skip_serializing_if = "Option::is_none")]
35 pub type_: Option<String>,
36
37 #[serde(rename = "jwk", skip_serializing_if = "Option::is_none")]
39 pub json_web_key: Option<JwkEcKey>,
40}
41
42impl TryFrom<KeyData> for Header {
43 type Error = anyhow::Error;
44
45 fn try_from(value: KeyData) -> std::result::Result<Self, Self::Error> {
46 let algorithm = match value.key_type() {
47 KeyType::P256Public => Some("ES256".to_string()),
48 KeyType::P256Private => Some("ES256".to_string()),
49 KeyType::P384Public => Some("ES384".to_string()),
50 KeyType::P384Private => Some("ES384".to_string()),
51 KeyType::K256Public => Some("ES256K".to_string()),
52 KeyType::K256Private => Some("ES256K".to_string()),
53 };
54
55 let public_key = to_public(&value)?;
56 let key_id = Some(public_key.to_string());
57
58 Ok(Self {
59 algorithm,
60 key_id,
61 type_: None,
62 json_web_key: None,
63 })
64 }
65}
66
67#[cfg_attr(debug_assertions, derive(Debug))]
69#[derive(Clone, Default, PartialEq, Serialize, Deserialize)]
70pub struct Claims {
71 #[serde(flatten)]
73 pub jose: JoseClaims,
74 #[serde(flatten)]
76 pub private: BTreeMap<String, serde_json::Value>,
77}
78
79impl Claims {
80 pub fn new(jose: JoseClaims) -> Self {
82 Claims {
83 jose,
84 private: BTreeMap::new(),
85 }
86 }
87}
88
89pub type SecondsSinceEpoch = u64;
91
92#[cfg_attr(debug_assertions, derive(Debug))]
94#[derive(Clone, Default, PartialEq, Serialize, Deserialize)]
95pub struct JoseClaims {
96 #[serde(rename = "iss", skip_serializing_if = "Option::is_none")]
98 pub issuer: Option<String>,
99
100 #[serde(rename = "sub", skip_serializing_if = "Option::is_none")]
102 pub subject: Option<String>,
103
104 #[serde(rename = "aud", skip_serializing_if = "Option::is_none")]
106 pub audience: Option<String>,
107
108 #[serde(rename = "exp", skip_serializing_if = "Option::is_none")]
110 pub expiration: Option<SecondsSinceEpoch>,
111
112 #[serde(rename = "nbf", skip_serializing_if = "Option::is_none")]
114 pub not_before: Option<SecondsSinceEpoch>,
115
116 #[serde(rename = "iat", skip_serializing_if = "Option::is_none")]
118 pub issued_at: Option<SecondsSinceEpoch>,
119
120 #[serde(rename = "jti", skip_serializing_if = "Option::is_none")]
122 pub json_web_token_id: Option<String>,
123
124 #[serde(rename = "htm", skip_serializing_if = "Option::is_none")]
126 pub http_method: Option<String>,
127
128 #[serde(rename = "htu", skip_serializing_if = "Option::is_none")]
130 pub http_uri: Option<String>,
131
132 #[serde(rename = "nonce", skip_serializing_if = "Option::is_none")]
134 pub nonce: Option<String>,
135
136 #[serde(rename = "ath", skip_serializing_if = "Option::is_none")]
138 pub auth: Option<String>,
139}
140
141pub fn mint(key_data: &KeyData, header: &Header, claims: &Claims) -> Result<String> {
143 let header = header.to_base64()?;
144 let claims = claims.to_base64()?;
145 let content = format!("{}.{}", header, claims);
146
147 let signature = sign(key_data, content.as_bytes())?;
148
149 Ok(format!(
150 "{}.{}",
151 content,
152 general_purpose::URL_SAFE_NO_PAD.encode(signature)
153 ))
154}
155
156pub fn verify(token: &str, key_data: &KeyData) -> Result<Claims> {
158 let parts: Vec<&str> = token.split('.').collect();
160 if parts.len() != 3 {
161 return Err(JWTError::InvalidFormat.into());
162 }
163
164 let encoded_header = parts[0];
165 let encoded_claims = parts[1];
166 let encoded_signature = parts[2];
167
168 let header_bytes = general_purpose::URL_SAFE_NO_PAD
170 .decode(encoded_header)
171 .map_err(|_| JWTError::InvalidHeader)?;
172
173 let header: Header =
174 serde_json::from_slice(&header_bytes).map_err(|_| JWTError::InvalidHeader)?;
175
176 match (header.algorithm.as_deref(), key_data.key_type()) {
177 (Some("ES256K"), KeyType::K256Private) | (Some("ES256K"), KeyType::K256Public) => {}
178 (Some("ES256"), KeyType::P256Private) | (Some("ES256"), KeyType::P256Public) => {}
179 (Some("ES384"), KeyType::P384Private) | (Some("ES384"), KeyType::P384Public) => {}
180 _ => {
181 return Err(JWTError::UnsupportedAlgorithm {
182 algorithm: header
183 .algorithm
184 .clone()
185 .unwrap_or_else(|| "none".to_string()),
186 key_type: format!("{}", key_data.key_type()),
187 }
188 .into());
189 }
190 }
191
192 let claims_bytes = general_purpose::URL_SAFE_NO_PAD
194 .decode(encoded_claims)
195 .map_err(|_| JWTError::InvalidClaims)?;
196
197 let claims: Claims =
198 serde_json::from_slice(&claims_bytes).map_err(|_| JWTError::InvalidClaims)?;
199
200 let signature_bytes = general_purpose::URL_SAFE_NO_PAD
202 .decode(encoded_signature)
203 .map_err(|_| JWTError::InvalidSignature)?;
204
205 let content = format!("{}.{}", encoded_header, encoded_claims);
206
207 validate(key_data, &signature_bytes, content.as_bytes())
208 .map_err(|_| JWTError::SignatureVerificationFailed)?;
209
210 let now = SystemTime::now()
212 .duration_since(UNIX_EPOCH)
213 .map_err(|_| JWTError::SystemTimeError)?
214 .as_secs();
215
216 if let Some(exp) = claims.jose.expiration
218 && now >= exp
219 {
220 return Err(JWTError::TokenExpired.into());
221 }
222
223 if let Some(nbf) = claims.jose.not_before
225 && now < nbf
226 {
227 return Err(JWTError::TokenNotValidYet.into());
228 }
229
230 Ok(claims)
232}
233
234#[cfg(test)]
235mod tests {
236 use super::*;
237 use atproto_identity::key::{KeyType, generate_key, identify_key, to_public};
238
239 #[test]
240 fn test_header_from_p256_private_key() -> Result<()> {
241 let key_data = generate_key(KeyType::P256Private)?;
242 let header: Header = key_data.clone().try_into()?;
243
244 assert_eq!(header.algorithm, Some("ES256".to_string()));
245 assert!(header.key_id.is_some());
246 assert_eq!(header.type_, None);
247 assert_eq!(header.json_web_key, None);
248
249 let public_key = to_public(&key_data)?;
251 let expected_key_id = format!("{}", public_key);
252 assert_eq!(header.key_id, Some(expected_key_id));
253
254 Ok(())
255 }
256
257 #[test]
258 fn test_header_from_p256_public_key() -> Result<()> {
259 let private_key = generate_key(KeyType::P256Private)?;
260 let public_key = to_public(&private_key)?;
261 let header: Header = public_key.clone().try_into()?;
262
263 assert_eq!(header.algorithm, Some("ES256".to_string()));
264 assert!(header.key_id.is_some());
265 assert_eq!(header.type_, None);
266 assert_eq!(header.json_web_key, None);
267
268 let expected_key_id = format!("{}", public_key);
270 assert_eq!(header.key_id, Some(expected_key_id));
271
272 Ok(())
273 }
274
275 #[test]
276 fn test_header_from_k256_private_key() -> Result<()> {
277 let key_data = generate_key(KeyType::K256Private)?;
278 let header: Header = key_data.clone().try_into()?;
279
280 assert_eq!(header.algorithm, Some("ES256K".to_string()));
281 assert!(header.key_id.is_some());
282 assert_eq!(header.type_, None);
283 assert_eq!(header.json_web_key, None);
284
285 let public_key = to_public(&key_data)?;
287 let expected_key_id = format!("{}", public_key);
288 assert_eq!(header.key_id, Some(expected_key_id));
289
290 Ok(())
291 }
292
293 #[test]
294 fn test_header_from_k256_public_key() -> Result<()> {
295 let private_key = generate_key(KeyType::K256Private)?;
296 let public_key = to_public(&private_key)?;
297 let header: Header = public_key.clone().try_into()?;
298
299 assert_eq!(header.algorithm, Some("ES256K".to_string()));
300 assert!(header.key_id.is_some());
301 assert_eq!(header.type_, None);
302 assert_eq!(header.json_web_key, None);
303
304 let expected_key_id = format!("{}", public_key);
306 assert_eq!(header.key_id, Some(expected_key_id));
307
308 Ok(())
309 }
310
311 #[test]
312 fn test_header_consistency_private_vs_public_key() -> Result<()> {
313 let p256_private = generate_key(KeyType::P256Private)?;
315 let p256_public = to_public(&p256_private)?;
316
317 let header_from_private: Header = p256_private.try_into()?;
318 let header_from_public: Header = p256_public.try_into()?;
319
320 assert_eq!(header_from_private.algorithm, header_from_public.algorithm);
321 assert_eq!(header_from_private.key_id, header_from_public.key_id);
322 assert_eq!(header_from_private.type_, header_from_public.type_);
323 assert_eq!(
324 header_from_private.json_web_key,
325 header_from_public.json_web_key
326 );
327
328 let k256_private = generate_key(KeyType::K256Private)?;
330 let k256_public = to_public(&k256_private)?;
331
332 let k256_header_from_private: Header = k256_private.try_into()?;
333 let k256_header_from_public: Header = k256_public.try_into()?;
334
335 assert_eq!(
336 k256_header_from_private.algorithm,
337 k256_header_from_public.algorithm
338 );
339 assert_eq!(
340 k256_header_from_private.key_id,
341 k256_header_from_public.key_id
342 );
343 assert_eq!(
344 k256_header_from_private.type_,
345 k256_header_from_public.type_
346 );
347 assert_eq!(
348 k256_header_from_private.json_web_key,
349 k256_header_from_public.json_web_key
350 );
351
352 Ok(())
353 }
354
355 #[test]
356 fn test_header_from_existing_test_keys() -> Result<()> {
357 let p256_private_key = "did:key:z42tnbHmmnhF11nwSnp5kQJbcZQw2Vbw5WF3ABDSxPtDgU2o";
359 let p256_public_key = "did:key:zDnaeXduWbJ1b1Kgjf3uCdCpMDF1LEDizUiyxAxGwerou3Nh2";
360 let k256_private_key = "did:key:z3vLY4nbXy2rV4Qr65gUtfnSF3A8Be7gmYzUiCX6eo2PR1Rt";
361 let k256_public_key = "did:key:zQ3shNzMp4oaaQ1gQRzCxMGXFrSW3NEM1M9T6KCY9eA7HhyEA";
362
363 let parsed_p256_private = identify_key(p256_private_key)?;
365 let parsed_p256_public = identify_key(p256_public_key)?;
366 let parsed_k256_private = identify_key(k256_private_key)?;
367 let parsed_k256_public = identify_key(k256_public_key)?;
368
369 let derived_p256_public = to_public(&parsed_p256_private)?;
371 let derived_k256_public = to_public(&parsed_k256_private)?;
372
373 let p256_private_header: Header = parsed_p256_private.try_into()?;
375 assert_eq!(p256_private_header.algorithm, Some("ES256".to_string()));
376 let expected_p256_key_id = format!("{}", derived_p256_public);
377 assert_eq!(p256_private_header.key_id, Some(expected_p256_key_id));
378
379 let p256_public_header: Header = parsed_p256_public.try_into()?;
381 assert_eq!(p256_public_header.algorithm, Some("ES256".to_string()));
382 assert_eq!(p256_public_header.key_id, Some(p256_public_key.to_string()));
383
384 let k256_private_header: Header = parsed_k256_private.try_into()?;
386 assert_eq!(k256_private_header.algorithm, Some("ES256K".to_string()));
387 let expected_k256_key_id = format!("{}", derived_k256_public);
388 assert_eq!(k256_private_header.key_id, Some(expected_k256_key_id));
389
390 let k256_public_header: Header = parsed_k256_public.try_into()?;
392 assert_eq!(k256_public_header.algorithm, Some("ES256K".to_string()));
393 assert_eq!(k256_public_header.key_id, Some(k256_public_key.to_string()));
394
395 let derived_p256_public_header: Header = derived_p256_public.try_into()?;
397 let derived_k256_public_header: Header = derived_k256_public.try_into()?;
398
399 assert_eq!(p256_private_header, derived_p256_public_header);
400 assert_eq!(k256_private_header, derived_k256_public_header);
401
402 Ok(())
403 }
404
405 #[test]
406 fn test_header_multiple_conversions_same_key() -> Result<()> {
407 let key_data = generate_key(KeyType::P256Private)?;
409
410 let header1: Header = key_data.clone().try_into()?;
411 let header2: Header = key_data.try_into()?;
412
413 assert_eq!(header1, header2);
414
415 Ok(())
416 }
417
418 #[test]
419 fn test_header_different_keys_different_headers() -> Result<()> {
420 let p256_key = generate_key(KeyType::P256Private)?;
422 let k256_key = generate_key(KeyType::K256Private)?;
423
424 let p256_header: Header = p256_key.try_into()?;
425 let k256_header: Header = k256_key.try_into()?;
426
427 assert_ne!(p256_header.algorithm, k256_header.algorithm);
429 assert_eq!(p256_header.algorithm, Some("ES256".to_string()));
430 assert_eq!(k256_header.algorithm, Some("ES256K".to_string()));
431
432 assert_ne!(p256_header.key_id, k256_header.key_id);
434
435 Ok(())
436 }
437
438 #[test]
439 fn test_header_from_invalid_key_data() {
440 let invalid_key_data = KeyData::new(KeyType::P256Private, vec![0u8; 10]); let result: Result<Header> = invalid_key_data.try_into();
444 assert!(result.is_err());
445 }
446
447 #[test]
448 fn test_header_serialization_deserialization() -> Result<()> {
449 let key_data = generate_key(KeyType::P256Private)?;
451 let header: Header = key_data.try_into()?;
452
453 let json = serde_json::to_string(&header)?;
455
456 let deserialized_header: Header = serde_json::from_str(&json)?;
458
459 assert_eq!(header, deserialized_header);
460
461 Ok(())
462 }
463
464 #[test]
465 fn test_header_json_field_names() -> Result<()> {
466 let key_data = generate_key(KeyType::P256Private)?;
468 let header: Header = key_data.try_into()?;
469
470 let json = serde_json::to_string(&header)?;
471 let json_value: serde_json::Value = serde_json::from_str(&json)?;
472
473 assert!(json_value.get("alg").is_some());
475 assert!(json_value.get("kid").is_some());
476 assert!(json_value.get("typ").is_none()); assert!(json_value.get("jwk").is_none()); assert_eq!(json_value["alg"], "ES256");
481 assert!(json_value["kid"].is_string());
482
483 Ok(())
484 }
485
486 #[test]
487 fn test_header_complete_workflow() -> Result<()> {
488 println!("\n=== Header TryFrom<KeyData> Test Workflow ===");
489
490 println!("1. Generating test keys...");
492 let p256_private = generate_key(KeyType::P256Private)?;
493 let p384_private = generate_key(KeyType::P384Private)?;
494 let k256_private = generate_key(KeyType::K256Private)?;
495 let p256_public = to_public(&p256_private)?;
496 let p384_public = to_public(&p384_private)?;
497 let k256_public = to_public(&k256_private)?;
498
499 println!("2. Converting KeyData to Headers...");
501 let p256_private_header: Header = p256_private.try_into()?;
502 let p256_public_header: Header = p256_public.try_into()?;
503 let p384_private_header: Header = p384_private.try_into()?;
504 let p384_public_header: Header = p384_public.try_into()?;
505 let k256_private_header: Header = k256_private.try_into()?;
506 let k256_public_header: Header = k256_public.try_into()?;
507
508 println!("3. Verifying algorithms...");
510 assert_eq!(p256_private_header.algorithm, Some("ES256".to_string()));
511 assert_eq!(p256_public_header.algorithm, Some("ES256".to_string()));
512 assert_eq!(p384_private_header.algorithm, Some("ES384".to_string()));
513 assert_eq!(p384_public_header.algorithm, Some("ES384".to_string()));
514 assert_eq!(k256_private_header.algorithm, Some("ES256K".to_string()));
515 assert_eq!(k256_public_header.algorithm, Some("ES256K".to_string()));
516 println!(" ✓ P-256 keys → ES256");
517 println!(" ✓ P-384 keys → ES384");
518 println!(" ✓ K-256 keys → ES256K");
519
520 println!("4. Verifying key ID consistency...");
522 assert_eq!(p256_private_header.key_id, p256_public_header.key_id);
523 assert_eq!(p384_private_header.key_id, p384_public_header.key_id);
524 assert_eq!(k256_private_header.key_id, k256_public_header.key_id);
525 println!(" ✓ Private and public keys produce same key_id");
526
527 println!("5. Verifying optional fields are None...");
529 for header in [
530 &p256_private_header,
531 &p256_public_header,
532 &p384_private_header,
533 &p384_public_header,
534 &k256_private_header,
535 &k256_public_header,
536 ] {
537 assert_eq!(header.type_, None);
538 assert_eq!(header.json_web_key, None);
539 }
540 println!(" ✓ type_ and json_web_key fields are None");
541
542 println!("6. Testing JSON serialization...");
544 let json = serde_json::to_string(&p384_private_header)?;
545 let parsed: Header = serde_json::from_str(&json)?;
546 assert_eq!(p384_private_header, parsed);
547 println!(" ✓ Headers serialize/deserialize correctly");
548
549 println!("=== All Header conversion tests passed! ===\n");
550
551 Ok(())
552 }
553
554 #[test]
555 fn test_header_from_p384_private_key() -> Result<()> {
556 let key_data = generate_key(KeyType::P384Private)?;
557 let header: Header = key_data.clone().try_into()?;
558
559 assert_eq!(header.algorithm, Some("ES384".to_string()));
560 assert!(header.key_id.is_some());
561 assert_eq!(header.type_, None);
562 assert_eq!(header.json_web_key, None);
563
564 let public_key = to_public(&key_data)?;
566 let expected_key_id = format!("{}", public_key);
567 assert_eq!(header.key_id, Some(expected_key_id));
568
569 Ok(())
570 }
571
572 #[test]
573 fn test_header_from_p384_public_key() -> Result<()> {
574 let private_key = generate_key(KeyType::P384Private)?;
575 let public_key = to_public(&private_key)?;
576 let header: Header = public_key.clone().try_into()?;
577
578 assert_eq!(header.algorithm, Some("ES384".to_string()));
579 assert!(header.key_id.is_some());
580 assert_eq!(header.type_, None);
581 assert_eq!(header.json_web_key, None);
582
583 let expected_key_id = format!("{}", public_key);
585 assert_eq!(header.key_id, Some(expected_key_id));
586
587 Ok(())
588 }
589}