Last active
May 26, 2024 19:52
-
-
Save calvinchengx/67393a3b06ea9f3c2cb417bf7f611bfc to your computer and use it in GitHub Desktop.
Threshold Signature Scheme
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#[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)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Related example using TSS' shared secret to handle encryption and decryption - https://github.com/poanetwork/threshold_crypto