secret store separated from util (#1304)

* bump rust-crypto

* initial version of account provider utilizing secret store

* update lazy_static to latest version

* AccountProvider accounts method

* new AccountProvider tests in progress

* basic tests for new AccountProvider

* ethcore compiles with new account provider and secret store

* ethcore-rpc build now compiling with new AccountProvider

* most rpc tests passing with new accounts_provider

* fixed basic_authority tests

* fixed eth_transaction_count rpc test

* fixed mocked/eth.rs tests

* fixed personal tests

* fixed personal signer rpc tests

* removed warnings

* parity compiling fine with new sstore

* fixed import direction

* do not unlock temporarily when we have the password

* removed TODO in account import

* display warning on auto account import failure

* fixed compiling of ethstore on windows

* ethstore as a part of parity repo

* added ethkey
This commit is contained in:
Marek Kotewicz
2016-06-20 00:10:34 +02:00
committed by Gav Wood
parent 08522eec37
commit 6b074e8fb2
84 changed files with 4027 additions and 2682 deletions

View File

@@ -0,0 +1,43 @@
use json;
#[derive(Debug, PartialEq, Clone)]
pub struct Aes128Ctr {
pub iv: [u8; 16],
}
#[derive(Debug, PartialEq, Clone)]
pub enum Cipher {
Aes128Ctr(Aes128Ctr),
}
impl From<json::Aes128Ctr> for Aes128Ctr {
fn from(json: json::Aes128Ctr) -> Self {
Aes128Ctr {
iv: json.iv.into()
}
}
}
impl Into<json::Aes128Ctr> for Aes128Ctr {
fn into(self) -> json::Aes128Ctr {
json::Aes128Ctr {
iv: From::from(self.iv)
}
}
}
impl From<json::Cipher> for Cipher {
fn from(json: json::Cipher) -> Self {
match json {
json::Cipher::Aes128Ctr(params) => Cipher::Aes128Ctr(From::from(params)),
}
}
}
impl Into<json::Cipher> for Cipher {
fn into(self) -> json::Cipher {
match self {
Cipher::Aes128Ctr(params) => json::Cipher::Aes128Ctr(params.into()),
}
}
}

109
ethstore/src/account/kdf.rs Normal file
View File

@@ -0,0 +1,109 @@
use json;
#[derive(Debug, PartialEq, Clone)]
pub enum Prf {
HmacSha256,
}
#[derive(Debug, PartialEq, Clone)]
pub struct Pbkdf2 {
pub c: u32,
pub dklen: u32,
pub prf: Prf,
pub salt: [u8; 32],
}
#[derive(Debug, PartialEq, Clone)]
pub struct Scrypt {
pub dklen: u32,
pub p: u32,
pub n: u32,
pub r: u32,
pub salt: [u8; 32],
}
#[derive(Debug, PartialEq, Clone)]
pub enum Kdf {
Pbkdf2(Pbkdf2),
Scrypt(Scrypt),
}
impl From<json::Prf> for Prf {
fn from(json: json::Prf) -> Self {
match json {
json::Prf::HmacSha256 => Prf::HmacSha256,
}
}
}
impl Into<json::Prf> for Prf {
fn into(self) -> json::Prf {
match self {
Prf::HmacSha256 => json::Prf::HmacSha256,
}
}
}
impl From<json::Pbkdf2> for Pbkdf2 {
fn from(json: json::Pbkdf2) -> Self {
Pbkdf2 {
c: json.c,
dklen: json.dklen,
prf: From::from(json.prf),
salt: json.salt.into(),
}
}
}
impl Into<json::Pbkdf2> for Pbkdf2 {
fn into(self) -> json::Pbkdf2 {
json::Pbkdf2 {
c: self.c,
dklen: self.dklen,
prf: self.prf.into(),
salt: From::from(self.salt),
}
}
}
impl From<json::Scrypt> for Scrypt {
fn from(json: json::Scrypt) -> Self {
Scrypt {
dklen: json.dklen,
p: json.p,
n: json.n,
r: json.r,
salt: json.salt.into(),
}
}
}
impl Into<json::Scrypt> for Scrypt {
fn into(self) -> json::Scrypt {
json::Scrypt {
dklen: self.dklen,
p: self.p,
n: self.n,
r: self.r,
salt: From::from(self.salt),
}
}
}
impl From<json::Kdf> for Kdf {
fn from(json: json::Kdf) -> Self {
match json {
json::Kdf::Pbkdf2(params) => Kdf::Pbkdf2(From::from(params)),
json::Kdf::Scrypt(params) => Kdf::Scrypt(From::from(params)),
}
}
}
impl Into<json::Kdf> for Kdf {
fn into(self) -> json::Kdf {
match self {
Kdf::Pbkdf2(params) => json::Kdf::Pbkdf2(params.into()),
Kdf::Scrypt(params) => json::Kdf::Scrypt(params.into()),
}
}
}

View File

@@ -0,0 +1,9 @@
mod cipher;
mod kdf;
mod safe_account;
mod version;
pub use self::cipher::{Cipher, Aes128Ctr};
pub use self::kdf::{Kdf, Pbkdf2, Scrypt, Prf};
pub use self::safe_account::{SafeAccount, Crypto};
pub use self::version::Version;

View File

@@ -0,0 +1,200 @@
use std::ops::{Deref, DerefMut};
use ethkey::{KeyPair, sign, Address, Secret, Signature, Message};
use {json, Error, crypto};
use crypto::Keccak256;
use random::Random;
use account::{Version, Cipher, Kdf, Aes128Ctr, Pbkdf2, Prf};
#[derive(Debug, PartialEq, Clone)]
pub struct Crypto {
pub cipher: Cipher,
pub ciphertext: [u8; 32],
pub kdf: Kdf,
pub mac: [u8; 32],
}
#[derive(Debug, PartialEq, Clone)]
pub struct SafeAccount {
pub id: [u8; 16],
pub version: Version,
pub address: Address,
pub crypto: Crypto,
}
impl From<json::Crypto> for Crypto {
fn from(json: json::Crypto) -> Self {
Crypto {
cipher: From::from(json.cipher),
ciphertext: json.ciphertext.into(),
kdf: From::from(json.kdf),
mac: json.mac.into(),
}
}
}
impl Into<json::Crypto> for Crypto {
fn into(self) -> json::Crypto {
json::Crypto {
cipher: self.cipher.into(),
ciphertext: From::from(self.ciphertext),
kdf: self.kdf.into(),
mac: From::from(self.mac),
}
}
}
impl From<json::KeyFile> for SafeAccount {
fn from(json: json::KeyFile) -> Self {
SafeAccount {
id: json.id.into(),
version: From::from(json.version),
address: From::from(json.address), //json.address.into(),
crypto: From::from(json.crypto),
}
}
}
impl Into<json::KeyFile> for SafeAccount {
fn into(self) -> json::KeyFile {
json::KeyFile {
id: From::from(self.id),
version: self.version.into(),
address: self.address.into(), //From::from(self.address),
crypto: self.crypto.into(),
}
}
}
impl Crypto {
pub fn create(secret: &Secret, password: &str, iterations: u32) -> Self {
let salt: [u8; 32] = Random::random();
let iv: [u8; 16] = Random::random();
// two parts of derived key
// DK = [ DK[0..15] DK[16..31] ] = [derived_left_bits, derived_right_bits]
let (derived_left_bits, derived_right_bits) = crypto::derive_key_iterations(password, &salt, iterations);
let mut ciphertext = [0u8; 32];
// aes-128-ctr with initial vector of iv
crypto::aes::encrypt(&derived_left_bits, &iv, secret.deref(), &mut ciphertext);
// KECCAK(DK[16..31] ++ <ciphertext>), where DK[16..31] - derived_right_bits
let mac = crypto::derive_mac(&derived_right_bits, &ciphertext).keccak256();
Crypto {
cipher: Cipher::Aes128Ctr(Aes128Ctr {
iv: iv,
}),
ciphertext: ciphertext,
kdf: Kdf::Pbkdf2(Pbkdf2 {
dklen: crypto::KEY_LENGTH as u32,
salt: salt,
c: iterations,
prf: Prf::HmacSha256,
}),
mac: mac,
}
}
pub fn secret(&self, password: &str) -> Result<Secret, Error> {
let (derived_left_bits, derived_right_bits) = match self.kdf {
Kdf::Pbkdf2(ref params) => crypto::derive_key_iterations(password, &params.salt, params.c),
Kdf::Scrypt(ref params) => crypto::derive_key_scrypt(password, &params.salt, params.n, params.p, params.r),
};
let mac = crypto::derive_mac(&derived_right_bits, &self.ciphertext).keccak256();
if mac != self.mac {
return Err(Error::InvalidPassword);
}
let mut secret = Secret::default();
match self.cipher {
Cipher::Aes128Ctr(ref params) => {
crypto::aes::decrypt(&derived_left_bits, &params.iv, &self.ciphertext, secret.deref_mut())
},
}
Ok(secret)
}
}
impl SafeAccount {
pub fn create(keypair: &KeyPair, id: [u8; 16], password: &str, iterations: u32) -> Self {
SafeAccount {
id: id,
version: Version::V3,
crypto: Crypto::create(keypair.secret(), password, iterations),
address: keypair.address(),
}
}
pub fn sign(&self, password: &str, message: &Message) -> Result<Signature, Error> {
let secret = try!(self.crypto.secret(password));
sign(&secret, message).map_err(From::from)
}
pub fn change_password(&self, old_password: &str, new_password: &str, iterations: u32) -> Result<Self, Error> {
let secret = try!(self.crypto.secret(old_password));
let result = SafeAccount {
id: self.id.clone(),
version: self.version.clone(),
crypto: Crypto::create(&secret, new_password, iterations),
address: self.address.clone(),
};
Ok(result)
}
pub fn check_password(&self, password: &str) -> bool {
self.crypto.secret(password).is_ok()
}
}
#[cfg(test)]
mod tests {
use ethkey::{Generator, Random, verify, Message};
use super::{Crypto, SafeAccount};
#[test]
fn crypto_create() {
let keypair = Random.generate().unwrap();
let crypto = Crypto::create(keypair.secret(), "this is sparta", 10240);
let secret = crypto.secret("this is sparta").unwrap();
assert_eq!(keypair.secret(), &secret);
}
#[test]
#[should_panic]
fn crypto_invalid_password() {
let keypair = Random.generate().unwrap();
let crypto = Crypto::create(keypair.secret(), "this is sparta", 10240);
let _ = crypto.secret("this is sparta!").unwrap();
}
#[test]
fn sign_and_verify() {
let keypair = Random.generate().unwrap();
let password = "hello world";
let message = Message::default();
let account = SafeAccount::create(&keypair, [0u8; 16], password, 10240);
let signature = account.sign(password, &message).unwrap();
assert!(verify(keypair.public(), &signature, &message).unwrap());
}
#[test]
fn change_password() {
let keypair = Random.generate().unwrap();
let first_password = "hello world";
let sec_password = "this is sparta";
let i = 10240;
let message = Message::default();
let account = SafeAccount::create(&keypair, [0u8; 16], first_password, i);
let new_account = account.change_password(first_password, sec_password, i).unwrap();
assert!(account.sign(first_password, &message).is_ok());
assert!(account.sign(sec_password, &message).is_err());
assert!(new_account.sign(first_password, &message).is_err());
assert!(new_account.sign(sec_password, &message).is_ok());
}
}

View File

@@ -0,0 +1,22 @@
use json;
#[derive(Debug, PartialEq, Clone)]
pub enum Version {
V3,
}
impl From<json::Version> for Version {
fn from(json: json::Version) -> Self {
match json {
json::Version::V3 => Version::V3,
}
}
}
impl Into<json::Version> for Version {
fn into(self) -> json::Version {
match self {
Version::V3 => json::Version::V3,
}
}
}