Skip to content

Commit

Permalink
fix(): init added secure repos trait impl
Browse files Browse the repository at this point in the history
  • Loading branch information
Christiantyemele committed Nov 26, 2024
1 parent 9af1f96 commit 261fd50
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 85 deletions.
46 changes: 1 addition & 45 deletions crates/database/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ use tokio::sync::RwLock;
pub trait Identifiable {
fn id(&self) -> Option<ObjectId>;
fn set_id(&mut self, id: ObjectId);
fn get_secret(&self) -> Option<Vec<u8>>;
}

/// Definition of custom errors for repository operations.
Expand Down Expand Up @@ -66,55 +65,12 @@ pub fn get_or_init_database() -> Arc<RwLock<Database>> {

/// Definition of a trait for secure repository operations.
#[async_trait]
pub trait SecureRepository<Entity>: Sync + Send
where
Entity: Sized + Clone + Send + Sync + 'static,
Entity: Identifiable + Unpin,
Entity: Serialize + for<'de> Deserialize<'de>,
Vec<u8>: Borrow<Entity>
{
fn get_collection(&self) -> Arc<RwLock<Collection<Entity>>>;
/// Stores a new entity.
async fn wrap_store(&self, mut entity: Entity) -> Result<Entity, RepositoryError> {
let collection = self.get_collection();

// Lock the Mutex and get the Collection
let collection = collection.read().await;

// read master key for encryption
let master_key = std::env::var("MASTER_KEY").unwrap_or_default();

let seed = &[0; 32];
let mut cocoon = MiniCocoon::from_key(master_key.as_bytes(), seed);
let secret = entity.get_secret().unwrap_or_default();
let wrapped_key = cocoon.wrap(&secret).unwrap_or_default();

// Insert the new entity into the database
let metadata = collection.insert_one(wrapped_key, None).await?;

// Set the ID if it was inserted and return the updated entity
if let Bson::ObjectId(oid) = metadata.inserted_id {
entity.set_id(oid);
}

Ok(entity)
}
async fn unwrap_find_one_by(&self, filter: BsonDocument) -> Result<Option<Entity>, RepositoryError> {
let collection = self.get_collection();

// Lock the Mutex and get the Collection
let collection = collection.read().await;
Ok(collection.find_one(filter, None).await?)
}

}
/// Definition of a trait for repository operations.
#[async_trait]
pub trait Repository<Entity>: Sync + Send
where
Entity: Sized + Clone + Send + Sync + 'static,
Entity: Identifiable + Unpin,
Entity: Serialize + for<'de> Deserialize<'de>,

{
fn get_collection(&self) -> Arc<RwLock<Collection<Entity>>>;

Expand Down
1 change: 1 addition & 0 deletions crates/keystore/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ database.workspace = true
serde.workspace = true
serde_json.workspace = true
tokio = { workspace = true, features = ["full"] }
cocoon = "0.4.3"

[features]
test-utils = []
177 changes: 144 additions & 33 deletions crates/keystore/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,138 @@
#![allow(unused_imports)]

use async_trait::async_trait;
use database::{Identifiable, Repository};
use cocoon::MiniCocoon;
use database::{Identifiable, Repository, RepositoryError};
use did_utils::jwk::Jwk;
use mongodb::{bson::oid::ObjectId, Collection};
use mongodb::{
bson::{doc, oid::ObjectId, Bson},
options::CountOptions,
Collection,
};
use once_cell::sync::OnceCell;
use serde::{Deserialize, Serialize};
use serde_json::to_vec;
use std::sync::Arc;
use tokio::sync::RwLock;
use std::{borrow::Borrow, sync::Arc};
use tokio::{runtime::EnterGuard, sync::RwLock};

static SECRETS_COLLECTION: OnceCell<Collection<Secrets>> = OnceCell::new();
#[async_trait]
pub trait Material {
async fn securestore(&self, secret: Secrets) -> Result<Secrets, RepositoryError>;
async fn find_one_by(&self, kid: String) -> Result<Option<Secrets>, RepositoryError>;
}

#[async_trait]
impl Material for SecretStore {
async fn securestore(&self, secret: Secrets) -> Result<Secrets, RepositoryError> {
// read master key for encryption
let master_key = std::env::var("MASTER_KEY").unwrap_or_default();

let mut secret = secret;

let seed = &[0; 32];
let secret_material = secret.secret_material.as_bytes();
let mut cocoon = MiniCocoon::from_key(master_key.as_bytes(), seed);
let wrapped_key = cocoon.wrap(secret_material).unwrap_or_default();
secret.secret_material = String::from_utf8(wrapped_key).unwrap_or_default();

// Insert the new entity into the database
let metadata = self
.keystore
.collection
.insert_one(secret.clone(), None)
.await?;

// Set the ID if it was inserted and return the updated entity
if let Bson::ObjectId(oid) = metadata.inserted_id {
secret.set_id(oid);
}

Ok(secret)
}

async fn find_one_by(&self, kid: String) -> Result<Option<Secrets>, RepositoryError> {
let collection = self.keystore.clone();

let secret = collection
.collection
.find_one(doc! {"kid": kid}, None)
.await?;
if let Some(mut secrets) = secret {
let wrapped_secret_material = secrets.secret_material;
let master_key = std::env::var("MASTER_KEY").unwrap_or_default();
let seed = &[0; 32];
let cocoon = MiniCocoon::from_key(master_key.as_bytes(), seed);
let unwrap_secret = cocoon
.unwrap(wrapped_secret_material.as_bytes())
.unwrap_or_default();
secrets.secret_material = String::from_utf8(unwrap_secret).unwrap_or_default();
Ok(Some(secrets))
} else {
Ok(None)
}
}
}

pub struct SecretStore {
keystore: KeyStore<Secrets>,
}

impl SecretStore {
pub fn new() -> Self {
let keystore = KeyStore::get();
Self { keystore }
}
}
// }
// pub async fn securestore(&self, secret: Secrets) -> Result<Secrets, RepositoryError> {
// // read master key for encryption
// let master_key = std::env::var("MASTER_KEY").unwrap_or_default();

// let mut secret = secret;

// let seed = &[0; 32];
// let secret_material = secret.secret_material.as_bytes();
// let mut cocoon = MiniCocoon::from_key(master_key.as_bytes(), seed);
// let wrapped_key = cocoon.wrap(secret_material).unwrap_or_default();
// secret.secret_material = String::from_utf8(wrapped_key).unwrap_or_default();

// // Insert the new entity into the database
// let metadata = self
// .keystore
// .collection
// .insert_one(secret.clone(), None)
// .await?;

// // Set the ID if it was inserted and return the updated entity
// if let Bson::ObjectId(oid) = metadata.inserted_id {
// secret.set_id(oid);
// }

// Ok(secret)
// }
// async fn find_one_by(&self, kid: String) -> Result<Option<Secrets>, RepositoryError> {
// let collection = self.keystore.clone();

// let secret = collection
// .collection
// .find_one(doc! {"kid": kid}, None)
// .await?;
// if let Some(mut secrets) = secret {
// let wrapped_secret_material = secrets.secret_material;
// let master_key = std::env::var("MASTER_KEY").unwrap_or_default();
// let seed = &[0; 32];
// let cocoon = MiniCocoon::from_key(master_key.as_bytes(), seed);
// let unwrap_secret = cocoon
// .unwrap(wrapped_secret_material.as_bytes())
// .unwrap_or_default();
// secrets.secret_material = String::from_utf8(unwrap_secret).unwrap_or_default();
// Ok(Some(secrets))
// } else {
// Ok(None)
// }
// }
// }

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct Secrets {
Expand All @@ -20,7 +142,7 @@ pub struct Secrets {

pub kid: String,

pub secret_material: Jwk,
pub secret_material: String,
}

impl Identifiable for Secrets {
Expand All @@ -31,10 +153,6 @@ impl Identifiable for Secrets {
fn set_id(&mut self, id: ObjectId) {
self.id = Some(id);
}
fn get_secret(&self) -> Option<Vec<u8>> {
let secret = to_vec(&self.secret_material).unwrap_or_default();
Some(secret)
}
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -98,21 +216,21 @@ where
}

#[async_trait]
impl<T> Repository<T> for KeyStore<T>
where
T: Sized + Clone + Send + Sync + 'static,
T: Identifiable + Unpin,
T: Serialize + for<'de> Deserialize<'de>,
{
fn get_collection(&self) -> Arc<RwLock<Collection<T>>> {
Arc::new(RwLock::new(self.collection.clone()))
impl Repository<Secrets> for SecretStore {
fn get_collection(&self) -> Arc<RwLock<Collection<Secrets>>> {
Arc::new(RwLock::new(self.keystore.collection.clone()))
}

async fn store(&self, entity: Secrets) -> Result<Secrets, RepositoryError> {
let secrets = self.securestore(entity).await?;
Ok(secrets)
}
}

#[cfg(any(test, feature = "test-utils"))]
pub mod tests {
use super::*;
use database::{Repository, RepositoryError, SecureRepository};
use database::{Repository, RepositoryError};
use mongodb::bson::{doc, Bson, Document};
use serde_json::json;
use std::{borrow::Borrow, collections::HashMap, sync::RwLock};
Expand Down Expand Up @@ -187,20 +305,6 @@ pub mod tests {
Ok(())
}
}
// impl SecureRepository<Secrets> for MockKeyStore {
// // Implement a dummy get_collection method
// fn get_collection(&self) -> Arc<tokio::sync::RwLock<Collection<Secrets>>> {
// // In-memory, we don't have an actual collection, but we can create a dummy Arc<Mutex> for compatibility.
// unimplemented!("This is a mock repository, no real collection exists.")
// }

// async fn wrap_store(&self, secrets: Secrets) -> Result<Secrets, RepositoryError> {
// let seed = &[0; 32];
// let mut cocoon = MiniCocoon::from_key("master_key".as_bytes(), seed);
// let secret = to_vec(&secrets.secret_material).unwrap();
// let wrapped_key = cocoon.wrap(&secret).unwrap_or_default();
// }
// }

#[tokio::test]
async fn test_keystore_flow() {
Expand All @@ -223,6 +327,13 @@ pub mod tests {
}"#,
)
.unwrap();
let secret1 = serde_json::to_vec(&secret1).unwrap();

let secret1 = String::from_utf8(secret1).unwrap_or_default();

let secret2 = serde_json::to_vec(&secret2).unwrap();

let secret2 = String::from_utf8(secret2).unwrap_or_default();

let secrets = vec![
Secrets {
Expand Down Expand Up @@ -262,4 +373,4 @@ pub mod tests {

assert_eq!(keystore.find_all().await.unwrap(), vec![secrets[1].clone()]);
}
}
}
16 changes: 10 additions & 6 deletions crates/web-plugins/did-endpoint/src/didgen.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
use database::{Repository, SecureRepository};
use database::Repository;
use did_utils::{
crypto::{Ed25519KeyPair, Generate, PublicKeyFormat, ToMultikey, X25519KeyPair},
didcore::{Document, KeyFormat, Service, VerificationMethodType},
jwk::Jwk,
methods::{DidPeer, Purpose, PurposedKey},
};
use filesystem::FileSystem;
use keystore::Secrets;
use keystore::{Material, Secrets};
use mongodb::bson::doc;
use serde_json::json;
use std::{borrow::Borrow, path::Path};
use std::path::Path;
use tokio::{runtime::Handle, task};

use crate::util;
Expand Down Expand Up @@ -38,6 +38,7 @@ pub fn didgen<K, F>(
) -> Result<Document, Error>
where
K: Repository<Secrets>,
K: Material,
F: FileSystem,
{
// Generate keys for did:peer generation
Expand Down Expand Up @@ -110,15 +111,18 @@ fn store_key<S>(
keystore: &S,
) -> Result<(), Error>
where
S: SecureRepository<Secrets>,
Vec<u8>: Borrow<[u8]>,
S: Repository<Secrets>,
S: Material

{
// Extract key ID from the DID document
let kid = match field.as_ref().unwrap()[0].clone() {
VerificationMethodType::Reference(kid) => kid,
VerificationMethodType::Embedded(method) => method.id,
};
let kid = util::handle_vm_id(&kid, diddoc);
let key = serde_json::to_vec(&key).unwrap_or_default();
let key = String::from_utf8(key).unwrap_or_default();

// Create Secrets for the key
let secret = Secrets {
Expand All @@ -130,7 +134,7 @@ where
// Store the secret in the keystore
task::block_in_place(move || {
Handle::current().block_on(async move {
match keystore.wrap_store(secret).await {
match keystore.securestore(secret).await {
Ok(_) => tracing::info!("Successfully stored secret."),
Err(error) => tracing::error!("Error storing secret: {:?}", error),
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ mod tests {
use super::*;
use did_utils::jwk::Jwk;
use keystore::tests::MockKeyStore;
use serde_json::Value;
use serde_json::{to_vec, Value};

fn setup() -> Document {
tests::setup().clone().diddoc.clone()
Expand Down Expand Up @@ -303,6 +303,8 @@ mod tests {
}"#,
)
.unwrap();
let secret = to_vec(&secret).unwrap();
let secret = String::from_utf8(secret).unwrap();

let test_secret = Secrets {
id: None,
Expand Down
Loading

0 comments on commit 261fd50

Please sign in to comment.