Skip to content

Commit

Permalink
Revamp features (#113)
Browse files Browse the repository at this point in the history
  • Loading branch information
kigawas authored Nov 3, 2023
1 parent 6fa85a0 commit 7e5d7e8
Show file tree
Hide file tree
Showing 11 changed files with 93 additions and 78 deletions.
33 changes: 16 additions & 17 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ name = "ecies"
version = "0.2.7"
# docs
authors = ["Weiliang Li <[email protected]>"]
description = "Elliptic Curve Integrated Encryption Scheme for secp256k1/curve25519"
description = "Elliptic Curve Integrated Encryption Scheme for secp256k1"
edition = "2021"
keywords = [
"secp256k1",
"curve25519",
"crypto",
"ecc",
"ecies",
Expand All @@ -22,18 +21,18 @@ repository = "https://github.com/ecies/rs"

[dependencies]
hkdf = {version = "0.12.3", default-features = false}
sha2 = {version = "0.10.7", default-features = false}
sha2 = {version = "0.10.8", default-features = false}

# elliptic curves
libsecp256k1 = {version = "0.7.1", default-features = false, features = ["static-context"], optional = true}
x25519-dalek = {version = "2.0.0", default-features = false, features = ["static_secrets"], optional = true}
libsecp256k1 = {version = "0.7.1", default-features = false, features = ["static-context"]}
# x25519-dalek = {version = "2.0.0", default-features = false, features = ["static_secrets"], optional = true}

# openssl aes
openssl = {version = "0.10.57", default-features = false, optional = true}

# pure rust aes
aes-gcm = {version = "0.10.2", default-features = false, optional = true}
typenum = {version = "1.16.0", default-features = false, optional = true}
aes-gcm = {version = "0.10.3", default-features = false, optional = true}
typenum = {version = "1.17.0", default-features = false, optional = true}

# chacha20 cipher
chacha20poly1305 = {version = "0.10.1", default-features = false, optional = true}
Expand All @@ -58,17 +57,17 @@ once_cell = {version = "1.18.0", default-features = false, features = ["std"]}

[features]
default = ["openssl"]
std = ["hkdf/std", "sha2/std", "once_cell/std"]
std = ["hkdf/std", "sha2/std", "once_cell/std", "libsecp256k1/std"]

# curve
secp256k1 = ["libsecp256k1"]
x25519 = ["x25519-dalek"]
# secp256k1 = ["libsecp256k1"]
# x25519 = ["x25519-dalek"]

# cipher
aes-12bytes-nonce = ["secp256k1"] # with feature "openssl" or "pure". default: 16 bytes
openssl = ["dep:openssl", "secp256k1"]
pure = ["aes-gcm/aes", "typenum", "secp256k1"]
xchacha20 = ["chacha20poly1305", "secp256k1"]
aes-12bytes-nonce = [] # with feature "openssl" or "pure". default: 16 bytes
openssl = ["dep:openssl"]
pure = ["aes-gcm/aes", "typenum"]
xchacha20 = ["chacha20poly1305"]

[dev-dependencies]
criterion = {version = "0.4.0", default-features = false}
Expand All @@ -78,9 +77,9 @@ hex = {version = "0.4.3", default-features = false, features = ["alloc"]}
wasm-bindgen-test = "0.3.37"

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
futures-util = "0.3.28"
reqwest = "0.11.18"
tokio = {version = "1.32.0", features = ["rt-multi-thread"]}
futures-util = "0.3.29"
reqwest = "0.11.22"
tokio = {version = "1.33.0", features = ["rt-multi-thread"]}

[[bench]]
harness = false
Expand Down
4 changes: 2 additions & 2 deletions bench/simple.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use core::time::Duration;

use criterion::{criterion_group, criterion_main, Criterion};
use ecies::{decrypt, encrypt, utils::generate_keypair};

const BIG_MSG_SIZE: usize = 100 * 1024 * 1024;
const BIGGER_MSG_SIZE: usize = 200 * 1024 * 1024;
Expand All @@ -10,6 +8,8 @@ const BIG_MSG: [u8; BIG_MSG_SIZE] = [1u8; BIG_MSG_SIZE];
const BIGGER_MSG: [u8; BIGGER_MSG_SIZE] = [2u8; BIGGER_MSG_SIZE];

fn criterion_benchmark(c: &mut Criterion) {
use ecies::{decrypt, encrypt, utils::generate_keypair};

let (sk, pk) = generate_keypair();
let (sk, pk) = (&sk.serialize(), &pk.serialize());

Expand Down
15 changes: 10 additions & 5 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub fn is_ephemeral_key_compressed() -> bool {
}

/// Get ephemeral key size: compressed(33) or uncompressed(65)
#[cfg(not(feature = "x25519"))]
// #[cfg(feature = "secp256k1")]
pub fn get_ephemeral_key_size() -> usize {
use crate::consts::{COMPRESSED_PUBLIC_KEY_SIZE, UNCOMPRESSED_PUBLIC_KEY_SIZE};

Expand All @@ -41,10 +41,15 @@ pub fn get_ephemeral_key_size() -> usize {
}
}

#[cfg(feature = "x25519")]
pub fn get_ephemeral_key_size() -> usize {
32
}
// #[cfg(feature = "x25519")]
// pub fn get_ephemeral_key_size() -> usize {
// 32
// }

// #[cfg(all(not(feature = "x25519"), not(feature = "secp256k1")))]
// pub fn get_ephemeral_key_size() -> usize {
// panic!("Not implemented")
// }

/// Get hkdf key derived from compressed shared point or not
pub fn is_hkdf_key_compressed() -> bool {
Expand Down
2 changes: 2 additions & 0 deletions src/consts.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/// Compressed public key size
// #[cfg(feature = "secp256k1")]
pub use libsecp256k1::util::COMPRESSED_PUBLIC_KEY_SIZE;
/// Uncompressed public key size
// #[cfg(feature = "secp256k1")]
pub use libsecp256k1::util::FULL_PUBLIC_KEY_SIZE as UNCOMPRESSED_PUBLIC_KEY_SIZE;

/// Nonce length. AES (12/16 bytes) or XChaCha20 (24 bytes)
Expand Down
55 changes: 9 additions & 46 deletions src/elliptic/mod.rs
Original file line number Diff line number Diff line change
@@ -1,50 +1,13 @@
use hkdf::Hkdf;
use sha2::Sha256;

use crate::compat::Vec;
use crate::consts::{SharedSecret, EMPTY_BYTES};

#[cfg(not(feature = "x25519"))]
// #[cfg(feature = "secp256k1")]
mod secp256k1;
#[cfg(not(feature = "x25519"))]
// #[cfg(feature = "secp256k1")]
pub use secp256k1::{decapsulate, encapsulate, generate_keypair};
#[cfg(not(feature = "x25519"))]
// #[cfg(feature = "secp256k1")]
pub(crate) use secp256k1::{parse_pk, parse_sk, pk_to_vec, Error};

#[cfg(feature = "x25519")]
mod x25519;
#[cfg(feature = "x25519")]
pub use x25519::{decapsulate, encapsulate, generate_keypair};
#[cfg(feature = "x25519")]
pub(crate) use x25519::{parse_pk, parse_sk, pk_to_vec, Error};

fn hkdf_derive(sender_point: &[u8], shared_point: &[u8]) -> SharedSecret {
let size = sender_point.len() + shared_point.len();
let mut master = Vec::with_capacity(size);
master.extend(sender_point);
master.extend(shared_point);
hkdf_sha256(&master)
}

fn hkdf_sha256(master: &[u8]) -> SharedSecret {
let h = Hkdf::<Sha256>::new(None, master);
let mut out = [0u8; 32];
// never fails because 32 < 255 * chunk_len, which is 32 on SHA256
h.expand(&EMPTY_BYTES, &mut out).unwrap();
out
}

#[cfg(test)]
mod tests {
use super::hkdf_sha256;

use crate::utils::tests::decode_hex;

#[test]
fn test_known_hkdf_vector() {
assert_eq!(
hkdf_sha256(b"secret").to_vec(),
decode_hex("2f34e5ff91ec85d53ca9b543683174d0cf550b60d5f52b24c97b386cfcf6cbbf")
);
}
}
// #[cfg(feature = "x25519")]
// mod x25519;
// #[cfg(feature = "x25519")]
// pub use x25519::{decapsulate, encapsulate, generate_keypair};
// #[cfg(feature = "x25519")]
// pub(crate) use x25519::{parse_pk, parse_sk, pk_to_vec, Error};
2 changes: 1 addition & 1 deletion src/elliptic/secp256k1.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use libsecp256k1::{PublicKey, SecretKey};
use rand_core::OsRng;

use super::hkdf_derive;
use crate::compat::Vec;
use crate::config::is_hkdf_key_compressed;
use crate::consts::SharedSecret;
use crate::symmetric::hkdf_derive;

pub use libsecp256k1::Error;

Expand Down
5 changes: 4 additions & 1 deletion src/elliptic/x25519.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
use rand_core::OsRng;
use x25519_dalek::{PublicKey, StaticSecret as SecretKey};

use super::hkdf_derive;
use crate::compat::Vec;
use crate::consts::SharedSecret;
use crate::symmetric::hkdf_derive;

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum Error {
InvalidMessage,
}

#[cfg(feature = "std")]
impl std::error::Error for Error {}

/// Generate a `(SecretKey, PublicKey)` pair
pub fn generate_keypair() -> (SecretKey, PublicKey) {
let sk = SecretKey::random_from_rng(&mut OsRng);
Expand Down
7 changes: 4 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ extern crate std;
#[cfg(not(feature = "std"))]
extern crate alloc;

#[cfg(not(feature = "x25519"))]
// #[cfg(all(feature = "secp256k1", not(feature = "x25519")))]
pub use libsecp256k1::{PublicKey, SecretKey};
#[cfg(feature = "x25519")]
pub use x25519_dalek::{PublicKey, StaticSecret as SecretKey};

// #[cfg(all(feature = "x25519", not(feature = "secp256k1")))]
// pub use x25519_dalek::{PublicKey, StaticSecret as SecretKey};

/// ECIES configuration
pub mod config;
Expand Down
36 changes: 36 additions & 0 deletions src/symmetric/hash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use hkdf::Hkdf;
use sha2::Sha256;

use crate::compat::Vec;
use crate::consts::{SharedSecret, EMPTY_BYTES};

pub fn hkdf_derive(sender_point: &[u8], shared_point: &[u8]) -> SharedSecret {
let size = sender_point.len() + shared_point.len();
let mut master = Vec::with_capacity(size);
master.extend(sender_point);
master.extend(shared_point);
hkdf_sha256(&master)
}

fn hkdf_sha256(master: &[u8]) -> SharedSecret {
let h = Hkdf::<Sha256>::new(None, master);
let mut out = [0u8; 32];
// never fails because 32 < 255 * chunk_len, which is 32 on SHA256
h.expand(&EMPTY_BYTES, &mut out).unwrap();
out
}

#[cfg(test)]
mod tests {
use super::hkdf_sha256;

use crate::utils::tests::decode_hex;

#[test]
fn test_known_vector() {
assert_eq!(
hkdf_sha256(b"secret").to_vec(),
decode_hex("2f34e5ff91ec85d53ca9b543683174d0cf550b60d5f52b24c97b386cfcf6cbbf")
);
}
}
11 changes: 8 additions & 3 deletions src/symmetric/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@ use crate::consts::NONCE_LENGTH;

#[cfg(any(feature = "pure", feature = "xchacha20"))]
mod aead;
#[cfg(feature = "openssl")]
mod openssl_aes;

#[cfg(any(feature = "pure", feature = "xchacha20"))]
use aead::{decrypt, encrypt};

#[cfg(feature = "openssl")]
mod openssl_aes;
#[cfg(feature = "openssl")]
use openssl_aes::{decrypt, encrypt};

// #[cfg(any(feature = "secp256k1", feature = "x25519"))]
mod hash;
// #[cfg(any(feature = "secp256k1", feature = "x25519"))]
pub(crate) use hash::hkdf_derive;

/// Symmetric encryption wrapper. Openssl AES-256-GCM, pure Rust AES-256-GCM, or XChaCha20-Poly1305
/// Nonces are generated randomly.
///
Expand Down
1 change: 1 addition & 0 deletions tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
not(feature = "x25519"),
not(feature = "aes-12bytes-nonce"),
not(feature = "xchacha20"),
// feature = "secp256k1"
))]
fn is_compatible_with_python() {
use futures_util::FutureExt;
Expand Down

0 comments on commit 7e5d7e8

Please sign in to comment.