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}