Skip to content

Commit

Permalink
Support no_std (#105)
Browse files Browse the repository at this point in the history
  • Loading branch information
kigawas authored Jul 27, 2023
1 parent 871bb39 commit 45851ac
Show file tree
Hide file tree
Showing 15 changed files with 118 additions and 54 deletions.
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

0 comments on commit 45851ac

Please sign in to comment.