frost-client: force encryption when using HTTP (#398)
* frost-client: force encryption when using HTTP * fix test * fix new test
This commit is contained in:
parent
30cdd00fa6
commit
11c4583091
|
@ -2688,8 +2688,8 @@ dependencies = [
|
|||
"serde_json",
|
||||
"serdect",
|
||||
"snow",
|
||||
"thiserror 2.0.4",
|
||||
"tempfile",
|
||||
"thiserror 2.0.4",
|
||||
"tokio",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
|
|
|
@ -26,11 +26,6 @@ pub struct Args {
|
|||
#[arg(long, default_value_t = false)]
|
||||
pub cli: bool,
|
||||
|
||||
/// HTTP mode. If enabled, it will use HTTP communication with a
|
||||
/// FROST server.
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub http: bool,
|
||||
|
||||
/// The comma-separated usernames of the signers to use in HTTP mode.
|
||||
/// If HTTP mode is enabled and this is empty, then the session ID
|
||||
/// will be printed and will have to be shared manually.
|
||||
|
@ -178,7 +173,7 @@ impl<C: Ciphersuite + 'static> ProcessedArgs<C> {
|
|||
|
||||
Ok(ProcessedArgs {
|
||||
cli: args.cli,
|
||||
http: args.http,
|
||||
http: false,
|
||||
signers,
|
||||
num_signers,
|
||||
public_key_package,
|
||||
|
|
|
@ -296,44 +296,40 @@ impl<C: Ciphersuite> HTTPComms<C> {
|
|||
})
|
||||
}
|
||||
|
||||
// Encrypts a message for a given recipient if encryption is enabled.
|
||||
fn encrypt_if_needed(
|
||||
&mut self,
|
||||
recipient: &Vec<u8>,
|
||||
msg: Vec<u8>,
|
||||
) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
if let Some(noise_map) = &mut self.send_noise {
|
||||
let noise = noise_map
|
||||
.get_mut(recipient)
|
||||
.ok_or_eyre("unknown recipient")?;
|
||||
let mut encrypted = vec![0; 65535];
|
||||
let len = noise.write_message(&msg, &mut encrypted)?;
|
||||
encrypted.truncate(len);
|
||||
Ok(encrypted)
|
||||
} else {
|
||||
Ok(msg)
|
||||
}
|
||||
// Encrypts a message for a given recipient.
|
||||
fn encrypt(&mut self, recipient: &Vec<u8>, msg: Vec<u8>) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
let noise_map = self
|
||||
.send_noise
|
||||
.as_mut()
|
||||
.expect("send_noise must have been set previously");
|
||||
let noise = noise_map
|
||||
.get_mut(recipient)
|
||||
.ok_or_eyre("unknown recipient")?;
|
||||
let mut encrypted = vec![0; 65535];
|
||||
let len = noise.write_message(&msg, &mut encrypted)?;
|
||||
encrypted.truncate(len);
|
||||
Ok(encrypted)
|
||||
}
|
||||
|
||||
// Decrypts a message if encryption is enabled.
|
||||
// Decrypts a message.
|
||||
// Note that this authenticates the `sender` in the `Msg` struct; if the
|
||||
// sender is tampered with, the message would fail to decrypt.
|
||||
fn decrypt_if_needed(&mut self, msg: Msg) -> Result<Msg, Box<dyn Error>> {
|
||||
if let Some(noise_map) = &mut self.recv_noise {
|
||||
let noise = noise_map
|
||||
.get_mut(&msg.sender)
|
||||
.ok_or_eyre("unknown sender")?;
|
||||
let mut decrypted = vec![0; 65535];
|
||||
decrypted.resize(65535, 0);
|
||||
let len = noise.read_message(&msg.msg, &mut decrypted)?;
|
||||
decrypted.truncate(len);
|
||||
Ok(Msg {
|
||||
sender: msg.sender,
|
||||
msg: decrypted,
|
||||
})
|
||||
} else {
|
||||
Ok(msg)
|
||||
}
|
||||
fn decrypt(&mut self, msg: Msg) -> Result<Msg, Box<dyn Error>> {
|
||||
let noise_map = self
|
||||
.recv_noise
|
||||
.as_mut()
|
||||
.expect("recv_noise must have been set previously");
|
||||
let noise = noise_map
|
||||
.get_mut(&msg.sender)
|
||||
.ok_or_eyre("unknown sender")?;
|
||||
let mut decrypted = vec![0; 65535];
|
||||
decrypted.resize(65535, 0);
|
||||
let len = noise.read_message(&msg.msg, &mut decrypted)?;
|
||||
decrypted.truncate(len);
|
||||
Ok(Msg {
|
||||
sender: msg.sender,
|
||||
msg: decrypted,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -411,48 +407,49 @@ impl<C: Ciphersuite + 'static> Comms<C> for HTTPComms<C> {
|
|||
self.session_id = Some(r.session_id);
|
||||
self.num_signers = num_signers;
|
||||
|
||||
// If encryption is enabled, create the Noise objects
|
||||
(self.send_noise, self.recv_noise) = if let (
|
||||
Some(comm_privkey),
|
||||
Some(comm_participant_pubkey_getter),
|
||||
) = (
|
||||
let (Some(comm_privkey), Some(comm_participant_pubkey_getter)) = (
|
||||
&self.args.comm_privkey,
|
||||
&self.args.comm_participant_pubkey_getter,
|
||||
) {
|
||||
let mut send_noise_map = HashMap::new();
|
||||
let mut recv_noise_map = HashMap::new();
|
||||
for pubkey in &self.args.signers {
|
||||
let comm_participant_pubkey = comm_participant_pubkey_getter(pubkey).ok_or_eyre("A participant in specified FROST session is not registered in the coordinator's address book")?;
|
||||
let builder = snow::Builder::new(
|
||||
"Noise_K_25519_ChaChaPoly_BLAKE2s"
|
||||
.parse()
|
||||
.expect("should be a valid cipher"),
|
||||
);
|
||||
let send_noise = Noise::new(
|
||||
builder
|
||||
.local_private_key(comm_privkey)
|
||||
.remote_public_key(&comm_participant_pubkey)
|
||||
.build_initiator()?,
|
||||
);
|
||||
let builder = snow::Builder::new(
|
||||
"Noise_K_25519_ChaChaPoly_BLAKE2s"
|
||||
.parse()
|
||||
.expect("should be a valid cipher"),
|
||||
);
|
||||
let recv_noise = Noise::new(
|
||||
builder
|
||||
.local_private_key(comm_privkey)
|
||||
.remote_public_key(&comm_participant_pubkey)
|
||||
.build_responder()?,
|
||||
);
|
||||
send_noise_map.insert(pubkey.clone(), send_noise);
|
||||
recv_noise_map.insert(pubkey.clone(), recv_noise);
|
||||
}
|
||||
(Some(send_noise_map), Some(recv_noise_map))
|
||||
} else {
|
||||
(None, None)
|
||||
) else {
|
||||
return Err(
|
||||
eyre!("comm_privkey and comm_participant_pubkey_getter must be specified").into(),
|
||||
);
|
||||
};
|
||||
|
||||
// If encryption is enabled, create the Noise objects
|
||||
|
||||
let mut send_noise_map = HashMap::new();
|
||||
let mut recv_noise_map = HashMap::new();
|
||||
for pubkey in &self.args.signers {
|
||||
let comm_participant_pubkey = comm_participant_pubkey_getter(pubkey).ok_or_eyre("A participant in specified FROST session is not registered in the coordinator's address book")?;
|
||||
let builder = snow::Builder::new(
|
||||
"Noise_K_25519_ChaChaPoly_BLAKE2s"
|
||||
.parse()
|
||||
.expect("should be a valid cipher"),
|
||||
);
|
||||
let send_noise = Noise::new(
|
||||
builder
|
||||
.local_private_key(comm_privkey)
|
||||
.remote_public_key(&comm_participant_pubkey)
|
||||
.build_initiator()?,
|
||||
);
|
||||
let builder = snow::Builder::new(
|
||||
"Noise_K_25519_ChaChaPoly_BLAKE2s"
|
||||
.parse()
|
||||
.expect("should be a valid cipher"),
|
||||
);
|
||||
let recv_noise = Noise::new(
|
||||
builder
|
||||
.local_private_key(comm_privkey)
|
||||
.remote_public_key(&comm_participant_pubkey)
|
||||
.build_responder()?,
|
||||
);
|
||||
send_noise_map.insert(pubkey.clone(), send_noise);
|
||||
recv_noise_map.insert(pubkey.clone(), recv_noise);
|
||||
}
|
||||
self.send_noise = Some(send_noise_map);
|
||||
self.recv_noise = Some(recv_noise_map);
|
||||
|
||||
eprint!("Waiting for participants to send their commitments...");
|
||||
|
||||
loop {
|
||||
|
@ -469,7 +466,7 @@ impl<C: Ciphersuite + 'static> Comms<C> for HTTPComms<C> {
|
|||
.json::<server::ReceiveOutput>()
|
||||
.await?;
|
||||
for msg in r.msgs {
|
||||
let msg = self.decrypt_if_needed(msg)?;
|
||||
let msg = self.decrypt(msg)?;
|
||||
self.state.recv(msg)?;
|
||||
}
|
||||
tokio::time::sleep(Duration::from_secs(2)).await;
|
||||
|
@ -505,8 +502,7 @@ impl<C: Ciphersuite + 'static> Comms<C> for HTTPComms<C> {
|
|||
// individually for each recipient.
|
||||
let pubkeys: Vec<_> = self.pubkeys.keys().cloned().collect();
|
||||
for recipient in pubkeys {
|
||||
let msg = self
|
||||
.encrypt_if_needed(&recipient, serde_json::to_vec(&send_signing_package_args)?)?;
|
||||
let msg = self.encrypt(&recipient, serde_json::to_vec(&send_signing_package_args)?)?;
|
||||
let _r = self
|
||||
.client
|
||||
.post(format!("{}/send", self.host_port))
|
||||
|
@ -546,7 +542,7 @@ impl<C: Ciphersuite + 'static> Comms<C> for HTTPComms<C> {
|
|||
.json::<server::ReceiveOutput>()
|
||||
.await?;
|
||||
for msg in r.msgs {
|
||||
let msg = self.decrypt_if_needed(msg)?;
|
||||
let msg = self.decrypt(msg)?;
|
||||
self.state.recv(msg)?;
|
||||
}
|
||||
tokio::time::sleep(Duration::from_secs(2)).await;
|
||||
|
|
|
@ -26,11 +26,6 @@ pub struct Args {
|
|||
#[arg(long, default_value_t = false)]
|
||||
pub cli: bool,
|
||||
|
||||
/// HTTP mode. If enabled, it will use HTTP communication with a
|
||||
/// FROST server.
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub http: bool,
|
||||
|
||||
/// Public key package to use. Can be a file with a JSON-encoded
|
||||
/// package, or "". If the file does not exist or if "" is specified,
|
||||
/// then it will be read from standard input.
|
||||
|
@ -110,7 +105,7 @@ impl<C: Ciphersuite + 'static> ProcessedArgs<C> {
|
|||
|
||||
Ok(ProcessedArgs {
|
||||
cli: args.cli,
|
||||
http: args.http,
|
||||
http: false,
|
||||
key_package,
|
||||
ip: args.ip.clone(),
|
||||
port: args.port,
|
||||
|
|
|
@ -132,29 +132,29 @@ where
|
|||
})
|
||||
}
|
||||
|
||||
// Encrypts a message for the coordinator if encryption is enabled.
|
||||
fn encrypt_if_needed(&mut self, msg: Vec<u8>) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
if let Some(noise) = &mut self.send_noise {
|
||||
let mut encrypted = vec![0; 65535];
|
||||
let len = noise.write_message(&msg, &mut encrypted)?;
|
||||
encrypted.truncate(len);
|
||||
Ok(encrypted)
|
||||
} else {
|
||||
Ok(msg)
|
||||
}
|
||||
// Encrypts a message for the coordinator.
|
||||
fn encrypt(&mut self, msg: Vec<u8>) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
let noise = self
|
||||
.send_noise
|
||||
.as_mut()
|
||||
.expect("send_noise must have been set previously");
|
||||
let mut encrypted = vec![0; 65535];
|
||||
let len = noise.write_message(&msg, &mut encrypted)?;
|
||||
encrypted.truncate(len);
|
||||
Ok(encrypted)
|
||||
}
|
||||
|
||||
// Decrypts a message from the coordinator if encryption is enabled.
|
||||
fn decrypt_if_needed(&mut self, msg: Vec<u8>) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
if let Some(noise) = &mut self.recv_noise {
|
||||
let mut decrypted = vec![0; 65535];
|
||||
decrypted.resize(65535, 0);
|
||||
let len = noise.read_message(&msg, &mut decrypted)?;
|
||||
decrypted.truncate(len);
|
||||
Ok(decrypted)
|
||||
} else {
|
||||
Ok(msg)
|
||||
}
|
||||
// Decrypts a message from the coordinator.
|
||||
fn decrypt(&mut self, msg: Vec<u8>) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
let noise = self
|
||||
.recv_noise
|
||||
.as_mut()
|
||||
.expect("recv_noise must have been set previously");
|
||||
let mut decrypted = vec![0; 65535];
|
||||
decrypted.resize(65535, 0);
|
||||
let len = noise.read_message(&msg, &mut decrypted)?;
|
||||
decrypted.truncate(len);
|
||||
Ok(decrypted)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,60 +241,61 @@ where
|
|||
};
|
||||
self.session_id = Some(session_id);
|
||||
|
||||
// If encryption is enabled, create the Noise objects
|
||||
(self.send_noise, self.recv_noise) = if let (
|
||||
Some(comm_privkey),
|
||||
Some(comm_coordinator_pubkey_getter),
|
||||
) = (
|
||||
let (Some(comm_privkey), Some(comm_coordinator_pubkey_getter)) = (
|
||||
&self.args.comm_privkey,
|
||||
&self.args.comm_coordinator_pubkey_getter,
|
||||
) {
|
||||
// We need to know what is the username of the coordinator in order
|
||||
// to encrypt message to them.
|
||||
let session_info = self
|
||||
.client
|
||||
.post(format!("{}/get_session_info", self.host_port))
|
||||
.json(&server::GetSessionInfoArgs { session_id })
|
||||
.bearer_auth(self.access_token.as_ref().expect("was just set"))
|
||||
.send()
|
||||
.await?
|
||||
.json::<server::GetSessionInfoOutput>()
|
||||
.await?;
|
||||
|
||||
let comm_coordinator_pubkey = comm_coordinator_pubkey_getter(&session_info.coordinator_pubkey).ok_or_eyre("The coordinator for the specified FROST session is not registered in the user's address book")?;
|
||||
let builder = snow::Builder::new(
|
||||
"Noise_K_25519_ChaChaPoly_BLAKE2s"
|
||||
.parse()
|
||||
.expect("should be a valid cipher"),
|
||||
) else {
|
||||
return Err(
|
||||
eyre!("comm_privkey and comm_coordinator_pubkey_getter must be specified").into(),
|
||||
);
|
||||
let send_noise = Noise::new(
|
||||
builder
|
||||
.local_private_key(comm_privkey)
|
||||
.remote_public_key(&comm_coordinator_pubkey)
|
||||
.build_initiator()?,
|
||||
);
|
||||
let builder = snow::Builder::new(
|
||||
"Noise_K_25519_ChaChaPoly_BLAKE2s"
|
||||
.parse()
|
||||
.expect("should be a valid cipher"),
|
||||
);
|
||||
let recv_noise = Noise::new(
|
||||
builder
|
||||
.local_private_key(comm_privkey)
|
||||
.remote_public_key(&comm_coordinator_pubkey)
|
||||
.build_responder()?,
|
||||
);
|
||||
(Some(send_noise), Some(recv_noise))
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
// If encryption is enabled, create the Noise objects
|
||||
|
||||
// We need to know what is the username of the coordinator in order
|
||||
// to encrypt message to them.
|
||||
let session_info = self
|
||||
.client
|
||||
.post(format!("{}/get_session_info", self.host_port))
|
||||
.json(&server::GetSessionInfoArgs { session_id })
|
||||
.bearer_auth(self.access_token.as_ref().expect("was just set"))
|
||||
.send()
|
||||
.await?
|
||||
.json::<server::GetSessionInfoOutput>()
|
||||
.await?;
|
||||
|
||||
let comm_coordinator_pubkey = comm_coordinator_pubkey_getter(&session_info.coordinator_pubkey).ok_or_eyre("The coordinator for the specified FROST session is not registered in the user's address book")?;
|
||||
let builder = snow::Builder::new(
|
||||
"Noise_K_25519_ChaChaPoly_BLAKE2s"
|
||||
.parse()
|
||||
.expect("should be a valid cipher"),
|
||||
);
|
||||
let send_noise = Noise::new(
|
||||
builder
|
||||
.local_private_key(comm_privkey)
|
||||
.remote_public_key(&comm_coordinator_pubkey)
|
||||
.build_initiator()?,
|
||||
);
|
||||
let builder = snow::Builder::new(
|
||||
"Noise_K_25519_ChaChaPoly_BLAKE2s"
|
||||
.parse()
|
||||
.expect("should be a valid cipher"),
|
||||
);
|
||||
let recv_noise = Noise::new(
|
||||
builder
|
||||
.local_private_key(comm_privkey)
|
||||
.remote_public_key(&comm_coordinator_pubkey)
|
||||
.build_responder()?,
|
||||
);
|
||||
self.send_noise = Some(send_noise);
|
||||
self.recv_noise = Some(recv_noise);
|
||||
|
||||
// Send Commitments to Server
|
||||
let send_commitments_args = SendCommitmentsArgs {
|
||||
identifier,
|
||||
commitments: vec![commitments],
|
||||
};
|
||||
let msg = self.encrypt_if_needed(serde_json::to_vec(&send_commitments_args)?)?;
|
||||
let msg = self.encrypt(serde_json::to_vec(&send_commitments_args)?)?;
|
||||
self.client
|
||||
.post(format!("{}/send", self.host_port))
|
||||
.bearer_auth(self.access_token.as_ref().expect("was just set"))
|
||||
|
@ -329,7 +330,7 @@ where
|
|||
eprint!(".");
|
||||
} else {
|
||||
eprintln!("\nSigning package received");
|
||||
let msg = self.decrypt_if_needed(r.msgs[0].msg.clone())?;
|
||||
let msg = self.decrypt(r.msgs[0].msg.clone())?;
|
||||
eprintln!("\n{}", String::from_utf8_lossy(&msg.clone()));
|
||||
break serde_json::from_slice(&msg)?;
|
||||
}
|
||||
|
@ -365,7 +366,7 @@ where
|
|||
signature_share: vec![signature_share],
|
||||
};
|
||||
|
||||
let msg = self.encrypt_if_needed(serde_json::to_vec(&send_signature_shares_args)?)?;
|
||||
let msg = self.encrypt(serde_json::to_vec(&send_signature_shares_args)?)?;
|
||||
|
||||
let _r = self
|
||||
.client
|
||||
|
|
|
@ -45,7 +45,6 @@ async fn check_valid_round_1_inputs() {
|
|||
ip: "0.0.0.0".to_string(),
|
||||
port: 80,
|
||||
session_id: "session-id".to_string(),
|
||||
http: false,
|
||||
};
|
||||
let input = SECRET_SHARE_JSON;
|
||||
let mut valid_input = input.as_bytes();
|
||||
|
|
|
@ -501,7 +501,7 @@ async fn test_http() -> Result<(), Box<dyn std::error::Error>> {
|
|||
// Test if passing the wrong session ID returns an error
|
||||
let wrong_session_id = Uuid::new_v4();
|
||||
let r = client
|
||||
.post("http://127.0.0.1:2744/get_session_info")
|
||||
.post("https://127.0.0.1:2744/get_session_info")
|
||||
.bearer_auth(access_token)
|
||||
.json(&server::GetSessionInfoArgs {
|
||||
session_id: wrong_session_id,
|
||||
|
@ -516,7 +516,7 @@ async fn test_http() -> Result<(), Box<dyn std::error::Error>> {
|
|||
// Attempt to close the session as a participant (Bob)
|
||||
// Log in as Bob
|
||||
let r = client
|
||||
.post("http://127.0.0.1:2744/challenge")
|
||||
.post("https://127.0.0.1:2744/challenge")
|
||||
.json(&server::ChallengeArgs {})
|
||||
.send()
|
||||
.await?;
|
||||
|
@ -526,7 +526,7 @@ async fn test_http() -> Result<(), Box<dyn std::error::Error>> {
|
|||
xed25519::PrivateKey::from(&TryInto::<[u8; 32]>::try_into(bob_keypair.private).unwrap());
|
||||
let bob_signature: [u8; 64] = bob_private.sign(bob_challenge.as_bytes(), &mut rng);
|
||||
let r = client
|
||||
.post("http://127.0.0.1:2744/login")
|
||||
.post("https://127.0.0.1:2744/login")
|
||||
.json(&server::KeyLoginArgs {
|
||||
uuid: bob_challenge,
|
||||
pubkey: bob_keypair.public.clone(),
|
||||
|
@ -538,7 +538,7 @@ async fn test_http() -> Result<(), Box<dyn std::error::Error>> {
|
|||
let bob_access_token = r.access_token;
|
||||
// Try to close the session
|
||||
let r = client
|
||||
.post("http://127.0.0.1:2744/close_session")
|
||||
.post("https://127.0.0.1:2744/close_session")
|
||||
.bearer_auth(bob_access_token)
|
||||
.json(&server::CloseSessionArgs { session_id })
|
||||
.send()
|
||||
|
|
Loading…
Reference in New Issue