Improve bls::SecretKey privacy (#1164)

* Improve bls::SecretKey privacy

* Add missed file

* Remove more methods from bls::SecretKey

* Add as_bytes() to SecretKey, remove as_raw

* Remove as_raw

* Add back as_raw

* Address review comments
This commit is contained in:
Paul Hauner
2020-05-19 11:23:08 +10:00
committed by GitHub
parent 314fae41fe
commit c93f9c351b
26 changed files with 102 additions and 295 deletions

View File

@@ -11,7 +11,7 @@ fn test_beacon_proposer_index<T: EthSpec>() {
// Build a state for testing.
let build_state = |validator_count: usize| -> BeaconState<T> {
let builder: TestingBeaconStateBuilder<T> =
TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(validator_count, &spec);
TestingBeaconStateBuilder::from_deterministic_keypairs(validator_count, &spec);
let (mut state, _keypairs) = builder.build();
state.build_committee_cache(relative_epoch, &spec).unwrap();
@@ -114,7 +114,7 @@ fn cache_initialization() {
let spec = MinimalEthSpec::default_spec();
let builder: TestingBeaconStateBuilder<MinimalEthSpec> =
TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(16, &spec);
TestingBeaconStateBuilder::from_deterministic_keypairs(16, &spec);
let (mut state, _keypairs) = builder.build();
state.slot =
@@ -176,7 +176,7 @@ fn clone_config() {
let spec = MinimalEthSpec::default_spec();
let builder: TestingBeaconStateBuilder<MinimalEthSpec> =
TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(16, &spec);
TestingBeaconStateBuilder::from_deterministic_keypairs(16, &spec);
let (mut state, _keypairs) = builder.build();
state.build_all_caches(&spec).unwrap();
@@ -374,7 +374,7 @@ mod get_outstanding_deposit_len {
fn state() -> BeaconState<MinimalEthSpec> {
let spec = MinimalEthSpec::default_spec();
let builder: TestingBeaconStateBuilder<MinimalEthSpec> =
TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(16, &spec);
TestingBeaconStateBuilder::from_deterministic_keypairs(16, &spec);
let (state, _keypairs) = builder.build();
state

View File

@@ -1,10 +1,10 @@
use super::super::{generate_deterministic_keypairs, KeypairsFile};
use super::super::generate_deterministic_keypairs;
use crate::test_utils::{AttestationTestTask, TestingPendingAttestationBuilder};
use crate::*;
use bls::get_withdrawal_credentials;
use log::debug;
use rayon::prelude::*;
use std::path::{Path, PathBuf};
use std::path::PathBuf;
pub const KEYPAIRS_FILE: &str = "keypairs.raw_keypairs";
@@ -29,44 +29,6 @@ pub struct TestingBeaconStateBuilder<T: EthSpec> {
}
impl<T: EthSpec> TestingBeaconStateBuilder<T> {
/// Attempts to load validators from a file in `$HOME/.lighthouse/keypairs.raw_keypairs`. If
/// the file is unavailable, it generates the keys at runtime.
///
/// If the `$HOME` environment variable is not set, the local directory is used.
///
/// See the `Self::from_keypairs_file` method for more info.
///
/// # Panics
///
/// If the file does not contain enough keypairs or is invalid.
pub fn from_default_keypairs_file_if_exists(validator_count: usize, spec: &ChainSpec) -> Self {
let dir = dirs::home_dir()
.map(|home| home.join(".lighthouse"))
.unwrap_or_else(|| PathBuf::from(""));
let file = dir.join(KEYPAIRS_FILE);
if file.exists() {
TestingBeaconStateBuilder::from_keypairs_file(validator_count, &file, spec)
} else {
TestingBeaconStateBuilder::from_deterministic_keypairs(validator_count, spec)
}
}
/// Loads the initial validator keypairs from a file on disk.
///
/// Loading keypairs from file is ~10x faster than generating them. Use the `gen_keys` command
/// on the `test_harness` binary to generate the keys. In the `test_harness` dir, run `cargo
/// run -- gen_keys -h` for help.
///
/// # Panics
///
/// If the file does not exist, is invalid or does not contain enough keypairs.
pub fn from_keypairs_file(validator_count: usize, path: &Path, spec: &ChainSpec) -> Self {
debug!("Loading {} keypairs from file...", validator_count);
let keypairs = Vec::from_raw_file(path, validator_count).unwrap();
TestingBeaconStateBuilder::from_keypairs(keypairs, spec)
}
/// Generates the validator keypairs deterministically.
pub fn from_deterministic_keypairs(validator_count: usize, spec: &ChainSpec) -> Self {
debug!("Generating {} deterministic keypairs...", validator_count);

View File

@@ -1,128 +0,0 @@
use crate::*;
use rayon::prelude::*;
use std::fs::File;
use std::io::{Error, ErrorKind, Read, Write};
use std::path::Path;
pub const PUBLIC_KEY_BYTES_LEN: usize = 96;
pub const SECRET_KEY_BYTES_LEN: usize = 32;
pub const BATCH_SIZE: usize = 1_000; // ~15MB
pub const KEYPAIR_BYTES_LEN: usize = PUBLIC_KEY_BYTES_LEN + SECRET_KEY_BYTES_LEN;
pub const BATCH_BYTE_LEN: usize = KEYPAIR_BYTES_LEN * BATCH_SIZE;
/// Defines a trait that allows reading/writing a vec of `Keypair` from/to a file.
pub trait KeypairsFile {
/// Write to file, without guaranteeing interoperability with other clients.
fn to_raw_file(&self, path: &Path, keypairs: &[Keypair]) -> Result<(), Error>;
/// Read from file, without guaranteeing interoperability with other clients.
fn from_raw_file(path: &Path, count: usize) -> Result<Vec<Keypair>, Error>;
}
impl KeypairsFile for Vec<Keypair> {
/// Write the keypairs to file, using the fastest possible method without guaranteeing
/// interoperability with other clients.
fn to_raw_file(&self, path: &Path, keypairs: &[Keypair]) -> Result<(), Error> {
let mut keypairs_file = File::create(path)?;
for keypair_batch in keypairs.chunks(BATCH_SIZE) {
let mut buf = Vec::with_capacity(BATCH_BYTE_LEN);
for keypair in keypair_batch {
buf.append(&mut keypair.sk.as_raw().as_bytes());
buf.append(&mut keypair.pk.clone().as_uncompressed_bytes());
}
keypairs_file.write_all(&buf)?;
}
Ok(())
}
/// Read the keypairs from file, using the fastest possible method without guaranteeing
/// interoperability with other clients.
fn from_raw_file(path: &Path, count: usize) -> Result<Vec<Keypair>, Error> {
let mut keypairs_file = File::open(path)?;
let mut keypairs = Vec::with_capacity(count);
let indices: Vec<usize> = (0..count).collect();
for batch in indices.chunks(BATCH_SIZE) {
let mut buf = vec![0; batch.len() * KEYPAIR_BYTES_LEN];
keypairs_file.read_exact(&mut buf)?;
let mut keypair_batch = batch
.par_iter()
.enumerate()
.map(|(i, _)| {
let sk_start = i * KEYPAIR_BYTES_LEN;
let sk_end = sk_start + SECRET_KEY_BYTES_LEN;
let sk = SecretKey::from_bytes(&buf[sk_start..sk_end])
.map_err(|_| Error::new(ErrorKind::Other, "Invalid SecretKey bytes"))
.unwrap();
let pk_start = sk_end;
let pk_end = pk_start + PUBLIC_KEY_BYTES_LEN;
let pk = PublicKey::from_uncompressed_bytes(&buf[pk_start..pk_end])
.map_err(|_| Error::new(ErrorKind::Other, "Invalid PublicKey bytes"))
.unwrap();
Keypair { sk, pk }
})
.collect();
keypairs.append(&mut keypair_batch);
}
Ok(keypairs)
}
}
#[cfg(test)]
mod tests {
use super::*;
use rand::{distributions::Alphanumeric, thread_rng, Rng};
use std::fs::remove_file;
fn random_keypairs(n: usize) -> Vec<Keypair> {
(0..n).into_par_iter().map(|_| Keypair::random()).collect()
}
fn random_tmp_file() -> String {
let rng = thread_rng();
rng.sample_iter(&Alphanumeric).take(7).collect()
}
#[test]
#[ignore]
fn read_write_consistency_small_batch() {
let num_keypairs = 10;
let keypairs = random_keypairs(num_keypairs);
let keypairs_path = Path::new("/tmp").join(random_tmp_file());
keypairs.to_raw_file(&keypairs_path, &keypairs).unwrap();
let decoded = Vec::from_raw_file(&keypairs_path, num_keypairs).unwrap();
remove_file(keypairs_path).unwrap();
assert_eq!(keypairs, decoded);
}
#[test]
#[ignore]
fn read_write_consistency_big_batch() {
let num_keypairs = BATCH_SIZE + 1;
let keypairs = random_keypairs(num_keypairs);
let keypairs_path = Path::new("/tmp").join(random_tmp_file());
keypairs.to_raw_file(&keypairs_path, &keypairs).unwrap();
let decoded = Vec::from_raw_file(&keypairs_path, num_keypairs).unwrap();
remove_file(keypairs_path).unwrap();
assert_eq!(keypairs, decoded);
}
}

View File

@@ -4,14 +4,12 @@
mod macros;
mod builders;
mod generate_deterministic_keypairs;
mod keypairs_file;
mod test_random;
pub use builders::*;
pub use generate_deterministic_keypairs::generate_deterministic_keypair;
pub use generate_deterministic_keypairs::generate_deterministic_keypairs;
pub use generate_deterministic_keypairs::load_keypairs_from_yaml;
pub use keypairs_file::KeypairsFile;
pub use rand::{RngCore, SeedableRng};
pub use rand_xorshift::XorShiftRng;
pub use test_random::{test_random_instance, TestRandom};