2016-02-10 23:06:35 +01:00
|
|
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
|
|
|
// This file is part of Parity.
|
|
|
|
|
|
|
|
// Parity is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
|
|
|
|
// Parity is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
//! SecretStore
|
|
|
|
//! module for managing key files, decrypting and encrypting arbitrary data
|
|
|
|
|
|
|
|
use common::*;
|
|
|
|
|
|
|
|
enum CryptoCipherType {
|
|
|
|
// aes-128-ctr with 128-bit initialisation vector(iv)
|
|
|
|
Aes128Ctr(U128)
|
|
|
|
}
|
|
|
|
|
|
|
|
enum KeyFileVersion {
|
2016-02-11 01:32:44 +01:00
|
|
|
V3(u64)
|
2016-02-10 23:06:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
enum Pbkdf2CryptoFunction {
|
|
|
|
HMacSha256
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
// Kdf of type `Pbkdf2`
|
|
|
|
// https://en.wikipedia.org/wiki/PBKDF2
|
|
|
|
struct KdfPbkdf2Params {
|
|
|
|
// desired length of the derived key, in octets
|
|
|
|
dkLen: u32,
|
|
|
|
// cryptographic salt
|
|
|
|
salt: H256,
|
|
|
|
// number of iterations for derived key
|
|
|
|
c: u32,
|
|
|
|
// pseudo-random 2-parameters function
|
|
|
|
prf: Pbkdf2CryptoFunction
|
|
|
|
}
|
|
|
|
|
2016-02-11 01:32:44 +01:00
|
|
|
#[derive(Debug)]
|
2016-02-11 02:22:59 +01:00
|
|
|
enum Pbkdf2ParseError {
|
2016-02-11 01:32:44 +01:00
|
|
|
InvalidParameter(String)
|
|
|
|
}
|
|
|
|
|
|
|
|
impl KdfPbkdf2Params {
|
2016-02-11 02:22:59 +01:00
|
|
|
fn new(_json: &BTreeMap<String, Json>) -> Result<KdfPbkdf2Params, Pbkdf2ParseError> {
|
|
|
|
Ok(KdfPbkdf2Params{
|
2016-02-11 01:32:44 +01:00
|
|
|
dkLen: 0,
|
|
|
|
salt: H256::zero(),
|
|
|
|
c: 0,
|
|
|
|
prf: Pbkdf2CryptoFunction::HMacSha256
|
2016-02-11 02:22:59 +01:00
|
|
|
})
|
2016-02-11 01:32:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-10 23:06:35 +01:00
|
|
|
#[allow(non_snake_case)]
|
|
|
|
// Kdf of type `Scrypt`
|
|
|
|
// https://en.wikipedia.org/wiki/Scrypt
|
|
|
|
struct KdfScryptParams {
|
|
|
|
// desired length of the derived key, in octets
|
|
|
|
dkLen: u32,
|
|
|
|
// parallelization
|
|
|
|
p: u32,
|
|
|
|
// cpu cost
|
|
|
|
n: u32,
|
|
|
|
// TODO: comment
|
|
|
|
r: u32,
|
|
|
|
}
|
|
|
|
|
2016-02-11 01:32:44 +01:00
|
|
|
#[derive(Debug)]
|
|
|
|
enum ScryptParseError {
|
|
|
|
InvalidParameter(String)
|
|
|
|
}
|
|
|
|
|
|
|
|
impl KdfScryptParams {
|
2016-02-11 02:22:59 +01:00
|
|
|
fn new(_json: &BTreeMap<String, Json>) -> Result<KdfScryptParams, ScryptParseError> {
|
2016-02-11 01:32:44 +01:00
|
|
|
Ok(KdfScryptParams{
|
|
|
|
dkLen: 0,
|
|
|
|
p: 0,
|
|
|
|
n: 0,
|
|
|
|
r: 0
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2016-02-10 23:06:35 +01:00
|
|
|
|
|
|
|
enum Kdf {
|
|
|
|
Pbkdf2(KdfPbkdf2Params),
|
|
|
|
Scrypt(KdfScryptParams)
|
|
|
|
}
|
|
|
|
|
2016-02-11 01:32:44 +01:00
|
|
|
enum KeyFileKdf {
|
|
|
|
Pbkdf2(KdfPbkdf2Params),
|
|
|
|
Scrypt(KdfScryptParams)
|
|
|
|
}
|
|
|
|
|
|
|
|
struct KeyFileCrypto {
|
|
|
|
cipher_type: CryptoCipherType,
|
|
|
|
cipher_text: Bytes,
|
|
|
|
kdf: KeyFileKdf,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl KeyFileCrypto {
|
|
|
|
fn new(json: &Json) -> Result<KeyFileCrypto, CryptoParseError> {
|
|
|
|
let as_object = match json.as_object() {
|
|
|
|
None => { return Err(CryptoParseError::InvalidJsonFormat); }
|
|
|
|
Some(obj) => obj
|
|
|
|
};
|
|
|
|
|
|
|
|
let cipher_type = match as_object["cipher"].as_string() {
|
|
|
|
None => { return Err(CryptoParseError::NoCipherType); }
|
|
|
|
Some("aes-128-ctr") => CryptoCipherType::Aes128Ctr(
|
2016-02-11 02:22:59 +01:00
|
|
|
match as_object["cipherparams"].as_object() {
|
2016-02-11 01:32:44 +01:00
|
|
|
None => { return Err(CryptoParseError::NoCipherParameters); },
|
2016-02-11 02:22:59 +01:00
|
|
|
Some(cipher_param) => match U128::from_str(match cipher_param["iv"].as_string() {
|
|
|
|
None => { return Err(CryptoParseError::NoInitialVector); },
|
|
|
|
Some(iv_hex_string) => iv_hex_string
|
|
|
|
})
|
|
|
|
{
|
|
|
|
Ok(iv_value) => iv_value,
|
|
|
|
Err(hex_error) => { return Err(CryptoParseError::InvalidInitialVector(hex_error)); }
|
|
|
|
}
|
2016-02-11 01:32:44 +01:00
|
|
|
}
|
|
|
|
),
|
2016-02-11 02:22:59 +01:00
|
|
|
Some(other_cipher_type) => {
|
2016-02-11 01:32:44 +01:00
|
|
|
return Err(CryptoParseError::InvalidCipherType(
|
|
|
|
Mismatch { expected: "aes-128-ctr".to_owned(), found: other_cipher_type.to_owned() }));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-02-11 02:22:59 +01:00
|
|
|
let kdf = match (as_object["kdf"].as_string(), as_object["kdfparams"].as_object()) {
|
2016-02-11 01:32:44 +01:00
|
|
|
(None, _) => { return Err(CryptoParseError::NoKdfType); },
|
|
|
|
(Some("scrypt"), Some(kdf_params)) =>
|
|
|
|
match KdfScryptParams::new(kdf_params) {
|
2016-02-11 02:22:59 +01:00
|
|
|
Err(scrypt_params_error) => { return Err(CryptoParseError::Scrypt(scrypt_params_error)); },
|
|
|
|
Ok(scrypt_params) => KeyFileKdf::Scrypt(scrypt_params)
|
2016-02-11 01:32:44 +01:00
|
|
|
},
|
|
|
|
(Some("pbkdf2"), Some(kdf_params)) =>
|
|
|
|
match KdfPbkdf2Params::new(kdf_params) {
|
2016-02-11 02:22:59 +01:00
|
|
|
Err(kdfPbkdf2_params_error) => { return Err(CryptoParseError::KdfPbkdf2(kdfPbkdf2_params_error)); },
|
|
|
|
Ok(kdfPbkdf2_params) => KeyFileKdf::Pbkdf2(kdfPbkdf2_params)
|
2016-02-11 01:32:44 +01:00
|
|
|
},
|
|
|
|
(Some(other_kdf), _) => {
|
|
|
|
return Err(CryptoParseError::InvalidKdfType(
|
2016-02-11 02:22:59 +01:00
|
|
|
Mismatch { expected: "pbkdf2/scrypt".to_owned(), found: other_kdf.to_owned()}));
|
2016-02-11 01:32:44 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let cipher_text = match as_object["ciphertext"].as_string() {
|
|
|
|
None => { return Err(CryptoParseError::NoCipherText); }
|
|
|
|
Some(text) => text
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(KeyFileCrypto {
|
|
|
|
cipher_text: Bytes::from(cipher_text),
|
|
|
|
cipher_type: cipher_type,
|
|
|
|
kdf: kdf,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type Uuid = String;
|
|
|
|
|
2016-02-10 23:06:35 +01:00
|
|
|
struct KeyFileContent {
|
|
|
|
version: KeyFileVersion,
|
|
|
|
crypto: KeyFileCrypto,
|
|
|
|
id: Uuid
|
|
|
|
}
|
2016-02-11 01:32:44 +01:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
enum CryptoParseError {
|
2016-02-11 02:22:59 +01:00
|
|
|
NoCryptoVersion,
|
|
|
|
NoCipherText,
|
|
|
|
NoCipherType,
|
2016-02-11 01:32:44 +01:00
|
|
|
InvalidJsonFormat,
|
|
|
|
InvalidCryptoVersion,
|
|
|
|
InvalidKdfType(Mismatch<String>),
|
|
|
|
InvalidCipherType(Mismatch<String>),
|
2016-02-11 02:22:59 +01:00
|
|
|
NoInitialVector,
|
|
|
|
NoCipherParameters,
|
|
|
|
InvalidInitialVector(FromHexError),
|
2016-02-11 01:32:44 +01:00
|
|
|
NoKdfType,
|
|
|
|
NoKdfParams,
|
|
|
|
Scrypt(ScryptParseError),
|
2016-02-11 02:22:59 +01:00
|
|
|
KdfPbkdf2(Pbkdf2ParseError)
|
2016-02-11 01:32:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
enum KeyFileParseError {
|
|
|
|
InvalidVersion,
|
|
|
|
UnsupportedVersion(OutOfBounds<u64>),
|
|
|
|
InvalidJsonFormat,
|
|
|
|
NoIdentifier,
|
|
|
|
NoCryptoSection,
|
|
|
|
Crypto(CryptoParseError),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl KeyFileContent {
|
|
|
|
fn new(json: &Json) -> Result<KeyFileContent, KeyFileParseError> {
|
|
|
|
let as_object = match json.as_object() {
|
|
|
|
None => { return Err(KeyFileParseError::InvalidJsonFormat); },
|
|
|
|
Some(obj) => obj
|
|
|
|
};
|
|
|
|
|
|
|
|
let version = match as_object["version"].as_u64() {
|
|
|
|
None => { return Err(KeyFileParseError::InvalidVersion); },
|
|
|
|
Some(json_version) => {
|
|
|
|
if json_version <= 2 {
|
|
|
|
return Err(KeyFileParseError::UnsupportedVersion(OutOfBounds { min: Some(3), max: None, found: json_version }))
|
|
|
|
};
|
|
|
|
KeyFileVersion::V3(json_version)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let id = match as_object["id"].as_string() {
|
|
|
|
None => { return Err(KeyFileParseError::NoIdentifier); },
|
|
|
|
Some(id) => id
|
|
|
|
};
|
|
|
|
|
|
|
|
let crypto = match as_object.get("crypto") {
|
|
|
|
None => { return Err(KeyFileParseError::NoCryptoSection); }
|
|
|
|
Some(crypto_json) => match KeyFileCrypto::new(crypto_json) {
|
|
|
|
Ok(crypto) => crypto,
|
|
|
|
Err(crypto_error) => { return Err(KeyFileParseError::Crypto(crypto_error)); }
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(KeyFileContent {
|
|
|
|
version: version,
|
|
|
|
id: id.to_owned(),
|
|
|
|
crypto: crypto
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|