| Crates.io | ecies_25519 |
| lib.rs | ecies_25519 |
| version | 0.2.3 |
| created_at | 2021-11-25 18:21:13.070752+00 |
| updated_at | 2025-05-13 23:46:36.321691+00 |
| description | Cross-platform Elliptic Curve Integrated Encryption Scheme (ECIES) using X25519, AES-256-GCM, and HKDF-SHA256. |
| homepage | |
| repository | https://github.com/normano/ecies_25519 |
| max_upload_size | |
| id | 487731 |
| size | 88,996 |
Elliptic Curve Integrated Encryption Scheme (ECIES) implemented in Rust using X25519, AES-256-GCM, and HKDF-SHA256.
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:
x25519-dalek)hkdf and sha2)aes-gcm)The implementation aims for simplicity and security, using well-vetted underlying cryptographic libraries and providing efficient key parsing for standard formats.
std (due to dependencies like pem).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
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(())
}
[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.encrypt is the concatenation: [ephemeral_public_key (32 bytes) || nonce (12 bytes) || AES ciphertext || auth_tag (16 bytes)].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 for receiver_pub using rng.EciesX25519::decrypt(...) -> Result<Vec<u8>, Error>: Decrypts data using receiver_sk.Key Handling:
generate_keypair<T>(csprng: &mut T) -> KeyPairDer: Generates a new X25519 key pair (DER encoded). Requires RngCore + 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 ECIES encrypt/decrypt errors.KeyParsingError: Enum for key parsing errors (re-exported from parser).rand::rngs::OsRng. The security of the ephemeral keys and AES nonces depends critically on the RNG quality.StaticSecret) must be kept confidential."ecies_x25519"). For better domain separation in complex applications using the same keys for multiple purposes, consider modifying the library or using distinct keys.decrypt function must be checked for errors. An Err(Error::DecryptionFailed) indicates potential tampering or use of the wrong key.unwrap(): Note that the current implementation of EciesX25519::encrypt uses unwrap() internally when parsing the generated ephemeral keys. While key generation should produce valid keys, this could theoretically panic if the underlying parser 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.Contributions (bug reports, pull requests, feature suggestions) are welcome! Please open an issue to discuss significant changes before submitting a PR.
Licensed under the Mozilla Public License Version 2.0 (LICENSE or https://opensource.org/licenses/MPL-2.0).