Skip to content

Commit

Permalink
change stronghold crate's structure, revert persist changes on drop
Browse files Browse the repository at this point in the history
  • Loading branch information
UMR1352 committed Apr 30, 2024
1 parent 9632e8d commit 479f4bc
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 149 deletions.
7 changes: 2 additions & 5 deletions identity_stronghold/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@
// SPDX-License-Identifier: Apache-2.0

pub(crate) mod ed25519;
mod stronghold_jwk_storage;
#[cfg(any(feature = "bbs-plus", test))]
mod stronghold_jwk_storage_bbs_plus_ext;
mod stronghold_key_id;
mod storage;
pub(crate) mod stronghold_key_type;
#[cfg(test)]
mod tests;
pub(crate) mod utils;

pub use stronghold_jwk_storage::*;
pub use storage::*;
163 changes: 163 additions & 0 deletions identity_stronghold/src/storage/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
// Copyright 2020-2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

mod stronghold_jwk_storage;
#[cfg(any(feature = "bbs-plus", test))]
mod stronghold_jwk_storage_bbs_plus_ext;
mod stronghold_key_id;

use std::sync::Arc;

#[cfg(feature = "bbs-plus")]
use identity_storage::key_storage::bls::encode_bls_jwk;
use identity_storage::KeyId;
use identity_storage::KeyStorageError;
use identity_storage::KeyStorageErrorKind;
use identity_storage::KeyStorageResult;
use identity_verification::jwk::EdCurve;
use identity_verification::jwk::Jwk;
use identity_verification::jwk::JwkParamsOkp;
use identity_verification::jws::JwsAlgorithm;
use identity_verification::jwu;
use iota_sdk::client::secret::stronghold::StrongholdSecretManager;
use iota_sdk::client::secret::SecretManager;
#[cfg(feature = "bbs-plus")]
use iota_stronghold::procedures::FatalProcedureError;
use iota_stronghold::procedures::KeyType as ProceduresKeyType;
#[cfg(feature = "bbs-plus")]
use iota_stronghold::procedures::Runner as _;
use iota_stronghold::procedures::StrongholdProcedure;
use iota_stronghold::Location;
use iota_stronghold::Stronghold;
#[cfg(feature = "bbs-plus")]
use jsonprooftoken::jpa::algs::ProofAlgorithm;
use tokio::sync::MutexGuard;
#[cfg(feature = "bbs-plus")]
use zeroize::Zeroizing;
#[cfg(feature = "bbs-plus")]
use zkryptium::bbsplus::keys::BBSplusSecretKey;

use crate::stronghold_key_type::StrongholdKeyType;
use crate::utils::get_client;
use crate::utils::IDENTITY_VAULT_PATH;

/// Wrapper around a [`StrongholdSecretManager`] that implements the [`KeyIdStorage`](crate::KeyIdStorage)
/// and [`JwkStorage`](crate::JwkStorage) interfaces.
#[derive(Clone, Debug)]
pub struct StrongholdStorage(Arc<SecretManager>);

impl StrongholdStorage {
/// Creates a new [`StrongholdStorage`].
pub fn new(stronghold_secret_manager: StrongholdSecretManager) -> Self {
Self(Arc::new(SecretManager::Stronghold(stronghold_secret_manager)))
}

/// Shared reference to the inner [`SecretManager`].
pub fn as_secret_manager(&self) -> &SecretManager {
self.0.as_ref()
}

/// Acquire lock of the inner [`Stronghold`].
pub(crate) async fn get_stronghold(&self) -> MutexGuard<'_, Stronghold> {
match *self.0 {
SecretManager::Stronghold(ref stronghold) => stronghold.inner().await,
_ => unreachable!("secret manager can be only constructed from stronghold"),
}
}

async fn get_ed25519_public_key(&self, key_id: &KeyId) -> KeyStorageResult<Jwk> {
let stronghold = self.get_stronghold().await;
let client = get_client(&stronghold)?;

let location = Location::generic(
IDENTITY_VAULT_PATH.as_bytes().to_vec(),
key_id.to_string().as_bytes().to_vec(),
);

let public_key_procedure = iota_stronghold::procedures::PublicKey {
ty: ProceduresKeyType::Ed25519,
private_key: location,
};

let procedure_result = client
.execute_procedure(StrongholdProcedure::PublicKey(public_key_procedure))
.map_err(|err| KeyStorageError::new(KeyStorageErrorKind::KeyNotFound).with_source(err))?;

let public_key: Vec<u8> = procedure_result.into();

let mut params = JwkParamsOkp::new();
params.x = jwu::encode_b64(public_key);
params.crv = EdCurve::Ed25519.name().to_owned();
let mut jwk: Jwk = Jwk::from_params(params);
jwk.set_alg(JwsAlgorithm::EdDSA.name());
jwk.set_kid(jwk.thumbprint_sha256_b64());

Ok(jwk)
}

#[cfg(feature = "bbs-plus")]
async fn get_bls12381g2_public_key(&self, key_id: &KeyId) -> KeyStorageResult<Jwk> {
let stronghold = self.get_stronghold().await;
let client = get_client(&stronghold)?;

let location = Location::generic(
IDENTITY_VAULT_PATH.as_bytes().to_vec(),
key_id.to_string().as_bytes().to_vec(),
);

client
.get_guards([location], |[sk]| {
let sk = BBSplusSecretKey::from_bytes(&sk.borrow()).map_err(|e| FatalProcedureError::from(e.to_string()))?;
let pk = sk.public_key();
let public_jwk = encode_bls_jwk(&sk, &pk, ProofAlgorithm::BLS12381_SHA256).1;

drop(Zeroizing::new(sk.to_bytes()));
Ok(public_jwk)
})
.map_err(|e| KeyStorageError::new(KeyStorageErrorKind::KeyNotFound).with_source(e))
}

/// Attepts to retrieve the public key corresponding to the key of id `key_id`,
/// returning it as a `key_type` encoded public JWK.
pub async fn get_public_key_with_type(&self, key_id: &KeyId, key_type: StrongholdKeyType) -> KeyStorageResult<Jwk> {
match key_type {
StrongholdKeyType::Ed25519 => self.get_ed25519_public_key(key_id).await,
#[cfg(feature = "bbs-plus")]
StrongholdKeyType::Bls12381G2 => self.get_bls12381g2_public_key(key_id).await,
#[allow(unreachable_patterns)]
_ => Err(KeyStorageErrorKind::UnsupportedKeyType.into()),
}
}

/// Retrieve the public key corresponding to `key_id`.
#[deprecated(since = "1.3.0", note = "use `get_public_key_with_type` instead")]
pub async fn get_public_key(&self, key_id: &KeyId) -> KeyStorageResult<Jwk> {
let stronghold = self.get_stronghold().await;
let client = get_client(&stronghold)?;

let location = Location::generic(
IDENTITY_VAULT_PATH.as_bytes().to_vec(),
key_id.to_string().as_bytes().to_vec(),
);

let public_key_procedure = iota_stronghold::procedures::PublicKey {
ty: ProceduresKeyType::Ed25519,
private_key: location,
};

let procedure_result = client
.execute_procedure(StrongholdProcedure::PublicKey(public_key_procedure))
.map_err(|err| KeyStorageError::new(KeyStorageErrorKind::KeyNotFound).with_source(err))?;

let public_key: Vec<u8> = procedure_result.into();

let mut params = JwkParamsOkp::new();
params.x = jwu::encode_b64(public_key);
params.crv = EdCurve::Ed25519.name().to_owned();
let mut jwk: Jwk = Jwk::from_params(params);
jwk.set_alg(JwsAlgorithm::EdDSA.name());
jwk.set_kid(jwk.thumbprint_sha256_b64());

Ok(jwk)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
//! Wrapper around [`StrongholdSecretManager`](StrongholdSecretManager).
use async_trait::async_trait;
use identity_storage::key_storage::bls::encode_bls_jwk;
use identity_storage::key_storage::JwkStorage;
use identity_storage::JwkGenOutput;
use identity_storage::KeyId;
Expand All @@ -17,143 +16,17 @@ use identity_verification::jwk::Jwk;
use identity_verification::jwk::JwkParamsOkp;
use identity_verification::jws::JwsAlgorithm;
use identity_verification::jwu;
use iota_sdk::client::secret::stronghold::StrongholdSecretManager;
use iota_sdk::client::secret::SecretManager;
use iota_stronghold::procedures::Ed25519Sign;
use iota_stronghold::procedures::FatalProcedureError;
use iota_stronghold::procedures::GenerateKey;
use iota_stronghold::procedures::KeyType as ProceduresKeyType;
use iota_stronghold::procedures::Runner;
use iota_stronghold::procedures::StrongholdProcedure;
use iota_stronghold::Location;
use iota_stronghold::Stronghold;
use jsonprooftoken::jpa::algs::ProofAlgorithm;
use std::str::FromStr;
use std::sync::Arc;
use tokio::sync::MutexGuard;
use zeroize::Zeroizing;
use zkryptium::bbsplus::keys::BBSplusSecretKey;

use crate::ed25519;
use crate::stronghold_key_type::StrongholdKeyType;
use crate::utils::*;

/// Wrapper around a [`StrongholdSecretManager`] that implements the [`KeyIdStorage`](crate::KeyIdStorage)
/// and [`JwkStorage`](crate::JwkStorage) interfaces.
#[derive(Clone, Debug)]
pub struct StrongholdStorage(Arc<SecretManager>);

impl StrongholdStorage {
/// Creates a new [`StrongholdStorage`].
pub fn new(stronghold_secret_manager: StrongholdSecretManager) -> Self {
Self(Arc::new(SecretManager::Stronghold(stronghold_secret_manager)))
}

/// Shared reference to the inner [`SecretManager`].
pub fn as_secret_manager(&self) -> &SecretManager {
self.0.as_ref()
}

/// Acquire lock of the inner [`Stronghold`].
pub(crate) async fn get_stronghold(&self) -> MutexGuard<'_, Stronghold> {
match *self.0 {
SecretManager::Stronghold(ref stronghold) => stronghold.inner().await,
_ => unreachable!("secret manager can be only constructed from stronghold"),
}
}

async fn get_ed25519_public_key(&self, key_id: &KeyId) -> KeyStorageResult<Jwk> {
let stronghold = self.get_stronghold().await;
let client = get_client(&stronghold)?;

let location = Location::generic(
IDENTITY_VAULT_PATH.as_bytes().to_vec(),
key_id.to_string().as_bytes().to_vec(),
);

let public_key_procedure = iota_stronghold::procedures::PublicKey {
ty: ProceduresKeyType::Ed25519,
private_key: location,
};

let procedure_result = client
.execute_procedure(StrongholdProcedure::PublicKey(public_key_procedure))
.map_err(|err| KeyStorageError::new(KeyStorageErrorKind::KeyNotFound).with_source(err))?;

let public_key: Vec<u8> = procedure_result.into();

let mut params = JwkParamsOkp::new();
params.x = jwu::encode_b64(public_key);
params.crv = EdCurve::Ed25519.name().to_owned();
let mut jwk: Jwk = Jwk::from_params(params);
jwk.set_alg(JwsAlgorithm::EdDSA.name());
jwk.set_kid(jwk.thumbprint_sha256_b64());

Ok(jwk)
}

async fn get_bls12381g2_public_key(&self, key_id: &KeyId) -> KeyStorageResult<Jwk> {
let stronghold = self.get_stronghold().await;
let client = get_client(&stronghold)?;

let location = Location::generic(
IDENTITY_VAULT_PATH.as_bytes().to_vec(),
key_id.to_string().as_bytes().to_vec(),
);

client
.get_guards([location], |[sk]| {
let sk = BBSplusSecretKey::from_bytes(&sk.borrow()).map_err(|e| FatalProcedureError::from(e.to_string()))?;
let pk = sk.public_key();
let public_jwk = encode_bls_jwk(&sk, &pk, ProofAlgorithm::BLS12381_SHA256).1;

drop(Zeroizing::new(sk.to_bytes()));
Ok(public_jwk)
})
.map_err(|e| KeyStorageError::new(KeyStorageErrorKind::KeyNotFound).with_source(e))
}

/// Attepts to retrieve the public key corresponding to the key of id `key_id`,
/// returning it as a `key_type` encoded public JWK.
pub async fn get_public_key_with_type(&self, key_id: &KeyId, key_type: StrongholdKeyType) -> KeyStorageResult<Jwk> {
match key_type {
StrongholdKeyType::Ed25519 => self.get_ed25519_public_key(key_id).await,
StrongholdKeyType::Bls12381G2 => self.get_bls12381g2_public_key(key_id).await,
}
}

/// Retrieve the public key corresponding to `key_id`.
#[deprecated(since = "1.3.0", note = "use `get_public_key_with_type` instead")]
pub async fn get_public_key(&self, key_id: &KeyId) -> KeyStorageResult<Jwk> {
let stronghold = self.get_stronghold().await;
let client = get_client(&stronghold)?;

let location = Location::generic(
IDENTITY_VAULT_PATH.as_bytes().to_vec(),
key_id.to_string().as_bytes().to_vec(),
);

let public_key_procedure = iota_stronghold::procedures::PublicKey {
ty: ProceduresKeyType::Ed25519,
private_key: location,
};

let procedure_result = client
.execute_procedure(StrongholdProcedure::PublicKey(public_key_procedure))
.map_err(|err| KeyStorageError::new(KeyStorageErrorKind::KeyNotFound).with_source(err))?;

let public_key: Vec<u8> = procedure_result.into();

let mut params = JwkParamsOkp::new();
params.x = jwu::encode_b64(public_key);
params.crv = EdCurve::Ed25519.name().to_owned();
let mut jwk: Jwk = Jwk::from_params(params);
jwk.set_alg(JwsAlgorithm::EdDSA.name());
jwk.set_kid(jwk.thumbprint_sha256_b64());

Ok(jwk)
}
}
use crate::StrongholdStorage;

#[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))]
#[cfg_attr(feature = "send-sync-storage", async_trait)]
Expand Down Expand Up @@ -208,6 +81,7 @@ impl JwkStorage for StrongholdStorage {
.with_source(err)
})?;
let public_key: Vec<u8> = procedure_result.into();
persist_changes(self.as_secret_manager(), stronghold).await?;

let mut params = JwkParamsOkp::new();
params.x = jwu::encode_b64(public_key);
Expand Down Expand Up @@ -259,6 +133,8 @@ impl JwkStorage for StrongholdStorage {
.with_source(err)
})?;

persist_changes(self.as_secret_manager(), stronghold).await?;

Ok(key_id)
}

Expand Down Expand Up @@ -333,6 +209,8 @@ impl JwkStorage for StrongholdStorage {
return Err(KeyStorageError::new(KeyStorageErrorKind::KeyNotFound));
}

persist_changes(self.as_secret_manager(), stronghold).await?;

Ok(())
}

Expand All @@ -351,17 +229,3 @@ impl JwkStorage for StrongholdStorage {
Ok(exists)
}
}

/// Calls `persist_changes` when `StrongholdStorage` gets dropped.
impl Drop for StrongholdStorage {
fn drop(&mut self) {
let secret_manager = std::mem::replace(&mut self.0, Arc::new(SecretManager::Placeholder));
tokio::spawn(async move {
let SecretManager::Stronghold(stronghold) = secret_manager.as_ref() else {
return;
};
let stronghold = stronghold.inner().await;
let _ = persist_changes(&secret_manager, stronghold).await;
});
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Copyright 2020-2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

//! Wrapper around [`StrongholdSecretManager`](StrongholdSecretManager).
use async_trait::async_trait;
use identity_storage::key_storage::bls::*;
use identity_storage::key_storage::JwkStorage;
Expand Down Expand Up @@ -73,6 +71,8 @@ impl JwkStorageBbsPlusExt for StrongholdStorage {
.with_source(e)
})?;

persist_changes(self.as_secret_manager(), stronghold).await?;

Ok(JwkGenOutput::new(kid, jwk))
}

Expand Down

0 comments on commit 479f4bc

Please sign in to comment.