Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Vec<u8> for FHE types #34

Merged
merged 3 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 15 additions & 14 deletions packages/ciphernode/core/src/ciphernode.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
use crate::{
data::{Data, Insert}, eventbus::EventBus, events::{ComputationRequested, EnclaveEvent, KeyshareCreated}, fhe::{Fhe, GenerateKeyshare}, wrapped::WrappedSecretKey, DecryptCiphertext, DecryptionRequested, DecryptionshareCreated, Get, Subscribe
data::{Data, Insert},
eventbus::EventBus,
events::{ComputationRequested, EnclaveEvent, KeyshareCreated},
fhe::{Fhe, GenerateKeyshare},
wrapped::SecretKeySerializer,
DecryptCiphertext, DecryptionRequested, DecryptionshareCreated, Get, Subscribe,
};
use actix::prelude::*;
use anyhow::Result;
Expand All @@ -21,8 +26,12 @@ impl Ciphernode {

pub async fn attach(bus: Addr<EventBus>, fhe: Addr<Fhe>, data: Addr<Data>) -> Addr<Self> {
let node = Ciphernode::new(bus.clone(), fhe, data).start();
let _ = bus.send(Subscribe::new("ComputationRequested", node.clone().into())).await;
let _ = bus.send(Subscribe::new("DecryptionRequested", node.clone().into())).await;
let _ = bus
.send(Subscribe::new("ComputationRequested", node.clone().into()))
.await;
let _ = bus
.send(Subscribe::new("DecryptionRequested", node.clone().into()))
.await;
node
}
}
Expand Down Expand Up @@ -84,16 +93,10 @@ async fn on_computation_requested(
// reencrypt secretkey locally with env var - this is so we don't have to serialize a secret
// best practice would be as you boot up a node you enter in a configured password from
// which we derive a kdf which gets used to generate this key
data.do_send(Insert(
format!("{}/sk", e3_id).into(),
sk.unsafe_serialize()?,
));
data.do_send(Insert(format!("{}/sk", e3_id).into(), sk));

// save public key against e3_id/pk
data.do_send(Insert(
format!("{}/pk", e3_id).into(),
pubkey.clone().into(),
));
data.do_send(Insert(format!("{}/pk", e3_id).into(), pubkey.clone()));

// broadcast the KeyshareCreated message
let event = EnclaveEvent::from(KeyshareCreated { pubkey, e3_id });
Expand All @@ -111,12 +114,10 @@ async fn on_decryption_requested(
let DecryptionRequested { e3_id, ciphertext } = event;

// get secret key by id from data
let Some(sk_bytes) = data.send(Get(format!("{}/sk", e3_id).into())).await? else {
let Some(unsafe_secret) = data.send(Get(format!("{}/sk", e3_id).into())).await? else {
return Err(anyhow::anyhow!("Secret key not stored for {}", e3_id));
};

let unsafe_secret = WrappedSecretKey::deserialize(sk_bytes)?;

let decryption_share = fhe
.send(DecryptCiphertext {
ciphertext,
Expand Down
21 changes: 11 additions & 10 deletions packages/ciphernode/core/src/committee_key.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::{
eventbus::EventBus,
events::{E3id, EnclaveEvent, KeyshareCreated, PublicKeyAggregated},
fhe::{Fhe, GetAggregatePublicKey}, ordered_set::OrderedSet, wrapped::{WrappedPublicKey, WrappedPublicKeyShare},
fhe::{Fhe, GetAggregatePublicKey},
ordered_set::OrderedSet,
};
use actix::prelude::*;
use anyhow::{anyhow, Result};
Expand All @@ -10,21 +11,21 @@ use anyhow::{anyhow, Result};
pub enum CommitteeKeyState {
Collecting {
nodecount: usize,
keyshares: OrderedSet<WrappedPublicKeyShare>,
keyshares: OrderedSet<Vec<u8>>,
},
Computing {
keyshares: OrderedSet<WrappedPublicKeyShare>,
keyshares: OrderedSet<Vec<u8>>,
},
Complete {
public_key: WrappedPublicKey,
keyshares: OrderedSet<WrappedPublicKeyShare>,
public_key: Vec<u8>,
keyshares: OrderedSet<Vec<u8>>,
},
}

#[derive(Message)]
#[rtype(result = "anyhow::Result<()>")]
struct ComputeAggregate {
pub keyshares: OrderedSet<WrappedPublicKeyShare>,
pub keyshares: OrderedSet<Vec<u8>>,
}

#[derive(Message)]
Expand All @@ -39,7 +40,7 @@ pub struct CommitteeKey {
}

/// Aggregate PublicKey for a committee of nodes. This actor listens for KeyshareCreated events
/// around a particular e3_id and aggregates the public key based on this and once done broadcasts
/// around a particular e3_id and aggregates the public key based on this and once done broadcasts
/// a EnclaveEvent::PublicKeyAggregated event on the event bus. Note events are hashed and
/// identical events will not be triggered twice.
/// It is expected to change this mechanism as we work through adversarial scenarios and write tests
Expand All @@ -57,7 +58,7 @@ impl CommitteeKey {
}
}

pub fn add_keyshare(&mut self, keyshare: WrappedPublicKeyShare) -> Result<CommitteeKeyState> {
pub fn add_keyshare(&mut self, keyshare: Vec<u8>) -> Result<CommitteeKeyState> {
let CommitteeKeyState::Collecting {
nodecount,
keyshares,
Expand All @@ -76,7 +77,7 @@ impl CommitteeKey {
Ok(self.state.clone())
}

pub fn set_pubkey(&mut self, pubkey: WrappedPublicKey) -> Result<CommitteeKeyState> {
pub fn set_pubkey(&mut self, pubkey: Vec<u8>) -> Result<CommitteeKeyState> {
let CommitteeKeyState::Computing { keyshares } = &mut self.state else {
return Ok(self.state.clone());
};
Expand Down Expand Up @@ -129,7 +130,7 @@ impl Handler<ComputeAggregate> for CommitteeKey {

fn handle(&mut self, msg: ComputeAggregate, _: &mut Self::Context) -> Self::Result {
// Futures are awkward in Actix from what I can tell we should try and structure events so
// that futures that don't reuire access to self filre like the following...
// that futures that don't require access to self like the following...
Box::pin(
// Run the async future.
self.fhe
Expand Down
2 changes: 0 additions & 2 deletions packages/ciphernode/core/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ impl Insert {
}
}


#[derive(Message, Clone, Debug, PartialEq, Eq, Hash)]
#[rtype(result = "Option<Vec<u8>>")]
pub struct Get(pub Vec<u8>);
Expand Down Expand Up @@ -59,7 +58,6 @@ impl Data {
impl Handler<Insert> for Data {
type Result = ();
fn handle(&mut self, event: Insert, _: &mut Self::Context) {

// insert data into sled
self.db.insert(event.key(), event.value());

Expand Down
5 changes: 1 addition & 4 deletions packages/ciphernode/core/src/enclave_contract.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

use actix::{Actor, Context};

/// Manage an internal web3 instance and express protocol specific behaviour through the events it
Expand All @@ -9,8 +8,6 @@ use actix::{Actor, Context};
/// Accept eventbus events and forward as appropriate contract calls as required
pub struct EnclaveContract;

impl Actor for EnclaveContract{
impl Actor for EnclaveContract {
type Context = Context<Self>;
}


23 changes: 10 additions & 13 deletions packages/ciphernode/core/src/events.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use crate::wrapped::{WrappedCiphertext, WrappedDecryptionShare, WrappedPublicKey, WrappedPublicKeyShare};
use actix::Message;
use bincode;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -157,21 +156,21 @@ impl fmt::Display for EnclaveEvent {
#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[rtype(result = "anyhow::Result<()>")]
pub struct KeyshareCreated {
pub pubkey: WrappedPublicKeyShare,
pub pubkey: Vec<u8>,
pub e3_id: E3id,
}

#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[rtype(result = "anyhow::Result<()>")]
pub struct DecryptionshareCreated {
pub decryption_share: WrappedDecryptionShare,
pub decryption_share: Vec<u8>,
pub e3_id: E3id,
}

#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[rtype(result = "()")]
pub struct PublicKeyAggregated {
pub pubkey: WrappedPublicKey,
pub pubkey: Vec<u8>,
pub e3_id: E3id,
}

Expand All @@ -192,7 +191,7 @@ pub struct ComputationRequested {
#[rtype(result = "()")]
pub struct DecryptionRequested {
pub e3_id: E3id,
pub ciphertext: WrappedCiphertext,
pub ciphertext: Vec<u8>,
}

fn extract_enclave_event_name(s: &str) -> &str {
Expand All @@ -214,19 +213,17 @@ impl EnclaveEvent {

#[cfg(test)]
mod tests {

use std::error::Error;

use super::EnclaveEvent;
use crate::{
events::extract_enclave_event_name, wrapped::PublicKeyShareSerializer, E3id, KeyshareCreated,
};
use fhe::{
bfv::{BfvParametersBuilder, SecretKey},
mbfv::{CommonRandomPoly, PublicKeyShare},
};
use rand::SeedableRng;
use rand_chacha::ChaCha20Rng;

use crate::{events::extract_enclave_event_name, wrapped::WrappedPublicKeyShare, E3id, KeyshareCreated};

use super::EnclaveEvent;
use std::error::Error;

#[test]
fn test_extract_enum_name() {
Expand Down Expand Up @@ -254,7 +251,7 @@ mod tests {
let crp = CommonRandomPoly::new(&params, &mut rng)?;
let sk_share = { SecretKey::random(&params, &mut rng) };
let pk_share = { PublicKeyShare::new(&sk_share, crp.clone(), &mut rng)? };
let pubkey = WrappedPublicKeyShare::from_fhe_rs(pk_share, params.clone(), crp.clone());
let pubkey = PublicKeyShareSerializer::to_bytes(pk_share, params.clone(), crp.clone())?;
let kse = EnclaveEvent::from(KeyshareCreated {
e3_id: E3id::from(1001),
pubkey,
Expand Down
87 changes: 46 additions & 41 deletions packages/ciphernode/core/src/fhe.rs
Original file line number Diff line number Diff line change
@@ -1,46 +1,43 @@
use crate::{
ordered_set::OrderedSet,
wrapped::{
WrappedCiphertext, WrappedDecryptionShare, WrappedPlaintext, WrappedPublicKey, WrappedPublicKeyShare, WrappedSecretKey
CiphertextSerializer, DecryptionShareSerializer, PlaintextSerializer, PublicKeySerializer,
PublicKeyShareSerializer, SecretKeySerializer,
},
};
use actix::{Actor, Context, Handler, Message};
use anyhow::*;
use fhe::{
bfv::{BfvParameters, BfvParametersBuilder, Plaintext, PublicKey, SecretKey},
bfv::{BfvParameters, BfvParametersBuilder, Ciphertext, Plaintext, PublicKey, SecretKey},
mbfv::{AggregateIter, CommonRandomPoly, DecryptionShare, PublicKeyShare},
};
use rand::SeedableRng;
use rand_chacha::ChaCha20Rng;
use std::{hash::Hash, sync::Arc};

#[derive(Message, Clone, Debug, PartialEq, Eq, Hash)]
#[rtype(result = "Result<(WrappedSecretKey, WrappedPublicKeyShare)>")]
// TODO: Result<(Vec<u8>,Vec<u8>)>
#[rtype(result = "Result<(Vec<u8>, Vec<u8>)>")]
pub struct GenerateKeyshare {
// responder_pk: Vec<u8>, // TODO: use this to encrypt the secret data
}

#[derive(Message, Clone, Debug, PartialEq, Eq)]
#[rtype(result = "Result<(WrappedPublicKey)>")]
// TODO: Result<Vec<u8>>
#[rtype(result = "Result<(Vec<u8>)>")]
pub struct GetAggregatePublicKey {
pub keyshares: OrderedSet<WrappedPublicKeyShare>,
pub keyshares: OrderedSet<Vec<u8>>,
}

#[derive(Message, Clone, Debug, PartialEq, Eq)]
#[rtype(result = "Result<(WrappedPlaintext)>")]
// TODO: Result<Vec<u8>>
#[rtype(result = "Result<(PlaintextSerializer)>")]
pub struct GetAggregatePlaintext {
pub decryptions: OrderedSet<WrappedDecryptionShare>,
pub decryptions: OrderedSet<Vec<u8>>,
}

#[derive(Message, Clone, Debug, PartialEq, Eq)]
#[rtype(result = "Result<(WrappedDecryptionShare)>")]
// TODO: Result<Vec<u8>>
#[rtype(result = "Result<(Vec<u8>)>")]
pub struct DecryptCiphertext {
pub unsafe_secret: WrappedSecretKey,
pub ciphertext: WrappedCiphertext,
pub unsafe_secret: Vec<u8>,
pub ciphertext: Vec<u8>,
}

/// Fhe library adaptor. All FHE computations should happen through this actor.
Expand Down Expand Up @@ -79,60 +76,68 @@ impl Fhe {
}

impl Handler<GenerateKeyshare> for Fhe {
type Result = Result<(WrappedSecretKey, WrappedPublicKeyShare)>;
type Result = Result<(Vec<u8>, Vec<u8>)>;
fn handle(&mut self, _event: GenerateKeyshare, _: &mut Self::Context) -> Self::Result {
let sk_share = { SecretKey::random(&self.params, &mut self.rng) };
let pk_share = { PublicKeyShare::new(&sk_share, self.crp.clone(), &mut self.rng)? };
Ok((
WrappedSecretKey::from_fhe_rs(sk_share, self.params.clone()),
WrappedPublicKeyShare::from_fhe_rs(pk_share, self.params.clone(), self.crp.clone()),
SecretKeySerializer::to_bytes(sk_share, self.params.clone())?,
PublicKeyShareSerializer::to_bytes(pk_share, self.params.clone(), self.crp.clone())?,
))
}
}

impl Handler<DecryptCiphertext> for Fhe {
type Result = Result<WrappedDecryptionShare>;
type Result = Result<Vec<u8>>;
fn handle(&mut self, msg: DecryptCiphertext, _: &mut Self::Context) -> Self::Result {
let DecryptCiphertext {
unsafe_secret, // TODO: fix security issues with sending secrets between actors
ciphertext,
} = msg;

let ct = Arc::new(ciphertext.inner);
let inner = DecryptionShare::new(&unsafe_secret.inner, &ct, &mut self.rng).unwrap();
let secret_key = SecretKeySerializer::from_bytes(&unsafe_secret)?;
let ct = Arc::new(CiphertextSerializer::from_bytes(&ciphertext)?);
let inner = DecryptionShare::new(&secret_key, &ct, &mut self.rng).unwrap();

Ok(WrappedDecryptionShare::from_fhe_rs(
Ok(DecryptionShareSerializer::to_bytes(
inner,
ciphertext.params,
self.params.clone(),
ct.clone(),
))
)?)
}
}

impl Handler<GetAggregatePublicKey> for Fhe {
type Result = Result<WrappedPublicKey>;
type Result = Result<Vec<u8>>;

fn handle(&mut self, msg: GetAggregatePublicKey, _: &mut Self::Context) -> Self::Result {
// Could implement Aggregate for Wrapped keys but that leaks traits
let public_key: PublicKey = msg.keyshares.iter().map(|k| k.clone_inner()).aggregate()?;
Ok(WrappedPublicKey::from_fhe_rs(
public_key,
self.params.clone(),
))
}
}

impl Handler<GetAggregatePlaintext> for Fhe {
type Result = Result<WrappedPlaintext>;
fn handle(&mut self, msg: GetAggregatePlaintext, _: &mut Self::Context) -> Self::Result {
let plaintext: Plaintext = msg
.decryptions
let public_key: PublicKey = msg
.keyshares
.iter()
.map(|k| k.clone().try_inner())
.collect::<Result<Vec<_>>>()? // NOTE: not optimal
.map(|k| PublicKeyShareSerializer::from_bytes(k))
.collect::<Result<Vec<_>>>()?
.into_iter()
.aggregate()?;

Ok(WrappedPlaintext::from_fhe_rs(plaintext))
Ok(PublicKeySerializer::to_bytes(
public_key,
self.params.clone(),
)?)
}
}

// TODO: add this once we have decryption aggregation ready
// impl Handler<GetAggregatePlaintext> for Fhe {
// type Result = Result<PlaintextSerializer>;
// fn handle(&mut self, msg: GetAggregatePlaintext, _: &mut Self::Context) -> Self::Result {
// let plaintext: Plaintext = msg
// .decryptions
// .iter()
// .map(|k| k.clone().try_inner())
// .collect::<Result<Vec<_>>>()? // NOTE: not optimal
// .into_iter()
// .aggregate()?;
//
// Ok(PlaintextSerializer::to_bytes(plaintext))
// }
// }
Loading
Loading