Skip to content

Instantly share code, notes, and snippets.

@calvinchengx
Last active May 26, 2024 19:52
Show Gist options
  • Save calvinchengx/67393a3b06ea9f3c2cb417bf7f611bfc to your computer and use it in GitHub Desktop.
Save calvinchengx/67393a3b06ea9f3c2cb417bf7f611bfc to your computer and use it in GitHub Desktop.
Threshold Signature Scheme
use aes::Aes256;
use block_modes::block_padding::Pkcs7;
use block_modes::{BlockMode, Cbc};
use bls_signatures::{AggregateSignature, Signature};
use pallier::Pallier;
use rand::Rng;
use rug::Integer;
type Aes256Cbc = Cbc<Aes256, Pkcs7>;
// Structure representing a participant in the DKG protocol and threshold signature scheme
struct Participant {
id: usize, // Participant ID
secret_key: Integer, // Secret key of the participant
public_key: Integer, // Public key of the participant
commitment: Integer, // Commitment of the participant
received_commitments: Vec<(usize, Integer)>, // Received commitments from other participants
}
impl Participant {
// Generate a random secret key and compute the public key
fn generate_key_pair() -> (Integer, Integer) {
let mut rng = rand::thread_rng();
let secret_key = Integer::from(rng.gen_range(1..100)); // Random secret key
let public_key = Integer::from(2) * &secret_key; // Compute public key
(secret_key, public_key)
}
// Generate an encryption key for the participant
fn generate_encryption_key() -> Vec<u8> {
let mut rng = rand::thread_rng();
let mut key = vec![0u8; 32];
rng.fill_bytes(&mut key);
key
}
// Generate a cryptographic commitment to the secret key
fn generate_commitment(&self) -> Integer {
let mut rng = rand::thread_rng();
let r = Integer::from(rng.gen_range(1..100)); // Random blinding factor
let commitment = (&self.secret_key + &r) % Integer::from(100); // Commitment computation
commitment
}
// Broadcast the commitment to all participants
fn broadcast_commitment(&self, participants: &[Participant]) {
for participant in participants {
// Skip broadcasting to oneself
if participant.id == self.id {
continue;
}
// Simulate broadcasting the commitment to participant
// You can replace this with actual network communication
println!("Participant {} broadcasts commitment {} to participant {}", self.id, self.commitment, participant.id);
}
}
// Verify the commitments received from other participants
fn verify_commitments(&mut self, participants: &[Participant]) -> bool {
for (participant_id, commitment) in &self.received_commitments {
let participant = participants
.iter()
.find(|p| p.id == *participant_id)
.unwrap();
let expected_commitment = participant.generate_commitment();
if commitment != &expected_commitment {
return false;
}
}
true
}
// Perform secure multi-party computation to compute the shared secret
fn compute_shared_secret(&self, participants: &[Participant]) -> Integer {
let mut shared_secret = Integer::from(0);
// Create a Pallier instance
let pallier = Pallier::new();
// Encrypt the secret key
let encrypted_secret_key = pallier.encrypt(&self.secret_key);
for participant in participants {
if participant.id != self.id {
// Decrypt the received encrypted secret key
let decrypted_secret_key = pallier.decrypt(&encrypted_secret_key);
// Perform secure addition of secret keys
shared_secret += &decrypted_secret_key;
}
}
shared_secret
}
// Encrypt a message
// converted to byte arrays and used as the encryption/decryption keys for AES-256 in CBC mode
fn encrypt_message(&self, message: &[u8]) -> Vec<u8> {
let cipher = Aes256Cbc::new_var(&self.encryption_key, &[0; 16]).unwrap();
let ciphertext = cipher.encrypt_vec(message);
ciphertext
}
// Decrypt a message
// converted to byte arrays and used as the encryption/decryption keys for AES-256 in CBC mode
fn decrypt_message(&self, ciphertext: &[u8]) -> Vec<u8> {
let cipher = Aes256Cbc::new_var(&self.encryption_key, &[0; 16]).unwrap();
let decrypted_message = cipher.decrypt_vec(ciphertext).unwrap();
decrypted_message
}
// Sign a message using the secret key
fn sign_message(&self, message: &[u8]) -> Signature {
let secret_key_bytes = self.secret_key.to_string().into_bytes();
let secret_key = bls_signatures::PrivateKey::from_bytes(&secret_key_bytes).unwrap();
let signature = secret_key.sign(message);
signature
}
}
fn main() {
// Number of participants
let num_participants = 5;
// Generate keys and commitments for all participants
let mut participants: Vec<Participant> = Vec::new();
for id in 0..num_participants {
let (secret_key, public_key) = Participant::generate_key_pair();
let commitment = Participant::generate_commitment(&secret_key);
// Generate encryption key
let mut encryption_key = [0u8; 32];
rand::thread_rng().fill(&mut encryption_key);
let participant = Participant {
id,
secret_key,
public_key,
commitment,
received_commitments: Vec::new(),
encryption_key,
};
participants.push(participant);
}
// Commitment Phase
for participant in &participants {
// Store the commitment in the participant
// You can replace this with actual network communication to broadcast the commitment
// and receive commitments from other participants
participants[participant.id].commitment = participant.commitment;
// Print the commitment for demonstration purposes
println!("Participant {} commitment: {}", participant.id, participant.commitment);
}
// Broadcast the commitments to all participants
for participant in &participants {
participant.broadcast_commitment(&participants);
}
// Verification Phase
for participant in &mut participants {
let verified = participant.verify_commitments(&participants);
if verified {
println!("Participant {} commitments verified", participant.id);
} else {
println!("Participant {} commitments verification failed", participant.id);
}
}
// Compute the shared secret
for participant in &participants {
let shared_secret = participant.compute_shared_secret(&participants);
println!("Participant {} shared secret: {}", participant.id, shared_secret);
}
// Sign a message using the secret keys
let message = b"Hello, world!";
let mut signatures: Vec<Signature> = Vec::new();
for participant in &participants {
let signature = participant.sign_message(message);
signatures.push(signature);
}
// Aggregate the signatures from all participants
let aggregated_signature = AggregateSignature::from_signatures(&signatures).unwrap();
// Verify the aggregated signature using the public keys of all participants
let public_keys: Vec<bls_signatures::PublicKey> = participants
.iter()
.map(|p| {
let secret_key_bytes = p.secret_key.to_string().into_bytes();
let secret_key = bls_signatures::PrivateKey::from_bytes(&secret_key_bytes).unwrap();
secret_key.public_key()
})
.collect();
let is_valid = aggregated_signature.verify(message, &public_keys);
if is_valid {
println!("Aggregated signature is valid");
} else {
println!("Aggregated signature is invalid");
}
// Encrypt and decrypt a message using the encryption keys
let encrypted_message = participants[0].encrypt_message(message);
println!("Encrypted message: {:?}", encrypted_message);
let decrypted_message = participants[0].decrypt_message(&encrypted_message);
println!("Decrypted message: {:?}", decrypted_message);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_generate_key_pair() {
let (secret_key, public_key) = Participant::generate_key_pair();
// Check if secret_key is not zero
assert_ne!(secret_key, Integer::from(0));
// Check if public_key is twice the value of secret_key
assert_eq!(public_key, Integer::from(2) * &secret_key);
}
#[test]
fn test_generate_encryption_key() {
let encryption_key = Participant::generate_encryption_key();
// Check if encryption_key length is 32 bytes
assert_eq!(encryption_key.len(), 32);
}
#[test]
fn test_generate_commitment() {
let participant = Participant {
id: 0,
secret_key: Integer::from(42),
public_key: Integer::from(84),
commitment: Integer::from(0),
received_commitments: Vec::new(),
};
let commitment = participant.generate_commitment();
// Check if commitment is within the range [0, 99]
assert!(commitment >= Integer::from(0) && commitment <= Integer::from(99));
}
#[test]
fn test_verify_commitments() {
let participant1 = Participant {
id: 0,
secret_key: Integer::from(42),
public_key: Integer::from(84),
commitment: Integer::from(0),
received_commitments: Vec::new(),
};
let participant2 = Participant {
id: 1,
secret_key: Integer::from(50),
public_key: Integer::from(100),
commitment: Integer::from(0),
received_commitments: Vec::new(),
};
participant1.commitment = participant1.generate_commitment();
participant2.commitment = participant2.generate_commitment();
participant1.received_commitments.push((1, participant2.commitment));
participant2.received_commitments.push((0, participant1.commitment));
let participants = vec![participant1, participant2];
let mut participant1_clone = participants[0].clone();
let mut participant2_clone = participants[1].clone();
// Check if commitments are verified successfully
assert!(participant1_clone.verify_commitments(&participants));
assert!(participant2_clone.verify_commitments(&participants));
// Modify participant1's commitment
participant1_clone.commitment = Integer::from(99);
// Check if commitments verification fails when a commitment is modified
assert!(!participant1_clone.verify_commitments(&participants));
assert!(!participant2_clone.verify_commitments(&participants));
}
#[test]
fn test_compute_shared_secret() {
let participant1 = Participant {
id: 0,
secret_key: Integer::from(42),
public_key: Integer::from(84),
commitment: Integer::from(0),
received_commitments: Vec::new(),
};
let participant2 = Participant {
id: 1,
secret_key: Integer::from(50),
public_key: Integer::from(100),
commitment: Integer::from(0),
received_commitments: Vec::new(),
};
participant1.commitment = participant1.generate_commitment();
participant2.commitment = participant2.generate_commitment();
let participants = vec![participant1, participant2];
let participant1_shared_secret = participants[0].compute_shared_secret(&participants);
let participant2_shared_secret = participants[1].compute_shared_secret(&participants);
// Check if shared secrets are equal when secret keys are added securely
assert_eq!(participant1_shared_secret, participant2_shared_secret);
// Modify participant1's secret key
let mut participant1_clone = participants[0].clone();
participant1_clone.secret_key = Integer::from(99);
let participant1_modified_shared_secret =
participant1_clone.compute_shared_secret(&participants);
// Check if shared secret changes when a participant's secret key is modified
assert_ne!(participant1_shared_secret, participant1_modified_shared_secret);
}
#[test]
fn test_encrypt_decrypt_message() {
let participant = Participant {
id: 0,
secret_key: Integer::from(42),
public_key: Integer::from(84),
commitment: Integer::from(0),
received_commitments: Vec::new(),
};
let message = b"Hello, world!";
let encrypted_message = participant.encrypt_message(message);
let decrypted_message = participant.decrypt_message(&encrypted_message);
// Check if decrypted message matches the original message
assert_eq!(decrypted_message, message);
}
#[test]
fn test_sign_message() {
let participant = Participant {
id: 0,
secret_key: Integer::from(42),
public_key: Integer::from(84),
commitment: Integer::from(0),
received_commitments: Vec::new(),
};
let message = b"Hello, world!";
let signature = participant.sign_message(message);
// Check if the signature is valid for the message using the participant's secret key
assert!(signature.verify(message, &participant.secret_key));
}
}
truct Participant {
// ...
ws: Option<tungstenite::WebSocket<tokio::net::TcpStream>>,
}
impl Participant {
// ...
// Send the commitment to another participant over WebSocket
async fn send_commitment(&self, participant_id: usize, commitment: Integer) {
if let Some(ws) = &self.ws {
let message = Message::Text(format!("{}:{}", participant_id, commitment));
if let Err(err) = ws.send(message).await {
eprintln!("Failed to send commitment to participant {}: {}", participant_id, err);
}
}
}
fn broadcast_commitment(&self, participants: &[Participant]) {
for participant in participants {
// Skip broadcasting to oneself
if participant.id == self.id {
continue;
}
// Get the WebSocket connection of the participant
if let Some(ws) = &self.ws {
// Serialize and send the commitment
let message = Message::Text(format!("{}:{}", self.id, self.commitment));
if let Err(err) = ws.send(message) {
eprintln!("Failed to send commitment to participant {}: {}", participant.id, err);
}
}
}
}
// Establish a WebSocket connection with the given server address
async fn connect(&mut self, server_address: &str) -> Result<(), tungstenite::Error> {
let (ws_stream, _) = TcpStream::connect(server_address).await?;
let (mut ws, _) = tungstenite::client_async(server_address, ws_stream).await?;
self.ws = Some(ws);
Ok(())
}
}
#[tokio::main]
async fn main() {
// ...
// Commitment Phase
for participant in &participants {
// ...
// Establish WebSocket connections with all participants
let mut participant_clone = participant.clone();
let server_address = "127.0.0.1:8080"; // Change this to the actual server address
if let Err(err) = participant_clone.connect(server_address).await {
eprintln!("Failed to connect to participant {}: {}", participant.id, err);
}
// Store the commitment in the participant
participant_clone.commitment = participant.commitment;
}
// ...
// WebSocket server address
let server_address = "127.0.0.1:8080"; // Change this to the desired server address
// Start the WebSocket server
let listener = TcpListener::bind(server_address).await.unwrap();
let server = listener
.incoming()
.for_each(|stream| {
let addr = stream.peer_addr().unwrap();
let ws_stream = tokio_tungstenite::accept_async(stream).map(move |ws| {
handle_connection(ws, addr, participants.clone());
});
tokio::spawn(ws_stream);
future::ready(())
});
if let Err(err) = server.await {
eprintln!("WebSocket server error: {:?}", err);
}
}
#[tokio::main]
async fn main() {
// ...
// Commitment Phase
for participant in &participants {
// ...
// Establish WebSocket connections with all participants
let mut participant_clone = participant.clone();
let server_address = "127.0.0.1:8080"; // Change this to the actual server address
if let Err(err) = participant_clone.connect(server_address).await {
eprintln!("Failed to connect to participant {}: {}", participant.id, err);
}
// Store the commitment in the participant
participant_clone.commitment = participant.commitment;
}
// ...
// WebSocket server address
let server_address = "127.0.0.1:8080"; // Change this to the desired server address
// Start the WebSocket server
let listener = TcpListener::bind(server_address).await.unwrap();
let server = listener
.incoming()
.for_each(|stream| {
let addr = stream.peer_addr().unwrap();
let ws_stream = tokio_tungstenite::accept_async(stream).map(move |ws| {
handle_connection(ws, addr, participants.clone());
});
tokio::spawn(ws_stream);
future::ready(())
});
if let Err(err) = server.await {
eprintln!("WebSocket server error: {:?}", err);
}
}
async fn handle_connection(
ws: tungstenite::WebSocket<tokio::net::TcpStream>,
addr: std::net::SocketAddr,
participants: Arc<Vec<Participant>>,
) {
// Find the participant based on the WebSocket connection
let participant_id = participants
.iter()
.position(|p| p.ws.as_ref().unwrap().peer_addr().unwrap() == addr);
if let Some(participant_id) = participant_id {
let participant = &participants[participant_id];
// Store the WebSocket connection in the participant
participants[participant_id].ws = Some(ws);
while let Some(message) = participant.ws.as_ref().unwrap().next().await {
match message {
Ok(Message::Text(text)) => {
// Extract the participant ID and commitment from the received message
let mut parts = text.splitn(2, ':');
if let (Some(participant_id_str), Some(commitment_str)) = (parts.next(), parts.next()) {
if let (Ok(participant_id), Ok(commitment)) = (
participant_id_str.parse::<usize>(),
commitment_str.parse::<Integer>(),
) {
// Store the received commitment in the participant
participants[participant_id].received_commitments.push((participant.id, commitment));
// Verify the commitments if all commitments are received
if participants[participant_id].received_commitments.len() == participants.len() - 1 {
let verified = participants[participant_id].verify_commitments(&participants);
if verified {
println!("Participant {} commitments verified", participant_id);
} else {
println!("Participant {} commitments verification failed", participant_id);
}
}
}
}
}
Ok(_) => {
eprintln!("Unexpected message format received from participant {}: {:?}", participant_id, message);
}
Err(err) => {
eprintln!("WebSocket error from participant {}: {:?}", participant_id, err);
break;
}
}
}
// Remove the WebSocket connection from the participant
participants[participant_id].ws = None;
}
}
@calvinchengx
Copy link
Author

Related example using TSS' shared secret to handle encryption and decryption - https://github.com/poanetwork/threshold_crypto

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment