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

Add Diffie-Hellman key exchange for encryption to Account #809

Merged
merged 39 commits into from
Jun 6, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
64e3a35
add key_exchange, encrypt_data, decrypt_data to storage and account
Apr 12, 2022
3ae13b0
add bindings and rust example
Apr 12, 2022
4fad35f
add bindings example
Apr 13, 2022
348f588
Add key_exchange, encrypt_data, decrypt_data to memstore, change encr…
Apr 13, 2022
fa2b7cd
Solve merge conflicts - docs
Apr 13, 2022
2653298
Add nonce to EncryptedData
Apr 13, 2022
5ff7c45
Add associated data into EncryptedData
Apr 13, 2022
5ee2a43
remove key_exchange from storage, remove encryption_key type, add alg…
Apr 18, 2022
7440131
remove unnecessary into
Apr 18, 2022
fdce9bf
Generate random shared secret location
Apr 18, 2022
80d00ad
Remove generic crypto error
Apr 18, 2022
60110d3
Add crypto::error::Error as source for new errors
Apr 19, 2022
3b9a871
Add encryption option - with cek enum
Apr 25, 2022
5ac4b30
Add EncryptionOptions to bindings and to memstore
Apr 25, 2022
d74f0fa
Make PublicKey mandatory in the storage trait, return an error when u…
Apr 25, 2022
17c6208
Merge pull request #840 from iotaledger/chore/merge-update-resolution…
eike-hass May 6, 2022
fe61a9f
Doc/resolve (#823)
May 6, 2022
f636bfe
Merge pull request #844 from iotaledger/docs/merge-resolve-docs
eike-hass May 6, 2022
0bfe13d
Upgrade to new `Stronghold` interface (#787)
PhilippGackstatter May 6, 2022
310909a
Solve merge conflict - change vault to client
May 6, 2022
a158282
add concat_kdf procedure
May 9, 2022
933409d
add concat kdf for memstore
May 9, 2022
30e76fd
rename Cekalgorithm struct; remove error variant; add constructor to …
May 20, 2022
5801bae
Solve merge conflicts with dev
May 23, 2022
7105108
Merge branch 'dev' into feat/dh-key-exchange
May 31, 2022
a7cf428
Improve error msg; Add feature to cargo toml; Replace client for reso…
May 31, 2022
a503a16
Add ephemeral key for ECDH-ES
May 31, 2022
4fce141
Add test for stronghold encryption; Fix rust example; Add feature for…
Jun 2, 2022
6c3b21c
Sync comments for traits, account, and wasm
Jun 2, 2022
411bebb
Improve docs; Fix Memstore cocat kdf; Remove EncryptionOptions
Jun 2, 2022
0048fcf
Add test for storage test suite; Fix padding in Memstore
Jun 2, 2022
d6f3bc6
Rename outdated file
Jun 2, 2022
e26e4df
Add exception for encryption methods in MemStore
Jun 3, 2022
1bb17be
Fix naming in javascript; Switch back to ThreadRng
Jun 3, 2022
e7cbc4b
Remove useless variable
Jun 3, 2022
fd66590
Improve docs; Make EncryptedData fields pub
Jun 6, 2022
aed448c
Fix readme
Jun 6, 2022
f9500a8
Undo removal of files
Jun 6, 2022
7efdfe0
Fix function call
Jun 6, 2022
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
9 changes: 9 additions & 0 deletions bindings/stronghold-nodejs/examples/src/tests/key_exchange.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { keyExchange } from "../../../../wasm/examples-account/src/key_exchange";
import { stronghold } from '../stronghold';

// Only verifies that no uncaught exceptions are thrown, including syntax errors etc.
describe("Test Stronghold Node.js examples", function () {
it("Key Exchange", async () => {
await keyExchange(await stronghold());
});
})
26 changes: 24 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 } from '../napi-dist/napi';
import { DID, KeyLocation, Signature, ChainState, Storage, KeyType, Document, EncryptedData } from "@iota/identity-wasm/node";

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

public async keyExchange(did: DID, keyLocation: KeyLocation, publicKey: Uint8Array, fragment: string): Promise<KeyLocation> {
const napiDID: NapiDID = NapiDID.fromJSON(did.toJSON());
const napiKeyLocation = NapiKeyLocation.fromJSON(keyLocation.toJSON());
const secretLocation = await this.napiStronghold.keyExchange(napiDID, napiKeyLocation, Array.from(publicKey), fragment);
return KeyLocation.fromJSON(secretLocation.toJSON());
}

public async encryptData(did: DID, keyLocation: KeyLocation, data: Uint8Array, associatedData: Uint8Array): Promise<EncryptedData> {
const napiDID: NapiDID = NapiDID.fromJSON(did.toJSON());
const napiKeyLocation = NapiKeyLocation.fromJSON(keyLocation.toJSON());
const napiEncryptedData = await this.napiStronghold.encryptData(napiDID, napiKeyLocation, Array.from(data), Array.from(associatedData));
return EncryptedData.fromJSON(napiEncryptedData.toJSON());
}

public async decryptData(did: DID, keyLocation: KeyLocation, data: EncryptedData): Promise<Uint8Array> {
const napiDID: NapiDID = NapiDID.fromJSON(did.toJSON());
const napiKeyLocation = NapiKeyLocation.fromJSON(keyLocation.toJSON());
const napiEncryptedData = NapiEncryptedData.fromJSON(data.toJSON());
const decryptedData = await this.napiStronghold.decryptData(napiDID, napiKeyLocation, napiEncryptedData);
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
58 changes: 58 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,7 @@ use napi::Result;
use napi_derive::napi;

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

/// Performs Diffie-Hellman key exchange using the private key of the first party with the
/// public key of the second party, resulting in a shared secret.
///
/// Returns the location where the shared secred was stored
#[napi]
pub async fn key_exchange(
&self,
did: &NapiDID,
location: &NapiKeyLocation,
public_key: Vec<u32>,
fragment: String,
) -> Result<NapiKeyLocation> {
let public_key: PublicKey = public_key.try_into_bytes()?.into();
let location: KeyLocation = self
.0
.key_exchange(&did.0, &location.0, public_key, &fragment)
.await
.napi_result()?;
Ok(NapiKeyLocation(location))
}

/// Encrypts the given `data` using the key at the specified `location`.
#[napi]
pub async fn encrypt_data(
&self,
did: &NapiDID,
location: &NapiKeyLocation,
data: Vec<u32>,
associated_data: Vec<u32>,
) -> Result<NapiEncryptedData> {
let data: Vec<u8> = data.try_into_bytes()?;
let associated_data: Vec<u8> = associated_data.try_into_bytes()?;
let encrypted_data: EncryptedData = self
.0
.encrypt_data(&did.0, &location.0, data, associated_data)
.await
.napi_result()?;
Ok(NapiEncryptedData(encrypted_data))
}

/// Decrypts the given `data` using the key at the specified `location`.
#[napi]
pub async fn decrypt_data(
&self,
did: &NapiDID,
location: &NapiKeyLocation,
data: &NapiEncryptedData,
) -> Result<Vec<u32>> {
let data: Vec<u8> = self
.0
.decrypt_data(&did.0, &location.0, data.0.clone())
.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/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()
}
}
2 changes: 2 additions & 0 deletions bindings/stronghold-nodejs/src/account/types/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// Copyright 2020-2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

pub use encrypted_data::NapiEncryptedData;
pub use key_location::NapiKeyLocation;
pub use key_type::NapiKeyType;
pub use signature::NapiSignature;

mod encrypted_data;
mod key_location;
mod key_type;
mod signature;
Loading