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.
This commit is contained in:
Kirk Baird
2020-11-19 08:52:51 +00:00
parent 1a530e5a93
commit c5e97b9bf7
4 changed files with 260 additions and 55 deletions

View File

@@ -90,33 +90,118 @@ fn file() {
#[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];
let salt = vec![42; 32];
let my_kdf = Kdf::Scrypt(Scrypt {
dklen: DKLEN,
@@ -141,7 +226,7 @@ fn custom_scrypt_kdf() {
fn custom_pbkdf2_kdf() {
let keypair = Keypair::random();
let salt = vec![42];
let salt = vec![42; 32];
let my_kdf = Kdf::Pbkdf2(Pbkdf2 {
dklen: DKLEN,