paseto-pq

Crates.iopaseto-pq
lib.rspaseto-pq
version0.1.2
created_at2025-11-16 14:41:06.127896+00
updated_at2025-11-17 16:09:46.350938+00
descriptionPost-quantum PASETO tokens with RFC-compliant footer authentication using ML-DSA signatures
homepagehttps://github.com/thatnewyorker/paseto-pq
repositoryhttps://github.com/thatnewyorker/paseto-pq
max_upload_size
id1935565
size340,731
Gerard (thatnewyorker)

documentation

README

PASETO-PQ: Post-Quantum PASETO Tokens

Crates.io crates.io Documentation CI License: MIT OR Apache-2.0 Rust

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

# 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:

[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)

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)

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:

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

Parameter Set Security Level Signature Size Public Key Size Token Size (approx.)
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:

Operation ML-DSA (avg) Ed25519 (reference) Ratio
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)

Operation PASETO-PQ Local Traditional PASETO v4.local Ratio
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)

Operation ML-KEM-768 ECDH P-256 (reference) Ratio
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

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

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

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

// 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

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

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:

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:

cargo run --example json_integration_demo

Token Parsing

Parse tokens for inspection without expensive cryptographic operations. Perfect for debugging, middleware routing, logging, and monitoring:

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:

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:

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:

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

[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:

# 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 for details.

Development Setup

git clone https://github.com/thatnewyorker/paseto-pq.git
cd paseto-pq
cargo build
cargo test

๐Ÿ“œ License

Licensed under either of:

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

Commit count: 0

cargo fmt