Rust implementation of the Bottle protocol - a layered message container system with encryption and signatures. This library provides secure, type-safe cryptographic operations for building privacy-preserving applications.
rust-bottle implements the Bottle protocol, which provides layered message containers with support for multiple encryption layers, multiple signatures, key management through IDCards and Keychains, and cryptographically signed group memberships. The library is designed to match the functionality of the Go implementation gobottle while leveraging Rust's type safety and memory safety guarantees.
- Layered message containers (Bottles) with multiple encryption and signature layers
- Metadata storage for application-specific data
- Binary serialization using bincode for efficient storage and transmission
- Opener API for decrypting and inspecting bottles without full decryption
- Elliptic Curve Diffie-Hellman (ECDH) encryption using X25519 and P-256
- RSA encryption/decryption using RSA-OAEP with SHA-256
- AES-256-GCM authenticated encryption for shared secret encryption
- Digital signatures using ECDSA (P-256, P-384, P-521), Ed25519, and RSA
- Automatic key type detection for encryption and decryption operations
- IDCards for declaring sub-keys with specific purposes (sign, decrypt, etc.)
- Key lifecycle management with expiration dates
- Keychains for secure storage and retrieval of private keys
- Public key fingerprinting for key identification
- Cryptographically signed group memberships
- Role and metadata storage in memberships
- Verification of memberships against group IDCards
- Secure memory clearing using zeroize
- Type-safe cryptographic operations
- Comprehensive error handling
- No unsafe code in public API
Add rust-bottle to your Cargo.toml:
[dependencies]
rust-bottle = "0.2.0"
rand = "0.8"use rust_bottle::*;
use rand::rngs::OsRng;
// Create a message container
let message = b"Hello, Bottle!";
let mut bottle = Bottle::new(message.to_vec());
// Generate encryption keys
let rng = &mut OsRng;
let key = X25519Key::generate(rng);
// Encrypt the bottle
bottle.encrypt(rng, &key.public_key_bytes()).unwrap();
// Decrypt and open the bottle
let opener = Opener::new();
let decrypted = opener.open(&bottle, Some(&key.private_key_bytes())).unwrap();
assert_eq!(decrypted, message);use rust_bottle::*;
use rand::rngs::OsRng;
let message = b"Signed message";
let mut bottle = Bottle::new(message.to_vec());
// Generate signing key
let rng = &mut OsRng;
let signing_key = Ed25519Key::generate(rng);
let public_key = signing_key.public_key_bytes();
// Sign the bottle
bottle.sign(rng, &signing_key, &public_key).unwrap();
// Verify signature
let opener = Opener::new();
let info = opener.open_info(&bottle).unwrap();
assert!(info.is_signed_by(&public_key));Bottles are layered message containers that support multiple encryption and signature layers. Each encryption layer can be for a different recipient, and multiple signers can sign the same bottle.
use rust_bottle::*;
use rand::rngs::OsRng;
// Create a bottle with a message
let message = b"Multi-layer encrypted and signed message";
let mut bottle = Bottle::new(message.to_vec());
// Add metadata
bottle.set_metadata("sender", "alice@example.com");
bottle.set_metadata("timestamp", "2024-01-01T00:00:00Z");
// Encrypt to multiple recipients (layered encryption)
let rng = &mut OsRng;
let bob_key = X25519Key::generate(rng);
let charlie_key = X25519Key::generate(rng);
// First encryption layer (innermost)
bottle.encrypt(rng, &bob_key.public_key_bytes()).unwrap();
// Second encryption layer (outermost)
bottle.encrypt(rng, &charlie_key.public_key_bytes()).unwrap();
// Sign with multiple signers
let alice_signing_key = Ed25519Key::generate(rng);
let alice_pub = alice_signing_key.public_key_bytes();
bottle.sign(rng, &alice_signing_key, &alice_pub).unwrap();
// Serialize for storage or transmission
let serialized = bottle.to_bytes().unwrap();
// Deserialize
let deserialized = Bottle::from_bytes(&serialized).unwrap();
// Open with appropriate key (decrypts all layers)
let opener = Opener::new();
let decrypted = opener.open(&deserialized, Some(&bob_key.private_key_bytes())).unwrap();
assert_eq!(decrypted, message);IDCards allow entities to declare multiple keys with specific purposes and manage key lifecycles. They can be signed to establish trust.
use rust_bottle::*;
use rand::rngs::OsRng;
use std::time::{Duration, SystemTime};
let rng = &mut OsRng;
// Generate primary signing key
let primary_key = Ed25519Key::generate(rng);
let primary_pub = primary_key.public_key_bytes();
// Create IDCard
let mut idcard = IDCard::new(&primary_pub);
// Add metadata
idcard.set_metadata("name", "Alice");
idcard.set_metadata("email", "alice@example.com");
idcard.set_metadata("organization", "Example Corp");
// Set purposes for the primary key
idcard.set_key_purposes(&primary_pub, &["sign", "decrypt"]);
// Add a dedicated encryption key with expiration
let encryption_key = X25519Key::generate(rng);
let encryption_pub = encryption_key.public_key_bytes();
idcard.set_key_purposes(&encryption_pub, &["decrypt"]);
idcard.set_key_duration(&encryption_pub, Duration::from_secs(365 * 24 * 3600)); // 1 year
// Sign the IDCard with the primary key
let signed_idcard = idcard.sign(rng, &primary_key, &primary_pub).unwrap();
// Verify key purposes
assert!(idcard.test_key_purpose(&primary_pub, "sign").is_ok());
assert!(idcard.test_key_purpose(&encryption_pub, "decrypt").is_ok());
assert!(idcard.test_key_purpose(&encryption_pub, "sign").is_err());
// Get all keys for a specific purpose
let decrypt_keys = idcard.get_keys("decrypt");
assert_eq!(decrypt_keys.len(), 2);Keychains provide secure storage for private keys, indexed by their public key fingerprints. They enable signing with specific keys without exposing the key selection logic.
use rust_bottle::*;
use rand::rngs::OsRng;
let rng = &mut OsRng;
// Create a keychain
let mut keychain = Keychain::new();
// Add multiple keys of different types
let ed25519_key = Ed25519Key::generate(rng);
let ecdsa_key = EcdsaP256Key::generate(rng);
let x25519_key = X25519Key::generate(rng);
keychain.add_key(ed25519_key);
keychain.add_key(ecdsa_key);
keychain.add_key(x25519_key);
// Retrieve a signer by public key
let ed25519_pub = ed25519_key.public_key_bytes();
let signer = keychain.get_signer(&ed25519_pub).unwrap();
// Sign a message
let message = b"Message to sign";
let signature = keychain.sign(rng, &ed25519_pub, message).unwrap();
// Use keychain with bottles
let mut bottle = Bottle::new(b"Keychain-signed message".to_vec());
bottle.sign(rng, signer, &ed25519_pub).unwrap();Memberships provide cryptographically signed group affiliations, allowing entities to prove membership in groups with specific roles.
use rust_bottle::*;
use rand::rngs::OsRng;
let rng = &mut OsRng;
// Create member and group IDCards
let member_key = Ed25519Key::generate(rng);
let member_pub = member_key.public_key_bytes();
let member_idcard = IDCard::new(&member_pub);
let group_key = Ed25519Key::generate(rng);
let group_pub = group_key.public_key_bytes();
let group_idcard = IDCard::new(&group_pub);
// Create a membership
let mut membership = Membership::new(&member_idcard, &group_pub);
membership.set_info("role", "admin");
membership.set_info("department", "Engineering");
membership.set_info("joined", "2024-01-01");
// Sign the membership with the group owner's key
membership.sign(rng, &group_key, &group_pub).unwrap();
// Verify the membership
assert!(membership.verify(&group_idcard).is_ok());
// Add membership to member's IDCard
let mut member_idcard = IDCard::new(&member_pub);
member_idcard.update_groups(vec![membership.to_bytes().unwrap()]);Direct ECDH encryption can be used independently of bottles for encrypting data to public keys.
use rust_bottle::*;
use rand::rngs::OsRng;
let plaintext = b"Secret message";
let rng = &mut OsRng;
// Generate key pairs for Alice and Bob
let alice_key = X25519Key::generate(rng);
let bob_key = X25519Key::generate(rng);
// Alice encrypts to Bob's public key
let ciphertext = ecdh_encrypt(
rng,
plaintext,
&bob_key.public_key_bytes()
).unwrap();
// Bob decrypts with his private key
let decrypted = ecdh_decrypt(
&ciphertext,
&bob_key.private_key_bytes()
).unwrap();
assert_eq!(decrypted, plaintext);Post-quantum cryptography support is available via feature flags. See PQC_FEATURE_FLAG.md for details on enabling PQC features.
ML-KEM (Module-Lattice-Based Key-Encapsulation Mechanism) provides post-quantum encryption. Requires the ml-kem feature:
#[cfg(feature = "ml-kem")]
use rust_bottle::*;
use rand::rngs::OsRng;
let plaintext = b"Post-quantum encrypted message";
let rng = &mut OsRng;
// Generate ML-KEM-768 keys
let alice_key = MlKem768Key::generate(rng);
let bob_key = MlKem768Key::generate(rng);
// Alice encrypts to Bob's public key
let ciphertext = mlkem768_encrypt(
rng,
plaintext,
&bob_key.public_key_bytes()
).unwrap();
// Bob decrypts
let decrypted = mlkem768_decrypt(
&ciphertext,
&bob_key.private_key_bytes()
).unwrap();
assert_eq!(decrypted, plaintext);ML-DSA (Module-Lattice-Based Digital Signature Algorithm) provides post-quantum signatures. Requires the post-quantum feature:
#[cfg(feature = "post-quantum")]
use rust_bottle::*;
use rand::rngs::OsRng;
let message = b"Post-quantum signed message";
let rng = &mut OsRng;
// Generate ML-DSA-44 signing key (128-bit security)
let signing_key = MlDsa44Key::generate(rng);
let pub_key = signing_key.public_key_bytes();
// Sign the message
let signature = signing_key.sign(rng, message).unwrap();
// Verify signature
assert!(signing_key.verify(message, &signature).is_ok());SLH-DSA (Stateless Hash-Based Digital Signature Algorithm) provides hash-based post-quantum signatures. Requires the post-quantum feature:
#[cfg(feature = "post-quantum")]
use rust_bottle::*;
use rand::rngs::OsRng;
let message = b"Hash-based signed message";
let rng = &mut OsRng;
// Generate SLH-DSA-128s signing key (128-bit security)
let signing_key = SlhDsa128sKey::generate(rng);
let pub_key = signing_key.public_key_bytes();
// Sign the message
let signature = signing_key.sign(rng, message).unwrap();
// Verify signature
assert!(signing_key.verify(message, &signature).is_ok());Hybrid encryption provides both post-quantum and classical security. Requires the ml-kem feature:
#[cfg(feature = "ml-kem")]
use rust_bottle::*;
use rand::rngs::OsRng;
let plaintext = b"Hybrid encrypted message";
let rng = &mut OsRng;
// Generate both post-quantum and classical keys
let mlkem_key = MlKem768Key::generate(rng);
let x25519_key = X25519Key::generate(rng);
// Encrypt with both (provides both post-quantum and classical security)
let ciphertext = hybrid_encrypt_mlkem768_x25519(
rng,
plaintext,
&mlkem_key.public_key_bytes(),
&x25519_key.public_key_bytes(),
).unwrap();
// Decrypt (tries ML-KEM first, falls back to X25519)
let mlkem_sec = mlkem_key.private_key_bytes();
let x25519_sec: [u8; 32] = x25519_key.private_key_bytes().try_into().unwrap();
let decrypted = hybrid_decrypt_mlkem768_x25519(
&ciphertext,
&mlkem_sec,
&x25519_sec,
).unwrap();
assert_eq!(decrypted, plaintext);Post-quantum keys work seamlessly with the Bottle API:
#[cfg(feature = "post-quantum")]
use rust_bottle::*;
use rand::rngs::OsRng;
let mut bottle = Bottle::new(b"Post-quantum secure message".to_vec());
let rng = &mut OsRng;
// Encrypt with ML-KEM (requires ml-kem feature)
#[cfg(feature = "ml-kem")]
{
let mlkem_key = MlKem768Key::generate(rng);
bottle.encrypt(rng, &mlkem_key.public_key_bytes()).unwrap();
}
// Sign with ML-DSA (requires post-quantum feature)
let mldsa_key = MlDsa44Key::generate(rng);
let pub_key = mldsa_key.public_key_bytes();
bottle.sign(rng, &mldsa_key, &pub_key).unwrap();
// Decrypt and verify
let opener = Opener::new();
#[cfg(feature = "ml-kem")]
{
let mlkem_key = MlKem768Key::generate(rng);
let decrypted = opener.open(&bottle, Some(&mlkem_key.private_key_bytes())).unwrap();
}
let info = opener.open_info(&bottle).unwrap();
assert!(info.is_signed_by(&pub_key));RSA support is available for both encryption and signing operations. RSA is useful for compatibility with legacy systems, though modern applications should prefer ECDSA/Ed25519 for signatures and X25519 for encryption.
use rust_bottle::*;
use rand::rngs::OsRng;
let rng = &mut OsRng;
// Generate RSA key pair (2048-bit or 4096-bit)
let rsa_key = RsaKey::generate(rng, 2048).unwrap();
// RSA Encryption (for small messages)
let plaintext = b"Small message";
let ciphertext = rsa_encrypt(rng, plaintext, rsa_key.public_key()).unwrap();
let decrypted = rsa_decrypt(&ciphertext, &rsa_key).unwrap();
assert_eq!(decrypted, plaintext);
// RSA Signing
let message = b"Message to sign";
let signature = rsa_key.sign(rng, message).unwrap();
assert!(rsa_key.verify(message, &signature).is_ok());
// Use RSA with Bottles
let mut bottle = Bottle::new(b"RSA-encrypted message".to_vec());
// Note: RSA encryption is limited to small messages (key_size - 42 bytes)
// For larger messages, use RSA to encrypt a symmetric keyNote: RSA can only encrypt small messages (typically up to key_size - 42 bytes for OAEP with SHA-256). For larger messages, use RSA to encrypt a symmetric key and then encrypt the message with that key.
The library also supports P-256 ECDH for compatibility with ECDSA keys.
use rust_bottle::*;
use rand::rngs::OsRng;
let plaintext = b"P-256 encrypted message";
let rng = &mut OsRng;
// Generate P-256 key pair
let key = EcdsaP256Key::generate(rng);
let public_key = key.public_key_bytes();
// Encrypt
let ciphertext = ecdh_encrypt(rng, plaintext, &public_key).unwrap();
// Decrypt (requires private key bytes)
let private_key_bytes = key.private_key_bytes();
let decrypted = ecdh_decrypt(&ciphertext, &private_key_bytes).unwrap();
assert_eq!(decrypted, plaintext);| Algorithm | Purpose | Status | Notes |
|---|---|---|---|
| ECDSA P-256 | Signing | Supported | Full implementation |
| ECDSA P-384 | Signing | Supported | Full implementation |
| ECDSA P-521 | Signing | Supported | Full implementation |
| Ed25519 | Signing | Supported | Full implementation |
| RSA | Signing/Encryption | Supported | RSA-OAEP encryption, PKCS#1 v1.5 signing |
| X25519 | Encryption | Supported | ECDH key exchange |
| P-256 ECDH | Encryption | Supported | ECDH key exchange |
| AES-256-GCM | Encryption | Supported | Used for shared secret encryption |
| SHA-256 | Hashing | Supported | Key fingerprinting and message hashing |
| SHA-3 | Hashing | Supported | Available for custom use |
Comprehensive post-quantum cryptography support is available via feature flags. The implementation includes:
Encryption (requires ml-kem feature):
- ML-KEM-768: Post-quantum encryption (192-bit security) - 1184 byte public keys, 2400 byte secret keys
- ML-KEM-1024: Post-quantum encryption (256-bit security) - 1568 byte public keys, 3168 byte secret keys
Signatures (requires post-quantum feature):
- ML-DSA-44: Post-quantum signatures (128-bit security) - Uses dilithium2
- ML-DSA-65: Post-quantum signatures (192-bit security) - Uses dilithium3
- ML-DSA-87: Post-quantum signatures (256-bit security) - Uses dilithium5
- SLH-DSA-128s/128f: Hash-based signatures (128-bit security) - SHAKE-256 variants
- SLH-DSA-192s/192f: Hash-based signatures (192-bit security) - SHAKE-256 variants
- SLH-DSA-256s/256f: Hash-based signatures (256-bit security) - SHAKE-256 variants
- SLH-DSA-SHA2-128s/128f: Hash-based signatures (128-bit security) - SHA-2 variants
- SLH-DSA-SHA2-192s/192f: Hash-based signatures (192-bit security) - SHA-2 variants
- SLH-DSA-SHA2-256s/256f: Hash-based signatures (256-bit security) - SHA-2 variants
- Total: 12 SLH-DSA variants matching FIPS 205 standard
Hybrid Encryption (requires ml-kem feature):
- ML-KEM-768 + X25519: Combines post-quantum and classical security
# Enable signatures only (ML-DSA and SLH-DSA)
cargo build --features post-quantum
# Enable encryption (ML-KEM) - works on all platforms including macOS/ARM
cargo build --features post-quantum,ml-kem
# Enable everything
cargo build --features post-quantum,ml-kemNote: ML-KEM uses RustCrypto's pure Rust ml-kem crate, which works on all platforms including macOS/ARM. No platform-specific issues or patches are required.
All post-quantum algorithms are integrated into the existing API and work seamlessly with:
- Bottles: Encrypt and sign with PQC keys
- IDCards: Use PQC keys for signing and encryption
- Keychains: Store and use PQC keys
- Memberships: Sign memberships with PQC keys
- Automatic Key Detection:
ecdh_encrypt/ecdh_decryptautomatically detect PQC key types
See POST_QUANTUM.md for detailed documentation.
RSA support is fully implemented with:
- RSA-OAEP encryption (with SHA-256)
- PKCS#1 v1.5 signing (with SHA-256)
- Support for 2048-bit and 4096-bit key sizes
- Short buffer encryption (key wrapping) for encrypting AES keys
rust-bottle provides a trait-based interface for integrating Trusted Platform Modules (TPM) and Hardware Security Modules (HSM) with ECDH operations. This allows you to use hardware-backed keys for enhanced security.
cargo build --features tpmTo use TPM/HSM with rust-bottle, implement the ECDHHandler trait for your TPM library:
use rust_bottle::tpm::ECDHHandler;
use rust_bottle::ecdh::ecdh_decrypt_with_handler;
use rust_bottle::errors::Result;
struct MyTpmHandler {
// Your TPM-specific fields
}
impl ECDHHandler for MyTpmHandler {
fn public_key(&self) -> Result<Vec<u8>> {
// Return the public key from TPM/HSM
// Format: 32 bytes for X25519, 65 bytes for P-256
Ok(vec![])
}
fn ecdh(&self, peer_public_key: &[u8]) -> Result<Vec<u8>> {
// Perform ECDH using TPM/HSM private key
// Return the shared secret
Ok(vec![])
}
}
// Use the handler for decryption
// let handler = MyTpmHandler::new()?;
// let decrypted = ecdh_decrypt_with_handler(&ciphertext, &[], Some(&handler))?;- The
ECDHHandlertrait provides a clean interface for any TPM/HSM backend - Handlers are primarily used during decryption when the recipient's private key is stored in hardware
- Encryption always uses software-generated ephemeral keys (as per ECDH protocol)
- You can use libraries like
tss-esapior platform-specific TPM libraries to implement the trait
See the tpm module documentation for more details.
bottle.rs: Core Bottle and Opener typesecdh.rs: ECDH encryption and decryption implementationskeys.rs: Key type implementations (ECDSA, Ed25519, X25519, RSA)signing.rs: Sign and Verify traitsidcard.rs: IDCard implementationkeychain.rs: Keychain implementationmembership.rs: Membership implementationtpm.rs: TPM/HSM support (trait-based interface)hash.rs: Hashing utilitiesutils.rs: Utility functionserrors.rs: Error types
- Trait-based Design: Uses Rust traits (
Sign,Verify,SignerKey) for polymorphism and flexibility - Type Safety: Strong typing prevents common cryptographic errors at compile time
- Memory Safety: No unsafe code in public API, secure memory clearing for sensitive data
- Error Handling: Comprehensive error types using
thiserrorfor detailed error information - Serialization: Uses
bincodefor efficient binary serialization
Keys implement traits based on their capabilities:
Sign: Types that can sign data (ECDSA, Ed25519, RSA)Verify: Types that can verify signatures (ECDSA, Ed25519, RSA)SignerKey: Keys that can be stored in keychains (all key types)
This design allows the library to work with different key types polymorphically while maintaining type safety.
The library uses a comprehensive error type system:
pub enum BottleError {
Encryption(String),
Decryption(String),
VerifyFailed,
InvalidFormat,
InvalidKeyType,
KeyNotFound,
KeyUnfit,
NoAppropriateKey,
Serialization(String),
Deserialization(String),
UnsupportedAlgorithm,
}All operations return Result<T, BottleError> for explicit error handling.
The library includes a comprehensive test suite that matches the gobottle test structure:
# Run all tests
cargo test
# Run tests with specific features
cargo test --features ml-kem
cargo test --features post-quantum
cargo test --features ml-kem,post-quantum-
tests/bottle_test.rs: Core bottle functionality (7 tests)- Bottle creation and manipulation
- Encryption and decryption
- Signing and verification
- Layered encryption
- Serialization
- Metadata handling
-
tests/ecdh_test.rs: ECDH encryption/decryption (3 tests)- Basic encrypt/decrypt operations
- Integration with bottles
- Key serialization
-
tests/aliceandbob_test.rs: End-to-end scenarios (4 tests)- Alice-Bob communication
- IDCard usage
- Keychain usage
- Group memberships
-
tests/pqc_test.rs: Post-quantum cryptography (24 tests)- ML-KEM encryption/decryption
- ML-DSA signing/verification
- SLH-DSA signing/verification
- Hybrid encryption
-
tests/pkix_test.rs: PKIX/PKCS#8 serialization (9 tests)- Key marshaling/unmarshaling
- DER and PEM encoding
- Multiple key types
-
tests/rsa_test.rs: RSA operations (11 tests)- Key generation
- Encryption/decryption
- Signing/verification
-
tests/short_buffer_test.rs: Short buffer encryption (7 tests)- RSA key wrapping
- Maximum message size handling
All tests pass and demonstrate the library's functionality.
Code coverage is tracked using cargo-tarpaulin. To generate coverage reports:
# Install cargo-tarpaulin (if not already installed)
cargo install cargo-tarpaulin
# Generate coverage report
cargo tarpaulin --out Html --output-dir coverage
# Generate coverage with specific features
cargo tarpaulin --features ml-kem --out Html --output-dir coverage
cargo tarpaulin --features post-quantum --out Html --output-dir coverage
cargo tarpaulin --features ml-kem,post-quantum --out Html --output-dir coverage
# View HTML report
open coverage/tarpaulin-report.htmlCoverage reports are generated in multiple formats:
- HTML: Interactive report (default:
coverage/tarpaulin-report.html) - XML: For CI/CD integration (Cobertura format)
- Stdout: Terminal output
The coverage configuration is in tarpaulin.toml. Coverage reports exclude test files, examples, and benchmarks.
Note: Coverage may vary depending on which features are enabled, as optional dependencies are only included when their features are active.
- All sensitive data is cleared from memory using the
zeroizecrate - No unsafe code in the public API
- Rust's ownership system prevents use-after-free and double-free errors
- Uses well-vetted cryptographic libraries (ring, ed25519-dalek, p256)
- AES-256-GCM for authenticated encryption
- Proper key derivation from ECDH shared secrets using SHA-256
- Deterministic ECDSA signing (RFC 6979)
- Always use cryptographically secure random number generators (
OsRngor equivalent) - Store private keys securely (consider using keychains or hardware security modules)
- Verify signatures before trusting data
- Check key purposes and expiration dates before using keys
- Clear sensitive data from memory when no longer needed
The library is designed for efficiency:
- Zero-cost abstractions where possible
- Efficient binary serialization with bincode
- Minimal allocations in hot paths
- Type-safe operations without runtime overhead
Performance characteristics:
- Encryption/decryption: O(n) where n is message size
- Signing/verification: O(1) for fixed key sizes
- Key generation: O(1) for all key types
- Serialization: Efficient binary format
- ML-KEM on macOS/ARM: Fixed - Using RustCrypto's pure Rust
ml-kemcrate - RSA Support: Complete - RSA-OAEP encryption and PKCS#1 v1.5 signing
- PKIX/PKCS#8 Serialization: Complete - DER and PEM formats supported
- TPM/HSM Integration: Available - Trait-based interface for TPM/HSM backends (requires
tpmfeature) - Short Buffer Encryption: Complete - RSA key wrapping implemented
- Multi-level Hashing: Not yet implemented
- PQC Key Reconstruction:
from_private_key_bytes()for PQC keys cannot derive public keys (limitation of underlying crates)
- ML-KEM on all platforms (completed - using RustCrypto's pure Rust implementation)
- RSA support (completed)
- PKIX/PKCS#8 serialization (completed)
- TPM/HSM interface (completed - users can implement
ECDHHandlertrait for their TPM library) - Short buffer encryption (completed)
- Performance benchmarking and optimization
- Additional SLH-DSA variants (f, simple variants)
- Hardware acceleration support for PQC algorithms
- Concrete TPM implementation examples (users can implement using tss-esapi or other TPM libraries)
rust-bottle aims to match gobottle's functionality while adapting to Rust's type system:
- Same core concepts: Bottles, IDCards, Keychains, Memberships
- Similar API structure adapted for Rust idioms
- Compatible test cases demonstrating equivalent functionality
- Same error types and error handling philosophy
Note: Serialization formats differ (bincode vs custom binary), so bottles created with one library cannot be directly read by the other. The protocol semantics are equivalent.
rust-bottle is a Rust implementation that matches the functionality of the Go implementation gobottle. Below is a summary comparison:
| Aspect | rust-bottle | gobottle | Status |
|---|---|---|---|
| Core Protocol | Complete | Complete | Equivalent |
| Classical Crypto | Complete | Complete | Equivalent |
| Post-Quantum Crypto | Complete (via features) | Complete | Equivalent |
| RSA Support | Complete | Complete | Equivalent |
| PKIX/PKCS#8 | Complete | Complete | Equivalent |
| TPM/HSM Support | Available (trait-based) | Complete | Equivalent |
| Type Safety | Strong (compile-time) | Runtime | Better |
| Memory Safety | Guaranteed | Manual | Better |
Classical Cryptography:
- Both support: ECDSA (P-256, P-384, P-521), Ed25519, X25519, RSA, AES-256-GCM
Post-Quantum Cryptography:
- Both support: ML-KEM-768, ML-KEM-1024, ML-DSA-44/65/87, SLH-DSA
- rust-bottle: 3 SLH-DSA variants (128s, 192s, 256s)
- gobottle: 12 SLH-DSA variants
Advantages of rust-bottle:
- Compile-time type checking prevents cryptographic errors
- Memory safety guaranteed by compiler
- Zero-cost abstractions
- No garbage collection overhead
- Optional PQC features (can disable for smaller binaries)
- Pure Rust PQC implementation (works on all platforms including macOS/ARM)
Advantages of gobottle:
- Faster compilation
- Better concurrency primitives
- Simpler deployment (single binary)
- More SLH-DSA variants (12 vs 3)
- All PQC features always included
rust-bottle:
- Rust-idiomatic API with trait-based polymorphism
- Strong ownership and borrowing rules
- Comprehensive error types using
thiserror - Uses
bincodefor serialization
gobottle:
- Go-idiomatic API with interface-based polymorphism
- Manual memory management (GC)
- Simple error variables
- Uses custom binary format
rust-bottle includes 372 tests covering:
- Core functionality (7 tests)
- ECDH encryption/decryption (3 tests)
- End-to-end scenarios (4 tests)
- Post-quantum cryptography (44 tests)
- PKIX/PKCS#8 serialization (13 tests)
- RSA operations (11 tests)
- Short buffer encryption (7 tests)
- Key generation and serialization (72 tests)
- Error handling paths (21 tests)
- Edge cases and boundary conditions (16 tests)
- Comprehensive coverage tests (174 tests)
Choose rust-bottle if:
- You need Rust's memory safety guarantees
- You're building a Rust application
- You need compile-time type safety
- You need post-quantum cryptography (available via features)
- You want optional PQC dependencies (can disable for smaller builds)
- You need ML-KEM on all platforms including macOS/ARM (pure Rust implementation)
- You need ML-DSA/SLH-DSA on all platforms
- You need PKIX/PKCS#8 key serialization (DER and PEM formats)
- You need RSA support (encryption and signing)
Choose gobottle if:
- You're building a Go application
- You need all 12 SLH-DSA variants (rust-bottle has 3)
- You prefer Go's concurrency model
For a detailed comparison, see COMPARISON.md.
Contributions are welcome. Areas that need work:
- Post-quantum cryptography implementations
- RSA support
- PKIX/PKCS#8 serialization
- Additional test cases
- Performance improvements
- Documentation improvements
MIT License - see LICENSE file for details.
- gobottle - Go implementation of the Bottle protocol
- Bottle Protocol Specification - Protocol documentation
- NIST Post-Quantum Cryptography - Post-quantum standards
- COMPARISON.md - Detailed comparison with gobottle
- IMPLEMENTATION.md - Implementation details