lighthouse/crypto/eth2_keystore/tests/tests.rs
Kirk Baird c5e97b9bf7 Add validation to kdf parameters (#1930)
## Issue Addressed

Closes #1906 
Closes #1907 

## Proposed Changes

- Emits warnings when the KDF parameters are two low.
- Returns errors when the KDF parameters are high enough to pose a potential DoS threat.
- Validates AES IV length is 128 bits, errors if empty, warnings otherwise.

## Additional Info

NIST advice used for PBKDF2 ranges https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-132.pdf. 
Scrypt ranges are based on the maximum value of the `u32` (i.e 4GB of memory)

The minimum range has been set to anything below the default fields.
2020-11-19 08:52:51 +00:00

305 lines
7.5 KiB
Rust

#![cfg(test)]
#![cfg(not(debug_assertions))]
use bls::Keypair;
use eth2_keystore::{
default_kdf,
json_keystore::{Kdf, Pbkdf2, Prf, Scrypt},
Error, Keystore, KeystoreBuilder, DKLEN,
};
use std::fs::OpenOptions;
use tempfile::tempdir;
const GOOD_PASSWORD: &[u8] = &[42, 42, 42];
const BAD_PASSWORD: &[u8] = &[43, 43, 43];
#[test]
fn empty_password() {
assert_eq!(
KeystoreBuilder::new(&Keypair::random(), "".as_bytes(), "".into())
.err()
.unwrap(),
Error::EmptyPassword
);
}
#[test]
fn string_round_trip() {
let keypair = Keypair::random();
let keystore = KeystoreBuilder::new(&keypair, GOOD_PASSWORD, "".into())
.unwrap()
.build()
.unwrap();
let json = keystore.to_json_string().unwrap();
let decoded = Keystore::from_json_str(&json).unwrap();
assert_eq!(
decoded.decrypt_keypair(BAD_PASSWORD).err().unwrap(),
Error::InvalidPassword,
"should not decrypt with bad password"
);
assert_eq!(
decoded.decrypt_keypair(GOOD_PASSWORD).unwrap().pk,
keypair.pk,
"should decrypt with good password"
);
}
#[test]
fn file() {
let keypair = Keypair::random();
let dir = tempdir().unwrap();
let path = dir.path().join("keystore.json");
let get_file = || {
OpenOptions::new()
.write(true)
.read(true)
.create(true)
.open(path.clone())
.expect("should create file")
};
let keystore = KeystoreBuilder::new(&keypair, GOOD_PASSWORD, "".into())
.unwrap()
.build()
.unwrap();
keystore
.to_json_writer(&mut get_file())
.expect("should write to file");
let decoded = Keystore::from_json_reader(&mut get_file()).expect("should read from file");
assert_eq!(
decoded.decrypt_keypair(BAD_PASSWORD).err().unwrap(),
Error::InvalidPassword,
"should not decrypt with bad password"
);
assert_eq!(
decoded.decrypt_keypair(GOOD_PASSWORD).unwrap().pk,
keypair.pk,
"should decrypt with good password"
);
}
#[test]
fn scrypt_params() {
let keypair = Keypair::random();
let salt = vec![42; 32];
let keystore = KeystoreBuilder::new(&keypair, GOOD_PASSWORD, "".into())
.unwrap()
.build()
.unwrap();
let json = keystore.to_json_string().unwrap();
let decoded = Keystore::from_json_str(&json).unwrap();
assert_eq!(
decoded.decrypt_keypair(BAD_PASSWORD).err().unwrap(),
Error::InvalidPassword,
"should not decrypt with bad password"
);
assert_eq!(
decoded.decrypt_keypair(GOOD_PASSWORD).unwrap().pk,
keypair.pk,
"should decrypt with good password"
);
// n <= 1
let my_kdf = Kdf::Scrypt(Scrypt {
dklen: DKLEN,
n: 1,
p: 1,
r: 8,
salt: salt.clone().into(),
});
let keystore = KeystoreBuilder::new(&keypair, GOOD_PASSWORD, "".into())
.unwrap()
.kdf(my_kdf.clone())
.build();
assert_eq!(keystore, Err(Error::InvalidScryptParam));
// p != 0
let my_kdf = Kdf::Scrypt(Scrypt {
dklen: DKLEN,
n: 16,
p: 0,
r: 8,
salt: salt.clone().into(),
});
let keystore = KeystoreBuilder::new(&keypair, GOOD_PASSWORD, "".into())
.unwrap()
.kdf(my_kdf.clone())
.build();
assert_eq!(keystore, Err(Error::InvalidScryptParam));
// r != 0
let my_kdf = Kdf::Scrypt(Scrypt {
dklen: DKLEN,
n: 16,
p: 1,
r: 0,
salt: salt.clone().into(),
});
let keystore = KeystoreBuilder::new(&keypair, GOOD_PASSWORD, "".into())
.unwrap()
.kdf(my_kdf.clone())
.build();
assert_eq!(keystore, Err(Error::InvalidScryptParam));
// 128 * n * p * r overflow
let my_kdf = Kdf::Scrypt(Scrypt {
dklen: DKLEN,
n: 1 << 31,
p: 1 << 31,
r: 1 << 31,
salt: salt.clone().into(),
});
let keystore = KeystoreBuilder::new(&keypair, GOOD_PASSWORD, "".into())
.unwrap()
.kdf(my_kdf.clone())
.build();
assert_eq!(keystore, Err(Error::InvalidScryptParam));
}
#[test]
fn pbkdf2_params() {
let keypair = Keypair::random();
let salt = vec![42; 32];
let my_kdf = Kdf::Pbkdf2(Pbkdf2 {
dklen: DKLEN,
c: 80_000_001,
prf: Prf::HmacSha256,
salt: salt.clone().into(),
});
let keystore = KeystoreBuilder::new(&keypair, GOOD_PASSWORD, "".into())
.unwrap()
.kdf(my_kdf.clone())
.build();
assert_eq!(keystore, Err(Error::InvalidPbkdf2Param));
let my_kdf = Kdf::Pbkdf2(Pbkdf2 {
dklen: DKLEN + 1,
c: 4,
prf: Prf::HmacSha256,
salt: salt.clone().into(),
});
let keystore = KeystoreBuilder::new(&keypair, GOOD_PASSWORD, "".into())
.unwrap()
.kdf(my_kdf.clone())
.build();
assert_eq!(keystore, Err(Error::InvalidPbkdf2Param));
}
#[test]
fn custom_scrypt_kdf() {
let keypair = Keypair::random();
let salt = vec![42; 32];
let my_kdf = Kdf::Scrypt(Scrypt {
dklen: DKLEN,
n: 2,
p: 1,
r: 8,
salt: salt.clone().into(),
});
assert!(my_kdf != default_kdf(salt));
let keystore = KeystoreBuilder::new(&keypair, GOOD_PASSWORD, "".into())
.unwrap()
.kdf(my_kdf.clone())
.build()
.unwrap();
assert_eq!(keystore.kdf(), &my_kdf);
}
#[test]
fn custom_pbkdf2_kdf() {
let keypair = Keypair::random();
let salt = vec![42; 32];
let my_kdf = Kdf::Pbkdf2(Pbkdf2 {
dklen: DKLEN,
c: 2,
prf: Prf::HmacSha256,
salt: salt.clone().into(),
});
assert!(my_kdf != default_kdf(salt));
let keystore = KeystoreBuilder::new(&keypair, GOOD_PASSWORD, "".into())
.unwrap()
.kdf(my_kdf.clone())
.build()
.unwrap();
assert_eq!(keystore.kdf(), &my_kdf);
}
#[test]
fn utf8_control_characters() {
let keypair = Keypair::random();
let invalid_character = 0u8;
let invalid_password = [invalid_character];
let keystore = KeystoreBuilder::new(&keypair, &invalid_password, "".into())
.unwrap()
.build();
assert_eq!(
keystore,
Err(Error::InvalidPasswordCharacter {
character: invalid_character,
index: 0
})
);
let invalid_character = 0x1Fu8;
let invalid_password = [50, invalid_character, 50];
let keystore = KeystoreBuilder::new(&keypair, &invalid_password, "".into())
.unwrap()
.build();
assert_eq!(
keystore,
Err(Error::InvalidPasswordCharacter {
character: invalid_character,
index: 1
})
);
let invalid_character = 0x80u8;
let invalid_password = [50, 50, invalid_character];
let keystore = KeystoreBuilder::new(&keypair, &invalid_password, "".into())
.unwrap()
.build();
assert_eq!(
keystore,
Err(Error::InvalidPasswordCharacter {
character: invalid_character,
index: 2
})
);
let invalid_character = 0x7Fu8;
let invalid_password = [50, 50, 50, 50, 50, 50, invalid_character];
let keystore = KeystoreBuilder::new(&keypair, &invalid_password, "".into())
.unwrap()
.build();
assert_eq!(
keystore,
Err(Error::InvalidPasswordCharacter {
character: invalid_character,
index: 6
})
);
}