Skip to content

Commit

Permalink
Update Stronghold (#691)
Browse files Browse the repository at this point in the history
* start handling stronghold errors

* project compiles with new stronghold version

* fix tests

* fix io error mapping

* improvements

* remove unnecessary empty check

* remove `records.rs`

* remove `get_strict`

* remove `StrongholdMutexPoisoned` error by using `parking_lot:Mutex`

* remove unnecessary empty check

* fix format issue

* Revert "remove `records.rs`"

This reverts commit fe8c90a.

* use `IotaStrongholdResult` for records.rs
  • Loading branch information
abdulmth authored Mar 9, 2022
1 parent 424ba5e commit b129ffe
Show file tree
Hide file tree
Showing 15 changed files with 285 additions and 429 deletions.
5 changes: 3 additions & 2 deletions identity-account/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ identity-iota = { version = "=0.5.0-dev.4", path = "../identity-iota", default-f
itoa = { version = "0.4" }
log = { version = "0.4", default-features = false }
once_cell = { version = "1.7", default-features = false, features = ["std"] }
parking_lot = { version = "0.12" }
paste = { version = "1.0" }
serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] }
slog = { version = "2.7" }
Expand All @@ -36,12 +37,12 @@ features = ["blake2b", "ed25519", "hmac", "pbkdf", "sha", "slip10", "std"]

[dependencies.iota_stronghold]
git = "https://github.com/iotaledger/stronghold.rs"
rev = "aea8a9dc8c3fa12e5444c5b4bb3303876e4c1a2f"
rev = "969df405661ba4977f2cf30e9909cef7e30cefa2"
optional = true

[dependencies.stronghold_engine]
git = "https://github.com/iotaledger/stronghold.rs"
rev = "aea8a9dc8c3fa12e5444c5b4bb3303876e4c1a2f"
rev = "969df405661ba4977f2cf30e9909cef7e30cefa2"
optional = true

[dev-dependencies]
Expand Down
20 changes: 3 additions & 17 deletions identity-account/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

//! Errors that may occur when working with Identity Accounts.
use crate::stronghold::StrongholdError;

/// Alias for a `Result` with the error type [`Error`].
pub type Result<T, E = Error> = ::core::result::Result<T, E>;

Expand All @@ -27,25 +29,9 @@ pub enum Error {
/// Caused by attempting to perform an invalid IO operation.
#[error(transparent)]
IoError(#[from] std::io::Error),
/// Caused by errors from the [iota_stronghold] crate.
#[cfg(feature = "stronghold")]
#[error(transparent)]
StrongholdError(#[from] iota_stronghold::Error),
/// Caused by errors from an invalid Stronghold procedure.
#[error("Stronghold error: {0}")]
StrongholdResult(String),
/// Caused by attempting to parse an invalid Stronghold resource index.
#[error("Stronghold resource index malformed")]
InvalidResourceIndex,
/// Caused by attempting to access a Stronghold snapshot without a password.
#[error("Stronghold snapshot password not found")]
StrongholdPasswordNotSet,
/// Caused by receiving an unexpected return value from a Stronghold procedure.
#[error("Stronghold procedure returned unexpected type")]
StrongholdProcedureFailure,
/// Caused by an internal panic in the Stronghold runtime.
#[error("Stronghold mutex poisoned: {0}")]
StrongholdMutexPoisoned(&'static str),
StrongholdError(#[from] StrongholdError),
/// Caused by attempting to read a poisoned shared resource.
#[error("Shared resource poisoned: read")]
SharedReadPoisoned,
Expand Down
109 changes: 61 additions & 48 deletions identity-account/src/storage/stronghold.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright 2020-2021 IOTA Stiftung
// Copyright 2020-2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use crypto::keys::slip10::Chain;
use futures::executor;

use identity_core::convert::FromJson;
Expand All @@ -11,8 +10,12 @@ use identity_core::crypto::PublicKey;
use identity_did::did::DID;
use identity_did::verification::MethodType;
use identity_iota::did::IotaDID;
use iota_stronghold::procedures::Chain;
use iota_stronghold::procedures::Ed25519Sign;
use iota_stronghold::procedures::Slip10Derive;
use iota_stronghold::procedures::Slip10DeriveInput;
use iota_stronghold::procedures::Slip10Generate;
use iota_stronghold::Location;
use iota_stronghold::SLIP10DeriveInput;
use std::convert::TryFrom;
use std::io;
use std::path::Path;
Expand Down Expand Up @@ -79,11 +82,13 @@ impl Stronghold {
#[async_trait::async_trait]
impl Storage for Stronghold {
async fn set_password(&self, password: EncryptionKey) -> Result<()> {
self.snapshot.set_password(password).await
self.snapshot.set_password(password).await?;
Ok(())
}

async fn flush_changes(&self) -> Result<()> {
self.snapshot.save().await
self.snapshot.save().await?;
Ok(())
}

async fn key_new(&self, did: &IotaDID, location: &KeyLocation) -> Result<PublicKey> {
Expand Down Expand Up @@ -147,23 +152,21 @@ impl Storage for Stronghold {
async fn key_exists(&self, did: &IotaDID, location: &KeyLocation) -> Result<bool> {
let vault: Vault<'_> = self.vault(did);

match location.method() {
Ok(match location.method() {
MethodType::Ed25519VerificationKey2018 => vault.exists(location_skey(location)).await,
MethodType::MerkleKeyCollection2021 => todo!("[Stronghold::key_exists] Handle MerkleKeyCollection2021"),
}
}?)
}

async fn chain_state(&self, did: &IotaDID) -> Result<Option<ChainState>> {
// Load the chain-specific store
let store: Store<'_> = self.store(&fmt_did(did));
let data: Option<Vec<u8>> = store.get(location_chain_state()).await?;

let data: Vec<u8> = store.get(location_chain_state()).await?;

if data.is_empty() {
return Ok(None);
match data {
None => return Ok(None),
Some(data) => Ok(Some(ChainState::from_json_slice(&data)?)),
}

Ok(Some(ChainState::from_json_slice(&data)?))
}

async fn set_chain_state(&self, did: &IotaDID, chain_state: &ChainState) -> Result<()> {
Expand All @@ -182,15 +185,12 @@ impl Storage for Stronghold {
let store: Store<'_> = self.store(&fmt_did(did));

// Read the state from the stronghold snapshot
let data: Vec<u8> = store.get(location_state()).await?;
let data: Option<Vec<u8>> = store.get(location_state()).await?;

// No state data found
if data.is_empty() {
return Ok(None);
match data {
None => return Ok(None),
Some(data) => Ok(Some(IdentityState::from_json_slice(&data)?)),
}

// Deserialize and return
Ok(Some(IdentityState::from_json_slice(&data)?))
}

async fn set_state(&self, did: &IotaDID, state: &IdentityState) -> Result<()> {
Expand All @@ -216,23 +216,24 @@ impl Storage for Stronghold {

let bytes = store.get(location_published_generation()).await?;

if bytes.is_empty() {
return Ok(None);
match bytes {
None => return Ok(None),
Some(bytes) => {
let le_bytes: [u8; 4] = <[u8; 4]>::try_from(bytes.as_ref()).map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidData,
format!(
"expected to read 4 bytes as the published generation, found {} instead",
bytes.len()
),
)
})?;

let gen = Generation::from_u32(u32::from_le_bytes(le_bytes));

Ok(Some(gen))
}
}

let le_bytes: [u8; 4] = <[u8; 4]>::try_from(bytes.as_ref()).map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidData,
format!(
"expected to read 4 bytes as the published generation, found {} instead",
bytes.len()
),
)
})?;

let gen = Generation::from_u32(u32::from_le_bytes(le_bytes));

Ok(Some(gen))
}

async fn set_published_generation(&self, did: &IotaDID, index: Generation) -> Result<()> {
Expand All @@ -256,32 +257,44 @@ impl Drop for Stronghold {

async fn generate_ed25519(vault: &Vault<'_>, location: &KeyLocation) -> Result<PublicKey> {
// Generate a SLIP10 seed as the private key
vault
.slip10_generate(location_seed(location), default_hint(), None)
.await?;
let procedure: Slip10Generate = Slip10Generate {
output: location_seed(location),
hint: default_hint(),
size_bytes: None,
};
vault.execute(procedure).await?;

let chain: Chain = Chain::from_u32_hardened(vec![0, 0, 0]);
let seed: SLIP10DeriveInput = SLIP10DeriveInput::Seed(location_seed(location));
let seed: Slip10DeriveInput = Slip10DeriveInput::Seed(location_seed(location));

// Use the SLIP10 seed to derive a child key
vault
.slip10_derive(chain, seed, location_skey(location), default_hint())
.await?;
let procedure: Slip10Derive = Slip10Derive {
chain,
input: seed,
output: location_skey(location),
hint: default_hint(),
};
vault.execute(procedure).await?;

// Retrieve the public key of the derived child key
retrieve_ed25519(vault, location).await
}

async fn retrieve_ed25519(vault: &Vault<'_>, location: &KeyLocation) -> Result<PublicKey> {
vault
.ed25519_public_key(location_skey(location))
.await
.map(|public| public.to_vec().into())
let procedure: iota_stronghold::procedures::PublicKey = iota_stronghold::procedures::PublicKey {
ty: iota_stronghold::procedures::KeyType::Ed25519,
private_key: location_skey(location),
};
Ok(vault.execute(procedure).await.map(|public| public.to_vec().into())?)
}

async fn sign_ed25519(vault: &Vault<'_>, payload: Vec<u8>, location: &KeyLocation) -> Result<Signature> {
let public_key: PublicKey = retrieve_ed25519(vault, location).await?;
let signature: [u8; 64] = vault.ed25519_sign(payload, location_skey(location)).await?;
let procedure: Ed25519Sign = Ed25519Sign {
private_key: location_skey(location),
msg: payload,
};
let signature: [u8; 64] = vault.execute(procedure).await?;

Ok(Signature::new(public_key, signature.into()))
}
Expand Down
Loading

0 comments on commit b129ffe

Please sign in to comment.