Skip to content

Commit 0d6ab1f

Browse files
committed
all: best-effort at zeroization of secrets
1 parent 95c3f8c commit 0d6ab1f

15 files changed

+58
-16
lines changed

Cargo.lock

+5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ zcash_keys = "0.6.0"
7474
zcash_primitives = "0.21.0"
7575
zcash_proofs = "0.21.0"
7676
zcash_protocol = "0.4.3"
77+
zeroize = "1.8.1"
7778

7879
[patch.crates-io]
7980
# TODO: remove this when https://github.com/zcash/orchard/issues/430 is fully

dkg/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ xeddsa = { workspace = true }
2222
reqwest = { workspace = true, features = ["json", "rustls-tls-native-roots"] }
2323
tokio = { workspace = true, features = ["full"] }
2424
snow = { workspace = true }
25+
zeroize = { workspace = true, features = ["serde", "zeroize_derive"] }
2526

2627
[features]
2728
default = []

dkg/src/args.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::rc::Rc;
33
use clap::Parser;
44
use frost_core::{Ciphersuite, Identifier};
55
use frostd::PublicKey;
6+
use zeroize::{Zeroize, ZeroizeOnDrop};
67

78
#[derive(Parser, Debug, Default)]
89
#[command(author, version, about, long_about = None)]
@@ -11,7 +12,7 @@ pub struct Args {
1112
pub ciphersuite: String,
1213
}
1314

14-
#[derive(Clone)]
15+
#[derive(Clone, Zeroize)]
1516
pub struct ProcessedArgs<C: Ciphersuite> {
1617
/// CLI mode. If enabled, it will prompt for inputs from stdin
1718
/// and print values to stdout, ignoring other flags.
@@ -39,6 +40,7 @@ pub struct ProcessedArgs<C: Ciphersuite> {
3940
// using `fn()` would preclude using closures and using generics would
4041
// require a lot of code change for something simple.
4142
#[allow(clippy::type_complexity)]
43+
#[zeroize(skip)]
4244
pub comm_participant_pubkey_getter: Option<Rc<dyn Fn(&PublicKey) -> Option<PublicKey>>>,
4345

4446
/// The threshold to use for the shares
@@ -52,9 +54,12 @@ pub struct ProcessedArgs<C: Ciphersuite> {
5254
pub participants: Vec<PublicKey>,
5355

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

61+
impl<C> ZeroizeOnDrop for ProcessedArgs<C> where C: Ciphersuite {}
62+
5863
impl<C> ProcessedArgs<C>
5964
where
6065
C: Ciphersuite,

dkg/src/cli.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use reddsa::frost::redpallas::keys::EvenY;
88
use std::collections::HashMap;
99
use std::error::Error;
1010
use std::io::{BufRead, Write};
11+
use zeroize::Zeroizing;
1112

1213
use crate::args::ProcessedArgs;
1314
use crate::comms::cli::CLIComms;
@@ -103,6 +104,7 @@ pub async fn cli_for_processed_args<C: Ciphersuite + 'static + MaybeIntoEvenY>(
103104

104105
let (round2_secret_package, round2_packages) =
105106
frost::keys::dkg::part2(round1_secret_package, &received_round1_packages)?;
107+
let round2_secret_package = Zeroizing::new(round2_secret_package);
106108

107109
let received_round2_packages = comms
108110
.get_round2_packages(input, logger, round2_packages)

frost-client/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,4 @@ stable-eyre = { workspace = true }
3333
itertools = { workspace = true }
3434
xeddsa = { workspace = true }
3535
thiserror = { workspace = true }
36+
zeroize = { workspace = true, features = ["serde", "zeroize_derive"] }

frost-client/src/config.rs

+16-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use eyre::{eyre, OptionExt};
1010
use frost_core::{Ciphersuite, Identifier};
1111
use frostd::PublicKey;
1212
use serde::{Deserialize, Serialize};
13+
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
1314

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

@@ -30,6 +31,14 @@ pub struct Config {
3031
pub group: BTreeMap<String, Group>,
3132
}
3233

34+
impl Zeroize for Config {
35+
fn zeroize(&mut self) {
36+
self.group.iter_mut().for_each(|(_, g)| g.zeroize());
37+
}
38+
}
39+
40+
impl ZeroizeOnDrop for Config {}
41+
3342
impl Config {
3443
pub fn contact_by_pubkey(&self, pubkey: &PublicKey) -> Result<Contact, Box<dyn Error>> {
3544
if Some(pubkey) == self.communication_key.as_ref().map(|c| &c.pubkey) {
@@ -49,7 +58,7 @@ impl Config {
4958
}
5059

5160
/// The communication key pair for the user.
52-
#[derive(Clone, Debug, Serialize, Deserialize)]
61+
#[derive(Clone, Debug, Serialize, Deserialize, ZeroizeOnDrop)]
5362
pub struct CommunicationKey {
5463
/// The private key.
5564
#[serde(
@@ -62,7 +71,7 @@ pub struct CommunicationKey {
6271
}
6372

6473
/// A FROST group the user belongs to.
65-
#[derive(Clone, Debug, Serialize, Deserialize)]
74+
#[derive(Clone, Debug, Serialize, Deserialize, Zeroize)]
6675
pub struct Group {
6776
/// A human-readable description of the group to make it easier to select
6877
/// groups
@@ -84,9 +93,12 @@ pub struct Group {
8493
/// The default server the participants are using, if any.
8594
pub server_url: Option<String>,
8695
/// The group participants, keyed by hex-encoded identifier
96+
#[zeroize(skip)]
8797
pub participant: BTreeMap<String, Participant>,
8898
}
8999

100+
impl ZeroizeOnDrop for Group {}
101+
90102
impl Group {
91103
/// Returns a human-readable summary of the contact; used when it is
92104
/// printed to the terminal.
@@ -169,7 +181,7 @@ impl Config {
169181
..Default::default()
170182
});
171183
}
172-
let bytes = std::fs::read(&path)?;
184+
let bytes = Zeroizing::new(std::fs::read(&path)?);
173185
let s = str::from_utf8(&bytes)?;
174186
let mut config: Config = toml::from_str(s)?;
175187
config.path = Some(path);
@@ -178,7 +190,7 @@ impl Config {
178190

179191
/// Write the config to path it was loaded from.
180192
pub fn write(&self) -> Result<(), Box<dyn Error>> {
181-
let s = toml::to_string_pretty(self)?;
193+
let s = Zeroizing::new(toml::to_string_pretty(self)?);
182194
let bytes = s.as_bytes();
183195
Ok(write_atomic::write_file(
184196
self.path

frost-client/src/contact.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -75,15 +75,15 @@ pub(crate) fn import(args: &Command) -> Result<(), Box<dyn Error>> {
7575
if config.contact.values().any(|c| c.pubkey == contact.pubkey) {
7676
return Err(eyre!(
7777
"pubkey {} already registered for {}",
78-
hex::encode(&contact.pubkey),
78+
hex::encode(&contact.pubkey.0),
7979
&contact.name,
8080
)
8181
.into());
8282
}
8383
if config.communication_key.as_ref().map(|c| &c.pubkey) == Some(&contact.pubkey) {
8484
return Err(eyre!(
8585
"pubkey {} already registered for yourself",
86-
hex::encode(&contact.pubkey)
86+
hex::encode(&contact.pubkey.0)
8787
)
8888
.into());
8989
}
@@ -114,7 +114,8 @@ pub(crate) fn export(args: &Command) -> Result<(), Box<dyn Error>> {
114114
pubkey: config
115115
.communication_key
116116
.ok_or(eyre!("pubkey not generated yet"))?
117-
.pubkey,
117+
.pubkey
118+
.clone(),
118119
};
119120

120121
eprintln!("Exporting this information:");

frost-client/src/dkg.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use dkg::cli::MaybeIntoEvenY;
1010
use frost_core::Ciphersuite;
1111
use frost_ed25519::Ed25519Sha512;
1212
use reqwest::Url;
13+
use zeroize::Zeroizing;
1314

1415
use crate::{
1516
args::Command,
@@ -57,7 +58,8 @@ pub(crate) async fn dkg_for_ciphersuite<C: Ciphersuite + MaybeIntoEvenY + 'stati
5758
.communication_key
5859
.clone()
5960
.ok_or_eyre("user not initialized")?
60-
.pubkey;
61+
.pubkey
62+
.clone();
6163

6264
let mut participants = participants
6365
.iter()
@@ -83,7 +85,8 @@ pub(crate) async fn dkg_for_ciphersuite<C: Ciphersuite + MaybeIntoEvenY + 'stati
8385
.communication_key
8486
.clone()
8587
.ok_or_eyre("user not initialized")?
86-
.privkey,
88+
.privkey
89+
.clone(),
8790
),
8891
comm_pubkey: Some(comm_pubkey),
8992
comm_participant_pubkey_getter: Some(Rc::new(move |participant_pubkey| {
@@ -101,6 +104,7 @@ pub(crate) async fn dkg_for_ciphersuite<C: Ciphersuite + MaybeIntoEvenY + 'stati
101104
// Generate key shares
102105
let (key_package, public_key_package, pubkey_map) =
103106
dkg::cli::cli_for_processed_args::<C>(dkg_config, &mut input, &mut output).await?;
107+
let key_package = Zeroizing::new(key_package);
104108

105109
// Reverse pubkey_map
106110
let pubkey_map = pubkey_map

frost-client/src/trusted_dealer.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ pub(crate) fn trusted_dealer_for_ciphersuite<C: Ciphersuite + MaybeIntoEvenY + '
7272
let pubkey = config
7373
.communication_key
7474
.ok_or_eyre("config not initialized")?
75-
.pubkey;
75+
.pubkey
76+
.clone();
7677
let participant = Participant {
7778
identifier: identifier.serialize(),
7879
pubkey: pubkey.clone(),

frostd/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ xeddsa = { workspace = true }
2828
futures-util = { workspace = true }
2929
futures = { workspace = true }
3030
thiserror = { workspace = true }
31+
zeroize = { workspace = true, features = ["serde", "zeroize_derive"] }
3132
# ring is enabled due to the following issue:
3233
# - we enable rustls for reqwest because it's required to workaround an issue
3334
# when adding root certificates (see test_http), and that imports rustls

frostd/src/types.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use frost_core::{Ciphersuite, SigningPackage};
22
use frost_rerandomized::Randomizer;
33
use serde::{Deserialize, Serialize};
44
pub use uuid::Uuid;
5+
use zeroize::Zeroize;
56

67
#[derive(Debug, Serialize, Deserialize)]
78
pub struct Error {
@@ -72,7 +73,7 @@ pub struct GetSessionInfoOutput {
7273
pub coordinator_pubkey: PublicKey,
7374
}
7475

75-
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
76+
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Zeroize)]
7677
#[serde(transparent)]
7778
pub struct PublicKey(
7879
#[serde(

participant/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ frostd = { workspace = true }
2424
rpassword = { workspace = true }
2525
snow = { workspace = true }
2626
xeddsa = { workspace = true }
27+
zeroize = { workspace = true, features = ["serde", "zeroize_derive"] }
2728

2829
[features]
2930
default = []

participant/src/args.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use frost_core::{
1212
Ciphersuite,
1313
};
1414
use frostd::PublicKey;
15+
use zeroize::{Zeroize, ZeroizeOnDrop};
1516

1617
use crate::input::read_from_file_or_stdin;
1718

@@ -46,7 +47,7 @@ pub struct Args {
4647
pub session_id: String,
4748
}
4849

49-
#[derive(Clone)]
50+
#[derive(Clone, Zeroize)]
5051
pub struct ProcessedArgs<C: Ciphersuite> {
5152
/// CLI mode. If enabled, it will prompt for inputs from stdin
5253
/// and print values to stdout, ignoring other flags.
@@ -83,9 +84,12 @@ pub struct ProcessedArgs<C: Ciphersuite> {
8384
// using `fn()` would preclude using closures and using generics would
8485
// require a lot of code change for something simple.
8586
#[allow(clippy::type_complexity)]
87+
#[zeroize(skip)]
8688
pub comm_coordinator_pubkey_getter: Option<Rc<dyn Fn(&PublicKey) -> Option<PublicKey>>>,
8789
}
8890

91+
impl<C> ZeroizeOnDrop for ProcessedArgs<C> where C: Ciphersuite {}
92+
8993
impl<C: Ciphersuite + 'static> ProcessedArgs<C> {
9094
/// Create a ProcessedArgs from a Args.
9195
///

participant/src/cli.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use frost_rerandomized::RandomizedCiphersuite;
1515
use rand::thread_rng;
1616
use reddsa::frost::redpallas::PallasBlake2b512;
1717
use std::io::{BufRead, Write};
18+
use zeroize::Zeroizing;
1819

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

4142
// Round 1
4243

43-
let key_package = pargs.key_package;
44+
let key_package = &pargs.key_package;
4445

4546
let mut rng = thread_rng();
46-
let (nonces, commitments) = generate_nonces_and_commitments(&key_package, &mut rng);
47+
let (nonces, commitments) = generate_nonces_and_commitments(key_package, &mut rng);
48+
let nonces = Zeroizing::new(nonces);
4749

4850
if pargs.cli {
4951
print_values(commitments, logger)?;
@@ -73,7 +75,7 @@ pub async fn cli_for_processed_args<C: RandomizedCiphersuite + 'static>(
7375
.confirm_message(input, logger, &round_2_config)
7476
.await?;
7577

76-
let signature = generate_signature(round_2_config, &key_package, &nonces)?;
78+
let signature = generate_signature(round_2_config, key_package, &nonces)?;
7779

7880
comms
7981
.send_signature_share(*key_package.identifier(), signature)

0 commit comments

Comments
 (0)