# PASETO-PQ: Post-Quantum PASETO Tokens
[](https://crates.io/crates/paseto-pq)
[](https://crates.io/crates/paseto-pq)
[](https://docs.rs/paseto-pq)
[](https://github.com/thatnewyorker/paseto-pq/actions)
[](LICENSE)
[](https://www.rust-lang.org)
A pure post-quantum implementation of PASETO tokens using **ML-DSA** (CRYSTALS-Dilithium) signatures and **ChaCha20-Poly1305** encryption. This crate provides quantum-safe authentication and encryption tokens with comprehensive metadata support, resistant to attacks by quantum computers implementing Shor's algorithm.
## ๐ Security & Quality Enhancements
**Latest Release**: Version 0.1.2 includes code quality improvements and critical security fixes:
- โ
**PAE-Based Footer Authentication**: Footers are now cryptographically authenticated using Pre-Authentication Encoding (v0.1.1+)
- โ
**RFC Compliance**: Full adherence to PASETO specification requirements
- โ
**Tamper Detection**: Footer tampering is now properly detected and rejected
- โ
**Code Quality**: Passes strict clippy linting with `-D warnings` (v0.1.2)
- โ
**Backward Compatibility**: No API changes, existing code works unchanged
**Users should upgrade to v0.1.2** for the latest security and quality improvements.
## ๐ก๏ธ Security Level Selection
**Default: ml-dsa-44** - Optimized for distributed systems and network protocols
- 128-bit post-quantum security (equivalent to AES-128)
- ~30% smaller tokens than ml-dsa-65
- Best for: networking protocols, authentication tokens, distributed systems
**Upgrade to ml-dsa-65** for high-value or long-term secrets
- 192-bit post-quantum security
- Larger tokens but stronger security margin
- Best for: financial systems, sensitive data, compliance requirements
**Upgrade to ml-dsa-87** for critical infrastructure
- 256-bit post-quantum security
- Largest tokens but maximum security
- Best for: government, military, long-term archival signatures
### Usage
```toml
# Default (recommended for most applications)
paseto-pq = "0.1.2"
# High security applications
paseto-pq = { version = "0.1.2", features = ["balanced"] }
# Maximum security applications
paseto-pq = { version = "0.1.2", features = ["maximum-security"] }
# Explicit parameter set selection
paseto-pq = { version = "0.1.2", features = ["ml-dsa-65"], default-features = false }
```
## ๐ Features
- **๐ Quantum-Safe**: Uses ML-DSA (NIST FIPS 204) signatures and ML-KEM-768 key exchange
- **๐ก๏ธ RFC-Compliant Security**: Proper footer authentication via PAE (v0.1.1+)
- **๐ฆ Pure Rust**: No C dependencies, built on RustCrypto
- **๐ฏ Full PASETO Parity**: Complete implementation with both public and local tokens
- **โก Practical Performance**: Optimized for real-world usage patterns
- **๐ Footer Authentication**: Cryptographic protection of footer metadata
- **๐ง Easy Integration**: Drop-in replacement for authentication and encryption tokens
- **๐ฆ Dual Token Types**: Public (signatures) and Local (symmetric encryption)
- **๐ฆถ Footer Support**: Authenticated metadata for key management and service integration
- **๐ Key Exchange**: ML-KEM for post-quantum key establishment
- **๐ JSON Integration**: Built-in JSON conversion for logging, databases, and tracing
- **๐ Token Parsing**: Fast token inspection for debugging, middleware, and monitoring
- **๐ Token Size Estimation**: Plan token usage and avoid deployment surprises
- **๐ RFC3339 Time Fields**: Standard time serialization for maximum compatibility
- **๐ก๏ธ Memory Safe**: Constant-time operations and proper secret zeroization
## ๐ Quick Start
Add this to your `Cargo.toml`:
```toml
[dependencies]
paseto-pq = "0.1.0" # Uses ml-dsa-44 by default for optimal network performance
time = { version = "0.3", features = ["serde", "formatting", "parsing"] }
rand = "0.10.0-rc.1"
```
### Public Tokens (Asymmetric Signatures)
```rust
use paseto_pq::{PasetoPQ, Claims, KeyPair};
use time::OffsetDateTime;
use rand::rng;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Generate a new key pair
let mut rng = rng();
let keypair = KeyPair::generate(&mut rng);
// Create claims
let mut claims = Claims::new();
claims.set_subject("user123")?;
claims.set_issuer("my-service")?;
claims.set_audience("api.example.com")?;
claims.set_expiration(OffsetDateTime::now_utc() + time::Duration::hours(1))?;
claims.add_custom("tenant_id", "org_abc123")?;
claims.add_custom("roles", &["user", "admin"])?;
// Sign the token (public)
let token = PasetoPQ::sign(&keypair.signing_key, &claims)?;
println!("Public Token: {}", token);
// Verify the token
let verified = PasetoPQ::verify(&keypair.verifying_key, &token)?;
let verified_claims = verified.claims();
assert_eq!(verified_claims.subject(), Some("user123"));
assert_eq!(verified_claims.issuer(), Some("my-service"));
Ok(())
}
```
### Local Tokens (Symmetric Encryption)
```rust
use paseto_pq::{PasetoPQ, Claims, SymmetricKey};
use time::OffsetDateTime;
use rand::rng;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Generate a symmetric key
let mut rng = rng();
let key = SymmetricKey::generate(&mut rng);
// Create claims (same as public tokens)
let mut claims = Claims::new();
claims.set_subject("user123")?;
claims.set_issuer("my-service")?;
claims.set_audience("api.example.com")?;
claims.set_expiration(OffsetDateTime::now_utc() + time::Duration::hours(1))?;
claims.add_custom("sensitive_data", "confidential-info")?;
// Encrypt the token (local)
let token = PasetoPQ::encrypt(&key, &claims)?;
println!("Local Token: {}", token);
// Decrypt the token
let verified = PasetoPQ::decrypt(&key, &token)?;
let verified_claims = verified.claims();
assert_eq!(verified_claims.subject(), Some("user123"));
assert_eq!(verified_claims.issuer(), Some("my-service"));
Ok(())
}
```
### Footer Support
PASETO-PQ supports authenticated footers for metadata that doesn't belong in claims:
```rust
use paseto_pq::{PasetoPQ, Claims, Footer, KeyPair};
use time::OffsetDateTime;
use rand::rng;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut rng = rng();
let keypair = KeyPair::generate(&mut rng);
// Create claims
let mut claims = Claims::new();
claims.set_subject("user123")?;
claims.set_issuer("my-service")?;
// Create footer with metadata
let mut footer = Footer::new();
footer.set_kid("signing-key-2025-01")?; // Key identifier for rotation
footer.set_version("v2.1.0")?; // Service version
footer.add_custom("trace_id", "trace-abc-123")?; // Distributed tracing
footer.add_custom("deployment", "us-east-1")?; // Infrastructure info
// Sign with footer
let token = PasetoPQ::sign_with_footer(&keypair.signing_key, &claims, Some(&footer))?;
// Verify and access footer
let verified = PasetoPQ::verify_with_footer(&keypair.verifying_key, &token)?;
let verified_footer = verified.footer().unwrap();
println!("Key ID: {}", verified_footer.kid().unwrap());
println!("Trace ID: {}", verified_footer.get_custom("trace_id").unwrap().as_str().unwrap());
Ok(())
}
```
## ๐ Token Formats
โ ๏ธ **NON-STANDARD VERSIONING**: PASETO-PQ uses a **non-standard** token format that is **incompatible** with official PASETO libraries. The `pq1` version identifier clearly indicates "post-quantum era" tokens, distinguishing them from classical algorithms defined in the PASETO specification.
### Compatibility Impact
- **NOT compatible** with existing PASETO libraries (paseto.js, paseto-dotnet, etc.)
- **NOT compatible** with standard PASETO tooling
- **Cannot be verified** by standard PASETO implementations
- **Intentionally incompatible** to prevent mixing with classical tokens
### When To Use This Crate
- โ
Greenfield applications requiring post-quantum security
- โ
Internal systems where PASETO compatibility is not required
- โ
Future migration paths when post-quantum PASETO standards emerge
- โ Systems requiring interoperability with existing PASETO ecosystems
Both token types support optional footers:
### Public Tokens (Signatures)
```
# Without footer
paseto.pq1.public.<base64url-payload>.<base64url-signature>
# With footer
paseto.pq1.public.<base64url-payload>.<base64url-signature>.<base64url-footer>
```
- **`paseto`**: Protocol identifier
- **`pq1`**: Post-quantum version identifier (non-standard, distinct from official PASETO)
- **`public`**: Purpose (signature-based tokens)
- **`payload`**: Base64url-encoded JSON claims
- **`signature`**: Base64url-encoded ML-DSA signature (size varies by parameter set)
- **`footer`**: Base64url-encoded JSON metadata (optional, authenticated)
### Local Tokens (Encryption)
```
# Without footer
paseto.pq1.local.<base64url-encrypted-payload>
# With footer
paseto.pq1.local.<base64url-encrypted-payload>.<base64url-footer>
```
- **`paseto`**: Protocol identifier
- **`pq1`**: Post-quantum version identifier (non-standard, distinct from official PASETO)
- **`local`**: Purpose (symmetric encryption)
- **`encrypted-payload`**: Base64url-encoded nonce + ChaCha20-Poly1305 ciphertext
- **`footer`**: Base64url-encoded JSON metadata (optional, encrypted with payload)
## ๐ Performance Characteristics
## ๐ง Performance & Size Comparison
| **ml-dsa-44** | 128-bit (default) | ~2,420 bytes | ~1,312 bytes | **~3.2-3.4KB** |
| **ml-dsa-65** | 192-bit | ~3,309 bytes | ~1,952 bytes | **~4.3-4.5KB** |
| **ml-dsa-87** | 256-bit | ~4,627 bytes | ~2,592 bytes | **~6.0-6.2KB** |
**Comparison to Classical Algorithms:**
| Key Generation | ~10-30ms | ~100ยตs | 100-300x slower |
| Signing | ~5-20ms | ~50ยตs | 100-400x slower |
| Verification | ~2-5ms | ~80ยตs | 25-60x slower |
| Signature Size | 2,420-4,627 bytes | 64 bytes | 38-72x larger |
| Public Key | 1,312-2,592 bytes | 32 bytes | 41-81x larger |
### Local Tokens (ChaCha20-Poly1305)
| Key Generation | ~1ยตs | ~1ยตs | ~1x |
| Encryption | ~1-5ยตs | ~1-5ยตs | ~1x |
| Decryption | ~1-5ยตs | ~1-5ยตs | ~1x |
| Token Overhead | ~30 bytes | ~30 bytes | ~1x |
| Symmetric Key | 32 bytes | 32 bytes | 1x |
| **Token Size** | **~100-300 bytes** | **~100-300 bytes** | **~1x** |
### Key Exchange (ML-KEM-768)
| Key Generation | ~100ยตs | ~50ยตs | 2x slower |
| Encapsulation | ~150ยตs | ~100ยตs | 1.5x slower |
| Decapsulation | ~200ยตs | ~100ยตs | 2x slower |
| Ciphertext Size | 1,088 bytes | 33 bytes | 33x larger |
| Public Key | 1,184 bytes | 33 bytes | 36x larger |
**Note**: Performance varies by hardware. These numbers are from benchmarks on modern x86-64.
**Token Size Implications**:
- **Public tokens (~4.4KB)**: Not suitable for cookies or URLs but perfect for Authorization headers
- **Local tokens (~200 bytes)**: Suitable for all transport methods including cookies
- **Footer overhead**: ~50-200 bytes depending on metadata complexity
## ๐ฏ Use Cases
### Public Tokens (ML-DSA Signatures)
**โ
Recommended For:**
- **Inter-service authentication** (API keys, service tokens)
- **Authorization headers** (4KB typical limit allows comfortable usage)
- **Non-repudiation requirements** (audit trails, legal evidence)
- **Public key infrastructure** (distributed verification)
- **Long-term security** (5+ year lifetime)
- **High-security applications** (financial, government, healthcare)
**โ ๏ธ Consider Carefully:**
- **Browser cookies** (4KB browser limit, no room for other data)
- **URL parameters** (2KB practical limit)
- **High-frequency operations** (>1000/sec per core)
- **Real-time applications** (signing latency considerations)
### Local Tokens (Symmetric Encryption)
**โ
Recommended For:**
- **Session management** (user sessions, temporary access)
- **Cookie-based authentication** (small size, fits easily)
- **Confidential data transport** (encrypted payloads)
- **High-performance scenarios** (fast encrypt/decrypt)
- **Internal services** (shared secret available)
- **Mobile applications** (bandwidth efficiency)
**โ ๏ธ Consider Carefully:**
- **Key distribution** (shared secret management complexity)
- **Multi-party scenarios** (single shared key limitation)
- **Long-term storage** (key rotation complexity)
### Key Exchange (ML-KEM)
**โ
Recommended For:**
- **Establishing shared secrets** for local tokens
- **Hybrid workflows** (KEM + local tokens)
- **Forward secrecy** requirements
- **Quantum-safe key agreement**
- **Zero-knowledge protocols**
## ๐ง Advanced Usage
### Public Token Validation
```rust
use paseto_pq::{PasetoPQ, Claims, KeyPair};
use time::Duration;
let verified = PasetoPQ::verify_with_options(
&verifying_key,
&token,
Some("expected-audience"), // Validate audience
Some("expected-issuer"), // Validate issuer
Duration::minutes(5), // Clock skew tolerance
)?;
```
### Footer Operations
```rust
use paseto_pq::{Footer, PasetoPQ};
// Create and populate footer
let mut footer = Footer::new();
footer.set_kid("key-2025-01")?;
footer.set_version("v1.0.0")?;
footer.add_custom("trace_id", "abc-123")?;
footer.add_custom("environment", "production")?;
// Use with public tokens (footer is authenticated by signature)
let token = PasetoPQ::sign_with_footer(&signing_key, &claims, Some(&footer))?;
let verified = PasetoPQ::verify_with_footer(&verifying_key, &token)?;
// Access footer data
if let Some(footer_data) = verified.footer() {
println!("Key ID: {}", footer_data.kid().unwrap_or("none"));
println!("Trace: {}", footer_data.get_custom("trace_id").unwrap().as_str().unwrap());
}
// Use with local tokens (footer is encrypted with payload)
let local_token = PasetoPQ::encrypt_with_footer(&symmetric_key, &claims, Some(&footer))?;
let decrypted = PasetoPQ::decrypt_with_footer(&symmetric_key, &local_token)?;
```
### Local Token Validation
```rust
use paseto_pq::{PasetoPQ, SymmetricKey};
use time::Duration;
let verified = PasetoPQ::decrypt_with_options(
&symmetric_key,
&token,
Some("expected-audience"), // Validate audience
Some("expected-issuer"), // Validate issuer
Duration::minutes(5), // Clock skew tolerance
)?;
```
### Key Serialization
```rust
// Public/Private keys
let signing_bytes = keypair.signing_key_to_bytes();
let verifying_bytes = keypair.verifying_key_to_bytes();
let signing_key = KeyPair::signing_key_from_bytes(&signing_bytes)?;
let verifying_key = KeyPair::verifying_key_from_bytes(&verifying_bytes)?;
// Symmetric keys
let sym_bytes = symmetric_key.to_bytes();
let symmetric_key = SymmetricKey::from_bytes(&sym_bytes)?;
// KEM keys
let enc_bytes = kem_keypair.encapsulation_key_to_bytes();
let dec_bytes = kem_keypair.decapsulation_key_to_bytes();
let enc_key = KemKeyPair::encapsulation_key_from_bytes(&enc_bytes)?;
let dec_key = KemKeyPair::decapsulation_key_from_bytes(&dec_bytes)?;
```
### Post-Quantum Key Exchange
```rust
use paseto_pq::{KemKeyPair, SymmetricKey, PasetoPQ};
use rand::rng;
// Generate KEM keypair
let mut rng = rng();
let kem_keypair = KemKeyPair::generate(&mut rng);
// Sender: encapsulate shared secret
let (shared_key_sender, ciphertext) = kem_keypair.encapsulate(&mut rng);
// Receiver: decapsulate shared secret
let shared_key_receiver = kem_keypair.decapsulate(&ciphertext)?;
// Both parties now have the same symmetric key
assert_eq!(shared_key_sender.to_bytes(), shared_key_receiver.to_bytes());
// Use for local tokens
let token = PasetoPQ::encrypt(&shared_key_sender, &claims)?;
let verified = PasetoPQ::decrypt(&shared_key_receiver, &token)?;
```
### Custom Claims
```rust
let mut claims = Claims::new();
claims.set_subject("user123")?;
// Add custom business logic
claims.add_custom("tenant_id", "org_123")?;
claims.add_custom("permissions", &["read:users", "write:posts"])?;
claims.add_custom("metadata", &serde_json::json!({
"client_version": "2.1.0",
"platform": "web"
}))?;
// Works with both public and local tokens
let public_token = PasetoPQ::sign(&keypair.signing_key, &claims)?;
let local_token = PasetoPQ::encrypt(&symmetric_key, &claims)?;
// Access custom claims after verification/decryption
if let Some(tenant) = verified_claims.get_custom("tenant_id") {
println!("Tenant: {}", tenant.as_str().unwrap());
}
```
### JSON Integration
PASETO-PQ provides seamless JSON integration for easy use with logging systems, databases, and distributed tracing:
```rust
use paseto_pq::Claims;
use serde_json::Value;
use time::OffsetDateTime;
let mut claims = Claims::new();
claims.set_subject("user123")?;
claims.set_issuer("auth-service")?;
claims.set_expiration(OffsetDateTime::now_utc() + time::Duration::hours(2))?;
claims.add_custom("tenant_id", "org_abc123")?;
claims.add_custom("roles", &["admin", "user"])?;
// Convert to JSON Value for flexible use
let json_value: Value = claims.clone().into();
// Convert to JSON string for logging
let json_string = claims.to_json_string()?;
println!("User authenticated: {}", json_string);
// Pretty JSON for debugging
let pretty_json = claims.to_json_string_pretty()?;
// Time fields are RFC3339 strings for maximum compatibility
// {"exp": "2025-01-15T10:30:00Z", "iat": "2025-01-14T10:30:00Z"}
```
**Integration Examples:**
- **Structured Logging**: Direct JSON serialization for ELK Stack, Datadog, Splunk
- **Database Storage**: PostgreSQL JSONB, MongoDB document storage
- **Distributed Tracing**: OpenTelemetry span attributes, Jaeger context
- **Audit Trails**: Compliance logging with embedded claims data
- **Monitoring**: Grafana dashboards with JSON-queryable token data
Run the JSON integration demo:
```bash
cargo run --example json_integration_demo
```
### Token Parsing
Parse tokens for inspection without expensive cryptographic operations. Perfect for debugging, middleware routing, logging, and monitoring:
```rust
use paseto_pq::{ParsedToken, PasetoPQ};
let token = "paseto.pq1.public.ABC123...";
let parsed = ParsedToken::parse(token)?;
// Inspect token structure (no crypto operations)
println!("Purpose: {}", parsed.purpose()); // "public" or "local"
println!("Version: {}", parsed.version()); // "pq1"
println!("Has footer: {}", parsed.has_footer());
println!("Size: {} bytes", parsed.total_length());
println!("Is public token: {}", parsed.is_public());
println!("Is local token: {}", parsed.is_local());
// Middleware routing based on token type
match parsed.purpose() {
"public" => route_to_signature_service(token),
"local" => route_to_decryption_service(token),
_ => return_error("unsupported token type"),
}
// Debugging information
println!("Token summary: {}", parsed.format_summary());
if let Some(footer) = parsed.footer() {
if let Some(kid) = footer.kid() {
println!("Key ID: {}", kid);
}
// Pretty-print footer JSON
println!("Footer: {}", parsed.footer_json_pretty()?);
}
// Quick access with PasetoPQ wrapper
let parsed_alt = PasetoPQ::parse_token(token)?;
```
**Use Cases:**
- **API Gateway Routing**: Route tokens to appropriate handlers based on type
- **Monitoring & Metrics**: Collect token statistics without crypto overhead
- **Debugging**: Inspect malformed or problematic tokens quickly
- **Load Balancing**: Route based on token size or metadata
- **Logging**: Extract non-sensitive metadata for log correlation
Run the token parsing demo:
```bash
cargo run --example token_parsing_demo
```
### Token Size Estimation
Estimate token sizes before creation to avoid runtime surprises with HTTP headers, cookies, or URL length limits:
```rust
use paseto_pq::{Claims, TokenSizeEstimator, PasetoPQ};
let mut claims = Claims::new();
claims.set_subject("user123")?;
claims.add_custom("role", "admin")?;
claims.add_custom("permissions", &["read", "write", "admin"])?;
// Estimate before creating
let estimator = TokenSizeEstimator::public(&claims, false);
println!("Public token will be ~{} bytes", estimator.total_bytes());
// Check transport compatibility
if !estimator.fits_in_cookie() {
println!("โ ๏ธ Warning: Token too large for cookies (4KB limit)!");
println!("๐ก Consider using session storage or local tokens instead");
}
if estimator.fits_in_header() {
println!("โ
Token fits in Authorization header (8KB typical limit)");
}
if estimator.fits_in_url() {
println!("โ
Token fits in URL parameters (2KB practical limit)");
} else {
println!("โ ๏ธ Token too large for URL parameters");
}
// Get detailed breakdown
let breakdown = estimator.breakdown();
println!("Size breakdown:");
println!(" Payload: {} bytes", breakdown.payload);
println!(" Signature: {} bytes", breakdown.signature_or_tag);
println!(" Base64 overhead: {} bytes", breakdown.base64_overhead);
println!(" Total: {} bytes", breakdown.total());
// Get optimization suggestions
if estimator.total_bytes() > 4000 {
for suggestion in estimator.optimization_suggestions() {
println!("๐ก {}", suggestion);
}
}
// Compare token types
let public_est = PasetoPQ::estimate_public_size(&claims, false);
let local_est = PasetoPQ::estimate_local_size(&claims, false);
println!("Size comparison:");
println!(" Public token: {} bytes", public_est.total_bytes());
println!(" Local token: {} bytes", local_est.total_bytes());
// Compare to JWT (for reference)
println!("Compared to JWT: {}", estimator.compare_to_jwt());
// Generate size summary
println!("Summary: {}", estimator.size_summary());
```
**Size Limits & Recommendations:**
- **HTTP Cookies**: 4KB browser limit (use local tokens or session storage)
- **URL Parameters**: 2KB practical limit (use Authorization header instead)
- **HTTP Headers**: 8KB typical server limit (public tokens fit comfortably)
- **JSON Payloads**: No practical limit (both token types work well)
**Use Cases:**
- **Production Planning**: Avoid deployment surprises and HTTP 413 errors
- **Architecture Decisions**: Choose between public/local tokens based on size
- **Transport Selection**: Select appropriate delivery method (header/cookie/body)
- **Performance Optimization**: Identify oversized tokens early
Run the token size estimation demo:
```bash
cargo run --example token_size_demo
```
## ๐ฌ Security Considerations
### Post-Quantum Security
- **ML-DSA-65**: NIST FIPS 204 standardized signature algorithm
- **Security Level**: NIST Level 3 (~192-bit classical security, quantum-safe)
- **Quantum Resistance**: Secure against Shor's algorithm and known quantum attacks
- **ChaCha20-Poly1305**: 256-bit symmetric encryption, quantum-resistant for key sizes
- **ML-KEM-768**: NIST-standardized key encapsulation, quantum-safe key establishment
### Implementation Security
- **Memory Safety**: Pure Rust implementation prevents buffer overflows
- **Constant-Time**: Operations designed to prevent timing attacks where possible
- **Secret Zeroization**: Symmetric keys automatically zeroized on drop via `ZeroizeOnDrop` trait
- **Key Cleanup**: All key types implement `Drop` for automatic cleanup when out of scope
- **RustCrypto**: Built on well-audited cryptographic primitives with zeroize features enabled
- **HKDF Key Derivation**: RFC 5869 HKDF-SHA256 for cryptographically sound key derivation from ML-KEM shared secrets
- **No Side Channels**: Careful implementation to prevent information leakage
### Operational Security
- **Token Parsing Safety**: `ParsedToken::parse()` performs no crypto operations, safe for untrusted input
- **Footer Authentication**: Public token footers covered by ML-DSA signature
- **Footer Confidentiality**: Local token footers encrypted with ChaCha20-Poly1305
- **Time Validation**: Built-in expiration and not-before checks with configurable clock skew
- **Audience Validation**: Cryptographically enforced recipient verification
### Token Size Security Implications
- **Public tokens (~4.4KB)**: Large size makes them impractical for cookies but perfect for headers
- **Information Leakage**: Token size may reveal information about claims structure
- **DoS Considerations**: Large tokens consume more bandwidth and processing time
- **Recommendation**: Use local tokens for size-sensitive applications
## ๐๏ธ Feature Flags
```toml
[dependencies]
paseto-pq = { version = "0.1.0", features = ["logging"] }
```
### Available Features
- **`logging`** - Enable structured logging with tracing
- **`std`** - Standard library support (enabled by default)
- `serde`: JSON serialization support (enabled by default)
- `time`: Time-based claims validation (enabled by default)
All major features are enabled by default for ease of use.
## ๐งช Testing
Run the complete test suite:
```bash
# Run all tests with full backtrace (recommended)
RUST_BACKTRACE=full cargo nextest run --workspace --all-targets
# Run specific test categories
cargo test test_keypair_generation
cargo test test_json_conversion
cargo test test_token_parsing
cargo test test_token_size_estimation
# Run examples to verify functionality
cargo run --example json_integration_demo
cargo run --example token_parsing_demo
cargo run --example token_size_demo
cargo run --example footer_demo
cargo run --example local_tokens_demo
# Run benchmarks
cargo bench
```
## ๐ค Contributing
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
### Development Setup
```bash
git clone https://github.com/thatnewyorker/paseto-pq.git
cd paseto-pq
cargo build
cargo test
```
## ๐ License
Licensed under either of:
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE))
- MIT License ([LICENSE-MIT](LICENSE-MIT))
at your option.
## โ ๏ธ Security Warning
**IMPORTANT**: This implementation has not yet undergone independent security audit. While built on NIST-standardized algorithms and well-tested Rust cryptographic libraries, please conduct your own security review before using in production systems.
### Cryptographic Foundations
- **ML-DSA (Dilithium)**: NIST FIPS 204 standardized post-quantum signature scheme
- **ChaCha20-Poly1305**: RFC 8439 authenticated encryption
- **ML-KEM (Kyber)**: NIST FIPS 203 standardized post-quantum KEM
- **SHA-3**: NIST FIPS 202 cryptographic hash function
### Footer Security Properties
- **Public Tokens**: Footer authenticated by ML-DSA signature (tamper-evident)
- **Local Tokens**: Footer encrypted with ChaCha20-Poly1305 (confidential and authenticated)
- **Size Considerations**: Footers add ~50-200 bytes depending on metadata complexity
## ๐ Acknowledgments
- NIST Post-Quantum Cryptography Standardization team
- RustCrypto organization for cryptographic primitives
- PASETO specification contributors
- Rust community for excellent tooling and libraries
## ๐ Further Reading
- [PASETO Specification](https://paseto.io/)
- [NIST Post-Quantum Cryptography Standards](https://csrc.nist.gov/projects/post-quantum-cryptography)
- [ML-DSA (FIPS 204)](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.204.pdf)
- [ML-KEM (FIPS 203)](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.pdf)
- [ChaCha20-Poly1305 (RFC 8439)](https://tools.ietf.org/html/rfc8439)