Skip to content

Commit

Permalink
renegade-dealer: Add authorization check via request signature
Browse files Browse the repository at this point in the history
  • Loading branch information
joeykraut committed Apr 21, 2024
1 parent 1cba761 commit da0e279
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 30 deletions.
6 changes: 6 additions & 0 deletions renegade-dealer-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ edition = "2021"
[dependencies]
ark-bn254 = "0.4.0"
ark-mpc = { git = "https://github.com/renegade-fi/ark-mpc.git" }

k256 = "0.13"

serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
uuid = "1.8"

[dev-dependencies]
rand = "0.8"
107 changes: 107 additions & 0 deletions renegade-dealer-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,40 @@
#![feature(generic_const_exprs)]
#![feature(inherent_associated_types)]

use k256::PublicKey;
use serde::{Deserialize, Serialize};

/// Serialize a public key
use serde::{de::Error as DeError, Deserializer, Serializer};

/// Custom serialization for `PublicKey`
fn serialize_key<S>(key: &PublicKey, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let bytes = key.to_sec1_bytes().to_vec();
serializer.serialize_bytes(&bytes)
}

/// Custom deserialization for `PublicKey`
fn deserialize_key<'de, D>(deserializer: D) -> Result<PublicKey, D::Error>
where
D: Deserializer<'de>,
{
let bytes = <Vec<u8>>::deserialize(deserializer)?;
PublicKey::from_sec1_bytes(&bytes)
.map_err(|e| DeError::custom(format!("Invalid public key bytes: {}", e)))
}

// -------------
// | Api Types |
// -------------

/// The header name for the party ID
pub const PARTY_ID_HEADER: &str = "X-Party-Id";
/// The header name for the signature
pub const SIGNATURE_HEADER: &str = "X-Signature";

/// A type alias for the request
pub type RequestId = uuid::Uuid;

Expand All @@ -33,6 +65,13 @@ pub struct ErrorResponse {
/// A request for offline phase randomness from the dealer
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
pub struct DealerRequest {
/// The public key of the first party in the exchange
#[serde(serialize_with = "serialize_key", deserialize_with = "deserialize_key")]
pub first_party_key: PublicKey,
/// The public key of the second party in the exchange
#[serde(serialize_with = "serialize_key", deserialize_with = "deserialize_key")]
pub second_party_key: PublicKey,

/// The number of random bits to generate
#[serde(default)]
pub n_random_bits: u32,
Expand All @@ -54,6 +93,19 @@ pub struct DealerRequest {
}

impl DealerRequest {
/// Create a new request from a pair of keys
pub fn new(first_party_key: PublicKey, second_party_key: PublicKey) -> Self {
Self {
first_party_key,
second_party_key,
n_random_bits: 0,
n_random_values: 0,
n_input_masks: 0,
n_inverse_pairs: 0,
n_triples: 0,
}
}

/// Return the total number of requested values
pub fn total_values(&self) -> u32 {
self.n_random_bits
Expand All @@ -62,6 +114,36 @@ impl DealerRequest {
+ self.n_inverse_pairs
+ self.n_triples
}

/// Set the number of random bits to generate
pub fn with_n_random_bits(mut self, n_random_bits: u32) -> Self {
self.n_random_bits = n_random_bits;
self
}

/// Set the number of shared random values to generate
pub fn with_n_random_values(mut self, n_random_values: u32) -> Self {
self.n_random_values = n_random_values;
self
}

/// Set the number of input masks to generate
pub fn with_n_input_masks(mut self, n_input_masks: u32) -> Self {
self.n_input_masks = n_input_masks;
self
}

/// Set the number of inverse pairs to generate
pub fn with_n_inverse_pairs(mut self, n_inverse_pairs: u32) -> Self {
self.n_inverse_pairs = n_inverse_pairs;
self
}

/// Set the number of Beaver triples to generate
pub fn with_n_triples(mut self, n_triples: u32) -> Self {
self.n_triples = n_triples;
self
}
}

/// A response from the Dealer
Expand Down Expand Up @@ -122,3 +204,28 @@ impl DealerResponse {
self.beaver_triples = (a, b, c);
}
}

#[cfg(test)]
mod test {
use k256::SecretKey;
use rand::thread_rng;

use crate::DealerRequest;

/// Test serialization + deserialization of the `DealerRequest`
#[test]
fn test_req_serialization() {
let mut rng = thread_rng();
let key1 = SecretKey::random(&mut rng);
let key2 = SecretKey::random(&mut rng);

let req = DealerRequest::new(key1.public_key(), key2.public_key())
.with_n_triples(100)
.with_n_input_masks(10);

let ser = serde_json::to_vec(&req).unwrap();
let de: DealerRequest = serde_json::from_slice(&ser).unwrap();

assert_eq!(req, de);
}
}
6 changes: 6 additions & 0 deletions renegade-dealer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,16 @@ renegade-dealer-api = { path = "../renegade-dealer-api" }
# === Cryptography === #
ark-bn254 = "0.4"
ark-mpc = { git = "https://github.com/renegade-fi/ark-mpc.git" }
k256 = "0.13"

# === Misc === #
base64 = "0.22"
clap = { version = "4.5", features = ["derive"] }
itertools = "0.12"
rand = "0.8"
serde_json = "1.0"
tokio = { version = "1.21", features = ["full"] }
uuid = { version = "1.8", features = ["v4"] }

[dev-dependencies]
k256 = "0.13"
64 changes: 47 additions & 17 deletions renegade-dealer/src/dealer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//! The dealer aggregates requests between matching parties and generates
//! offline phase results

use ark_mpc::network::PartyId;
use itertools::Itertools;
use rand::{thread_rng, Rng};
use std::{
Expand All @@ -19,6 +20,8 @@ use tokio::sync::mpsc::{
use renegade_dealer_api::{DealerRequest, DealerResponse, RequestId};
use uuid::Uuid;

use crate::BadRequestError;

// ---------
// | Types |
// ---------
Expand All @@ -40,9 +43,9 @@ pub fn create_dealer_sender_receiver() -> (DealerSender, DealerReceiver) {
}

/// The response channel sender from the dealer
pub type ResponseSender = Sender<DealerResponse>;
pub type ResponseSender = Sender<Result<DealerResponse, BadRequestError>>;
/// The response channel receiver from the dealer
pub type ResponseReceiver = Receiver<DealerResponse>;
pub type ResponseReceiver = Receiver<Result<DealerResponse, BadRequestError>>;
/// Create a new sender and receiver
pub fn create_response_sender_receiver() -> (ResponseSender, ResponseReceiver) {
unbounded_channel()
Expand All @@ -52,6 +55,8 @@ pub fn create_response_sender_receiver() -> (ResponseSender, ResponseReceiver) {
pub struct DealerJob {
/// The request ID
pub request_id: RequestId,
/// The id of the requesting party
pub party_id: PartyId,
/// The request
pub request: DealerRequest,
/// The channel on which to respond
Expand All @@ -60,8 +65,13 @@ pub struct DealerJob {

impl DealerJob {
/// Constructor
pub fn new(request_id: RequestId, request: DealerRequest, chan: ResponseSender) -> Self {
Self { request_id, request, chan }
pub fn new(
request_id: RequestId,
party_id: PartyId,
request: DealerRequest,
chan: ResponseSender,
) -> Self {
Self { request_id, party_id, request, chan }
}
}

Expand Down Expand Up @@ -106,7 +116,16 @@ impl Dealer {
let mut open_requests = self.open_requests.lock().unwrap();
if let Some(existing_req) = open_requests.remove(&id) {
assert_eq!(existing_req.request, request.request);
tokio::task::spawn_blocking(move || Self::handle_ready_pair(&existing_req, &request));

// Requests should be from different parties
if existing_req.party_id == request.party_id {
let err = BadRequestError("Duplicate party ID");
request.chan.send(Err(err.clone())).unwrap();
existing_req.chan.send(Err(err)).unwrap();
return;
}

Self::handle_ready_pair(&existing_req, &request);
} else {
open_requests.insert(id, request);
}
Expand All @@ -132,8 +151,8 @@ impl Dealer {
Self::gen_inverse_pairs(req.n_inverse_pairs as usize, mac_key, &mut resp1, &mut resp2);
Self::gen_triples(req.n_triples as usize, mac_key, &mut resp1, &mut resp2);

req1.chan.send(resp1).unwrap();
req2.chan.send(resp2).unwrap();
req1.chan.send(Ok(resp1)).unwrap();
req2.chan.send(Ok(resp2)).unwrap();
}

// ------------------------------------
Expand Down Expand Up @@ -279,7 +298,10 @@ impl Dealer {

#[cfg(test)]
mod test {
use ark_mpc::{PARTY0, PARTY1};
use itertools::{izip, Itertools};
use k256::SecretKey;
use rand::thread_rng;
use renegade_dealer_api::{DealerRequest, DealerResponse};
use uuid::Uuid;

Expand All @@ -292,6 +314,20 @@ mod test {
// | Helpers |
// -----------

/// Get a mock dealer request
fn mock_dealer_req(n: u32) -> DealerRequest {
let mut rng = thread_rng();
let key1 = SecretKey::random(&mut rng);
let key2 = SecretKey::random(&mut rng);

DealerRequest::new(key1.public_key(), key2.public_key())
.with_n_triples(n)
.with_n_input_masks(n)
.with_n_inverse_pairs(n)
.with_n_random_bits(n)
.with_n_random_values(n)
}

/// Run a mock dealer
async fn get_mock_dealer_response(n: u32) -> (DealerResponse, DealerResponse) {
let (send, recv) = create_dealer_sender_receiver();
Expand All @@ -300,23 +336,17 @@ mod test {
let (send1, mut recv1) = create_response_sender_receiver();
let (send2, mut recv2) = create_response_sender_receiver();
let rid = Uuid::new_v4();
let req = DealerRequest {
n_triples: n,
n_random_bits: n,
n_random_values: n,
n_input_masks: n,
n_inverse_pairs: n,
};
let req = mock_dealer_req(n);

// Simulate two clients
let job1 = DealerJob::new(rid, req.clone(), send1);
let job2 = DealerJob::new(rid, req, send2);
let job1 = DealerJob::new(rid, PARTY0, req.clone(), send1);
let job2 = DealerJob::new(rid, PARTY1, req, send2);

send.send(job1).unwrap();
send.send(job2).unwrap();

// Get two responses
(recv1.recv().await.unwrap(), recv2.recv().await.unwrap())
(recv1.recv().await.unwrap().unwrap(), recv2.recv().await.unwrap().unwrap())
}

/// Check that the macs correctly authenticate the given pairs of shares
Expand Down
Loading

0 comments on commit da0e279

Please sign in to comment.