lighthouse-pulse/common/validator_dir/src/insecure_keys.rs
Kirk Baird 3db9072fee Reject invalid utf-8 characters during encryption (#1928)
## Issue Addressed

Closes #1889 

## Proposed Changes

- Error when passwords which use invalid UTF-8 characters during encryption. 
- Add some tests

## Additional Info

I've decided to error when bad characters are used to create/encrypt a keystore but think we should allow them during decryption since either the keystore was created
-  with invalid UTF-8 characters (possibly by another client or someone whose password is random bytes) in which case we'd want them to be able to decrypt their keystore using the right key.
-  without invalid characters then the password checksum would almost certainly fail.

Happy to add them to decryption if we want to make the decryption more trigger happy 😋 , it would only be a one line change and would tell the user which character index is causing the issue.

See https://eips.ethereum.org/EIPS/eip-2335#password-requirements
2020-11-19 00:37:43 +00:00

87 lines
2.8 KiB
Rust

//! These features exist to allow for generating deterministic, well-known, unsafe keys for use in
//! testing.
//!
//! **NEVER** use these keys in production!
#![cfg(feature = "insecure_keys")]
use crate::{Builder, BuilderError};
use eth2_keystore::{
json_keystore::{Kdf, Scrypt},
Keystore, KeystoreBuilder, PlainText, DKLEN,
};
use std::path::PathBuf;
use types::test_utils::generate_deterministic_keypair;
/// A very weak password with which to encrypt the keystores.
pub const INSECURE_PASSWORD: &[u8] = &[50; 51];
impl<'a> Builder<'a> {
/// Generate the voting keystore using a deterministic, well-known, **unsafe** keypair.
///
/// **NEVER** use these keys in production!
pub fn insecure_voting_keypair(
mut self,
deterministic_key_index: usize,
) -> Result<Self, BuilderError> {
self.voting_keystore = Some(
generate_deterministic_keystore(deterministic_key_index)
.map_err(BuilderError::InsecureKeysError)?,
);
Ok(self)
}
}
/// Generate a keystore, encrypted with `INSECURE_PASSWORD` using a deterministic, well-known,
/// **unsafe** secret key.
///
/// **NEVER** use these keys in production!
pub fn generate_deterministic_keystore(i: usize) -> Result<(Keystore, PlainText), String> {
let keypair = generate_deterministic_keypair(i);
let keystore = KeystoreBuilder::new(&keypair, INSECURE_PASSWORD, "".into())
.map_err(|e| format!("Unable to create keystore builder: {:?}", e))?
.kdf(insecure_kdf())
.build()
.map_err(|e| format!("Unable to build keystore: {:?}", e))?;
Ok((keystore, INSECURE_PASSWORD.to_vec().into()))
}
/// Returns an INSECURE key derivation function.
///
/// **NEVER** use this KDF in production!
fn insecure_kdf() -> Kdf {
Kdf::Scrypt(Scrypt {
dklen: DKLEN,
// `n` is set very low, making it cheap to encrypt/decrypt keystores.
//
// This is very insecure, only use during testing.
n: 2,
p: 1,
r: 8,
salt: vec![1; 32].into(),
})
}
/// A helper function to use the `Builder` to generate deterministic, well-known, **unsafe**
/// validator directories for the given validator `indices`.
///
/// **NEVER** use these keys in production!
pub fn build_deterministic_validator_dirs(
validators_dir: PathBuf,
password_dir: PathBuf,
indices: &[usize],
) -> Result<(), String> {
for &i in indices {
Builder::new(validators_dir.clone())
.password_dir(password_dir.clone())
.insecure_voting_keypair(i)
.map_err(|e| format!("Unable to generate insecure keypair: {:?}", e))?
.store_withdrawal_keystore(false)
.build()
.map_err(|e| format!("Unable to build keystore: {:?}", e))?;
}
Ok(())
}