Skip to content

Commit b006a6b

Browse files
ckammxiangzhu70
authored andcommitted
Keypair: implement clone() (solana-labs#26248)
* Keypair: implement clone() This was not implemented upstream in ed25519-dalek to force everyone to think twice before creating another copy of a potentially sensitive private key in memory. See dalek-cryptography/ed25519-dalek#76 However, there are now 9 instances of Keypair::from_bytes(&keypair.to_bytes()) in the solana codebase and it would be preferable to have a function. In particular since this also comes up when writing programs and can cause users to either start messing with lifetimes or discover the from_bytes() workaround themselves. This patch opts to not implement the Clone trait. This avoids automatic use in order to preserve some of the original "let developers think twice about this" intention. * Use Keypair::clone
1 parent 032e87c commit b006a6b

File tree

5 files changed

+34
-16
lines changed

5 files changed

+34
-16
lines changed

core/src/replay_stage.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -3522,7 +3522,7 @@ pub(crate) mod tests {
35223522
hash::{hash, Hash},
35233523
instruction::InstructionError,
35243524
poh_config::PohConfig,
3525-
signature::{Keypair, Signer},
3525+
signature::{Keypair, KeypairInsecureClone, Signer},
35263526
system_transaction,
35273527
transaction::TransactionError,
35283528
},
@@ -3602,7 +3602,7 @@ pub(crate) mod tests {
36023602
let my_pubkey = my_keypairs.node_keypair.pubkey();
36033603
let cluster_info = ClusterInfo::new(
36043604
Node::new_localhost_with_pubkey(&my_pubkey).info,
3605-
Arc::new(Keypair::from_bytes(&my_keypairs.node_keypair.to_bytes()).unwrap()),
3605+
Arc::new(my_keypairs.node_keypair.clone()),
36063606
SocketAddrSpace::Unspecified,
36073607
);
36083608
assert_eq!(my_pubkey, cluster_info.id());

local-cluster/src/local_cluster.rs

+7-9
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use {
3737
message::Message,
3838
poh_config::PohConfig,
3939
pubkey::Pubkey,
40-
signature::{Keypair, Signer},
40+
signature::{Keypair, KeypairInsecureClone, Signer},
4141
stake::{
4242
config as stake_config, instruction as stake_instruction,
4343
state::{Authorized, Lockup},
@@ -55,6 +55,7 @@ use {
5555
collections::HashMap,
5656
io::{Error, ErrorKind, Result},
5757
iter,
58+
ops::Deref,
5859
path::{Path, PathBuf},
5960
sync::{Arc, RwLock},
6061
},
@@ -203,10 +204,8 @@ impl LocalCluster {
203204
if *in_genesis {
204205
Some((
205206
ValidatorVoteKeypairs {
206-
node_keypair: Keypair::from_bytes(&node_keypair.to_bytes())
207-
.unwrap(),
208-
vote_keypair: Keypair::from_bytes(&vote_keypair.to_bytes())
209-
.unwrap(),
207+
node_keypair: node_keypair.deref().clone(),
208+
vote_keypair: vote_keypair.deref().clone(),
210209
stake_keypair: Keypair::new(),
211210
},
212211
stake,
@@ -265,9 +264,8 @@ impl LocalCluster {
265264
let mut leader_config = safe_clone_config(&config.validator_configs[0]);
266265
leader_config.rpc_addrs = Some((leader_node.info.rpc, leader_node.info.rpc_pubsub));
267266
Self::sync_ledger_path_across_nested_config_fields(&mut leader_config, &leader_ledger_path);
268-
let leader_keypair = Arc::new(Keypair::from_bytes(&leader_keypair.to_bytes()).unwrap());
269-
let leader_vote_keypair =
270-
Arc::new(Keypair::from_bytes(&leader_vote_keypair.to_bytes()).unwrap());
267+
let leader_keypair = Arc::new(leader_keypair.clone());
268+
let leader_vote_keypair = Arc::new(leader_vote_keypair.clone());
271269

272270
let leader_server = Validator::new(
273271
leader_node,
@@ -315,7 +313,7 @@ impl LocalCluster {
315313
.map(|keypairs| {
316314
(
317315
keypairs.node_keypair.pubkey(),
318-
Arc::new(Keypair::from_bytes(&keypairs.vote_keypair.to_bytes()).unwrap()),
316+
Arc::new(keypairs.vote_keypair.clone()),
319317
)
320318
})
321319
.collect();

local-cluster/tests/local_cluster_slow_1.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,13 @@ use {
2929
clock::{Slot, MAX_PROCESSING_AGE},
3030
hash::Hash,
3131
pubkey::Pubkey,
32-
signature::{Keypair, Signer},
32+
signature::{KeypairInsecureClone, Signer},
3333
},
3434
solana_streamer::socket::SocketAddrSpace,
3535
solana_vote_program::{vote_state::MAX_LOCKOUT_HISTORY, vote_transaction},
3636
std::{
3737
collections::{BTreeSet, HashSet},
38+
ops::Deref,
3839
path::Path,
3940
sync::{
4041
atomic::{AtomicBool, Ordering},
@@ -462,7 +463,7 @@ fn test_duplicate_shreds_broadcast_leader() {
462463
let (gossip_service, _tcp_listener, cluster_info) = gossip_service::make_gossip_node(
463464
// Need to use our validator's keypair to gossip EpochSlots and votes for our
464465
// node later.
465-
Keypair::from_bytes(&node_keypair.to_bytes()).unwrap(),
466+
node_keypair.deref().clone(),
466467
Some(&cluster.entry_point_info.gossip),
467468
&exit,
468469
None,

runtime/src/genesis_utils.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use {
77
genesis_config::{ClusterType, GenesisConfig},
88
pubkey::Pubkey,
99
rent::Rent,
10-
signature::{Keypair, Signer},
10+
signature::{Keypair, KeypairInsecureClone, Signer},
1111
stake::state::StakeState,
1212
system_program,
1313
},
@@ -109,8 +109,7 @@ pub fn create_genesis_config_with_vote_accounts_and_cluster_type(
109109
assert_eq!(voting_keypairs.len(), stakes.len());
110110

111111
let mint_keypair = Keypair::new();
112-
let voting_keypair =
113-
Keypair::from_bytes(&voting_keypairs[0].borrow().vote_keypair.to_bytes()).unwrap();
112+
let voting_keypair = voting_keypairs[0].borrow().vote_keypair.clone();
114113

115114
let validator_pubkey = voting_keypairs[0].borrow().node_keypair.pubkey();
116115
let genesis_config = create_genesis_config_with_leader_ex(

sdk/src/signer/keypair.rs

+20
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,26 @@ where
9797
}
9898
}
9999

100+
/// Allows Keypair cloning
101+
///
102+
/// Note that the `Clone` trait is intentionally unimplemented because making a
103+
/// second copy of sensitive secret keys in memory is usually a bad idea.
104+
///
105+
/// Only use this in tests or when strictly required.
106+
pub trait KeypairInsecureClone {
107+
fn clone(&self) -> Self;
108+
}
109+
110+
impl KeypairInsecureClone for Keypair {
111+
fn clone(&self) -> Self {
112+
Self(ed25519_dalek::Keypair {
113+
// This will never error since self is a valid keypair
114+
secret: ed25519_dalek::SecretKey::from_bytes(self.0.secret.as_bytes()).unwrap(),
115+
public: self.0.public,
116+
})
117+
}
118+
}
119+
100120
/// Reads a JSON-encoded `Keypair` from a `Reader` implementor
101121
pub fn read_keypair<R: Read>(reader: &mut R) -> Result<Keypair, Box<dyn error::Error>> {
102122
let bytes: Vec<u8> = serde_json::from_reader(reader)?;

0 commit comments

Comments
 (0)