184 lines
5.9 KiB
Rust
184 lines
5.9 KiB
Rust
use std::{collections::{HashMap, HashSet}, str::FromStr};
|
|
use anyhow::{Result, Error};
|
|
|
|
use aes_gcm::{aead::{Aead, Payload}, AeadCore, Aes256Gcm, KeyInit};
|
|
use log::debug;
|
|
use rand::thread_rng;
|
|
use serde::{Deserialize, Serialize};
|
|
use serde_json::{Map, Value};
|
|
use sp_client::{bitcoin::{hashes::{sha256t_hash_newtype, Hash, HashEngine}, hex::{DisplayHex, FromHex}, Txid}, silentpayments::utils::SilentPaymentAddress};
|
|
use tsify::Tsify;
|
|
|
|
use crate::crypto::AAD;
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Tsify)]
|
|
#[tsify(into_wasm_abi, from_wasm_abi)]
|
|
pub struct Member {
|
|
sp_addresses: Vec<String>
|
|
}
|
|
|
|
impl Member {
|
|
pub fn new(
|
|
sp_addresses: Vec<SilentPaymentAddress>,
|
|
) -> Result<Self> {
|
|
if sp_addresses.is_empty() {
|
|
return Err(Error::msg("empty address set"));
|
|
}
|
|
|
|
let mut seen = HashSet::new();
|
|
for s in sp_addresses.iter() {
|
|
if !seen.insert(s.clone()) {
|
|
return Err(Error::msg("Duplicate addresses found"));
|
|
}
|
|
}
|
|
|
|
let res: Vec<String> = sp_addresses.iter()
|
|
.map(|a| Into::<String>::into(*a))
|
|
.collect();
|
|
|
|
Ok(Self {
|
|
sp_addresses: res
|
|
})
|
|
}
|
|
|
|
pub fn get_addresses(&self) -> Vec<String> {
|
|
self.sp_addresses.clone()
|
|
}
|
|
}
|
|
|
|
sha256t_hash_newtype! {
|
|
pub struct AnkPcdTag = hash_str("4nk/Pcd");
|
|
|
|
#[hash_newtype(forward)]
|
|
pub struct AnkPcdHash(_);
|
|
}
|
|
|
|
impl AnkPcdHash {
|
|
pub fn from_value(value: &Value) -> Self {
|
|
let mut eng = AnkPcdHash::engine();
|
|
eng.input(value.to_string().as_bytes());
|
|
AnkPcdHash::from_engine(eng)
|
|
}
|
|
|
|
pub fn from_map(map: &Map<String, Value>) -> Self {
|
|
let value = Value::Object(map.clone());
|
|
let mut eng = AnkPcdHash::engine();
|
|
eng.input(value.to_string().as_bytes());
|
|
AnkPcdHash::from_engine(eng)
|
|
}
|
|
}
|
|
|
|
pub trait Pcd<'a>: Serialize + Deserialize<'a> {
|
|
fn hash(&self) -> AnkPcdHash {
|
|
AnkPcdHash::from_value(&self.to_value())
|
|
}
|
|
|
|
fn encrypt_fields(&self, fields2keys: &mut Map<String, Value>, fields2cipher: &mut Map<String, Value>) -> Result<()> {
|
|
let as_value = self.to_value();
|
|
let as_map = as_value.as_object().unwrap();
|
|
let mut rng = thread_rng();
|
|
for (key, value) in as_map {
|
|
let aes_key: [u8; 32] = Aes256Gcm::generate_key(&mut rng).into();
|
|
let nonce: [u8; 12] = Aes256Gcm::generate_nonce(&mut rng).into();
|
|
fields2keys.insert(key.to_owned(), Value::String(aes_key.to_lower_hex_string()));
|
|
|
|
let encryption = Aes256Gcm::new(&aes_key.into());
|
|
let value_string = value.to_string();
|
|
let payload = Payload {
|
|
msg: value_string.as_bytes(),
|
|
aad: AAD,
|
|
};
|
|
let cipher = encryption.encrypt(&nonce.into(), payload)
|
|
.map_err(|e| Error::msg(format!("{}", e)))?;
|
|
|
|
let mut res = Vec::with_capacity(nonce.len() + cipher.len());
|
|
res.extend_from_slice(&nonce);
|
|
res.extend_from_slice(&cipher);
|
|
|
|
fields2cipher.insert(key.to_owned(), Value::String(res.to_lower_hex_string()));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn decrypt_fields(&mut self, fields2keys: &Map<String, Value>) -> Result<()> {
|
|
let as_value = self.to_value();
|
|
let as_map = as_value.as_object().unwrap();
|
|
for (key, value) in as_map {
|
|
if let Some(aes_key) = fields2keys.get(key) {
|
|
let mut nonce = [0u8; 12];
|
|
let mut key_buf = [0u8; 32];
|
|
key_buf.copy_from_slice(&Vec::from_hex(&aes_key.to_string().trim_matches('\"'))?);
|
|
let decrypt = Aes256Gcm::new(&key_buf.into());
|
|
let raw_cipher = Vec::from_hex(&value.to_string().trim_matches('\"'))?;
|
|
nonce.copy_from_slice(&raw_cipher[..12]);
|
|
let payload = Payload {
|
|
msg: &raw_cipher[12..],
|
|
aad: AAD,
|
|
};
|
|
let plain = decrypt.decrypt(&nonce.into(), payload)
|
|
.map_err(|_| Error::msg(format!("Failed to decrypt field {}", key)))?;
|
|
self.to_value()
|
|
.as_object_mut()
|
|
.unwrap()
|
|
.insert(key.to_owned(), Value::String(plain.to_lower_hex_string()));
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn to_value(&self) -> Value {
|
|
Value::from_str(&serde_json::to_string(&self).unwrap()).unwrap()
|
|
}
|
|
}
|
|
|
|
impl Pcd<'_> for Value {}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Tsify)]
|
|
#[tsify(into_wasm_abi, from_wasm_abi)]
|
|
pub struct ValidationRule {
|
|
quorum: f32, // Must be >= 0.0, <= 1.0, 0.0 means reading right
|
|
pub fields: Vec<String>, // Which fields are concerned by this rule
|
|
min_sig_member: f32, // Must be >= 0.0, <= 1.0, does each member need to sign with all it's devices?
|
|
}
|
|
|
|
impl ValidationRule {
|
|
pub fn new(quorum: f32, fields: Vec<String>, min_sig_member: f32) -> Result<Self> {
|
|
if quorum < 0.0 || quorum > 1.0 {
|
|
return Err(Error::msg("quorum must be 0.0 < quorum <= 1.0"));
|
|
}
|
|
|
|
if min_sig_member < 0.0 || min_sig_member > 1.0 {
|
|
return Err(Error::msg("min_signatures_member must be 0.0 < min_signatures_member <= 1.0"));
|
|
}
|
|
|
|
if fields.is_empty() {
|
|
return Err(Error::msg("Fields can't be empty"));
|
|
}
|
|
|
|
let res = Self {
|
|
quorum,
|
|
fields,
|
|
min_sig_member,
|
|
};
|
|
|
|
Ok(res)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Tsify)]
|
|
#[tsify(into_wasm_abi, from_wasm_abi)]
|
|
pub struct RoleDefinition {
|
|
pub members: Vec<Member>,
|
|
pub validation_rules: Vec<ValidationRule>,
|
|
}
|
|
|
|
// #[derive(Debug, Clone, Serialize, Deserialize, Tsify)]
|
|
// #[tsify(into_wasm_abi, from_wasm_abi)]
|
|
// pub struct Roles {
|
|
// pub roles: HashMap<String, RoleDefinition>
|
|
// }
|