-
-
Save calvinchengx/67393a3b06ea9f3c2cb417bf7f611bfc to your computer and use it in GitHub Desktop.
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; | |
} | |
} |
Using the secret key for encryption and decryption may have the following security implications:
-
Encryption Key Reuse: The secret key is primarily designed for signing operations in the context of threshold signatures. Reusing the secret key for encryption purposes violates the principle of key separation. It is generally recommended to use separate key pairs for different cryptographic operations to mitigate potential vulnerabilities. Reusing the secret key for encryption may weaken the security of the threshold signature scheme and expose the system to attacks.
-
Key Length and Security: Threshold signature schemes typically require larger key sizes compared to encryption schemes. Secret keys used for threshold signatures are generally generated with the aim of providing sufficient security against attacks such as brute-force or factorization. However, encryption schemes have different key length requirements and security considerations. The secret key used for threshold signatures may not meet the recommended key length for encryption, potentially weakening the security of the encrypted data.
-
Key Exposure: When using the secret key for encryption and decryption, there is a higher likelihood of key exposure. Encryption keys are typically used in a wider range of operations and may be more susceptible to accidental or intentional exposure. If the secret key used for threshold signatures is exposed through the encryption process, it can compromise the security of the entire system, including the integrity and authenticity of the signatures.
-
Compatibility and Interoperability: Using the secret key for encryption and decryption may introduce compatibility and interoperability challenges. Encryption algorithms, modes, and protocols may have specific requirements and expectations for the encryption key format and size. The secret key used in threshold signatures may not adhere to these requirements, leading to potential compatibility issues with existing encryption libraries or systems.
To maintain strong security and minimize potential vulnerabilities, it is generally recommended to use dedicated and separate key pairs for different cryptographic operations. This ensures proper key separation, enables the use of algorithms and key lengths specifically designed for each operation, and reduces the risk of key exposure and compatibility issues.
Related example using TSS' shared secret to handle encryption and decryption - https://github.com/poanetwork/threshold_crypto
The computed shared secret serves as the collective secret key in a threshold signature scheme. It is used to perform distributed key signing, where multiple participants collaboratively generate a signature without any single participant possessing the full signing capability. The shared secret allows for distributed and secure signing of messages.
Here's a high-level overview of how the computed shared secret is used in the threshold signature scheme:
Distributed Key Generation: The participants collectively compute the shared secret by securely combining their individual secret keys using secure multi-party computation (SMPC) techniques. Each participant contributes their secret key, and the shared secret is derived without revealing the individual secret keys.
Signature Generation: To generate a threshold signature, each participant uses their partial signing capability. They take the message to be signed, the shared secret, and their individual secret key to compute a partial signature. The participants then combine their partial signatures using a distributed signature aggregation protocol.
Signature Verification: The resulting threshold signature is verified by anyone in possession of the participants' public keys. The verification process ensures the authenticity and integrity of the signature. If the verification is successful, it confirms that the signature was generated by a quorum of participants and that the message has not been tampered with.
Encryption and Decryption: Additionally, the
encrypt_message
method uses the participant's secret key directly for encryption, while thedecrypt_message
method uses the shared secret as the decryption key. The secret key and shared secret are converted to byte arrays and used as the encryption/decryption keys for AES-256 in CBC mode. Please note that using the participant's secret key directly for encryption and decryption might have security implications, as the secret key is typically intended for signing rather than encryption.By leveraging the shared secret in the signature generation process, the threshold signature scheme provides several benefits:
Security: The distributed nature of the shared secret ensures that no single participant can perform the signing operation alone, enhancing the security of the signature generation process.
Resilience: The threshold signature scheme remains operational even if some participants are unavailable or compromised. As long as a sufficient number of participants collaborate, the signature can be generated.
Non-repudiation: The threshold signature provides non-repudiation, meaning that a participant cannot deny their involvement in the signature generation process. This property is crucial in various applications, such as digital contracts, financial transactions, and secure communication protocols.
Please note that the usage of the pallier crate in this example assumes that the participants have access to the same modulus n and key pair (p, q, g). Adjustments may be required depending on your specific requirements and use case.