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