1mod attenuate;
2mod inspect;
3mod jit;
4mod mint;
5mod revocation;
6mod verify;
7
8pub use attenuate::add_identity_attenuation_to_token;
9pub use inspect::{inspect_identity_token, InspectResult};
10pub use jit::create_short_lived_identity_token;
11pub use mint::{
12 create_domain_restricted_identity_token, create_identity_token,
13 create_non_delegatable_identity_token, HessraIdentity,
14};
15pub use revocation::{
16 get_active_identity_revocation, get_identity_revocations, IdentityRevocation,
17};
18pub use verify::{verify_bearer_token, verify_identity_token, IdentityVerifier};
19
20#[cfg(test)]
21mod tests {
22 use super::*;
23 use hessra_token_core::{KeyPair, TokenTimeConfig};
24
25 #[test]
26 fn test_basic_identity_token_creation_and_verification() {
27 let keypair = KeyPair::new();
29 let public_key = keypair.public();
30
31 let subject = "urn:hessra:alice".to_string();
33 let token = HessraIdentity::new(subject.clone(), TokenTimeConfig::default())
34 .issue(&keypair)
35 .expect("Failed to create identity token");
36
37 assert!(
39 verify_identity_token(token.clone(), public_key, subject.clone()).is_ok(),
40 "Verification should succeed with exact identity match"
41 );
42
43 assert!(
45 verify_bearer_token(token.clone(), public_key).is_ok(),
46 "Verification should succeed as a bearer token"
47 );
48
49 assert!(
51 verify_identity_token(token.clone(), public_key, "urn:hessra:bob".to_string()).is_err(),
52 "Verification should fail with different identity"
53 );
54
55 assert!(
58 verify_identity_token(
59 token.clone(),
60 public_key,
61 "urn:hessra:alice:agent".to_string()
62 )
63 .is_err(),
64 "Hierarchical identity should fail for non-delegatable realm identity"
65 );
66
67 let delegatable_token = HessraIdentity::new(subject.clone(), TokenTimeConfig::default())
69 .delegatable(true)
70 .issue(&keypair)
71 .expect("Failed to create delegatable identity token");
72
73 assert!(
75 verify_identity_token(delegatable_token.clone(), public_key, subject.clone()).is_ok(),
76 "Exact identity should work with delegatable token"
77 );
78
79 assert!(
81 verify_identity_token(
82 delegatable_token.clone(),
83 public_key,
84 "urn:hessra:alice:agent".to_string()
85 )
86 .is_ok(),
87 "Hierarchical identity should work with delegatable realm identity"
88 );
89 }
90
91 #[test]
92 fn test_single_level_delegation() {
93 let keypair = KeyPair::new();
94 let public_key = keypair.public();
95
96 let base_identity = "urn:hessra:alice".to_string();
98 let token = HessraIdentity::new(base_identity.clone(), TokenTimeConfig::default())
99 .delegatable(true)
100 .issue(&keypair)
101 .expect("Failed to create delegatable identity token");
102
103 let delegated_identity = "urn:hessra:alice:laptop".to_string();
105 let attenuated_result = add_identity_attenuation_to_token(
106 token.clone(),
107 delegated_identity.clone(),
108 public_key,
109 TokenTimeConfig::default(),
110 );
111
112 let attenuated_token = match attenuated_result {
113 Ok(t) => t,
114 Err(e) => panic!("Failed to attenuate token: {e:?}"),
115 };
116
117 let base_verify_result =
119 verify_identity_token(attenuated_token.clone(), public_key, base_identity.clone());
120 assert!(
121 base_verify_result.is_err(),
122 "Base identity should NOT verify with attenuated token - use original token instead"
123 );
124
125 assert!(
127 verify_identity_token(
128 attenuated_token.clone(),
129 public_key,
130 delegated_identity.clone()
131 )
132 .is_ok(),
133 "Delegated identity should verify"
134 );
135
136 assert!(
138 verify_bearer_token(attenuated_token.clone(), public_key).is_err(),
139 "Delegated identity should not verify as a bearer token"
140 );
141
142 assert!(
144 verify_identity_token(
145 attenuated_token.clone(),
146 public_key,
147 "urn:hessra:alice:phone".to_string()
148 )
149 .is_err(),
150 "Different branch of delegation should fail"
151 );
152
153 assert!(
155 verify_identity_token(
156 attenuated_token.clone(),
157 public_key,
158 "urn:hessra:bob".to_string()
159 )
160 .is_err(),
161 "Completely different identity should fail"
162 );
163 }
164
165 #[test]
166 fn test_multi_level_delegation_chain() {
167 let keypair = KeyPair::new();
168 let public_key = keypair.public();
169
170 let org_identity = "urn:hessra:company".to_string();
172 let dept_identity = "urn:hessra:company:dept_eng".to_string();
173 let user_identity = "urn:hessra:company:dept_eng:alice".to_string();
174 let device_identity = "urn:hessra:company:dept_eng:alice:laptop".to_string();
175
176 let token = HessraIdentity::new(org_identity.clone(), TokenTimeConfig::default())
178 .delegatable(true)
179 .issue(&keypair)
180 .expect("Failed to create delegatable org token");
181
182 let token = add_identity_attenuation_to_token(
184 token,
185 dept_identity.clone(),
186 public_key,
187 TokenTimeConfig::default(),
188 )
189 .expect("Failed to attenuate to department");
190
191 let token = add_identity_attenuation_to_token(
193 token,
194 user_identity.clone(),
195 public_key,
196 TokenTimeConfig::default(),
197 )
198 .expect("Failed to attenuate to user");
199
200 let token = add_identity_attenuation_to_token(
202 token,
203 device_identity.clone(),
204 public_key,
205 TokenTimeConfig::default(),
206 )
207 .expect("Failed to attenuate to device");
208
209 assert!(
212 verify_identity_token(token.clone(), public_key, org_identity).is_err(),
213 "Organization level should NOT work after delegation to device"
214 );
215 assert!(
216 verify_identity_token(token.clone(), public_key, dept_identity).is_err(),
217 "Department level should NOT work after delegation to device"
218 );
219 assert!(
220 verify_identity_token(token.clone(), public_key, user_identity).is_err(),
221 "User level should NOT work after delegation to device"
222 );
223 assert!(
224 verify_identity_token(token.clone(), public_key, device_identity).is_ok(),
225 "Device level SHOULD work - it's the delegated identity"
226 );
227
228 assert!(
230 verify_identity_token(
231 token.clone(),
232 public_key,
233 "urn:hessra:company:dept_hr".to_string()
234 )
235 .is_err(),
236 "Different department should fail"
237 );
238 assert!(
239 verify_identity_token(
240 token.clone(),
241 public_key,
242 "urn:hessra:company:dept_eng:bob".to_string()
243 )
244 .is_err(),
245 "Different user in same department should fail"
246 );
247 assert!(
248 verify_identity_token(
249 token.clone(),
250 public_key,
251 "urn:hessra:company:dept_eng:alice:phone".to_string()
252 )
253 .is_err(),
254 "Different device for same user should fail"
255 );
256 }
257
258 #[test]
259 fn test_time_based_expiration() {
260 let identity = "urn:hessra:alice".to_string();
261
262 let expired_config = TokenTimeConfig {
264 start_time: Some(0), duration: 1, };
267
268 let expired_keypair = KeyPair::new();
269 let expired_public_key = expired_keypair.public();
270 let expired_token = HessraIdentity::new(identity.clone(), expired_config)
271 .issue(&expired_keypair)
272 .expect("Failed to create expired token");
273
274 assert!(
276 verify_identity_token(expired_token, expired_public_key, identity.clone()).is_err(),
277 "Expired token should fail verification"
278 );
279
280 let valid_config = TokenTimeConfig {
282 start_time: None,
283 duration: 3600, };
285
286 let valid_keypair = KeyPair::new();
287 let valid_public_key = valid_keypair.public();
288 let valid_token = HessraIdentity::new(identity.clone(), valid_config)
289 .delegatable(true)
290 .issue(&valid_keypair)
291 .expect("Failed to create valid delegatable token");
292
293 assert!(
296 add_identity_attenuation_to_token(
297 valid_token.clone(),
298 "urn:hessra:alice:laptop".to_string(),
299 valid_public_key, expired_config,
301 )
302 .is_err(),
303 "Attenuating a token with an expired time should fail"
304 );
305 }
306
307 #[test]
308 fn test_uri_validation_edge_cases() {
309 let test_cases = vec![
312 ("urn:hessra:user", "urn:hessra:user:device"),
313 (
314 "https://example.com/user",
315 "https://example.com/user:device",
316 ), ("mailto:user@example.com", "mailto:user@example.com:device"),
318 ("user", "user:device"), ];
320
321 for (base, delegated) in test_cases {
322 let keypair = KeyPair::new();
324 let public_key = keypair.public();
325
326 let token = HessraIdentity::new(base.to_string(), TokenTimeConfig::default())
327 .delegatable(true)
328 .issue(&keypair)
329 .unwrap_or_else(|_| panic!("Failed to create delegatable token for {base}"));
330
331 let attenuated = add_identity_attenuation_to_token(
332 token,
333 delegated.to_string(),
334 public_key, TokenTimeConfig::default(),
336 )
337 .unwrap_or_else(|_| panic!("Failed to attenuate {base} to {delegated}"));
338
339 assert!(
341 verify_identity_token(attenuated.clone(), public_key, base.to_string()).is_err(),
342 "Base identity {base} should NOT verify after delegation"
343 );
344 assert!(
345 verify_identity_token(attenuated, public_key, delegated.to_string()).is_ok(),
346 "Delegated identity {delegated} should verify"
347 );
348 }
349 }
350
351 #[test]
352 fn test_prefix_attack_prevention() {
353 let keypair = KeyPair::new();
354 let public_key = keypair.public();
355
356 let alice_token =
358 HessraIdentity::new("urn:hessra:alice".to_string(), TokenTimeConfig::default())
359 .delegatable(true)
360 .issue(&keypair)
361 .expect("Failed to create delegatable alice token");
362
363 assert!(
365 verify_identity_token(
366 alice_token.clone(),
367 public_key,
368 "urn:hessra:alice2".to_string()
369 )
370 .is_err(),
371 "alice2 should not verify against alice token"
372 );
373
374 let attenuated = add_identity_attenuation_to_token(
376 alice_token,
377 "urn:hessra:alice:device".to_string(),
378 public_key,
379 TokenTimeConfig::default(),
380 )
381 .expect("Failed to attenuate");
382
383 assert!(
385 verify_identity_token(
386 attenuated.clone(),
387 public_key,
388 "urn:hessra:alice:device2".to_string()
389 )
390 .is_err(),
391 "device2 should not verify against device"
392 );
393 assert!(
394 verify_identity_token(
395 attenuated,
396 public_key,
397 "urn:hessra:alice2:device".to_string()
398 )
399 .is_err(),
400 "alice2:device should not verify"
401 );
402 }
403
404 #[test]
405 fn test_empty_identity_handling() {
406 let keypair = KeyPair::new();
407 let public_key = keypair.public();
408
409 let result =
411 HessraIdentity::new("".to_string(), TokenTimeConfig::default()).issue(&keypair);
412
413 assert!(
415 result.is_ok(),
416 "Should be able to create non-delegatable token with empty identity"
417 );
418
419 let token = result.unwrap();
420
421 assert!(
423 verify_identity_token(token.clone(), public_key, "".to_string()).is_ok(),
424 "Empty identity should verify against empty identity token"
425 );
426
427 assert!(
429 verify_identity_token(token.clone(), public_key, "urn:hessra:anyone".to_string())
430 .is_err(),
431 "Non-empty identity should not verify against non-delegatable empty identity token"
432 );
433
434 assert!(
435 verify_identity_token(token, public_key, ":something".to_string()).is_err(),
436 "Identity starting with : should not match non-delegatable empty identity"
437 );
438
439 let delegatable_token = HessraIdentity::new("".to_string(), TokenTimeConfig::default())
441 .delegatable(true)
442 .issue(&keypair)
443 .expect("Failed to create delegatable empty identity token");
444
445 assert!(
447 verify_identity_token(delegatable_token.clone(), public_key, "".to_string()).is_ok(),
448 "Empty identity should verify against delegatable empty identity token"
449 );
450
451 assert!(
453 verify_identity_token(delegatable_token, public_key, ":something".to_string()).is_ok(),
454 "Identity starting with : would match delegatable empty identity's hierarchy check"
455 );
456 }
457
458 #[test]
459 fn test_domain_restricted_identity_verification() {
460 use verify::IdentityVerifier;
461
462 let keypair = KeyPair::new();
463 let public_key = keypair.public();
464 let subject = "urn:hessra:alice".to_string();
465 let domain = "example.com".to_string();
466
467 let domain_token = HessraIdentity::new(subject.clone(), TokenTimeConfig::default())
469 .domain_restricted(domain.clone())
470 .issue(&keypair)
471 .expect("Failed to create domain-restricted token");
472
473 assert!(
475 IdentityVerifier::new(domain_token.clone(), public_key)
476 .with_identity(subject.clone())
477 .with_domain(domain.clone())
478 .verify()
479 .is_ok(),
480 "Verification should succeed with matching domain"
481 );
482
483 assert!(
485 IdentityVerifier::new(domain_token.clone(), public_key)
486 .with_identity(subject.clone())
487 .with_domain(domain.clone())
488 .ensure_subject_in_domain()
489 .verify()
490 .is_ok(),
491 "Verification should succeed with matching domain"
492 );
493
494 assert!(
496 verify_identity_token(domain_token.clone(), public_key, subject.clone()).is_err(),
497 "Verification should fail without domain context"
498 );
499
500 assert!(
502 IdentityVerifier::new(domain_token.clone(), public_key)
503 .with_identity(subject.clone())
504 .with_domain("wrong.com".to_string())
505 .verify()
506 .is_err(),
507 "Verification should fail with wrong domain"
508 );
509
510 assert!(
512 verify_bearer_token(domain_token.clone(), public_key).is_err(),
513 "Bearer verification should fail without domain context"
514 );
515
516 assert!(
518 IdentityVerifier::new(domain_token.clone(), public_key)
519 .with_domain(domain.clone())
520 .verify()
521 .is_ok(),
522 "Bearer verification should pass with domain context"
523 );
524
525 let delegatable_domain_token =
527 HessraIdentity::new(subject.clone(), TokenTimeConfig::default())
528 .delegatable(true)
529 .domain_restricted(domain.clone())
530 .issue(&keypair)
531 .expect("Failed to create delegatable domain-restricted token");
532
533 assert!(
535 IdentityVerifier::new(delegatable_domain_token.clone(), public_key)
536 .with_identity(subject.clone())
537 .with_domain(domain.clone())
538 .ensure_subject_in_domain()
539 .verify()
540 .is_ok(),
541 "Delegatable token should verify with exact identity and domain"
542 );
543
544 assert!(
546 IdentityVerifier::new(delegatable_domain_token.clone(), public_key)
547 .with_identity("urn:hessra:alice:laptop".to_string())
548 .with_domain(domain.clone())
549 .ensure_subject_in_domain()
550 .verify()
551 .is_ok(),
552 "Delegatable token should verify with hierarchical identity and domain"
553 );
554
555 assert!(
557 verify_identity_token(
558 delegatable_domain_token.clone(),
559 public_key,
560 "urn:hessra:alice:laptop".to_string()
561 )
562 .is_err(),
563 "Delegatable token should fail without domain context"
564 );
565
566 let regular_token = HessraIdentity::new(subject.clone(), TokenTimeConfig::default())
569 .issue(&keypair)
570 .expect("Failed to create regular token");
571
572 assert!(
574 verify_identity_token(regular_token.clone(), public_key, subject.clone()).is_ok(),
575 "Regular token should verify without domain context"
576 );
577
578 assert!(
579 IdentityVerifier::new(regular_token.clone(), public_key)
580 .with_identity(subject.clone())
581 .with_domain(domain.clone())
582 .verify()
583 .is_ok(),
584 "Regular token should verify even with extra domain context"
585 );
586
587 assert!(
590 IdentityVerifier::new(regular_token.clone(), public_key)
591 .with_identity(subject.clone())
592 .with_domain(domain.clone())
593 .ensure_subject_in_domain()
594 .verify()
595 .is_err(),
596 "Regular token should fail to verify with ensure subject in domain"
597 );
598 }
599}