Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support no_std #105

Merged
merged 1 commit into from
Jul 27, 2023
Merged
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
File renamed without changes.
4 changes: 1 addition & 3 deletions .cspell.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@
// flagWords - list of words to be always considered incorrect
// This is useful for offensive words and common spelling errors.
// For example "hte" should be "the"
"flagWords": [
"hte"
],
"flagWords": ["hte"],
"ignorePaths": [
".git",
".github",
Expand Down
22 changes: 14 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
toolchain: [stable, beta, nightly]
feature: [pure, xchacha20, "pure,aes-12bytes-nonce"]
feature: [openssl, pure, xchacha20]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
Expand Down Expand Up @@ -58,18 +58,24 @@ jobs:
- name: Check and run lint
run: cargo check && cargo fmt && cargo clippy

- name: Run openssl tests
run: cargo test
- name: Run openssl 12 bytes nonce tests
run: cargo test --features aes-12bytes-nonce

- name: Run pure rust tests
- name: Run tests
run: cargo test --no-default-features --features ${{ matrix.feature }}
- name: Run tests with std
run: cargo test --no-default-features --features ${{ matrix.feature }},std

- name: Run tests with 12 bytes nonce
run: cargo test --no-default-features --features ${{ matrix.feature }},aes-12bytes-nonce
if: matrix.feature != 'xchacha20'

- name: Run tests with std and 12 bytes nonce
run: cargo test --no-default-features --features ${{ matrix.feature }},std,aes-12bytes-nonce
if: matrix.feature != 'xchacha20'

- name: Install wasm dep
run: cargo install wasm-bindgen-cli || true
- name: Run wasm tests
- name: Run tests on wasm target
run: cargo test --no-default-features --features ${{ matrix.feature }} --target=wasm32-unknown-unknown
if: matrix.feature != 'openssl'

- name: Check cargo package
run: cargo publish --dry-run
Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Changelog

## 0.2.1 ~ 0.2.5
## 0.2.1 ~ 0.2.6

- Support `no_std`
- Revamp documentation
- Revamp configuration and add xchacha20-poly1305 backend
- Add configuration for more compatibility
Expand Down
35 changes: 23 additions & 12 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ecies"
version = "0.2.5"
version = "0.2.6"
# docs
authors = ["Weiliang Li <[email protected]>"]
description = "Elliptic Curve Integrated Encryption Scheme for secp256k1 in Rust"
Expand All @@ -20,13 +20,12 @@ homepage = "https://ecies.org/rs/"
repository = "https://github.com/ecies/rs"

[dependencies]
hkdf = "0.12.3"
hkdf = {version = "0.12.3", default-features = false}
libsecp256k1 = {version = "0.7.1", default-features = false, features = ["static-context"]}
once_cell = "1.18.0"
sha2 = "0.10.7"
sha2 = {version = "0.10.7", default-features = false}

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

# pure rust aes
aes-gcm = {version = "0.10.2", default-features = false, optional = true}
Expand All @@ -35,23 +34,35 @@ typenum = {version = "1.16.0", default-features = false, optional = true}
# chacha20 cipher
chacha20poly1305 = {version = "0.10.1", default-features = false, optional = true}

[target.'cfg(target_arch = "wasm32")'.dependencies]
getrandom = {version = "0.2.10", features = ["js"]}
rand = {version = "0.8.5", features = ["getrandom"]}
# random number generator
getrandom = {version = "0.2.10", default-features = false}
rand_core = {version = "0.6.4", default-features = false, features = ["getrandom"]}

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
rand = {version = "0.8.5"}
# configuration
once_cell = {version = "1.18.0", default-features = false}
parking_lot = "0.12.1"

[target.'cfg(all(target_arch = "wasm32", target_os="unknown"))'.dependencies]
# only for js (browser or node). if it's not js, like substrate, it won't build
getrandom = {version = "0.2.10", default-features = false, features = ["js"]}
wasm-bindgen = {version = "0.2.87", default-features = false}

[target.'cfg(all(target_arch = "wasm32", not(target_os="unknown")))'.dependencies]
# allows wasm32-wasi to build
once_cell = {version = "1.18.0", default-features = false, features = ["std"]}

[features]
std = ["hkdf/std", "sha2/std", "once_cell/std"]

default = ["openssl"]

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

[dev-dependencies]
criterion = {version = "0.5.1", default-features = false}
hex = "0.4.3"
hex = {version = "0.4.3", default-features = false, features = ["alloc"]}

[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
wasm-bindgen-test = "0.3.37"
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ This library can be compiled to the WASM target at your option, see [WASM compat

## Quick Start

`no_std` is enabled by default. You can enable `std` with `std` feature.

```toml
ecies = {version = "0.2", features = ["std"]}
```

```rust
use ecies::{decrypt, encrypt, utils::generate_keypair};

Expand Down
5 changes: 5 additions & 0 deletions src/compat.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#[cfg(feature = "std")]
pub(crate) use std::vec::Vec;

#[cfg(not(feature = "std"))]
pub(crate) use alloc::vec::Vec;
13 changes: 6 additions & 7 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::sync::Mutex;

use once_cell::sync::Lazy;
use parking_lot::RwLock;

use crate::consts::{COMPRESSED_PUBLIC_KEY_SIZE, UNCOMPRESSED_PUBLIC_KEY_SIZE};

Expand All @@ -12,14 +11,14 @@ pub struct Config {
}

/// Global config variable
pub static ECIES_CONFIG: Lazy<Mutex<Config>> = Lazy::new(|| {
pub static ECIES_CONFIG: Lazy<RwLock<Config>> = Lazy::new(|| {
let config: Config = Config::default();
Mutex::new(config)
RwLock::new(config)
});

/// Update global config
pub fn update_config(config: Config) {
*ECIES_CONFIG.lock().unwrap() = config;
*ECIES_CONFIG.write() = config;
}

/// Reset global config to default
Expand All @@ -29,7 +28,7 @@ pub fn reset_config() {

/// Get ephemeral key compressed or not
pub fn is_ephemeral_key_compressed() -> bool {
ECIES_CONFIG.lock().unwrap().is_ephemeral_key_compressed
ECIES_CONFIG.read().is_ephemeral_key_compressed
}

/// Get ephemeral key size: compressed(33) or uncompressed(65)
Expand All @@ -43,5 +42,5 @@ pub fn get_ephemeral_key_size() -> usize {

/// Get hkdf key derived from compressed shared point or not
pub fn is_hkdf_key_compressed() -> bool {
ECIES_CONFIG.lock().unwrap().is_hkdf_key_compressed
ECIES_CONFIG.read().is_hkdf_key_compressed
}
11 changes: 11 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
#![doc = include_str!("../README.md")]
#![no_std]

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

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

pub use libsecp256k1::{Error as SecpError, PublicKey, SecretKey};

Expand All @@ -11,10 +18,14 @@ pub mod symmetric;
/// Utility functions
pub mod utils;

mod compat;

use config::{get_ephemeral_key_size, is_ephemeral_key_compressed};
use symmetric::{sym_decrypt, sym_encrypt};
use utils::{decapsulate, encapsulate, generate_keypair};

use crate::compat::Vec;

/// Encrypt a message by a public key
///
/// # Arguments
Expand Down
6 changes: 4 additions & 2 deletions src/symmetric/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use pure_aes::{decrypt, encrypt};
#[cfg(feature = "xchacha20")]
use xchacha20::{decrypt, encrypt};

use crate::compat::Vec;

/// Symmetric encryption wrapper. Openssl AES-256-GCM, pure Rust AES-256-GCM, or XChaCha20-Poly1305
pub fn sym_encrypt(key: &[u8], msg: &[u8]) -> Option<Vec<u8>> {
encrypt(key, msg)
Expand All @@ -25,7 +27,7 @@ pub fn sym_decrypt(key: &[u8], encrypted_msg: &[u8]) -> Option<Vec<u8>> {
#[cfg(test)]
pub(crate) mod tests {
use hex::decode; // dev dep
use rand::{thread_rng, Rng};
use rand_core::{OsRng, RngCore};

use super::*;

Expand All @@ -52,7 +54,7 @@ pub(crate) mod tests {
fn test_random_key() {
let text = b"this is a text";
let mut key = [0u8; 32];
thread_rng().fill(&mut key);
OsRng.fill_bytes(&mut key);

assert_eq!(
text,
Expand Down
5 changes: 3 additions & 2 deletions src/symmetric/openssl_aes.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use openssl::symm::{decrypt_aead, encrypt_aead, Cipher};
use rand::{thread_rng, Rng};
use rand_core::{OsRng, RngCore};

use crate::consts::{AEAD_TAG_LENGTH, AES_NONCE_LENGTH, EMPTY_BYTES};
use crate::Vec;

const NONCE_TAG_LENGTH: usize = AES_NONCE_LENGTH + AEAD_TAG_LENGTH;

Expand All @@ -10,7 +11,7 @@ pub fn encrypt(key: &[u8], msg: &[u8]) -> Option<Vec<u8>> {
let cipher = Cipher::aes_256_gcm();

let mut iv = [0u8; AES_NONCE_LENGTH];
thread_rng().fill(&mut iv);
OsRng.fill_bytes(&mut iv);

let mut tag = [0u8; AEAD_TAG_LENGTH];

Expand Down
5 changes: 3 additions & 2 deletions src/symmetric/pure_aes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ use aes_gcm::{
aes::Aes256,
AesGcm, KeyInit,
};
use rand::{thread_rng, Rng};
use rand_core::{OsRng, RngCore};
#[allow(unused_imports)]
use typenum::consts::{U12, U16};

use crate::compat::Vec;
use crate::consts::{AEAD_TAG_LENGTH, AES_NONCE_LENGTH, EMPTY_BYTES};

#[cfg(not(feature = "aes-12bytes-nonce"))]
Expand All @@ -23,7 +24,7 @@ pub fn encrypt(key: &[u8], msg: &[u8]) -> Option<Vec<u8>> {
let aead = Aes256Gcm::new(key);

let mut iv = [0u8; AES_NONCE_LENGTH];
thread_rng().fill(&mut iv);
OsRng.fill_bytes(&mut iv);
let nonce = GenericArray::from_slice(&iv);

let mut out = Vec::with_capacity(msg.len());
Expand Down
5 changes: 3 additions & 2 deletions src/symmetric/xchacha20.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ use chacha20poly1305::{
aead::{generic_array::GenericArray, AeadInPlace},
KeyInit, XChaCha20Poly1305,
};
use rand::{thread_rng, Rng};
use rand_core::{OsRng, RngCore};

use crate::compat::Vec;
use crate::consts::{AEAD_TAG_LENGTH, EMPTY_BYTES, XCHACHA20_NONCE_LENGTH};

const NONCE_TAG_LENGTH: usize = XCHACHA20_NONCE_LENGTH + AEAD_TAG_LENGTH;
Expand All @@ -14,7 +15,7 @@ pub fn encrypt(key: &[u8], msg: &[u8]) -> Option<Vec<u8>> {
let aead = XChaCha20Poly1305::new(key);

let mut iv = [0u8; XCHACHA20_NONCE_LENGTH];
thread_rng().fill(&mut iv);
OsRng.fill_bytes(&mut iv);
let nonce = GenericArray::from_slice(&iv);

let mut out = Vec::with_capacity(msg.len());
Expand Down
5 changes: 3 additions & 2 deletions src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use hkdf::Hkdf;
use libsecp256k1::{Error as SecpError, PublicKey, SecretKey};
use rand::thread_rng;
use rand_core::OsRng;
use sha2::Sha256;

use crate::compat::Vec;
use crate::config::{get_ephemeral_key_size, is_hkdf_key_compressed};
use crate::consts::EMPTY_BYTES;

Expand All @@ -11,7 +12,7 @@ pub type SharedSecret = [u8; 32];

/// Generate a `(SecretKey, PublicKey)` pair
pub fn generate_keypair() -> (SecretKey, PublicKey) {
let sk = SecretKey::random(&mut thread_rng());
let sk = SecretKey::random(&mut OsRng);
(sk, PublicKey::from_secret_key(&sk))
}

Expand Down
Loading