// 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 . //! 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 { V3(u64) } 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 } #[derive(Debug)] enum Pbkdf2ParseError { InvalidParameter(String) } impl KdfPbkdf2Params { fn new(_json: &BTreeMap) -> Result { Ok(KdfPbkdf2Params{ dkLen: 0, salt: H256::zero(), c: 0, prf: Pbkdf2CryptoFunction::HMacSha256 }) } } #[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, } #[derive(Debug)] enum ScryptParseError { InvalidParameter(String) } impl KdfScryptParams { fn new(_json: &BTreeMap) -> Result { Ok(KdfScryptParams{ dkLen: 0, p: 0, n: 0, r: 0 }) } } enum Kdf { Pbkdf2(KdfPbkdf2Params), Scrypt(KdfScryptParams) } enum KeyFileKdf { Pbkdf2(KdfPbkdf2Params), Scrypt(KdfScryptParams) } struct KeyFileCrypto { cipher_type: CryptoCipherType, cipher_text: Bytes, kdf: KeyFileKdf, } impl KeyFileCrypto { fn new(json: &Json) -> Result { 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( match as_object["cipherparams"].as_object() { None => { return Err(CryptoParseError::NoCipherParameters); }, 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)); } } } ), Some(other_cipher_type) => { return Err(CryptoParseError::InvalidCipherType( Mismatch { expected: "aes-128-ctr".to_owned(), found: other_cipher_type.to_owned() })); } }; let kdf = match (as_object["kdf"].as_string(), as_object["kdfparams"].as_object()) { (None, _) => { return Err(CryptoParseError::NoKdfType); }, (Some("scrypt"), Some(kdf_params)) => match KdfScryptParams::new(kdf_params) { Err(scrypt_params_error) => { return Err(CryptoParseError::Scrypt(scrypt_params_error)); }, Ok(scrypt_params) => KeyFileKdf::Scrypt(scrypt_params) }, (Some("pbkdf2"), Some(kdf_params)) => match KdfPbkdf2Params::new(kdf_params) { Err(kdfPbkdf2_params_error) => { return Err(CryptoParseError::KdfPbkdf2(kdfPbkdf2_params_error)); }, Ok(kdfPbkdf2_params) => KeyFileKdf::Pbkdf2(kdfPbkdf2_params) }, (Some(other_kdf), _) => { return Err(CryptoParseError::InvalidKdfType( Mismatch { expected: "pbkdf2/scrypt".to_owned(), found: other_kdf.to_owned()})); } }; 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; struct KeyFileContent { version: KeyFileVersion, crypto: KeyFileCrypto, id: Uuid } #[derive(Debug)] enum CryptoParseError { NoCryptoVersion, NoCipherText, NoCipherType, InvalidJsonFormat, InvalidCryptoVersion, InvalidKdfType(Mismatch), InvalidCipherType(Mismatch), NoInitialVector, NoCipherParameters, InvalidInitialVector(FromHexError), NoKdfType, NoKdfParams, Scrypt(ScryptParseError), KdfPbkdf2(Pbkdf2ParseError) } #[derive(Debug)] enum KeyFileParseError { InvalidVersion, UnsupportedVersion(OutOfBounds), InvalidJsonFormat, NoIdentifier, NoCryptoSection, Crypto(CryptoParseError), } impl KeyFileContent { fn new(json: &Json) -> Result { 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 }) } }