Skip to content

Commit

Permalink
Add Diffie-Hellman key exchange to Account (#809)
Browse files Browse the repository at this point in the history
* add key_exchange, encrypt_data, decrypt_data to storage and account

* add bindings and rust example

* add bindings example

* Add key_exchange, encrypt_data, decrypt_data to memstore, change encrypt/decrypt data signature

* Add nonce to EncryptedData

* Add associated data into EncryptedData

* remove key_exchange from storage, remove encryption_key type, add algorithm encryption, move key_exchange to encrypt/decrypt functions

* remove unnecessary into

* Generate random shared secret location

* Remove generic crypto error

* Add crypto::error::Error as source for new errors

* Add encryption option - with cek enum

* Add EncryptionOptions to bindings and to memstore

* Make PublicKey mandatory in the storage trait, return an error when using a ED25519 key for encryption/decryption

* Doc/resolve (#823)

* Upgrade to new `Stronghold` interface (#787)

* Rename stronghold module

* Postfix old stronghold with `_old`

* Migrate to new stronghold interface

* Impl did_create properly with client syncing

* Add context to `StrongholdError`s

* Add `Stronghold` wrapper test

* Add `test_key_delete`

* Add storage_test_suite setup & did_create test

* Re-export test suite feature

* Expose test suite in Wasm

* Extend `did_create` test, fix index persistence

* Test `key_generate`

* Move `key_delete` to test suite

* Remove test suite from this branch

* Add initial test suite and expose to Wasm

* rm `Error` postfix from `StrongholdError` variants

* Remove duplicate `mod tests` in Wasm

* Handle client sync error; document syncing

* Use updated stronghold

* Use dedicated `load_snapshot` function

* Purge client in `did_purge`

* Revert cfg_attr shenanigans

* Make `Stronghold::client` not async

* Remove asyncness from fns where not necessary

* Make `mutate_client` not async either

* Move test_util mod where it belongs

* Remove `source` errors from `Display` impl

* Remove `RecordHint` everywhere

* Use base crate `MemoryError`; remove engine dep

* Revert temporary send/sync change

* Document `Stronghold` wrapper

* Use same export style as other crates

* Create parent directories if they don't exist

* Remove outdated TODO

* Fix index writing in purge; update stronghold rev

* Remove old stronghold wrapper

* Reactivate multi identity example

* Add `dropsave` getter/setter

* Fully qualify `std::any::type_name`

* Remove tests which are already in test suite

* Reactivate `Send`-assertion test

* Return `Stronghold` instance from test `storages`

* Test incorrect password returns error

* Use `OsRng` instead of `thread_rng`

* Bump stronghold revision

* Remove unused `getrandom` depenency

* Remove unused `actix` dependency

* Remove tokio `rt-multi-thread` feature

* Prefer `sample_string` over `sample_iter`

* Enable `didPurge` test for NAPI stronghold

* Simplify `did_create` by using `mutate_client`

* Rename doc/state client paths to store keys

* Add procedure_error fn to reduce err map code

* Remove unnecessary clone

* Disable multiple identities example temporarily

* Disable musl build

* Remove musl target from stronghold-nodejs

* use local workflow file

* Revert "use local workflow file"

This reverts commit 2f12afd.

Co-authored-by: Eike Haß <eike-hass@web.de>

* add concat_kdf procedure

* add concat kdf for memstore

* rename Cekalgorithm struct; remove error variant; add constructor to bindings new function

* Improve error msg; Add feature to cargo toml; Replace client for resolver

* Add ephemeral key for ECDH-ES

* Add test for stronghold encryption; Fix rust example; Add feature for account encryption; Improve docs

* Sync comments for traits, account, and wasm

* Improve docs; Fix Memstore cocat kdf; Remove EncryptionOptions

* Add test for storage test suite; Fix padding in Memstore

* Rename outdated file

* Add exception for encryption methods in MemStore

* Fix naming in javascript; Switch back to ThreadRng

* Remove useless variable

* Improve docs; Make EncryptedData fields pub

* Fix readme

* Undo removal of files

* Fix function call

Co-authored-by: Eike Haß <eike-hass@web.de>
Co-authored-by: Oliver E. Anderson <oliver.anderson@iota.org>
Co-authored-by: Philipp <philipp.gackstatter@iota.org>
  • Loading branch information
4 people authored Jun 6, 2022
1 parent fb1948a commit 9176761
Show file tree
Hide file tree
Showing 44 changed files with 1,997 additions and 161 deletions.
49 changes: 29 additions & 20 deletions bindings/stronghold-nodejs/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion bindings/stronghold-nodejs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ publish = false
crate-type = ["cdylib"]

[dependencies]
identity-account-storage = { version = "=0.5.0", path = "../../identity-account-storage", default-features = false, features = ["stronghold", "send-sync-storage"] }
identity-account-storage = { version = "=0.5.0", path = "../../identity-account-storage", default-features = false, features = ["stronghold", "send-sync-storage", "encryption"] }
identity-core = { version = "=0.5.0", path = "../../identity-core", default-features = false }
identity-iota-core = { version = "=0.5.0", path = "../../identity-iota-core", default-features = false }
napi = { version = "=2.2.0", default-features = false, features = ["napi4", "tokio_rt", "serde-json"] }
Expand Down
9 changes: 9 additions & 0 deletions bindings/stronghold-nodejs/examples/src/tests/encryption.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { encryption } from "../../../../wasm/examples-account/src/encryption";
import { stronghold } from '../stronghold';

// Only verifies that no uncaught exceptions are thrown, including syntax errors etc.
describe("Test Stronghold Node.js examples", function () {
it("encryption", async () => {
await encryption(await stronghold());
});
})
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,7 @@ describe("Test Stronghold Node.js", function () {
it("didPurge", async () => {
await StorageTestSuite.didPurgeTest(await stronghold());
});
it("encryption", async () => {
await StorageTestSuite.encryptionTest(await stronghold(), await stronghold());
});
});
22 changes: 20 additions & 2 deletions bindings/stronghold-nodejs/js/stronghold.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NapiStronghold, NapiDID, NapiKeyLocation, NapiChainState, NapiDocument, NapiKeyType, NapiDidLocation } from '../napi-dist/napi';
import { DID, KeyLocation, Signature, ChainState, Storage, KeyType, Document } from "@iota/identity-wasm/node";
import { NapiStronghold, NapiDID, NapiKeyLocation, NapiChainState, NapiDocument, NapiKeyType, NapiDidLocation, NapiEncryptedData, NapiEncryptionAlgorithm, NapiCekAlgorithm } from '../napi-dist/napi';
import { DID, KeyLocation, Signature, ChainState, Storage, KeyType, Document, EncryptedData, EncryptionAlgorithm, CekAlgorithm } from "@iota/identity-wasm/node";

export class Stronghold implements Storage {
private napiStronghold: NapiStronghold;
Expand Down Expand Up @@ -97,6 +97,24 @@ export class Stronghold implements Storage {
return Signature.fromJSON(napiSignature.toJSON());
}

public async dataEncrypt(did: DID, plaintext: Uint8Array, associatedData: Uint8Array, encryptionAlgorithm: EncryptionAlgorithm, cekAlgorithm: CekAlgorithm, publicKey: Uint8Array): Promise<EncryptedData> {
const napiDID: NapiDID = NapiDID.fromJSON(did.toJSON());
const napiCekAlgorithm = NapiCekAlgorithm.fromJSON(cekAlgorithm.toJSON());
const napiEncryptionAlgorithm = NapiEncryptionAlgorithm.fromJSON(encryptionAlgorithm.toJSON());
const napiEncryptedData = await this.napiStronghold.dataEncrypt(napiDID, Array.from(plaintext), Array.from(associatedData), napiEncryptionAlgorithm, napiCekAlgorithm, Array.from(publicKey));
return EncryptedData.fromJSON(napiEncryptedData.toJSON());
}

public async dataDecrypt(did: DID, data: EncryptedData, encryptionAlgorithm: EncryptionAlgorithm, cekAlgorithm: CekAlgorithm, privateKey: KeyLocation): Promise<Uint8Array> {
const napiDID: NapiDID = NapiDID.fromJSON(did.toJSON());
const napiPrivateKeyLocation = NapiKeyLocation.fromJSON(privateKey.toJSON());
const napiCekAlgorithm = NapiCekAlgorithm.fromJSON(cekAlgorithm.toJSON());
const napiEncryptionAlgorithm = NapiEncryptionAlgorithm.fromJSON(encryptionAlgorithm.toJSON());
const napiEncryptedData = NapiEncryptedData.fromJSON(data.toJSON());
const decryptedData = await this.napiStronghold.dataDecrypt(napiDID, napiEncryptedData, napiEncryptionAlgorithm, napiCekAlgorithm, napiPrivateKeyLocation);
return Uint8Array.from(decryptedData);
}

public async chainStateGet(did: DID): Promise<ChainState | undefined> {
const napiDID: NapiDID = NapiDID.fromJSON(did.toJSON());
const napiChainState: NapiChainState | undefined = await this.napiStronghold.chainStateGet(napiDID);
Expand Down
61 changes: 61 additions & 0 deletions bindings/stronghold-nodejs/src/account/storage/stronghold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

use identity_account_storage::storage::Storage;
use identity_account_storage::storage::Stronghold;
use identity_account_storage::types::EncryptedData;
use identity_account_storage::types::KeyLocation;
use identity_core::crypto::PrivateKey;
use identity_core::crypto::PublicKey;
Expand All @@ -13,6 +14,9 @@ use napi::Result;
use napi_derive::napi;

use crate::account::identity::NapiDidLocation;
use crate::account::types::NapiCekAlgorithm;
use crate::account::types::NapiEncryptedData;
use crate::account::types::NapiEncryptionAlgorithm;
use crate::account::types::NapiKeyType;
use crate::account::NapiChainState;
use crate::account::NapiDocument;
Expand Down Expand Up @@ -175,6 +179,63 @@ impl NapiStronghold {
self.0.key_exists(&did.0, &location.0).await.napi_result()
}

/// Encrypts the given `plaintext` with the specified `encryption_algorithm` and `cek_algorithm`.
///
/// Returns an [`EncryptedData`] instance.
#[napi]
pub async fn data_encrypt(
&self,
did: &NapiDID,
plaintext: Vec<u32>,
associated_data: Vec<u32>,
encryption_algorithm: &NapiEncryptionAlgorithm,
cek_algorithm: &NapiCekAlgorithm,
public_key: Vec<u32>,
) -> Result<NapiEncryptedData> {
let public_key: Vec<u8> = public_key.try_into_bytes()?;
let plaintext: Vec<u8> = plaintext.try_into_bytes()?;
let associated_data: Vec<u8> = associated_data.try_into_bytes()?;
let encrypted_data: EncryptedData = self
.0
.data_encrypt(
&did.0,
plaintext,
associated_data,
&encryption_algorithm.0,
&cek_algorithm.0,
public_key.into(),
)
.await
.napi_result()?;
Ok(NapiEncryptedData(encrypted_data))
}

/// Decrypts the given `data` with the specified `encryption_algorithm` and `cek_algorithm`.
///
/// Returns the decrypted text.
#[napi]
pub async fn data_decrypt(
&self,
did: &NapiDID,
data: &NapiEncryptedData,
encryption_algorithm: &NapiEncryptionAlgorithm,
cek_algorithm: &NapiCekAlgorithm,
private_key: &NapiKeyLocation,
) -> Result<Vec<u32>> {
let data: Vec<u8> = self
.0
.data_decrypt(
&did.0,
data.0.clone(),
&encryption_algorithm.0,
&cek_algorithm.0,
&private_key.0,
)
.await
.napi_result()?;
Ok(data.into_iter().map(u32::from).collect())
}

/// Returns the chain state of the identity specified by `did`.
#[napi]
pub async fn chain_state_get(&self, did: &NapiDID) -> Result<Option<NapiChainState>> {
Expand Down
24 changes: 24 additions & 0 deletions bindings/stronghold-nodejs/src/account/types/cek_algorithm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2020-2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use identity_account_storage::types::CekAlgorithm;
use napi::Result;
use napi_derive::napi;

use crate::error::NapiResult;

#[napi]
pub struct NapiCekAlgorithm(pub(crate) CekAlgorithm);

#[napi]
impl NapiCekAlgorithm {
#[napi(js_name = fromJSON)]
pub fn from_json(json_value: serde_json::Value) -> Result<NapiCekAlgorithm> {
serde_json::from_value(json_value).map(Self).napi_result()
}

#[napi(js_name = toJSON)]
pub fn to_json(&self) -> Result<serde_json::Value> {
serde_json::to_value(&self.0).napi_result()
}
}
24 changes: 24 additions & 0 deletions bindings/stronghold-nodejs/src/account/types/encrypted_data.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2020-2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use identity_account_storage::types::EncryptedData;
use napi::Result;
use napi_derive::napi;

use crate::error::NapiResult;

#[napi]
pub struct NapiEncryptedData(pub(crate) EncryptedData);

#[napi]
impl NapiEncryptedData {
#[napi(js_name = fromJSON)]
pub fn from_json(json_value: serde_json::Value) -> Result<NapiEncryptedData> {
serde_json::from_value(json_value).map(Self).napi_result()
}

#[napi(js_name = toJSON)]
pub fn to_json(&self) -> Result<serde_json::Value> {
serde_json::to_value(&self.0).napi_result()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2020-2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use identity_account_storage::types::EncryptionAlgorithm;
use napi::Result;
use napi_derive::napi;

use crate::error::NapiResult;

#[napi]
pub struct NapiEncryptionAlgorithm(pub(crate) EncryptionAlgorithm);

#[napi]
impl NapiEncryptionAlgorithm {
#[napi(js_name = fromJSON)]
pub fn from_json(json_value: serde_json::Value) -> Result<NapiEncryptionAlgorithm> {
serde_json::from_value(json_value).map(Self).napi_result()
}

#[napi(js_name = toJSON)]
pub fn to_json(&self) -> Result<serde_json::Value> {
serde_json::to_value(&self.0).napi_result()
}
}
6 changes: 6 additions & 0 deletions bindings/stronghold-nodejs/src/account/types/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
// Copyright 2020-2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

pub use cek_algorithm::NapiCekAlgorithm;
pub use encrypted_data::NapiEncryptedData;
pub use encryption_algorithm::NapiEncryptionAlgorithm;
pub use key_location::NapiKeyLocation;
pub use key_type::NapiKeyType;
pub use signature::NapiSignature;

mod cek_algorithm;
mod encrypted_data;
mod encryption_algorithm;
mod key_location;
mod key_type;
mod signature;
2 changes: 1 addition & 1 deletion bindings/wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ wasm-bindgen-futures = { version = "0.4", default-features = false }
version = "=0.5.0"
path = "../../identity"
default-features = false
features = ["account", "storage-test-suite"]
features = ["account", "storage-test-suite", "unstable-encryption"]

[dev-dependencies]
wasm-bindgen-test = { version = "0.3" }
Expand Down
Loading

0 comments on commit 9176761

Please sign in to comment.