Skip to content

Commit

Permalink
Add memory protections for secret values (#108)
Browse files Browse the repository at this point in the history
  • Loading branch information
zmre authored Apr 27, 2020
1 parent 7b09520 commit f7183c4
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 54 deletions.
10 changes: 9 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ keywords = ["cryptography", "proxy-re-encryption", "PRE", "ECC", "transform-encr
description = "A pure-Rust implementation of Transform Encryption, a Proxy Re-encryption scheme"
edition = "2018"

[target.'cfg(all(unix, not(target_arch = "wasm32")))'.dependencies]
libc = { version = "0.2" }

[target.'cfg(all(windows, not(target_arch = "wasm32")))'.dependencies]
winapi = { version = "0.3", features = [ "memoryapi", "sysinfoapi" ] }

[dependencies]
#Explicit dependency so we can pass the wasm-bindgen flag to it
getrandom = {version = "~0.1.1", optional = true}
Expand All @@ -29,6 +35,7 @@ hex="~0.3"
arrayref = "^0.3.5"
log = "~0.4"
derivative = "1.0.3"
cfg-if = "~0.1.10"

[profile.dev]
opt-level = 2
Expand All @@ -48,11 +55,12 @@ proptest = "~0.9"
criterion = "~0.2"

[features]
unstable = []
default = ["u64_backend"]
u64_backend = ["ed25519-dalek/u64_backend"]
u32_backend = ["ed25519-dalek/u32_backend"]
wasm = ["u32_backend", "clear_on_drop/no_cc", "getrandom/wasm-bindgen"]
#Can be used to disable the automatic mlock detection for architectures.
disable_memlock = []

[[bench]]
name = "api_benchmark"
Expand Down
65 changes: 36 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,39 @@
recrypt
=======
[![](https://img.shields.io/crates/v/recrypt.svg)](https://crates.io/crates/recrypt) [![](https://docs.rs/recrypt/badge.svg)](https://docs.rs/recrypt) [![Build Status](https://travis-ci.org/IronCoreLabs/recrypt-rs.svg?branch=master)](https://travis-ci.org/IronCoreLabs/recrypt-rs)
# recrypt

A pure-Rust library that implements a set of cryptographic primitives for building a _multi-hop Proxy Re-encryption_ scheme, known as Transform Encryption.
[![](https://img.shields.io/crates/v/recrypt.svg)](https://crates.io/crates/recrypt) [![](https://docs.rs/recrypt/badge.svg)](https://docs.rs/recrypt) [![Build Status](https://travis-ci.org/IronCoreLabs/recrypt-rs.svg?branch=master)](https://travis-ci.org/IronCoreLabs/recrypt-rs)

A pure-Rust library that implements a set of cryptographic primitives for building a _multi-hop Proxy Re-encryption_ scheme, known as Transform Encryption.

## What is Transform Encryption?

Suppose you have two (public, private) key pairs: **(A, AA)** and **(B, BB)**.

Transform Encryption allows data encrypted to one public key (A) to be _transformed_ so that it can be decrypted using another user's private key (BB). This transformation process requires a special _transform key_ (A -> B) that is computed using the first user's private key (AA) and the second user's public key (B). **Having a transform key and performing the transformation does not allow the person doing this process to decrypt the data or to recover either user's private key.**
Transform Encryption allows data encrypted to one public key (A) to be _transformed_ so that it can be decrypted using another user's private key (BB). This transformation process requires a special _transform key_ (A -> B) that is computed using the first user's private key (AA) and the second user's public key (B). **Having a transform key and performing the transformation does not allow the person doing this process to decrypt the data or to recover either user's private key.**

See the [Single-hop Transform Encryption Example](https://docs.rs/recrypt/) for more details on computing a transform key and applying a transform using recrypt.

## Usage
## Usage

If you are building an application and would like to use Transform Encryption, you might try looking at the IronCore SDKs as they provide higher level of abstraction as part of the IronCore Privacy Platform:

- [ironweb](https://github.com/IronCoreLabs/ironweb) - Javascript implementation of IronCore's Privacy Platform. Appropriate for all modern browsers.
- [ironoxide](https://github.com/IronCoreLabs/ironoxide) - Pure Rust implementation of IronCore's Privacy Platform.
- [ironoxide-java](https://github.com/IronCoreLabs/ironoxide-java) - Java bindings for `ironoxide`. Appropriate for all JVM languages.
- [ironoxide-scala](https://github.com/IronCoreLabs/ironoxide-scala) - Scala wrappers around `ironoxide-java`.
- [ironode](https://github.com/IronCoreLabs/ironnode) - NodeJS implementation of IronCore's Privacy Platform.


All SDKs are intended to be compatible with one another.

### Rust Dependency

See https://crates.io/crates/recrypt for the most recent version.

### Other bindings
In addition to the native Rust implementation, we provide additional bindings to Recrypt:

* [Node.js](https://github.com/IronCoreLabs/recrypt-node-binding)
* [WebAssembly](https://github.com/IronCoreLabs/recrypt-wasm-binding)
In addition to the native Rust implementation, we provide additional bindings to Recrypt:

- [Node.js](https://github.com/IronCoreLabs/recrypt-node-binding)
- [WebAssembly](https://github.com/IronCoreLabs/recrypt-wasm-binding)

A [Scala implementation](https://github.com/IronCoreLabs/recrypt) of recrypt is also available.

Expand All @@ -46,15 +47,21 @@ NCC Group's [Cryptography Services](https://www.nccgroup.trust/us/our-services/c

To learn more about our approach to cryptography and to read our publications, please [go here](https://ironcorelabs.com/docs/concepts/ironcore-cryptography).

### Memory Protection

We do support memory protection via mlock. This will be detected and turned on automatically for supported platforms. If you need to disable this you can use the `disable_memlock` feature flag
which is disabled by default.

## Benchmarks

### Results from [79b6e6](https://github.com/IronCoreLabs/recrypt-rs/tree/79b6e62956f524109c8df81c4cf0cdf65291b5c5) (from 2019-12-04)

_Note: The most accurate way to characterize performance is to [run the benchmarks for yourself](#running-benchmarks) in your target environment!_

These benchmarks were done on a Thinkpad X1 Extreme (Gen2)

Abbreviated entry from `/proc/cpuinfo`

```
vendor_id : GenuineIntel
cpu family : 6
Expand All @@ -67,50 +74,50 @@ If you are unfamiliar with the output of criterion.rs benchmarks, please see [th
```
$ cargo bench
256-bit generate key pair
256-bit generate key pair
time: [757.61 us 760.48 us 764.48 us]
256-bit generate plaintext
256-bit generate plaintext
time: [2.9911 ms 2.9962 ms 3.0032 ms]
256-bit generate ed25519 keypair
256-bit generate ed25519 keypair
time: [15.627 us 15.686 us 15.793 us]
Found 1 outliers among 20 measurements (5.00%)
1 (5.00%) high severe
256-bit generate transform key
256-bit generate transform key
time: [15.050 ms 15.072 ms 15.094 ms]
256-bit compute public key
256-bit compute public key
time: [715.24 us 717.64 us 720.06 us]
Found 2 outliers among 20 measurements (10.00%)
2 (10.00%) high mild
256-bit derive symmetric key
256-bit derive symmetric key
time: [1.9093 us 1.9625 us 2.0225 us]
Found 3 outliers among 20 measurements (15.00%)
2 (10.00%) high mild
1 (5.00%) high severe
256-bit encrypt (level 0)
256-bit encrypt (level 0)
time: [7.0845 ms 7.1014 ms 7.1337 ms]
Found 4 outliers among 20 measurements (20.00%)
1 (5.00%) low mild
3 (15.00%) high severe
256-bit decrypt (level 0)
256-bit decrypt (level 0)
time: [6.4466 ms 6.4530 ms 6.4598 ms]
Found 1 outliers among 20 measurements (5.00%)
1 (5.00%) high mild
256-bit transform (level 1)
256-bit transform (level 1)
time: [18.466 ms 18.492 ms 18.519 ms]
256-bit decrypt (level 1)
256-bit decrypt (level 1)
time: [22.856 ms 22.888 ms 22.927 ms]
Found 1 outliers among 20 measurements (5.00%)
1 (5.00%) high severe
256-bit transform (level 2)
256-bit transform (level 2)
time: [41.160 ms 41.339 ms 41.541 ms]
256-bit decrypt (level 2)
256-bit decrypt (level 2)
time: [38.508 ms 38.560 ms 38.617 ms]
Found 2 outliers among 20 measurements (10.00%)
1 (5.00%) low mild
1 (5.00%) high severe
```
```

## Contributing
## Contributing

#### Building

Expand All @@ -127,6 +134,7 @@ $ cargo test
```

#### Running benchmarks

```
$ cargo bench
```
Expand All @@ -135,13 +143,12 @@ $ cargo bench

In the academic literature, _transform encryption_ is referred to as _proxy re-encryption_. A proxy re-encryption (PRE) scheme is a public-key encryption scheme, where each participant has a pair of related keys, one public and one private, which are mathematically related. Alice encrypts a message to Bob using his public key, and Bob decrypts the encrypted message using his public key to retrieve the original message.

PRE allows someone (the _delegator_) to delegate the ability to decrypt her messages to another person (the _delegatee_). Rather than just sharing her private key with the delegatee, the delegator computes a _transform key_ (or _re-encryption key_) that allows messages encrypted to her public key to be transformed so they appear can be decrypted using the delegatee's private key. Computing this transform key requires the delegator's private key and the delegatee's public key; once it is computed, the key is stored on a _semi-trusted proxy_.
PRE allows someone (the _delegator_) to delegate the ability to decrypt her messages to another person (the _delegatee_). Rather than just sharing her private key with the delegatee, the delegator computes a _transform key_ (or _re-encryption key_) that allows messages encrypted to her public key to be transformed so they appear can be decrypted using the delegatee's private key. Computing this transform key requires the delegator's private key and the delegatee's public key; once it is computed, the key is stored on a _semi-trusted proxy_.

When the proxy receives a message encrypted to the delegator, it applies the transform algorithm using the transform key and delivers the transformed message to the delegatee. __The proxy does not need to be trusted, because possession of the transform key does not allow the proxy to decrypt the message or to recover any information about either the delegator's or the delegatee's private keys, even if it collaborates with the delegatee.__
When the proxy receives a message encrypted to the delegator, it applies the transform algorithm using the transform key and delivers the transformed message to the delegatee. **The proxy does not need to be trusted, because possession of the transform key does not allow the proxy to decrypt the message or to recover any information about either the delegator's or the delegatee's private keys, even if it collaborates with the delegatee.**

When the delegator no longer wants to allow access, she just requests that the proxy discard the transform key. She must trust the proxy to perform this action.


### PRE Scheme Properties

There are a number of ways to categorize PRE schemes; some of the most important are the following:
Expand Down Expand Up @@ -190,5 +197,5 @@ _Guide to Pairing-Based Cryptography_ by N.E. Mrabet and M. Joye, Chapman and Ha
Recrypt-rust is licensed under the [GNU Affero General Public License](LICENSE).
We also offer commercial licenses - [email](mailto:[email protected]) for more information.

Copyright (c) 2018-present IronCore Labs, Inc.
Copyright (c) 2018-present IronCore Labs, Inc.
All rights reserved.
29 changes: 18 additions & 11 deletions src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::internal::fp12elem::Fp12Elem;
pub use crate::internal::hashable::Hashable;
use crate::internal::hashable::Hashable32;
use crate::internal::homogeneouspoint::TwistedHPoint;
use crate::internal::memlock;
use crate::internal::pairing;
pub use crate::internal::rand_bytes::*;
use crate::internal::schnorr::{SchnorrSign, SchnorrSigning};
Expand Down Expand Up @@ -125,25 +126,30 @@ bytes_eq_and_hash!(Plaintext);

impl From<Fp12Elem<Monty256>> for Plaintext {
fn from(fp12: Fp12Elem<Monty256>) -> Self {
Plaintext {
let p = Plaintext {
bytes: fp12.to_bytes_fp256(),
_internal_fp12: fp12,
}
};
memlock::mlock(&p);
p
}
}

impl Default for Plaintext {
fn default() -> Self {
Plaintext {
let p = Plaintext {
bytes: [0u8; Plaintext::ENCODED_SIZE_BYTES],
_internal_fp12: Fp12Elem::default(),
}
};
memlock::mlock(&p);
p
}
}
impl Drop for Plaintext {
fn drop(&mut self) {
self.bytes.clear();
self._internal_fp12.clear();
memlock::munlock(&self);
}
}
impl BytesDecoder for Plaintext {
Expand Down Expand Up @@ -1001,10 +1007,7 @@ impl PrivateKey {

pub fn new(bytes: [u8; PrivateKey::ENCODED_SIZE_BYTES]) -> PrivateKey {
let internal_key = internal::PrivateKey::from_fp256(Fp256::from(bytes).to_monty());
PrivateKey {
bytes: internal_key.value.to_bytes_32(),
_internal_key: internal_key,
}
internal_key.into()
}

new_from_slice!(PrivateKey);
Expand Down Expand Up @@ -1044,18 +1047,22 @@ impl Hashable32 for PrivateKey {

impl From<internal::PrivateKey<Monty256>> for PrivateKey {
fn from(internal_pk: internal::PrivateKey<Monty256>) -> Self {
PrivateKey {
let pk = PrivateKey {
bytes: internal_pk.value.to_bytes_32(),
_internal_key: internal_pk,
}
};
memlock::mlock(&pk);
pk
}
}

// use Drop to call clear on members of PrivateKey to zero memory before moving the stack pointer
impl Drop for PrivateKey {
fn drop(&mut self) {
self.bytes.clear();
self._internal_key.clear()
self._internal_key.clear();
// unlock after zeroing
memlock::munlock(&self)
}
}
new_bytes_type!(SchnorrSignature, 64);
Expand Down
28 changes: 17 additions & 11 deletions src/api_480.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::internal::fp12elem::Fp12Elem;
pub use crate::internal::hashable::Hashable;
use crate::internal::hashable::Hashable60;
use crate::internal::homogeneouspoint::TwistedHPoint;
use crate::internal::memlock;
use crate::internal::pairing;
pub use crate::internal::rand_bytes::*;
use crate::internal::schnorr::{SchnorrSign, SchnorrSigning};
Expand Down Expand Up @@ -124,25 +125,30 @@ bytes_eq_and_hash!(Plaintext);

impl From<Fp12Elem<Monty480>> for Plaintext {
fn from(fp12: Fp12Elem<Monty480>) -> Self {
Plaintext {
let p = Plaintext {
bytes: fp12.to_bytes_fp480(),
_internal_fp12: fp12,
}
};
memlock::mlock(&p);
p
}
}

impl Default for Plaintext {
fn default() -> Self {
Plaintext {
let p = Plaintext {
bytes: [0u8; Plaintext::ENCODED_SIZE_BYTES],
_internal_fp12: Fp12Elem::default(),
}
};
memlock::mlock(&p);
p
}
}
impl Drop for Plaintext {
fn drop(&mut self) {
self.bytes.clear();
self._internal_fp12.clear();
memlock::munlock(&self);
}
}
impl BytesDecoder for Plaintext {
Expand Down Expand Up @@ -1038,10 +1044,7 @@ impl PrivateKey {

pub fn new(bytes: [u8; PrivateKey::ENCODED_SIZE_BYTES]) -> PrivateKey {
let internal_key = internal::PrivateKey::from_fp480(Fp480::from(bytes).to_monty());
PrivateKey {
bytes: SixtyBytes(internal_key.value.to_bytes_60()),
_internal_key: internal_key,
}
internal_key.into()
}

new_from_slice!(PrivateKey);
Expand Down Expand Up @@ -1087,18 +1090,21 @@ impl Hashable for PrivateKey {

impl From<internal::PrivateKey<Monty480>> for PrivateKey {
fn from(internal_pk: internal::PrivateKey<Monty480>) -> Self {
PrivateKey {
let pk = PrivateKey {
bytes: SixtyBytes(internal_pk.value.to_bytes_60()),
_internal_key: internal_pk,
}
};
memlock::mlock(&pk);
pk
}
}

// use Drop to call clear on members of PrivateKey to zero memory before moving the stack pointer
impl Drop for PrivateKey {
fn drop(&mut self) {
self.bytes.clear();
self._internal_key.clear()
self._internal_key.clear();
memlock::munlock(&self)
}
}
new_bytes_type!(SchnorrSignature, 120);
Expand Down
8 changes: 6 additions & 2 deletions src/internal/ed25519.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::api_common::RecryptErr;
use crate::internal::hashable::Hashable;
use crate::internal::memlock;
use crate::internal::ByteVector;
use crate::internal::{array_split_64, take_lock};
use clear_on_drop::clear::Clear;
Expand Down Expand Up @@ -105,7 +106,9 @@ impl SigningKeypair {
}

pub(crate) fn new_unchecked(bytes: [u8; 64]) -> SigningKeypair {
SigningKeypair { bytes }
let skp = SigningKeypair { bytes };
memlock::mlock_slice(&skp.bytes[..]);
skp
}

///Get the public_key portion of this SigningKeypair.
Expand Down Expand Up @@ -137,7 +140,8 @@ impl<'a> From<&'a SigningKeypair> for PublicSigningKey {

impl Drop for SigningKeypair {
fn drop(&mut self) {
self.bytes.clear()
self.bytes.clear();
memlock::munlock_slice(&self.bytes[..])
}
}
new_bytes_type!(Ed25519Signature, 64);
Expand Down
Loading

0 comments on commit f7183c4

Please sign in to comment.