Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 24 additions & 14 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,35 +1,45 @@
[package]
name = "ecies-ed25519"
version = "0.5.1"
version = "0.6.0"
authors = ["phayes <[email protected]>"]
edition = "2018"
edition = "2024"
license = "MIT OR Apache-2.0"
description = "ECIES on Twisted Edwards Curve25519 using AES-GCM and HKDF-SHA256"
categories = ["cryptography"]
keywords = ["ecies", "encryption", "ed25519", "curve25519", "aes-gcm"]
repository = "https://github.com/phayes/ecies-ed25519"
readme = "README.md"
resolver = "3"

[dependencies]
rand = "0.8.5"
rand = { version = "0.9.2", default-features = false, features = ["os_rng", "small_rng", "alloc"] }
curve25519-dalek = { version = "4.1.3", features = ["legacy_compatibility"] }
thiserror = "1.0.64"
hex = "0.4.3"
thiserror = { version = "2.0.16", default-features = false}
hex = { version = "0.4.3", default-features = false, features = ["alloc"] }
zeroize = "1.8.1"
# "serde" feature
serde = { version = "1.0.210", optional = true }
serde = { version = "1.0.225", optional = true, default-features = false}
# "ring" feature
ring = { version = "0.17.8", optional = true, features = [] }
ring = { version = "0.17.14", optional = true, features = [] }
# "pure_rust" feature
aes-gcm = { version = "0.10.3", optional = true }
sha2 = { version = "0.10.8", optional = true }
digest = { version = "0.10.7", optional = true }
hkdf = { version = "0.12.4", optional = true }
aes-gcm = { version = "0.10.3", features = ["alloc"] }
sha2 = "0.10.9"
digest = "0.10.7"
hkdf = "0.12.4"

[features]
default = ["pure_rust"]
pure_rust = ["aes-gcm", "sha2", "digest", "hkdf"]
default = ["std"]
std = [
"hex/std",
"serde?/std",
"aes-gcm/std",
"thiserror/std",
"rand/std",
"rand/std_rng",
"rand/thread_rng",
]
serde = ["dep:serde", "rand/serde", "hex/serde"]

[dev-dependencies]
serde_json = "1.0.128"
serde_json = "1.0.145"
serde_cbor = "0.11.2"
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@

ECIES on Twisted Edwards Curve25519 using AES-GCM and HKDF-SHA256.

ECIES can be used to encrypt data using a public key such that it can only be decrypted by the holder of the corresponding private key.
ECIES can be used to encrypt data using a public key such that it can only be decrypted by the holder of the corresponding private key.

*This project has not undergone a security audit. A 1.0 release will not happen until it does.*


### Backends

It uses the excellent [curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek) library for ECC operations, and provides two different backends for HKDF-SHA256 / AES-GCM operation operations.
1. The `pure_rust` backend (default).
It uses the excellent [curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek) library for ECC operations, and provides two different backends for HKDF-SHA256 / AES-GCM operation operations.

1. The `pure_rust` backend (default).
It uses a collection of pure-rust implementations of SHA2, HKDF, AES, and AEAD.

2. The `ring` backend uses [ring](https://github.com/briansmith/ring). It uses rock solid primitives based on BoringSSL, but cannot run on all platforms. For example it won't work on WASM. To activate this backend add this to your Cargo.toml file:
2. The `ring` backend uses [ring](https://github.com/briansmith/ring). It uses rock solid primitives based on BoringSSL, but cannot run on all platforms. For example it won't work on WASM. To activate this backend add this to your Cargo.toml file:

` ecies-ed25519 = { version = "0.3", features = ["ring"] }`

Expand Down Expand Up @@ -47,7 +47,7 @@ The `serde` feature is provided for serializing / deserializing private and publ
You should run tests on both backends:
```
cargo test --no-default-features --features "ring serde"
cargo test --no-default-features --features "pure_rust serde"
cargo test --no-default-features --features "serde"
```

### Performance
Expand All @@ -62,18 +62,18 @@ RUSTFLAGS="-Ctarget-cpu=sandybridge -Ctarget-feature=+aes,+sse2,+sse4.1,+ssse3"
### Future Plans

- I will be making this crate generic over both the AEAD and HKDF implementation once [const-generics](https://github.com/rust-lang/rust/issues/44580) is resolved.

- Add support for [AVX2 and AVX512](https://github.com/dalek-cryptography/curve25519-dalek#backends-and-features)


### Security Audits
This project has not undergone a security audit. A 1.0 release will not happen until it does. Please contact me if you would like to fund or perform a security audit.

This project has not undergone a security audit. A 1.0 release will not happen until it does. Please contact me if you would like to fund or perform a security audit.

While this library has not undergone a security audit, some of its dependencies have. Dependency audits:
- [curve25519-dalek](https://blog.quarkslab.com/resources/2019-08-26-audit-dalek-libraries/19-06-594-REP.pdf)
- [ring](https://github.com/ctz/rustls/raw/master/audit/TLS-01-report.pdf)
- [aes-gcm](https://research.nccgroup.com/wp-content/uploads/2020/02/NCC_Group_MobileCoin_RustCrypto_AESGCM_ChaCha20Poly1305_Implementation_Review_2020-02-12_v1.0.pdf)



7 changes: 5 additions & 2 deletions src/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ use hex::{FromHex, ToHex};
use rand::{CryptoRng, RngCore};
use zeroize::Zeroize;

#[cfg(not(feature = "std"))]
use alloc::vec::Vec;

/// The length of a `SecretKey`, in bytes.
pub const SECRET_KEY_LENGTH: usize = 32;

Expand Down Expand Up @@ -181,8 +184,8 @@ impl AsRef<[u8]> for PublicKey {

#[cfg(feature = "serde")]
use serde::{
de::Error as SerdeError, de::Unexpected, de::Visitor, Deserialize, Deserializer, Serialize,
Serializer,
Deserialize, Deserializer, Serialize, Serializer, de::Error as SerdeError, de::Unexpected,
de::Visitor,
};

#[cfg(feature = "serde")]
Expand Down
37 changes: 15 additions & 22 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@
//! The `serde` feature is provided for serializing / deserializing private and public keys.
//!

#![cfg_attr(not(feature = "std"), no_std)]

#[cfg(not(feature = "std"))]
extern crate alloc;

#[cfg(not(feature = "std"))]
use alloc::{borrow::ToOwned, vec::Vec};

use curve25519_dalek::scalar::Scalar;
use rand::{CryptoRng, RngCore};

Expand All @@ -42,23 +50,13 @@ pub use keys::*;
mod ring_backend;

#[cfg(feature = "ring")]
use ring_backend::*;
use ring_backend::{aes_decrypt, aes_encrypt, hkdf_sha256};

#[cfg(feature = "pure_rust")]
#[cfg(not(feature = "ring"))]
mod pure_rust_backend;

#[cfg(feature = "pure_rust")]
use pure_rust_backend::*;

#[cfg(not(any(feature = "ring", feature = "pure_rust")))]
compile_error!(
"ecies-rd25519: Either feature 'ring' or 'pure_rust' must be enabled for this crate."
);

#[cfg(all(feature = "ring", feature = "pure_rust"))]
compile_error!(
"ecies-rd25519: Feature 'ring' and 'pure_rust' cannot both be enabled. Please choose one."
);
Comment on lines -58 to -61
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having an XOR feature set will cause issues for downstream users:
https://doc.rust-lang.org/cargo/reference/features.html#feature-unification

#[cfg(not(feature = "ring"))]
use pure_rust_backend::{aes_decrypt, aes_encrypt, hkdf_sha256};

const HKDF_INFO: &[u8; 13] = b"ecies-ed25519";

Expand Down Expand Up @@ -114,9 +112,7 @@ fn generate_shared(secret: &SecretKey, public: &PublicKey) -> SharedSecret {
let shared_point = public * secret;
let shared_point_compressed = shared_point.compress();

let output = shared_point_compressed.as_bytes().to_owned();

output
shared_point_compressed.as_bytes().to_owned()
}

fn encapsulate(emphemeral_sk: &SecretKey, peer_pk: &PublicKey) -> AesKey {
Expand All @@ -141,10 +137,7 @@ fn decapsulate(sk: &SecretKey, emphemeral_pk: &PublicKey) -> AesKey {
hkdf_sha256(&master)
}

/// Error types
use thiserror::Error;

#[derive(Debug, Error)]
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// Encryption failed
#[error("ecies-rd25519: encryption failed")]
Expand Down Expand Up @@ -175,8 +168,8 @@ pub enum Error {
pub mod tests {
use super::*;

use rand::thread_rng;
use rand::SeedableRng;
use rand::thread_rng;

#[test]
fn test_shared() {
Expand Down
9 changes: 6 additions & 3 deletions src/pure_rust_backend.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
use aes_gcm::aead::{self, generic_array::GenericArray, Aead, KeyInit};
use aes_gcm::Aes256Gcm;
use aes_gcm::aead::{self, Aead, KeyInit, generic_array::GenericArray};
use hkdf::Hkdf;
use rand::{CryptoRng, RngCore};
use rand::{CryptoRng, RngCore, TryRngCore};
use sha2::Sha256;

use super::AES_IV_LENGTH;
use super::AesKey;
use super::Error;
use super::AES_IV_LENGTH;
use super::HKDF_INFO;

#[cfg(not(feature = "std"))]
use alloc::vec::Vec;

pub(crate) fn hkdf_sha256(master: &[u8]) -> AesKey {
let h = Hkdf::<Sha256>::new(None, master);
let mut out = [0u8; 32];
Expand Down
7 changes: 5 additions & 2 deletions src/ring_backend.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
use rand::{CryptoRng, RngCore};
use rand::{CryptoRng, RngCore, TryRngCore};
use ring::aead::*;
use ring::hkdf::*;

use super::AES_IV_LENGTH;
use super::AesKey;
use super::Error;
use super::AES_IV_LENGTH;
use super::HKDF_INFO;

#[cfg(not(feature = "std"))]
use alloc::{borrow::ToOwned, vec::Vec};

const AES_TAG_LEN: usize = 16;

pub(crate) fn hkdf_sha256(master: &[u8]) -> AesKey {
Expand Down