1mod attest;
43mod error;
44mod mint;
45mod utils;
46mod verify;
47
48pub use attest::{
49 add_multi_party_attestation, add_multi_party_attestation_to_token, add_service_node_attestation,
50};
51pub use error::TokenError;
52pub use mint::{
53 create_biscuit, create_multi_party_biscuit, create_multi_party_biscuit_with_time,
54 create_multi_party_token, create_multi_party_token_with_time, create_raw_multi_party_biscuit,
55 create_service_chain_biscuit, create_service_chain_token, create_service_chain_token_with_time,
56 create_token, create_token_with_time, TokenTimeConfig,
57};
58pub use utils::{decode_token, encode_token, parse_token, public_key_from_pem_file};
59pub use verify::{
60 biscuit_key_from_string, verify_biscuit_local, verify_service_chain_biscuit_local, ServiceNode,
61};
62pub use verify::{verify_service_chain_token_local, verify_token_local};
63
64pub use biscuit_auth::{Biscuit, KeyPair, PublicKey};
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70 use biscuit_auth::macros::biscuit;
71 use serde_json::Value;
72 use std::fs;
73
74 #[test]
75 fn test_verify_biscuit_local() {
76 let keypair = KeyPair::new();
78 let public_key = keypair.public();
79
80 let biscuit_builder = biscuit!(
82 r#"
83 right("alice", "resource1", "read");
84 "#
85 );
86 let biscuit = biscuit_builder.build(&keypair).unwrap();
87 let token_bytes = biscuit.to_vec().unwrap();
88
89 let result = verify_biscuit_local(
91 token_bytes,
92 public_key,
93 "alice".to_string(),
94 "resource1".to_string(),
95 "read".to_string(),
96 );
97 assert!(result.is_ok());
98 }
99
100 #[test]
101 fn test_verify_service_chain_biscuit() {
102 let root_keypair = KeyPair::new();
104 let service_keypair = KeyPair::new();
105 let service_public_key_hex = hex::encode(service_keypair.public().to_bytes());
106 let service_public_key_str = format!("ed25519/{}", service_public_key_hex);
107
108 let biscuit_builder = biscuit!(
110 r#"
111 right("alice", "resource1", "write");
112 node("resource1", "service1");
113 "#
114 );
115 let biscuit = biscuit_builder.build(&root_keypair).unwrap();
116 let token_bytes = biscuit.to_vec().unwrap();
117
118 let service_nodes = vec![ServiceNode {
120 component: "service1".to_string(),
121 public_key: service_public_key_str,
122 }];
123
124 let result = verify_service_chain_biscuit_local(
126 token_bytes,
127 root_keypair.public(),
128 "alice".to_string(),
129 "resource1".to_string(),
130 "write".to_string(),
131 service_nodes,
132 None,
133 );
134 assert!(result.is_ok());
135 }
136
137 #[test]
138 fn test_add_service_node_attestation() {
139 let root_keypair = KeyPair::new();
141 let service_keypair = KeyPair::new();
142
143 let biscuit_builder = biscuit!(
145 r#"
146 right("alice", "resource1", "read");
147 right("alice", "resource1", "write");
148 "#
149 );
150 let biscuit = biscuit_builder.build(&root_keypair).unwrap();
151 let token_bytes = biscuit.to_vec().unwrap();
152
153 let attested_token = add_service_node_attestation(
155 token_bytes,
156 root_keypair.public(),
157 "resource1",
158 &service_keypair,
159 );
160 assert!(attested_token.is_ok());
161
162 let result = verify_biscuit_local(
164 attested_token.unwrap(),
165 root_keypair.public(),
166 "alice".to_string(),
167 "resource1".to_string(),
168 "read".to_string(),
169 );
170 assert!(result.is_ok());
171 }
172
173 #[test]
174 fn test_base64_utils() {
175 let keypair = KeyPair::new();
177 let biscuit_builder = biscuit!(
178 r#"
179 right("alice", "resource1", "read");
180 "#
181 );
182 let biscuit = biscuit_builder.build(&keypair).unwrap();
183 let original_bytes = biscuit.to_vec().unwrap();
184
185 let encoded = encode_token(&original_bytes);
187 assert!(!encoded.is_empty());
188
189 let decoded = decode_token(&encoded).unwrap();
191 assert_eq!(original_bytes, decoded);
192
193 let result = decode_token("invalid-base64!");
195 assert!(result.is_err());
196 }
197
198 #[test]
199 fn test_verify_token_string() {
200 let keypair = KeyPair::new();
202 let biscuit_builder = biscuit!(
203 r#"
204 right("alice", "resource1", "read");
205 right("alice", "resource1", "write");
206 "#
207 );
208 let biscuit = biscuit_builder.build(&keypair).unwrap();
209 let token_bytes = biscuit.to_vec().unwrap();
210 let token_string = encode_token(&token_bytes);
211
212 let result = verify_token_local(
214 &token_string,
215 keypair.public(),
216 "alice",
217 "resource1",
218 "read",
219 );
220 assert!(result.is_ok());
221
222 let result =
224 verify_token_local(&token_string, keypair.public(), "bob", "resource1", "read");
225 assert!(result.is_err());
226 }
227
228 #[test]
229 fn test_token_verification_from_json() {
230 let json_data =
232 fs::read_to_string("tests/test_tokens.json").expect("Failed to read test_tokens.json");
233 let tokens: Value =
234 serde_json::from_str(&json_data).expect("Failed to parse test_tokens.json");
235
236 let public_key = public_key_from_pem_file("tests/hessra_key.pem")
238 .expect("Failed to load test public key");
239
240 for token_value in tokens["tokens"].as_array().unwrap() {
242 let name = token_value["name"].as_str().unwrap();
243 let token_string = token_value["token"].as_str().unwrap();
244 let metadata = &token_value["metadata"];
245
246 let subject = metadata["subject"].as_str().unwrap();
248 let resource = metadata["resource"].as_str().unwrap();
249 let expected_result = metadata["expected_result"].as_bool().unwrap();
250 let description = metadata["description"].as_str().unwrap_or("No description");
251
252 println!("Testing token '{}': {}", name, description);
253
254 let result = parse_token(token_string, public_key).and_then(|biscuit| {
256 println!("Token blocks: {}", biscuit.print());
258
259 if metadata["type"].as_str().unwrap() == "singleton" {
260 verify_token_local(token_string, public_key, subject, resource, "read")
261 } else {
262 let service_nodes = vec![
264 ServiceNode {
265 component: "auth_service".to_string(),
266 public_key: "ed25519/0123456789abcdef0123456789abcdef".to_string(),
267 },
268 ServiceNode {
269 component: "payment_service".to_string(),
270 public_key: "ed25519/fedcba9876543210fedcba9876543210".to_string(),
271 },
272 ];
273
274 verify_service_chain_token_local(
275 token_string,
276 public_key,
277 subject,
278 resource,
279 "read",
280 service_nodes,
281 None,
282 )
283 }
284 });
285
286 let verification_succeeded = result.is_ok();
288 assert_eq!(
289 verification_succeeded, expected_result,
290 "Token '{}' verification resulted in {}, expected: {} - {}",
291 name, verification_succeeded, expected_result, description
292 );
293
294 println!(
295 "✓ Token '{}' - Verification: {}",
296 name,
297 if verification_succeeded == expected_result {
298 "PASSED"
299 } else {
300 "FAILED"
301 }
302 );
303 }
304 }
305
306 #[test]
307 fn test_service_chain_tokens_from_json() {
308 let json_data =
310 fs::read_to_string("tests/test_tokens.json").expect("Failed to read test_tokens.json");
311 let tokens: Value =
312 serde_json::from_str(&json_data).expect("Failed to parse test_tokens.json");
313
314 let public_key = public_key_from_pem_file("tests/hessra_key.pem")
316 .expect("Failed to load test public key");
317
318 if let Some(tokens_array) = tokens["tokens"].as_array() {
320 if let Some(order_service_token) = tokens_array
321 .iter()
322 .find(|t| t["name"].as_str().unwrap() == "argo-cli1_access_order_service")
323 {
324 let token_string = order_service_token["token"].as_str().unwrap();
325 let subject = order_service_token["metadata"]["subject"].as_str().unwrap();
326 let resource = order_service_token["metadata"]["resource"]
327 .as_str()
328 .unwrap();
329 let expected_result = order_service_token["metadata"]["expected_result"]
330 .as_bool()
331 .unwrap();
332
333 let service_nodes = vec![
335 ServiceNode {
336 component: "auth_service".to_string(),
337 public_key: "ed25519/0123456789abcdef0123456789abcdef".to_string(),
338 },
339 ServiceNode {
340 component: "payment_service".to_string(),
341 public_key: "ed25519/fedcba9876543210fedcba9876543210".to_string(),
342 },
343 ];
344
345 let result = verify_service_chain_token_local(
347 token_string,
348 public_key,
349 subject,
350 resource,
351 "read",
352 service_nodes,
353 None,
354 );
355
356 assert_eq!(
358 result.is_ok(),
359 expected_result,
360 "Service chain verification for '{}' resulted in {}, expected: {}",
361 order_service_token["name"].as_str().unwrap(),
362 result.is_ok(),
363 expected_result
364 );
365 }
366 }
367 }
368
369 #[test]
370 fn test_service_chain_lifecycle() {
371 let json_data = fs::read_to_string("tests/service_chain_tokens.json")
373 .expect("Failed to read service_chain_tokens.json");
374 let tokens: Value =
375 serde_json::from_str(&json_data).expect("Failed to parse service_chain_tokens.json");
376
377 let initial_token = tokens["tokens"][0]["token"].as_str().unwrap();
379 let token_after_auth = tokens["tokens"][1]["token"].as_str().unwrap();
380 let token_after_payment = tokens["tokens"][2]["token"].as_str().unwrap();
381 let final_token = tokens["tokens"][3]["token"].as_str().unwrap();
382
383 let subject = "uri:urn:test:argo-cli1";
385 let resource = "order_service";
386
387 let root_public_key = public_key_from_pem_file("tests/hessra_key.pem")
389 .expect("Failed to load test public key");
390
391 let auth_service_pk_str = tokens["tokens"][1]["metadata"]["service_nodes"][0]["public_key"]
393 .as_str()
394 .unwrap();
395 let payment_service_pk_str = tokens["tokens"][2]["metadata"]["service_nodes"][1]
396 ["public_key"]
397 .as_str()
398 .unwrap();
399 let order_service_pk_str = tokens["tokens"][3]["metadata"]["service_nodes"][2]
400 ["public_key"]
401 .as_str()
402 .unwrap();
403
404 let result = verify_token_local(initial_token, root_public_key, subject, resource, "read");
409 assert!(result.is_ok(), "Initial token verification failed");
410
411 let service_nodes_for_payment = vec![ServiceNode {
413 component: "auth_service".to_string(),
414 public_key: auth_service_pk_str.to_string(),
415 }];
416
417 let result = verify_service_chain_token_local(
418 token_after_auth,
419 root_public_key,
420 subject,
421 resource,
422 "read",
423 service_nodes_for_payment.clone(),
424 None,
425 );
426 assert!(
427 result.is_ok(),
428 "Token with auth attestation verification failed"
429 );
430
431 let service_nodes_for_order = vec![
433 ServiceNode {
434 component: "auth_service".to_string(),
435 public_key: auth_service_pk_str.to_string(),
436 },
437 ServiceNode {
438 component: "payment_service".to_string(),
439 public_key: payment_service_pk_str.to_string(),
440 },
441 ];
442
443 let result = verify_service_chain_token_local(
444 token_after_payment,
445 root_public_key,
446 subject,
447 resource,
448 "read",
449 service_nodes_for_order.clone(),
450 None,
451 );
452 assert!(
453 result.is_ok(),
454 "Token with payment attestation verification failed"
455 );
456
457 let service_nodes_complete = vec![
459 ServiceNode {
460 component: "auth_service".to_string(),
461 public_key: auth_service_pk_str.to_string(),
462 },
463 ServiceNode {
464 component: "payment_service".to_string(),
465 public_key: payment_service_pk_str.to_string(),
466 },
467 ServiceNode {
468 component: "order_service".to_string(),
469 public_key: order_service_pk_str.to_string(),
470 },
471 ];
472
473 let result = verify_service_chain_token_local(
474 final_token,
475 root_public_key,
476 subject,
477 resource,
478 "read",
479 service_nodes_complete.clone(),
480 None,
481 );
482
483 if result.is_err() {
485 println!("Error details: {:?}", result);
486
487 let decoded_final = decode_token(final_token).unwrap();
489 if let Ok(biscuit) = Biscuit::from(&decoded_final, root_public_key) {
490 println!("Token blocks: {}", biscuit.print());
491 } else {
492 println!("Failed to parse token");
493 }
494 }
495
496 assert!(result.is_ok(), "Final token verification failed");
497
498 let result = verify_service_chain_token_local(
500 token_after_auth,
501 root_public_key,
502 subject,
503 resource,
504 "read",
505 service_nodes_complete,
506 None,
507 );
508 assert!(
510 result.is_err(),
511 "Incomplete service chain should be rejected"
512 );
513 }
514
515 #[test]
516 fn test_multi_party_token_verification_lifecycle() {
517 let subject = "test@test.com".to_owned();
518 let resource = "res1".to_string();
519 let operation = "read".to_string();
520 let root = KeyPair::new();
521 let public_key = root.public();
522
523 let approval_service_key = KeyPair::new();
525 let approval_service_public_key = hex::encode(approval_service_key.public().to_bytes());
526 let approval_service_public_key = format!("ed25519/{}", approval_service_public_key);
527 let approval_service_node = ServiceNode {
528 component: "approval_service".to_string(),
529 public_key: approval_service_public_key.clone(),
530 };
531 let nodes = vec![approval_service_node];
532
533 let token = create_multi_party_biscuit(
535 subject.clone(),
536 resource.clone(),
537 operation.clone(),
538 root,
539 &nodes,
540 );
541 assert!(token.is_ok(), "Failed to create multi-party token");
542 let token = token.unwrap();
543 let token_string = encode_token(&token);
544
545 println!("✓ Multi-party token created successfully");
546
547 let result = verify_token_local(&token_string, public_key, &subject, &resource, &operation);
549 assert!(
550 result.is_err(),
551 "Multi-party token should fail verification without attestation"
552 );
553 println!("✓ Unattested multi-party token correctly failed verification");
554
555 let attested_token = add_multi_party_attestation(
557 token,
558 public_key,
559 "approval_service".to_string(),
560 approval_service_key,
561 );
562 assert!(
563 attested_token.is_ok(),
564 "Failed to add multi-party attestation"
565 );
566 let attested_token = attested_token.unwrap();
567 let attested_token_string = encode_token(&attested_token);
568
569 println!("✓ Multi-party attestation added successfully");
570
571 let result = verify_token_local(
573 &attested_token_string,
574 public_key,
575 &subject,
576 &resource,
577 &operation,
578 );
579 assert!(
580 result.is_ok(),
581 "Attested multi-party token should pass verification"
582 );
583 println!("✓ Attested multi-party token correctly passed verification");
584
585 let wrong_service_key = KeyPair::new();
587 let wrong_attested_token = add_multi_party_attestation(
588 decode_token(&token_string).unwrap(),
589 public_key,
590 "wrong_service".to_string(),
591 wrong_service_key,
592 );
593 assert!(wrong_attested_token.is_ok(), "Attestation should succeed");
594 let wrong_attested_token_string = encode_token(&wrong_attested_token.unwrap());
595
596 let result = verify_token_local(
597 &wrong_attested_token_string,
598 public_key,
599 &subject,
600 &resource,
601 &operation,
602 );
603 assert!(
604 result.is_err(),
605 "Token attested by wrong namespace should fail verification"
606 );
607 println!("✓ Token attested by wrong namespace correctly failed verification");
608 }
609
610 #[test]
611 fn test_multi_party_token_with_multiple_parties() {
612 let subject = "test@test.com".to_owned();
613 let resource = "sensitive_resource".to_string();
614 let operation = "admin".to_string();
615 let root = KeyPair::new();
616 let public_key = root.public();
617
618 let legal_dept_key = KeyPair::new();
620 let legal_dept_public_key = hex::encode(legal_dept_key.public().to_bytes());
621 let legal_dept_public_key = format!("ed25519/{}", legal_dept_public_key);
622 let legal_dept_node = ServiceNode {
623 component: "legal_dept".to_string(),
624 public_key: legal_dept_public_key.clone(),
625 };
626
627 let security_team_key = KeyPair::new();
628 let security_team_public_key = hex::encode(security_team_key.public().to_bytes());
629 let security_team_public_key = format!("ed25519/{}", security_team_public_key);
630 let security_team_node = ServiceNode {
631 component: "security_team".to_string(),
632 public_key: security_team_public_key.clone(),
633 };
634
635 let nodes = vec![legal_dept_node, security_team_node];
636
637 let token = create_multi_party_biscuit(
639 subject.clone(),
640 resource.clone(),
641 operation.clone(),
642 root,
643 &nodes,
644 );
645 assert!(token.is_ok(), "Failed to create multi-party token");
646 let token = token.unwrap();
647 let token_string = encode_token(&token);
648
649 let result = verify_token_local(&token_string, public_key, &subject, &resource, &operation);
651 assert!(result.is_err(), "Token should fail without attestations");
652
653 let partially_attested_token = add_multi_party_attestation(
655 decode_token(&token_string).unwrap(),
656 public_key,
657 "legal_dept".to_string(),
658 legal_dept_key,
659 )
660 .unwrap();
661 let partially_attested_token_string = encode_token(&partially_attested_token);
662
663 let result = verify_token_local(
665 &partially_attested_token_string,
666 public_key,
667 &subject,
668 &resource,
669 &operation,
670 );
671 assert!(
672 result.is_err(),
673 "Token should fail with only one of two required attestations"
674 );
675
676 let fully_attested_token = add_multi_party_attestation(
678 partially_attested_token,
679 public_key,
680 "security_team".to_string(),
681 security_team_key,
682 )
683 .unwrap();
684 let fully_attested_token_string = encode_token(&fully_attested_token);
685
686 let result = verify_token_local(
688 &fully_attested_token_string,
689 public_key,
690 &subject,
691 &resource,
692 &operation,
693 );
694 assert!(
695 result.is_ok(),
696 "Token should pass with both required attestations"
697 );
698 println!("✓ Multi-party token with multiple parties verified successfully");
699 }
700}