Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

all: best-effort at zeroization of secrets #482

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ zcash_keys = "0.6.0"
zcash_primitives = "0.21.0"
zcash_proofs = "0.21.0"
zcash_protocol = "0.4.3"
zeroize = "1.8.1"

[patch.crates-io]
# TODO: remove this when https://github.com/zcash/orchard/issues/430 is fully
Expand Down
1 change: 1 addition & 0 deletions dkg/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ xeddsa = { workspace = true }
reqwest = { workspace = true, features = ["json", "rustls-tls-native-roots"] }
tokio = { workspace = true, features = ["full"] }
snow = { workspace = true }
zeroize = { workspace = true, features = ["serde", "zeroize_derive"] }

[features]
default = []
7 changes: 6 additions & 1 deletion dkg/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::rc::Rc;

use clap::Parser;
use frost_core::{Ciphersuite, Identifier};
use zeroize::{Zeroize, ZeroizeOnDrop};

#[derive(Parser, Debug, Default)]
#[command(author, version, about, long_about = None)]
Expand All @@ -10,7 +11,7 @@ pub struct Args {
pub ciphersuite: String,
}

#[derive(Clone)]
#[derive(Clone, Zeroize)]
pub struct ProcessedArgs<C: Ciphersuite> {
/// CLI mode. If enabled, it will prompt for inputs from stdin
/// and print values to stdout, ignoring other flags.
Expand Down Expand Up @@ -38,6 +39,7 @@ pub struct ProcessedArgs<C: Ciphersuite> {
// using `fn()` would preclude using closures and using generics would
// require a lot of code change for something simple.
#[allow(clippy::type_complexity)]
#[zeroize(skip)]
pub comm_participant_pubkey_getter: Option<Rc<dyn Fn(&Vec<u8>) -> Option<Vec<u8>>>>,

/// The threshold to use for the shares
Expand All @@ -51,9 +53,12 @@ pub struct ProcessedArgs<C: Ciphersuite> {
pub participants: Vec<Vec<u8>>,

/// Identifier to use for the participant. Only needed for CLI mode.
#[zeroize(skip)]
pub identifier: Option<Identifier<C>>,
}

impl<C> ZeroizeOnDrop for ProcessedArgs<C> where C: Ciphersuite {}

impl<C> ProcessedArgs<C>
where
C: Ciphersuite,
Expand Down
2 changes: 2 additions & 0 deletions dkg/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use reddsa::frost::redpallas::keys::EvenY;
use std::collections::HashMap;
use std::error::Error;
use std::io::{BufRead, Write};
use zeroize::Zeroizing;

use crate::args::ProcessedArgs;
use crate::comms::cli::CLIComms;
Expand Down Expand Up @@ -102,6 +103,7 @@ pub async fn cli_for_processed_args<C: Ciphersuite + 'static + MaybeIntoEvenY>(

let (round2_secret_package, round2_packages) =
frost::keys::dkg::part2(round1_secret_package, &received_round1_packages)?;
let round2_secret_package = Zeroizing::new(round2_secret_package);

let received_round2_packages = comms
.get_round2_packages(input, logger, round2_packages)
Expand Down
1 change: 1 addition & 0 deletions frost-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ rand = { workspace = true }
stable-eyre = { workspace = true }
itertools = { workspace = true }
xeddsa = { workspace = true }
zeroize = { workspace = true, features = ["serde", "zeroize_derive"] }
20 changes: 16 additions & 4 deletions frost-client/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::{
use eyre::{eyre, OptionExt};
use frost_core::{Ciphersuite, Identifier};
use serde::{Deserialize, Serialize};
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};

use crate::{ciphersuite_helper::ciphersuite_helper, contact::Contact, write_atomic};

Expand All @@ -29,6 +30,14 @@ pub struct Config {
pub group: BTreeMap<String, Group>,
}

impl Zeroize for Config {
fn zeroize(&mut self) {
self.group.iter_mut().for_each(|(_, g)| g.zeroize());
}
}

impl ZeroizeOnDrop for Config {}

impl Config {
pub fn contact_by_pubkey(&self, pubkey: &[u8]) -> Result<Contact, Box<dyn Error>> {
if Some(pubkey) == self.communication_key.as_ref().map(|c| c.pubkey.as_slice()) {
Expand All @@ -48,7 +57,7 @@ impl Config {
}

/// The communication key pair for the user.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, ZeroizeOnDrop)]
pub struct CommunicationKey {
/// The private key.
#[serde(
Expand All @@ -65,7 +74,7 @@ pub struct CommunicationKey {
}

/// A FROST group the user belongs to.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, Zeroize)]
pub struct Group {
/// A human-readable description of the group to make it easier to select
/// groups
Expand All @@ -87,9 +96,12 @@ pub struct Group {
/// The default server the participants are using, if any.
pub server_url: Option<String>,
/// The group participants, keyed by hex-encoded identifier
#[zeroize(skip)]
pub participant: BTreeMap<String, Participant>,
}

impl ZeroizeOnDrop for Group {}

impl Group {
/// Returns a human-readable summary of the contact; used when it is
/// printed to the terminal.
Expand Down Expand Up @@ -176,7 +188,7 @@ impl Config {
..Default::default()
});
}
let bytes = std::fs::read(&path)?;
let bytes = Zeroizing::new(std::fs::read(&path)?);
let s = str::from_utf8(&bytes)?;
let mut config: Config = toml::from_str(s)?;
config.path = Some(path);
Expand All @@ -185,7 +197,7 @@ impl Config {

/// Write the config to path it was loaded from.
pub fn write(&self) -> Result<(), Box<dyn Error>> {
let s = toml::to_string_pretty(self)?;
let s = Zeroizing::new(toml::to_string_pretty(self)?);
let bytes = s.as_bytes();
Ok(write_atomic::write_file(
self.path
Expand Down
3 changes: 2 additions & 1 deletion frost-client/src/contact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ pub(crate) fn export(args: &Command) -> Result<(), Box<dyn Error>> {
pubkey: config
.communication_key
.ok_or(eyre!("pubkey not generated yet"))?
.pubkey,
.pubkey
.clone(),
};

eprintln!("Exporting this information:");
Expand Down
8 changes: 6 additions & 2 deletions frost-client/src/dkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use dkg::cli::MaybeIntoEvenY;
use frost_core::Ciphersuite;
use frost_ed25519::Ed25519Sha512;
use reqwest::Url;
use zeroize::Zeroizing;

use crate::{
args::Command,
Expand Down Expand Up @@ -57,7 +58,8 @@ pub(crate) async fn dkg_for_ciphersuite<C: Ciphersuite + MaybeIntoEvenY + 'stati
.communication_key
.clone()
.ok_or_eyre("user not initialized")?
.pubkey;
.pubkey
.clone();

let mut participants = participants
.iter()
Expand All @@ -83,7 +85,8 @@ pub(crate) async fn dkg_for_ciphersuite<C: Ciphersuite + MaybeIntoEvenY + 'stati
.communication_key
.clone()
.ok_or_eyre("user not initialized")?
.privkey,
.privkey
.clone(),
),
comm_pubkey: Some(comm_pubkey),
comm_participant_pubkey_getter: Some(Rc::new(move |participant_pubkey| {
Expand All @@ -101,6 +104,7 @@ pub(crate) async fn dkg_for_ciphersuite<C: Ciphersuite + MaybeIntoEvenY + 'stati
// Generate key shares
let (key_package, public_key_package, pubkey_map) =
dkg::cli::cli_for_processed_args::<C>(dkg_config, &mut input, &mut output).await?;
let key_package = Zeroizing::new(key_package);

// Reverse pubkey_map
let pubkey_map = pubkey_map
Expand Down
3 changes: 2 additions & 1 deletion frost-client/src/trusted_dealer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ pub(crate) fn trusted_dealer_for_ciphersuite<C: Ciphersuite + MaybeIntoEvenY + '
let pubkey = config
.communication_key
.ok_or_eyre("config not initialized")?
.pubkey;
.pubkey
.clone();
let participant = Participant {
identifier: identifier.serialize(),
pubkey: pubkey.clone(),
Expand Down
1 change: 1 addition & 0 deletions participant/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ frostd = { workspace = true }
rpassword = { workspace = true }
snow = { workspace = true }
xeddsa = { workspace = true }
zeroize = { workspace = true, features = ["serde", "zeroize_derive"] }

[features]
default = []
6 changes: 5 additions & 1 deletion participant/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use frost_core::{
keys::{KeyPackage, SecretShare},
Ciphersuite,
};
use zeroize::{Zeroize, ZeroizeOnDrop};

use crate::input::read_from_file_or_stdin;

Expand Down Expand Up @@ -45,7 +46,7 @@ pub struct Args {
pub session_id: String,
}

#[derive(Clone)]
#[derive(Clone, Zeroize)]
pub struct ProcessedArgs<C: Ciphersuite> {
/// CLI mode. If enabled, it will prompt for inputs from stdin
/// and print values to stdout, ignoring other flags.
Expand Down Expand Up @@ -82,9 +83,12 @@ pub struct ProcessedArgs<C: Ciphersuite> {
// using `fn()` would preclude using closures and using generics would
// require a lot of code change for something simple.
#[allow(clippy::type_complexity)]
#[zeroize(skip)]
pub comm_coordinator_pubkey_getter: Option<Rc<dyn Fn(&Vec<u8>) -> Option<Vec<u8>>>>,
}

impl<C> ZeroizeOnDrop for ProcessedArgs<C> where C: Ciphersuite {}

impl<C: Ciphersuite + 'static> ProcessedArgs<C> {
/// Create a ProcessedArgs from a Args.
///
Expand Down
8 changes: 5 additions & 3 deletions participant/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use frost_rerandomized::RandomizedCiphersuite;
use rand::thread_rng;
use reddsa::frost::redpallas::PallasBlake2b512;
use std::io::{BufRead, Write};
use zeroize::Zeroizing;

pub async fn cli<C: RandomizedCiphersuite + 'static>(
args: &Args,
Expand All @@ -40,10 +41,11 @@ pub async fn cli_for_processed_args<C: RandomizedCiphersuite + 'static>(

// Round 1

let key_package = pargs.key_package;
let key_package = &pargs.key_package;

let mut rng = thread_rng();
let (nonces, commitments) = generate_nonces_and_commitments(&key_package, &mut rng);
let (nonces, commitments) = generate_nonces_and_commitments(key_package, &mut rng);
let nonces = Zeroizing::new(nonces);

if pargs.cli {
print_values(commitments, logger)?;
Expand Down Expand Up @@ -80,7 +82,7 @@ pub async fn cli_for_processed_args<C: RandomizedCiphersuite + 'static>(
return Err(eyre!("signing cancelled").into());
}

let signature = generate_signature(round_2_config, &key_package, &nonces)?;
let signature = generate_signature(round_2_config, key_package, &nonces)?;

comms
.send_signature_share(*key_package.identifier(), signature)
Expand Down
Loading