pq_jwt/lib.rs
1//! # pq-jwt
2//!
3//! A post-quantum JWT implementation using ML-DSA (Module-Lattice Digital Signature Algorithm)
4//! signatures for quantum-resistant authentication tokens.
5//!
6//! ## Features
7//!
8//! - **Quantum-Resistant**: Uses ML-DSA (FIPS 204) signatures that are secure against quantum attacks
9//! - **Multiple Security Levels**: Support for ML-DSA-44, ML-DSA-65, and ML-DSA-87
10//! - **Standards Compliant**: JWT format following RFC 7519
11//! - **Easy to Use**: Simple API for key generation, signing, and verification
12//!
13//! ## Quick Start
14//!
15//! ```rust
16//! use pq_jwt::{generate_keypair, sign, verify, MlDsaAlgo};
17//! use std::time::{SystemTime, UNIX_EPOCH};
18//!
19//! // Generate a keypair
20//! let (private_key, public_key) = generate_keypair(MlDsaAlgo::Dsa65)?;
21//!
22//! // Sign with issuer and expiration
23//! let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
24//! let (jwt, _public_key, _jti) = sign(
25//! MlDsaAlgo::Dsa65,
26//! "https://myapp.com",
27//! now + 3600,
28//! &private_key
29//! )?;
30//!
31//! // Verify the JWT
32//! let verified_payload = verify(&jwt, &public_key, "https://myapp.com")?;
33//! assert!(verified_payload.contains("https://myapp.com"));
34//! # Ok::<(), String>(())
35//! ```
36//!
37//! ## Security Levels
38//!
39//! | Variant | NIST Level | Signature Size | Use Case |
40//! |---------|-----------|----------------|----------|
41//! | ML-DSA-44 | Category 2 | ~2.4 KB | IoT, constrained devices |
42//! | ML-DSA-65 | Category 3 | ~3.3 KB | Recommended for most uses |
43//! | ML-DSA-87 | Category 5 | ~4.6 KB | High security requirements |
44
45mod algorithm;
46mod header; // Internal only
47pub mod keygen;
48pub mod signer;
49pub mod verifier;
50
51// Re-export public API
52pub use algorithm::MlDsaAlgo;
53pub use keygen::{KeySource, generate_keypair};
54pub use signer::sign;
55pub use verifier::verify;
56
57#[cfg(test)]
58mod tests {
59 use uuid::Uuid;
60
61 use super::*;
62 use std::time::{SystemTime, UNIX_EPOCH};
63
64 #[test]
65 fn test_full_workflow() {
66 let now = SystemTime::now()
67 .duration_since(UNIX_EPOCH)
68 .unwrap()
69 .as_secs();
70
71 // Generate keypair
72 let (private_key, public_key) = generate_keypair(MlDsaAlgo::Dsa65).unwrap();
73
74 // Sign with issuer and expiration
75 let (jwt, returned_pub_key, jti) = sign(
76 MlDsaAlgo::Dsa65,
77 "https://test.com",
78 now + 3600,
79 &private_key,
80 )
81 .unwrap();
82
83 // Verify public key matches
84 assert_eq!(public_key, returned_pub_key);
85
86 // Verify JWT
87 let verified_payload = verify(&jwt, &public_key, "https://test.com").unwrap();
88 assert!(verified_payload.contains("https://test.com"));
89 assert!(Uuid::parse_str(&jti).is_ok());
90 }
91
92 #[test]
93 fn test_all_algorithms() {
94 let now = SystemTime::now()
95 .duration_since(UNIX_EPOCH)
96 .unwrap()
97 .as_secs();
98
99 for algo in [MlDsaAlgo::Dsa44, MlDsaAlgo::Dsa65, MlDsaAlgo::Dsa87] {
100 let (private_key, public_key) = generate_keypair(algo).unwrap();
101 let (jwt, _, _) = sign(algo, "https://test.com", now + 3600, &private_key).unwrap();
102 let verified_payload = verify(&jwt, &public_key, "https://test.com").unwrap();
103 assert!(verified_payload.contains("https://test.com"));
104 }
105 }
106
107 #[test]
108 fn test_verification_fails_with_wrong_key() {
109 let now = SystemTime::now()
110 .duration_since(UNIX_EPOCH)
111 .unwrap()
112 .as_secs();
113
114 let (private_key1, _) = generate_keypair(MlDsaAlgo::Dsa65).unwrap();
115 let (_, public_key2) = generate_keypair(MlDsaAlgo::Dsa65).unwrap();
116
117 let (jwt, _, _) = sign(
118 MlDsaAlgo::Dsa65,
119 "https://test.com",
120 now + 3600,
121 &private_key1,
122 )
123 .unwrap();
124 let result = verify(&jwt, &public_key2, "https://test.com");
125
126 assert!(result.is_err());
127 }
128}