Crates.io | ecies_25519 |
lib.rs | ecies_25519 |
version | 0.2.3 |
source | src |
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).