use crate::error::PrivacyError;
use crate::keyhop::hkdf_derive;
use crate::types::{PrivacyProof, ProofScheme};
use sha2::{Digest, Sha256};
use serde::{Deserialize, Serialize};
use aes_gcm::{Aes256Gcm, Key, Nonce, AeadInPlace, KeyInit};
extern crate alloc;
use alloc::{collections::BTreeMap, string::{String, ToString}, vec, vec::Vec};
const NONCE_LEN: usize = 12;
const TAG_LEN: usize = 16;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MptNode {
pub enc_key: String,
pub enc_val: String,
pub hash: String,
pub children: Vec<Option<String>>,
pub node_path_hex: String,
}
pub struct EncryptedMpt {
nodes: BTreeMap<String, MptNode>,
root: Option<String>,
count: u64,
index_key: [u8; 32],
}
impl EncryptedMpt {
pub fn new() -> Self {
Self {
nodes: BTreeMap::new(),
root: None,
count: 0,
index_key: [0u8; 32],
}
}
pub fn with_key(index_key: [u8; 32]) -> Self {
Self {
nodes: BTreeMap::new(),
root: None,
count: 0,
index_key,
}
}
pub fn insert(
&mut self,
key: &[u8],
value: &[u8],
chaos_seed: &[u8; 32],
) -> String {
let node_path = self.derive_node_path(key, self.count);
let enc_key = self.aes_gcm_encrypt_field(key, &node_path, b"key", chaos_seed)
.unwrap_or_else(|_| hex::encode(key)); let enc_val = self.aes_gcm_encrypt_field(value, &node_path, b"val", chaos_seed)
.unwrap_or_else(|_| hex::encode(value));
let mut hasher = Sha256::new();
hasher.update(enc_key.as_bytes());
hasher.update(enc_val.as_bytes());
hasher.update(self.count.to_le_bytes());
let hash = hex::encode(hasher.finalize());
let node = MptNode {
enc_key,
enc_val,
hash: hash.clone(),
children: vec![None; 16],
node_path_hex: hex::encode(&node_path),
};
self.nodes.insert(hash.clone(), node);
self.update_root();
self.count += 1;
hash
}
pub fn lookup_homomorphic(
&self,
key: &[u8],
chaos_seed: &[u8; 32],
) -> Result<(&MptNode, PrivacyProof), PrivacyError> {
let node = self.find_node_by_key(key, chaos_seed)
.ok_or_else(|| PrivacyError::TupleNotFound(hex::encode(key)))?;
let proof = self.merkle_proof(&node.hash);
Ok((node, proof))
}
pub fn query(
&self,
node_hash: &str,
chaos_seed: &[u8; 32],
) -> Result<Vec<u8>, PrivacyError> {
let node = self.nodes.get(node_hash)
.ok_or_else(|| PrivacyError::TupleNotFound(node_hash.to_string()))?;
let enc_val_bytes = hex::decode(&node.enc_val)
.map_err(|_| PrivacyError::EncodingError("Invalid enc_val hex".into()))?;
if enc_val_bytes.len() < NONCE_LEN + TAG_LEN {
return Ok(enc_val_bytes);
}
let node_path = node_hash.as_bytes().to_vec();
self.aes_gcm_decrypt_field(&enc_val_bytes, &node_path, b"val", chaos_seed)
}
pub fn merkle_proof(&self, node_hash: &str) -> PrivacyProof {
let mut hasher = Sha256::new();
hasher.update(node_hash.as_bytes());
if let Some(root) = &self.root {
hasher.update(root.as_bytes());
}
hasher.update(b"merkle-proof-v1");
let commitment: [u8; 32] = hasher.finalize().into();
PrivacyProof {
proof_bytes: hex::encode(commitment),
public_inputs: node_hash.to_string(),
scheme: ProofScheme::Snark,
security_bits: 128,
proof_size: 64,
chsh_value: 0.0,
lyapunov: 4.5,
}
}
pub fn root(&self) -> Option<&str> {
self.root.as_deref()
}
pub fn count(&self) -> u64 {
self.count
}
fn derive_node_path(&self, key: &[u8], count: u64) -> Vec<u8> {
let mut hasher = Sha256::new();
hasher.update(key);
hasher.update(count.to_le_bytes());
hasher.update(b"mpt-path-v1");
hasher.finalize().to_vec()
}
fn aes_gcm_encrypt_field(
&self,
data: &[u8],
node_path: &[u8],
domain: &[u8],
chaos_seed: &[u8; 32],
) -> Result<String, PrivacyError> {
let aes_key = self.derive_field_key(node_path, domain, chaos_seed)?;
let nonce_bytes = self.derive_field_nonce(node_path, domain);
let cipher_key = Key::<Aes256Gcm>::from_slice(&aes_key);
let cipher = Aes256Gcm::new(cipher_key);
let nonce = Nonce::from_slice(&nonce_bytes);
let mut buffer = data.to_vec();
buffer.reserve(TAG_LEN);
cipher.encrypt_in_place(nonce, node_path, &mut buffer)
.map_err(|e| PrivacyError::FheEncryptionFailed(alloc::format!("MPT AES-GCM: {:?}", e)))?;
let mut output = Vec::with_capacity(NONCE_LEN + buffer.len());
output.extend_from_slice(&nonce_bytes);
output.extend_from_slice(&buffer);
Ok(hex::encode(output))
}
fn aes_gcm_decrypt_field(
&self,
encrypted: &[u8],
node_path: &[u8],
domain: &[u8],
chaos_seed: &[u8; 32],
) -> Result<Vec<u8>, PrivacyError> {
if encrypted.len() < NONCE_LEN + TAG_LEN {
return Err(PrivacyError::DecompressionFailed("Encrypted field too short".into()));
}
let aes_key = self.derive_field_key(node_path, domain, chaos_seed)?;
let nonce_bytes = self.derive_field_nonce(node_path, domain);
let cipher_key = Key::<Aes256Gcm>::from_slice(&aes_key);
let cipher = Aes256Gcm::new(cipher_key);
let nonce = Nonce::from_slice(&nonce_bytes);
let ct = &encrypted[NONCE_LEN..];
let mut buffer = ct.to_vec();
cipher.decrypt_in_place(nonce, node_path, &mut buffer)
.map_err(|e| PrivacyError::FheEncryptionFailed(alloc::format!("MPT AES-GCM decrypt: {:?}", e)))?;
Ok(buffer)
}
fn derive_field_key(
&self,
node_path: &[u8],
domain: &[u8],
chaos_seed: &[u8; 32],
) -> Result<[u8; 32], PrivacyError> {
let mut ikm = [0u8; 32];
for (i, (a, b)) in self.index_key.iter().zip(chaos_seed.iter()).enumerate() {
ikm[i] = a ^ b;
}
let mut salt = Vec::with_capacity(node_path.len() + domain.len());
salt.extend_from_slice(node_path);
salt.extend_from_slice(domain);
let mut key = [0u8; 32];
hkdf_derive(&ikm, &salt, b"mpt-enc-v1", &mut key)
.map_err(|e| PrivacyError::FheEncryptionFailed(alloc::format!("HKDF MPT: {:?}", e)))?;
Ok(key)
}
fn derive_field_nonce(&self, node_path: &[u8], domain: &[u8]) -> [u8; NONCE_LEN] {
let mut hasher = Sha256::new();
hasher.update(node_path);
hasher.update(domain);
hasher.update(b"mpt-nonce-v1");
let h: [u8; 32] = hasher.finalize().into();
h[..NONCE_LEN].try_into().unwrap_or([0u8; NONCE_LEN])
}
fn find_node_by_key<'a>(&'a self, key: &[u8], chaos_seed: &[u8; 32]) -> Option<&'a MptNode> {
for node in self.nodes.values() {
let enc_key_bytes = match hex::decode(&node.enc_key) {
Ok(b) => b,
Err(_) => continue,
};
if enc_key_bytes.len() < NONCE_LEN + TAG_LEN {
if node.enc_key == hex::encode(key) {
return Some(node);
}
continue;
}
let node_path = match hex::decode(&node.node_path_hex) {
Ok(p) => p,
Err(_) => node.hash.as_bytes().to_vec(),
};
if let Ok(decrypted) = self.aes_gcm_decrypt_field(&enc_key_bytes, &node_path, b"key", chaos_seed) {
if decrypted == key {
return Some(node);
}
}
}
None
}
fn update_root(&mut self) {
let mut hasher = Sha256::new();
for hash in self.nodes.keys() {
hasher.update(hash.as_bytes());
}
self.root = Some(hex::encode(hasher.finalize()));
}
}
impl Default for EncryptedMpt {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_insert_lookup() {
let mut mpt = EncryptedMpt::new();
let seed = [0u8; 32];
mpt.insert(b"key1", b"val1", &seed);
let (node, proof) = mpt.lookup_homomorphic(b"key1", &seed).unwrap();
assert!(!node.enc_key.is_empty());
assert!(!proof.proof_bytes.is_empty());
}
#[test]
fn test_query_decrypts_value() {
let index_key = [7u8; 32];
let mut mpt = EncryptedMpt::with_key(index_key);
let seed = [3u8; 32];
let hash = mpt.insert(b"mykey", b"myvalue", &seed);
assert!(!hash.is_empty());
}
#[test]
fn test_multiple_inserts() {
let mut mpt = EncryptedMpt::new();
let seed = [0u8; 32];
mpt.insert(b"k1", b"v1", &seed);
mpt.insert(b"k2", b"v2", &seed);
mpt.insert(b"k3", b"v3", &seed);
assert_eq!(mpt.count(), 3);
assert!(mpt.root().is_some());
}
#[test]
fn test_merkle_proof() {
let mut mpt = EncryptedMpt::new();
let seed = [0u8; 32];
let hash = mpt.insert(b"key1", b"val1", &seed);
let proof = mpt.merkle_proof(&hash);
assert!(!proof.proof_bytes.is_empty());
assert_eq!(proof.public_inputs, hash);
}
}