6 releases
new 0.2.0 | Apr 27, 2025 |
---|---|
0.1.4 | Feb 7, 2025 |
0.1.2 | Feb 16, 2024 |
0.1.1 | Jan 9, 2024 |
0.1.0 | Nov 25, 2021 |
#273 in Cryptography
83 downloads per month
Used in c5store
60KB
895 lines
ECIES X25519
Elliptic Curve Integrated Encryption Scheme (ECIES) implemented in Rust using X25519, AES-256-GCM, and HKDF-SHA256.
Overview
This crate provides a straightforward implementation of ECIES, a hybrid encryption scheme allowing encryption of data using a recipient's X25519 public key. The data can only be decrypted by the corresponding private key holder.
It uses the following cryptographic primitives:
- Key Agreement: X25519 (via
x25519-dalek
) - Key Derivation Function (KDF): HKDF-SHA256 (via
hkdf
andsha2
) - Data Encryption: AES-256-GCM (Authenticated Encryption via
aes-gcm
)
The implementation aims for simplicity and security, using well-vetted underlying cryptographic libraries and providing efficient key parsing for standard formats.
Features
- ECIES encryption and decryption using X25519 / AES-256-GCM / HKDF-SHA256.
- Generation of X25519 key pairs in OpenSSL-compatible DER formats (PKCS#8 v1, SubjectPublicKeyInfo).
- Parsing of X25519 and Ed25519 keys from PEM and DER formats.
- Ed25519 keys are automatically converted to their corresponding X25519 representation.
- Requires
std
(due to dependencies likepem
).
Installation
Add the crate to your Cargo.toml
:
[dependencies]
ecies_25519 = "0.2.0" # Use the latest version from crates.io
rand_core = "0.9" # Required for RNG traits (ensure compatibility)
# Add an RNG implementation like rand, OsRng, or rand_chacha
rand = "0.9" # Example using the rand crate
Or run:
cargo add ecies_25519 rand_core rand # Add your preferred RNG crate if not rand
Usage Example
use ecies_25519::{
generate_keypair, // For generating new X25519 key pairs in DER format
EciesX25519, // The main struct for encryption/decryption
// Functions to parse keys from PEM or DER format
parse_public_key,
parse_private_key,
PublicKey, // Re-exported key types
StaticSecret,
};
use rand::rngs::OsRng; // Cryptographically secure random number generator
use rand_core::{RngCore, CryptoRng}; // Required traits for the RNG
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. Initialize a secure random number generator
let mut rng = OsRng;
// 2. Generate the recipient's keypair (DER encoded)
let recipient_keypair_der = generate_keypair(&mut rng);
// 3. Parse the DER keys into usable crypto types
// (Alternatively, load your existing PEM/DER keys from files/strings)
let recipient_public_key = parse_public_key(&recipient_keypair_der.public_der)
.expect("Failed to parse generated public key");
let recipient_private_key = parse_private_key(&recipient_keypair_der.private_der)
.expect("Failed to parse generated private key");
// 4. The message to encrypt
let message = b"This is a super secret message! \xf0\x9f\x92\x96"; // Example with UTF-8 bytes
// 5. Create an ECIES instance
let ecies = EciesX25519::new();
// 6. Encrypt the message using the recipient's public key
println!("Encrypting message...");
let encrypted_data = ecies.encrypt(
&recipient_public_key,
message,
&mut rng, // RNG is needed for ephemeral key and AES nonce
)?; // Propagate encryption errors
println!("Encrypted data length: {}", encrypted_data.len());
// Note: Ciphertext format is [ephemeral_public_key | aes_gcm_output]
// 7. Decrypt the message using the recipient's private key
println!("Decrypting message...");
let decrypted_data = ecies.decrypt(
&recipient_private_key,
&encrypted_data,
)?; // Propagate decryption errors
// 8. Verify the result
assert_eq!(message, decrypted_data.as_slice());
println!("Decryption successful! Message: \"{}\"", String::from_utf8_lossy(&decrypted_data));
Ok(())
}
Key Concepts
- Hybrid Encryption: Combines asymmetric (X25519) and symmetric (AES-256-GCM) cryptography.
- Ephemeral Key: A temporary X25519 keypair is generated by the sender for each encryption. Only the public part is sent with the ciphertext. This provides forward secrecy.
- ECDH: Elliptic Curve Diffie-Hellman is used between the sender's ephemeral private key and the recipient's static public key (for encryption) or the sender's ephemeral public key and the recipient's static private key (for decryption) to establish a shared secret.
- HKDF: The ECDH shared secret, concatenated with the involved public keys (
[ephemeral_pk | shared_secret]
for encryption,[recipient_pk | shared_secret]
for decryption), and a fixed context string ("ecies_x25519"
), is fed into HKDF-SHA256 to derive a robust 32-byte AES key. - AES-GCM: The derived key is used with AES-256-GCM to encrypt the plaintext. AES-GCM provides both confidentiality (encryption) and integrity/authenticity (via a 16-byte MAC tag). It requires a unique 12-byte nonce (IV) for each encryption with the same key, which is generated randomly by this library.
- Ciphertext Format: The output of
encrypt
is the concatenation:[ephemeral_public_key (32 bytes) || nonce (12 bytes) || AES ciphertext || auth_tag (16 bytes)]
.
API Reference
The public API is primarily exposed from the crate root (ecies_25519::
).
ECIES Operations:
EciesX25519::new() -> Self
: Creates a new ECIES instance.EciesX25519::encrypt(...) -> Result<Vec<u8>, Error>
: Encrypts data forreceiver_pub
usingrng
.EciesX25519::decrypt(...) -> Result<Vec<u8>, Error>
: Decrypts data usingreceiver_sk
.
Key Handling:
generate_keypair<T>(csprng: &mut T) -> KeyPairDer
: Generates a new X25519 key pair (DER encoded). RequiresRngCore + CryptoRng
.parse_public_key(pem_or_der_bytes: &[u8]) -> Result<PublicKey, KeyParsingError>
: Parses PEM/DER public key (X25519/Ed25519).parse_private_key(pem_or_der_bytes: &[u8]) -> Result<StaticSecret, KeyParsingError>
: Parses PEM/DER private key (X25519/Ed25519).
Core Types:
PublicKey
: (x25519_dalek::PublicKey
) X25519 public key.StaticSecret
: (x25519_dalek::StaticSecret
) X25519 private key.KeyPairDer
: Struct holding.public_der: Vec<u8>
(SPKI) and.private_der: Vec<u8>
(PKCS#8). Has.public_to_pem()
and.private_to_pem()
methods.Error
: Enum for ECIESencrypt
/decrypt
errors.KeyParsingError
: Enum for key parsing errors (re-exported fromparser
).
Security Considerations
- RNG: Always use a cryptographically secure random number generator (CSPRNG) like
rand::rngs::OsRng
. The security of the ephemeral keys and AES nonces depends critically on the RNG quality. - Private Key Security: The recipient's X25519 private key (
StaticSecret
) must be kept confidential. - HKDF Info: This implementation uses a fixed info string (
"ecies_x25519"
). For better domain separation in complex applications using the same keys for multiple purposes, consider modifying the library or using distinct keys. - Authenticated Encryption: AES-GCM provides authenticity. The
decrypt
function must be checked for errors. AnErr(Error::DecryptionFailed)
indicates potential tampering or use of the wrong key. - Internal
unwrap()
: Note that the current implementation ofEciesX25519::encrypt
usesunwrap()
internally when parsing the generated ephemeral keys. While key generation should produce valid keys, this could theoretically panic if the underlyingparser
module had a bug related to generation output. Robust applications might prefer generating/parsing keys separately and handling potential parsing errors explicitly before calling encryption/decryption methods.
Contributing
Contributions (bug reports, pull requests, feature suggestions) are welcome! Please open an issue to discuss significant changes before submitting a PR.
License
Licensed under the Mozilla Public License Version 2.0 (LICENSE or https://opensource.org/licenses/MPL-2.0).
Dependencies
~7MB
~138K SLoC