Consolidate crypto functionality in ethcore-crypto
. (#8432)
* Consolidate crypto functionality in `ethcore-crypto`. - Move `ecdh`/`ecies` modules to `ethkey`. - Refactor `ethcore-crypto` to use file per module. - Replace `subtle` with `ethcore_crypto::is_equal`. - Add `aes_gcm` module to `ethcore-crypto`. * Rename `aes::{encrypt,decrypt,decrypt_cbc}` ... ... to `aes::{encrypt_128_ctr,decrypt_128_ctr,decrypt_128_cbc}`.
This commit is contained in:
parent
a4c7843a07
commit
e30839e85f
26
Cargo.lock
generated
26
Cargo.lock
generated
@ -509,6 +509,7 @@ dependencies = [
|
||||
"ethash 1.12.0",
|
||||
"ethcore-bloom-journal 0.1.0",
|
||||
"ethcore-bytes 0.1.0",
|
||||
"ethcore-crypto 0.1.0",
|
||||
"ethcore-io 1.12.0",
|
||||
"ethcore-logger 1.12.0",
|
||||
"ethcore-miner 1.12.0",
|
||||
@ -545,7 +546,6 @@ dependencies = [
|
||||
"rlp 0.2.1",
|
||||
"rlp_compress 0.1.0",
|
||||
"rlp_derive 0.1.0",
|
||||
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"snappy 0.1.0 (git+https://github.com/paritytech/rust-snappy)",
|
||||
"stats 0.1.0",
|
||||
@ -576,11 +576,10 @@ version = "0.1.0"
|
||||
name = "ethcore-crypto"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"eth-secp256k1 0.5.7 (git+https://github.com/paritytech/rust-secp256k1)",
|
||||
"ethereum-types 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethkey 0.3.0",
|
||||
"quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.12.1 (git+https://github.com/paritytech/ring)",
|
||||
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"subtle 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tiny-keccak 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -925,13 +924,14 @@ dependencies = [
|
||||
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"edit-distance 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"eth-secp256k1 0.5.7 (git+https://github.com/paritytech/rust-secp256k1)",
|
||||
"ethcore-crypto 0.1.0",
|
||||
"ethereum-types 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mem 0.1.0",
|
||||
"parity-wordlist 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tiny-keccak 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@ -966,13 +966,11 @@ dependencies = [
|
||||
"parity-wordlist 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"subtle 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tiny-keccak 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2197,7 +2195,6 @@ dependencies = [
|
||||
"pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rlp 0.2.1",
|
||||
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2362,7 +2359,6 @@ dependencies = [
|
||||
"ordered-float 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.12.1 (git+https://github.com/paritytech/ring)",
|
||||
"rlp 0.2.1",
|
||||
"serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2621,6 +2617,11 @@ dependencies = [
|
||||
"quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "0.5.1"
|
||||
@ -3035,11 +3036,6 @@ name = "strsim"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.13.1"
|
||||
@ -3927,6 +3923,7 @@ dependencies = [
|
||||
"checksum quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18c45c4854d6d1cf5d531db97c75880feb91c958b0720f4ec1057135fec358b3"
|
||||
"checksum quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9e25fa23c044c1803f43ca59c98dac608976dd04ce799411edd58ece776d4"
|
||||
"checksum quasi_macros 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29cec87bc2816766d7e4168302d505dd06b0a825aed41b00633d296e922e02dd"
|
||||
"checksum quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eda5fe9b71976e62bc81b781206aaa076401769b2143379d3eb2118388babac4"
|
||||
"checksum quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7b0ff51282f28dc1b53fd154298feaa2e77c5ea0dba68e1fd8b03b72fbe13d2a"
|
||||
"checksum rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)" = "512870020642bb8c221bf68baa1b2573da814f6ccfe5c9699b1c303047abe9b1"
|
||||
"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5"
|
||||
@ -3973,7 +3970,6 @@ dependencies = [
|
||||
"checksum snappy-sys 0.1.0 (git+https://github.com/paritytech/rust-snappy)" = "<none>"
|
||||
"checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b"
|
||||
"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
|
||||
"checksum subtle 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc7f6353c2ee5407358d063a14cccc1630804527090a6fb5a9489ce4924280fb"
|
||||
"checksum syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "91b52877572087400e83d24b9178488541e3d535259e04ff17a63df1e5ceff59"
|
||||
"checksum syntex 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a8f5e3aaa79319573d19938ea38d068056b826db9883a5d47f86c1cecc688f0e"
|
||||
"checksum syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "867cc5c2d7140ae7eaad2ae9e8bf39cb18a67ca651b7834f88d46ca98faadb9c"
|
||||
|
@ -20,6 +20,7 @@ fetch = { path = "../util/fetch" }
|
||||
hashdb = { path = "../util/hashdb" }
|
||||
memorydb = { path = "../util/memorydb" }
|
||||
patricia-trie = { path = "../util/patricia_trie" }
|
||||
ethcore-crypto = { path = "crypto" }
|
||||
error-chain = { version = "0.11", default-features = false }
|
||||
ethcore-io = { path = "../util/io" }
|
||||
ethcore-logger = { path = "../logger" }
|
||||
@ -56,7 +57,6 @@ util-error = { path = "../util/error" }
|
||||
snappy = { git = "https://github.com/paritytech/rust-snappy" }
|
||||
stop-guard = { path = "../util/stop-guard" }
|
||||
macros = { path = "../util/macros" }
|
||||
rust-crypto = "0.2.34"
|
||||
rustc-hex = "1.0"
|
||||
stats = { path = "../util/stats" }
|
||||
trace-time = { path = "../util/trace-time" }
|
||||
|
@ -20,7 +20,7 @@ extern crate test;
|
||||
extern crate ethcore_util as util;
|
||||
extern crate rand;
|
||||
extern crate bn;
|
||||
extern crate crypto;
|
||||
extern crate ethcore_crypto;
|
||||
extern crate ethkey;
|
||||
extern crate rustc_hex;
|
||||
extern crate ethcore_bigint;
|
||||
@ -61,16 +61,13 @@ fn bn_128_mul(b: &mut Bencher) {
|
||||
|
||||
#[bench]
|
||||
fn sha256(b: &mut Bencher) {
|
||||
use crypto::sha2::Sha256;
|
||||
use crypto::digest::Digest;
|
||||
use ethcore_crypto::digest::sha256;
|
||||
|
||||
let mut input: [u8; 256] = [0; 256];
|
||||
let mut out = [0; 32];
|
||||
|
||||
b.iter(|| {
|
||||
let mut sha = Sha256::new();
|
||||
sha.input(&input);
|
||||
sha.result(&mut input[0..32]);
|
||||
sha256(&input);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -4,13 +4,9 @@ version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
ethereum-types = "0.3"
|
||||
quick-error = "1.2"
|
||||
ring = "0.12"
|
||||
rust-crypto = "0.2.36"
|
||||
tiny-keccak = "1.3"
|
||||
eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1", optional = true }
|
||||
ethkey = { path = "../../ethkey", optional = true }
|
||||
ethereum-types = "0.3"
|
||||
subtle = "0.5"
|
||||
|
||||
[features]
|
||||
default = ["secp256k1"]
|
||||
secp256k1 = ["eth-secp256k1", "ethkey"]
|
||||
|
54
ethcore/crypto/src/aes.rs
Normal file
54
ethcore/crypto/src/aes.rs
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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/>.
|
||||
|
||||
use error::SymmError;
|
||||
use rcrypto::blockmodes::{CtrMode, CbcDecryptor, PkcsPadding};
|
||||
use rcrypto::aessafe::{AesSafe128Encryptor, AesSafe128Decryptor};
|
||||
use rcrypto::symmetriccipher::{Encryptor, Decryptor};
|
||||
use rcrypto::buffer::{RefReadBuffer, RefWriteBuffer, WriteBuffer};
|
||||
|
||||
/// Encrypt a message (CTR mode).
|
||||
///
|
||||
/// Key (`k`) length and initialisation vector (`iv`) length have to be 16 bytes each.
|
||||
/// An error is returned if the input lengths are invalid.
|
||||
pub fn encrypt_128_ctr(k: &[u8], iv: &[u8], plain: &[u8], dest: &mut [u8]) -> Result<(), SymmError> {
|
||||
let mut encryptor = CtrMode::new(AesSafe128Encryptor::new(k), iv.to_vec());
|
||||
encryptor.encrypt(&mut RefReadBuffer::new(plain), &mut RefWriteBuffer::new(dest), true)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Decrypt a message (CTR mode).
|
||||
///
|
||||
/// Key (`k`) length and initialisation vector (`iv`) length have to be 16 bytes each.
|
||||
/// An error is returned if the input lengths are invalid.
|
||||
pub fn decrypt_128_ctr(k: &[u8], iv: &[u8], encrypted: &[u8], dest: &mut [u8]) -> Result<(), SymmError> {
|
||||
let mut encryptor = CtrMode::new(AesSafe128Encryptor::new(k), iv.to_vec());
|
||||
encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut RefWriteBuffer::new(dest), true)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Decrypt a message (CBC mode).
|
||||
///
|
||||
/// Key (`k`) length and initialisation vector (`iv`) length have to be 16 bytes each.
|
||||
/// An error is returned if the input lengths are invalid.
|
||||
pub fn decrypt_128_cbc(k: &[u8], iv: &[u8], encrypted: &[u8], dest: &mut [u8]) -> Result<usize, SymmError> {
|
||||
let mut encryptor = CbcDecryptor::new(AesSafe128Decryptor::new(k), PkcsPadding, iv.to_vec());
|
||||
let len = dest.len();
|
||||
let mut buffer = RefWriteBuffer::new(dest);
|
||||
encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut buffer, true)?;
|
||||
Ok(len - buffer.remaining())
|
||||
}
|
||||
|
199
ethcore/crypto/src/aes_gcm.rs
Normal file
199
ethcore/crypto/src/aes_gcm.rs
Normal file
@ -0,0 +1,199 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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/>.
|
||||
|
||||
use error::SymmError;
|
||||
use ring;
|
||||
|
||||
enum Mode { Aes128Gcm, Aes256Gcm }
|
||||
|
||||
/// AES GCM encryptor.
|
||||
pub struct Encryptor<'a> {
|
||||
mode: Mode,
|
||||
key: ring::aead::SealingKey,
|
||||
ad: &'a [u8],
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl<'a> Encryptor<'a> {
|
||||
pub fn aes_128_gcm(key: &[u8; 16]) -> Result<Encryptor<'a>, SymmError> {
|
||||
let sk = ring::aead::SealingKey::new(&ring::aead::AES_128_GCM, key)?;
|
||||
Ok(Encryptor {
|
||||
mode: Mode::Aes128Gcm,
|
||||
key: sk,
|
||||
ad: &[],
|
||||
offset: 0,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn aes_256_gcm(key: &[u8; 32]) -> Result<Encryptor<'a>, SymmError> {
|
||||
let sk = ring::aead::SealingKey::new(&ring::aead::AES_256_GCM, key)?;
|
||||
Ok(Encryptor {
|
||||
mode: Mode::Aes256Gcm,
|
||||
key: sk,
|
||||
ad: &[],
|
||||
offset: 0,
|
||||
})
|
||||
}
|
||||
|
||||
/// Optional associated data which is not encrypted but authenticated.
|
||||
pub fn associate(&mut self, data: &'a [u8]) -> &mut Self {
|
||||
self.ad = data;
|
||||
self
|
||||
}
|
||||
|
||||
/// Optional offset value. Only the slice `[offset..]` will be encrypted.
|
||||
pub fn offset(&mut self, off: usize) -> &mut Self {
|
||||
self.offset = off;
|
||||
self
|
||||
}
|
||||
|
||||
/// Please note that the pair (key, nonce) must never be reused. Using random nonces
|
||||
/// limits the number of messages encrypted with the same key to 2^32 (cf. [[1]])
|
||||
///
|
||||
/// [1]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
|
||||
pub fn encrypt(&self, nonce: &[u8; 12], mut data: Vec<u8>) -> Result<Vec<u8>, SymmError> {
|
||||
if self.offset > data.len() {
|
||||
return Err(SymmError::offset_error(self.offset))
|
||||
}
|
||||
let tag_len = match self.mode {
|
||||
Mode::Aes128Gcm => ring::aead::AES_128_GCM.tag_len(),
|
||||
Mode::Aes256Gcm => ring::aead::AES_256_GCM.tag_len(),
|
||||
};
|
||||
data.extend(::std::iter::repeat(0).take(tag_len));
|
||||
let len = ring::aead::seal_in_place(&self.key, nonce, self.ad, &mut data[self.offset ..], tag_len)?;
|
||||
data.truncate(self.offset + len);
|
||||
Ok(data)
|
||||
}
|
||||
}
|
||||
|
||||
/// AES GCM decryptor.
|
||||
pub struct Decryptor<'a> {
|
||||
key: ring::aead::OpeningKey,
|
||||
ad: &'a [u8],
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl<'a> Decryptor<'a> {
|
||||
pub fn aes_128_gcm(key: &[u8; 16]) -> Result<Decryptor<'a>, SymmError> {
|
||||
let ok = ring::aead::OpeningKey::new(&ring::aead::AES_128_GCM, key)?;
|
||||
Ok(Decryptor {
|
||||
key: ok,
|
||||
ad: &[],
|
||||
offset: 0,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn aes_256_gcm(key: &[u8; 32]) -> Result<Decryptor<'a>, SymmError> {
|
||||
let ok = ring::aead::OpeningKey::new(&ring::aead::AES_256_GCM, key)?;
|
||||
Ok(Decryptor {
|
||||
key: ok,
|
||||
ad: &[],
|
||||
offset: 0,
|
||||
})
|
||||
}
|
||||
|
||||
/// Optional associated data which is not encrypted but authenticated.
|
||||
pub fn associate(&mut self, data: &'a [u8]) -> &mut Self {
|
||||
self.ad = data;
|
||||
self
|
||||
}
|
||||
|
||||
/// Optional offset value. Only the slice `[offset..]` will be decrypted.
|
||||
pub fn offset(&mut self, off: usize) -> &mut Self {
|
||||
self.offset = off;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn decrypt(&self, nonce: &[u8; 12], mut data: Vec<u8>) -> Result<Vec<u8>, SymmError> {
|
||||
if self.offset > data.len() {
|
||||
return Err(SymmError::offset_error(self.offset))
|
||||
}
|
||||
let len = ring::aead::open_in_place(&self.key, nonce, self.ad, 0, &mut data[self.offset ..])?.len();
|
||||
data.truncate(self.offset + len);
|
||||
Ok(data)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Encryptor, Decryptor};
|
||||
|
||||
#[test]
|
||||
fn aes_gcm_128() {
|
||||
let secret = b"1234567890123456";
|
||||
let nonce = b"123456789012";
|
||||
let message = b"So many books, so little time";
|
||||
|
||||
let ciphertext = Encryptor::aes_128_gcm(secret)
|
||||
.unwrap()
|
||||
.encrypt(nonce, message.to_vec())
|
||||
.unwrap();
|
||||
|
||||
assert!(ciphertext != message);
|
||||
|
||||
let plaintext = Decryptor::aes_128_gcm(secret)
|
||||
.unwrap()
|
||||
.decrypt(nonce, ciphertext)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(plaintext, message)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aes_gcm_256() {
|
||||
let secret = b"12345678901234567890123456789012";
|
||||
let nonce = b"123456789012";
|
||||
let message = b"So many books, so little time";
|
||||
|
||||
let ciphertext = Encryptor::aes_256_gcm(secret)
|
||||
.unwrap()
|
||||
.encrypt(nonce, message.to_vec())
|
||||
.unwrap();
|
||||
|
||||
assert!(ciphertext != message);
|
||||
|
||||
let plaintext = Decryptor::aes_256_gcm(secret)
|
||||
.unwrap()
|
||||
.decrypt(nonce, ciphertext)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(plaintext, message)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aes_gcm_256_offset() {
|
||||
let secret = b"12345678901234567890123456789012";
|
||||
let nonce = b"123456789012";
|
||||
let message = b"prefix data; So many books, so little time";
|
||||
|
||||
let ciphertext = Encryptor::aes_256_gcm(secret)
|
||||
.unwrap()
|
||||
.offset(13) // length of "prefix data; "
|
||||
.encrypt(nonce, message.to_vec())
|
||||
.unwrap();
|
||||
|
||||
assert!(ciphertext != &message[..]);
|
||||
|
||||
let plaintext = Decryptor::aes_256_gcm(secret)
|
||||
.unwrap()
|
||||
.offset(13) // length of "prefix data; "
|
||||
.decrypt(nonce, ciphertext)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(plaintext, &message[..])
|
||||
}
|
||||
}
|
||||
|
109
ethcore/crypto/src/digest.rs
Normal file
109
ethcore/crypto/src/digest.rs
Normal file
@ -0,0 +1,109 @@
|
||||
// Copyright 2018 Parity Technologies (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/>.
|
||||
|
||||
use rcrypto::ripemd160;
|
||||
use ring::digest::{self, Context, SHA256, SHA512};
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Deref;
|
||||
|
||||
/// The message digest.
|
||||
pub struct Digest<T>(InnerDigest, PhantomData<T>);
|
||||
|
||||
enum InnerDigest {
|
||||
Ring(digest::Digest),
|
||||
Ripemd160([u8; 20]),
|
||||
}
|
||||
|
||||
impl<T> Deref for Digest<T> {
|
||||
type Target = [u8];
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self.0 {
|
||||
InnerDigest::Ring(ref d) => d.as_ref(),
|
||||
InnerDigest::Ripemd160(ref d) => &d[..]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Single-step sha256 digest computation.
|
||||
pub fn sha256(data: &[u8]) -> Digest<Sha256> {
|
||||
Digest(InnerDigest::Ring(digest::digest(&SHA256, data)), PhantomData)
|
||||
}
|
||||
|
||||
/// Single-step sha512 digest computation.
|
||||
pub fn sha512(data: &[u8]) -> Digest<Sha512> {
|
||||
Digest(InnerDigest::Ring(digest::digest(&SHA512, data)), PhantomData)
|
||||
}
|
||||
|
||||
/// Single-step ripemd160 digest computation.
|
||||
pub fn ripemd160(data: &[u8]) -> Digest<Ripemd160> {
|
||||
let mut hasher = Hasher::ripemd160();
|
||||
hasher.update(data);
|
||||
hasher.finish()
|
||||
}
|
||||
|
||||
pub enum Sha256 {}
|
||||
pub enum Sha512 {}
|
||||
pub enum Ripemd160 {}
|
||||
|
||||
/// Stateful digest computation.
|
||||
pub struct Hasher<T>(Inner, PhantomData<T>);
|
||||
|
||||
enum Inner {
|
||||
Ring(Context),
|
||||
Ripemd160(ripemd160::Ripemd160)
|
||||
}
|
||||
|
||||
impl Hasher<Sha256> {
|
||||
pub fn sha256() -> Hasher<Sha256> {
|
||||
Hasher(Inner::Ring(Context::new(&SHA256)), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hasher<Sha512> {
|
||||
pub fn sha512() -> Hasher<Sha512> {
|
||||
Hasher(Inner::Ring(Context::new(&SHA512)), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hasher<Ripemd160> {
|
||||
pub fn ripemd160() -> Hasher<Ripemd160> {
|
||||
Hasher(Inner::Ripemd160(ripemd160::Ripemd160::new()), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Hasher<T> {
|
||||
pub fn update(&mut self, data: &[u8]) {
|
||||
match self.0 {
|
||||
Inner::Ring(ref mut ctx) => ctx.update(data),
|
||||
Inner::Ripemd160(ref mut ctx) => {
|
||||
use rcrypto::digest::Digest;
|
||||
ctx.input(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finish(self) -> Digest<T> {
|
||||
match self.0 {
|
||||
Inner::Ring(ctx) => Digest(InnerDigest::Ring(ctx.finish()), PhantomData),
|
||||
Inner::Ripemd160(mut ctx) => {
|
||||
use rcrypto::digest::Digest;
|
||||
let mut d = [0; 20];
|
||||
ctx.result(&mut d);
|
||||
Digest(InnerDigest::Ripemd160(d), PhantomData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
83
ethcore/crypto/src/error.rs
Normal file
83
ethcore/crypto/src/error.rs
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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/>.
|
||||
|
||||
use rcrypto;
|
||||
use ring;
|
||||
|
||||
quick_error! {
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Scrypt(e: ScryptError) {
|
||||
cause(e)
|
||||
from()
|
||||
}
|
||||
Symm(e: SymmError) {
|
||||
cause(e)
|
||||
from()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
quick_error! {
|
||||
#[derive(Debug)]
|
||||
pub enum ScryptError {
|
||||
// log(N) < r / 16
|
||||
InvalidN {
|
||||
display("Invalid N argument of the scrypt encryption")
|
||||
}
|
||||
// p <= (2^31-1 * 32)/(128 * r)
|
||||
InvalidP {
|
||||
display("Invalid p argument of the scrypt encryption")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
quick_error! {
|
||||
#[derive(Debug)]
|
||||
pub enum SymmError wraps PrivSymmErr {
|
||||
RustCrypto(e: rcrypto::symmetriccipher::SymmetricCipherError) {
|
||||
display("symmetric crypto error")
|
||||
from()
|
||||
}
|
||||
Ring(e: ring::error::Unspecified) {
|
||||
display("symmetric crypto error")
|
||||
cause(e)
|
||||
from()
|
||||
}
|
||||
Offset(x: usize) {
|
||||
display("offset {} greater than slice length", x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SymmError {
|
||||
pub(crate) fn offset_error(x: usize) -> SymmError {
|
||||
SymmError(PrivSymmErr::Offset(x))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ring::error::Unspecified> for SymmError {
|
||||
fn from(e: ring::error::Unspecified) -> SymmError {
|
||||
SymmError(PrivSymmErr::Ring(e))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<rcrypto::symmetriccipher::SymmetricCipherError> for SymmError {
|
||||
fn from(e: rcrypto::symmetriccipher::SymmetricCipherError) -> SymmError {
|
||||
SymmError(PrivSymmErr::RustCrypto(e))
|
||||
}
|
||||
}
|
||||
|
89
ethcore/crypto/src/hmac.rs
Normal file
89
ethcore/crypto/src/hmac.rs
Normal file
@ -0,0 +1,89 @@
|
||||
// Copyright 2018 Parity Technologies (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/>.
|
||||
|
||||
use digest;
|
||||
use ring::digest::{SHA256, SHA512};
|
||||
use ring::hmac::{self, SigningContext};
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Deref;
|
||||
|
||||
/// HMAC signature.
|
||||
pub struct Signature<T>(hmac::Signature, PhantomData<T>);
|
||||
|
||||
impl<T> Deref for Signature<T> {
|
||||
type Target = [u8];
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
/// HMAC signing key.
|
||||
pub struct SigKey<T>(hmac::SigningKey, PhantomData<T>);
|
||||
|
||||
impl SigKey<digest::Sha256> {
|
||||
pub fn sha256(key: &[u8]) -> SigKey<digest::Sha256> {
|
||||
SigKey(hmac::SigningKey::new(&SHA256, key), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl SigKey<digest::Sha512> {
|
||||
pub fn sha512(key: &[u8]) -> SigKey<digest::Sha512> {
|
||||
SigKey(hmac::SigningKey::new(&SHA512, key), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute HMAC signature of `data`.
|
||||
pub fn sign<T>(k: &SigKey<T>, data: &[u8]) -> Signature<T> {
|
||||
Signature(hmac::sign(&k.0, data), PhantomData)
|
||||
}
|
||||
|
||||
/// Stateful HMAC computation.
|
||||
pub struct Signer<T>(SigningContext, PhantomData<T>);
|
||||
|
||||
impl<T> Signer<T> {
|
||||
pub fn with(key: &SigKey<T>) -> Signer<T> {
|
||||
Signer(hmac::SigningContext::with_key(&key.0), PhantomData)
|
||||
}
|
||||
|
||||
pub fn update(&mut self, data: &[u8]) {
|
||||
self.0.update(data)
|
||||
}
|
||||
|
||||
pub fn sign(self) -> Signature<T> {
|
||||
Signature(self.0.sign(), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
/// HMAC signature verification key.
|
||||
pub struct VerifyKey<T>(hmac::VerificationKey, PhantomData<T>);
|
||||
|
||||
impl VerifyKey<digest::Sha256> {
|
||||
pub fn sha256(key: &[u8]) -> VerifyKey<digest::Sha256> {
|
||||
VerifyKey(hmac::VerificationKey::new(&SHA256, key), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl VerifyKey<digest::Sha512> {
|
||||
pub fn sha512(key: &[u8]) -> VerifyKey<digest::Sha512> {
|
||||
VerifyKey(hmac::VerificationKey::new(&SHA512, key), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify HMAC signature of `data`.
|
||||
pub fn verify<T>(k: &VerifyKey<T>, data: &[u8], sig: &[u8]) -> bool {
|
||||
hmac::verify(&k.0, data, sig).is_ok()
|
||||
}
|
||||
|
@ -18,23 +18,22 @@
|
||||
|
||||
extern crate crypto as rcrypto;
|
||||
extern crate ethereum_types;
|
||||
extern crate subtle;
|
||||
#[macro_use]
|
||||
extern crate quick_error;
|
||||
extern crate ring;
|
||||
extern crate tiny_keccak;
|
||||
|
||||
#[cfg(feature = "secp256k1")]
|
||||
extern crate secp256k1;
|
||||
#[cfg(feature = "secp256k1")]
|
||||
extern crate ethkey;
|
||||
pub mod aes;
|
||||
pub mod aes_gcm;
|
||||
pub mod error;
|
||||
pub mod scrypt;
|
||||
pub mod digest;
|
||||
pub mod hmac;
|
||||
pub mod pbkdf2;
|
||||
|
||||
pub use error::Error;
|
||||
|
||||
use std::fmt;
|
||||
use tiny_keccak::Keccak;
|
||||
use rcrypto::pbkdf2::pbkdf2;
|
||||
use rcrypto::scrypt::{scrypt, ScryptParams};
|
||||
use rcrypto::sha2::Sha256;
|
||||
use rcrypto::hmac::Hmac;
|
||||
|
||||
#[cfg(feature = "secp256k1")]
|
||||
use secp256k1::Error as SecpError;
|
||||
|
||||
pub const KEY_LENGTH: usize = 32;
|
||||
pub const KEY_ITERATIONS: usize = 10240;
|
||||
@ -43,65 +42,6 @@ pub const KEY_LENGTH_AES: usize = KEY_LENGTH / 2;
|
||||
/// Default authenticated data to use (in RPC).
|
||||
pub const DEFAULT_MAC: [u8; 2] = [0, 0];
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum ScryptError {
|
||||
// log(N) < r / 16
|
||||
InvalidN,
|
||||
// p <= (2^31-1 * 32)/(128 * r)
|
||||
InvalidP,
|
||||
}
|
||||
|
||||
impl fmt::Display for ScryptError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
let s = match *self {
|
||||
ScryptError::InvalidN => "Invalid N argument of the scrypt encryption" ,
|
||||
ScryptError::InvalidP => "Invalid p argument of the scrypt encryption",
|
||||
};
|
||||
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum Error {
|
||||
#[cfg(feature = "secp256k1")]
|
||||
Secp(SecpError),
|
||||
Scrypt(ScryptError),
|
||||
InvalidMessage,
|
||||
}
|
||||
|
||||
impl From<ScryptError> for Error {
|
||||
fn from(err: ScryptError) -> Self {
|
||||
Error::Scrypt(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
let s = match *self {
|
||||
#[cfg(feature = "secp256k1")]
|
||||
Error::Secp(ref err) => err.to_string(),
|
||||
Error::Scrypt(ref err) => err.to_string(),
|
||||
Error::InvalidMessage => "Invalid message".into(),
|
||||
};
|
||||
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<String> for Error {
|
||||
fn into(self) -> String {
|
||||
format!("{}", self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "secp256k1")]
|
||||
impl From<SecpError> for Error {
|
||||
fn from(e: SecpError) -> Self {
|
||||
Error::Secp(e)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Keccak256<T> {
|
||||
fn keccak256(&self) -> T where T: Sized;
|
||||
}
|
||||
@ -117,33 +57,13 @@ impl<T> Keccak256<[u8; 32]> for T where T: AsRef<[u8]> {
|
||||
}
|
||||
|
||||
pub fn derive_key_iterations(password: &str, salt: &[u8; 32], c: u32) -> (Vec<u8>, Vec<u8>) {
|
||||
let mut h_mac = Hmac::new(Sha256::new(), password.as_bytes());
|
||||
let mut derived_key = vec![0u8; KEY_LENGTH];
|
||||
pbkdf2(&mut h_mac, salt, c, &mut derived_key);
|
||||
let mut derived_key = [0u8; KEY_LENGTH];
|
||||
pbkdf2::sha256(c, pbkdf2::Salt(salt), pbkdf2::Secret(password.as_bytes()), &mut derived_key);
|
||||
let derived_right_bits = &derived_key[0..KEY_LENGTH_AES];
|
||||
let derived_left_bits = &derived_key[KEY_LENGTH_AES..KEY_LENGTH];
|
||||
(derived_right_bits.to_vec(), derived_left_bits.to_vec())
|
||||
}
|
||||
|
||||
pub fn derive_key_scrypt(password: &str, salt: &[u8; 32], n: u32, p: u32, r: u32) -> Result<(Vec<u8>, Vec<u8>), Error> {
|
||||
// sanity checks
|
||||
let log_n = (32 - n.leading_zeros() - 1) as u8;
|
||||
if log_n as u32 >= r * 16 {
|
||||
return Err(Error::Scrypt(ScryptError::InvalidN));
|
||||
}
|
||||
|
||||
if p as u64 > ((u32::max_value() as u64 - 1) * 32)/(128 * (r as u64)) {
|
||||
return Err(Error::Scrypt(ScryptError::InvalidP));
|
||||
}
|
||||
|
||||
let mut derived_key = vec![0u8; KEY_LENGTH];
|
||||
let scrypt_params = ScryptParams::new(log_n, r, p);
|
||||
scrypt(password.as_bytes(), salt, &scrypt_params, &mut derived_key);
|
||||
let derived_right_bits = &derived_key[0..KEY_LENGTH_AES];
|
||||
let derived_left_bits = &derived_key[KEY_LENGTH_AES..KEY_LENGTH];
|
||||
Ok((derived_right_bits.to_vec(), derived_left_bits.to_vec()))
|
||||
}
|
||||
|
||||
pub fn derive_mac(derived_left_bits: &[u8], cipher_text: &[u8]) -> Vec<u8> {
|
||||
let mut mac = vec![0u8; KEY_LENGTH_AES + cipher_text.len()];
|
||||
mac[0..KEY_LENGTH_AES].copy_from_slice(derived_left_bits);
|
||||
@ -151,194 +71,7 @@ pub fn derive_mac(derived_left_bits: &[u8], cipher_text: &[u8]) -> Vec<u8> {
|
||||
mac
|
||||
}
|
||||
|
||||
/// AES encryption
|
||||
pub mod aes {
|
||||
use rcrypto::blockmodes::{CtrMode, CbcDecryptor, PkcsPadding};
|
||||
use rcrypto::aessafe::{AesSafe128Encryptor, AesSafe128Decryptor};
|
||||
use rcrypto::symmetriccipher::{Encryptor, Decryptor, SymmetricCipherError};
|
||||
use rcrypto::buffer::{RefReadBuffer, RefWriteBuffer, WriteBuffer};
|
||||
|
||||
/// Encrypt a message (CTR mode)
|
||||
pub fn encrypt(k: &[u8], iv: &[u8], plain: &[u8], dest: &mut [u8]) {
|
||||
let mut encryptor = CtrMode::new(AesSafe128Encryptor::new(k), iv.to_vec());
|
||||
encryptor.encrypt(&mut RefReadBuffer::new(plain), &mut RefWriteBuffer::new(dest), true).expect("Invalid length or padding");
|
||||
}
|
||||
|
||||
/// Decrypt a message (CTR mode)
|
||||
pub fn decrypt(k: &[u8], iv: &[u8], encrypted: &[u8], dest: &mut [u8]) {
|
||||
let mut encryptor = CtrMode::new(AesSafe128Encryptor::new(k), iv.to_vec());
|
||||
encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut RefWriteBuffer::new(dest), true).expect("Invalid length or padding");
|
||||
}
|
||||
|
||||
|
||||
/// Decrypt a message using cbc mode
|
||||
pub fn decrypt_cbc(k: &[u8], iv: &[u8], encrypted: &[u8], dest: &mut [u8]) -> Result<usize, SymmetricCipherError> {
|
||||
let mut encryptor = CbcDecryptor::new(AesSafe128Decryptor::new(k), PkcsPadding, iv.to_vec());
|
||||
let len = dest.len();
|
||||
let mut buffer = RefWriteBuffer::new(dest);
|
||||
encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut buffer, true)?;
|
||||
Ok(len - buffer.remaining())
|
||||
}
|
||||
}
|
||||
|
||||
/// ECDH functions
|
||||
#[cfg(feature = "secp256k1")]
|
||||
pub mod ecdh {
|
||||
use secp256k1::{ecdh, key, Error as SecpError};
|
||||
use ethkey::{Secret, Public, SECP256K1};
|
||||
use Error;
|
||||
|
||||
/// Agree on a shared secret
|
||||
pub fn agree(secret: &Secret, public: &Public) -> Result<Secret, Error> {
|
||||
let context = &SECP256K1;
|
||||
let pdata = {
|
||||
let mut temp = [4u8; 65];
|
||||
(&mut temp[1..65]).copy_from_slice(&public[0..64]);
|
||||
temp
|
||||
};
|
||||
|
||||
let publ = key::PublicKey::from_slice(context, &pdata)?;
|
||||
let sec = key::SecretKey::from_slice(context, &secret)?;
|
||||
let shared = ecdh::SharedSecret::new_raw(context, &publ, &sec);
|
||||
|
||||
Secret::from_unsafe_slice(&shared[0..32])
|
||||
.map_err(|_| Error::Secp(SecpError::InvalidSecretKey))
|
||||
}
|
||||
}
|
||||
|
||||
/// ECIES function
|
||||
#[cfg(feature = "secp256k1")]
|
||||
pub mod ecies {
|
||||
use rcrypto::digest::Digest;
|
||||
use rcrypto::sha2::Sha256;
|
||||
use rcrypto::hmac::Hmac;
|
||||
use rcrypto::mac::Mac;
|
||||
use ethereum_types::H128;
|
||||
use ethkey::{Random, Generator, Public, Secret};
|
||||
use {Error, ecdh, aes};
|
||||
|
||||
/// Encrypt a message with a public key, writing an HMAC covering both
|
||||
/// the plaintext and authenticated data.
|
||||
///
|
||||
/// Authenticated data may be empty.
|
||||
pub fn encrypt(public: &Public, auth_data: &[u8], plain: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
let r = Random.generate()
|
||||
.expect("context known to have key-generation capabilities; qed");
|
||||
|
||||
let z = ecdh::agree(r.secret(), public)?;
|
||||
let mut key = [0u8; 32];
|
||||
let mut mkey = [0u8; 32];
|
||||
kdf(&z, &[0u8; 0], &mut key);
|
||||
let mut hasher = Sha256::new();
|
||||
let mkey_material = &key[16..32];
|
||||
hasher.input(mkey_material);
|
||||
hasher.result(&mut mkey);
|
||||
let ekey = &key[0..16];
|
||||
|
||||
let mut msg = vec![0u8; 1 + 64 + 16 + plain.len() + 32];
|
||||
msg[0] = 0x04u8;
|
||||
{
|
||||
let msgd = &mut msg[1..];
|
||||
msgd[0..64].copy_from_slice(r.public());
|
||||
let iv = H128::random();
|
||||
msgd[64..80].copy_from_slice(&iv);
|
||||
{
|
||||
let cipher = &mut msgd[(64 + 16)..(64 + 16 + plain.len())];
|
||||
aes::encrypt(ekey, &iv, plain, cipher);
|
||||
}
|
||||
let mut hmac = Hmac::new(Sha256::new(), &mkey);
|
||||
{
|
||||
let cipher_iv = &msgd[64..(64 + 16 + plain.len())];
|
||||
hmac.input(cipher_iv);
|
||||
}
|
||||
hmac.input(auth_data);
|
||||
hmac.raw_result(&mut msgd[(64 + 16 + plain.len())..]);
|
||||
}
|
||||
Ok(msg)
|
||||
}
|
||||
|
||||
/// Decrypt a message with a secret key, checking HMAC for ciphertext
|
||||
/// and authenticated data validity.
|
||||
pub fn decrypt(secret: &Secret, auth_data: &[u8], encrypted: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
let meta_len = 1 + 64 + 16 + 32;
|
||||
if encrypted.len() < meta_len || encrypted[0] < 2 || encrypted[0] > 4 {
|
||||
return Err(Error::InvalidMessage); //invalid message: publickey
|
||||
}
|
||||
|
||||
let e = &encrypted[1..];
|
||||
let p = Public::from_slice(&e[0..64]);
|
||||
let z = ecdh::agree(secret, &p)?;
|
||||
let mut key = [0u8; 32];
|
||||
kdf(&z, &[0u8; 0], &mut key);
|
||||
let ekey = &key[0..16];
|
||||
let mkey_material = &key[16..32];
|
||||
let mut hasher = Sha256::new();
|
||||
let mut mkey = [0u8; 32];
|
||||
hasher.input(mkey_material);
|
||||
hasher.result(&mut mkey);
|
||||
|
||||
let clen = encrypted.len() - meta_len;
|
||||
let cipher_with_iv = &e[64..(64+16+clen)];
|
||||
let cipher_iv = &cipher_with_iv[0..16];
|
||||
let cipher_no_iv = &cipher_with_iv[16..];
|
||||
let msg_mac = &e[(64+16+clen)..];
|
||||
|
||||
// Verify tag
|
||||
let mut hmac = Hmac::new(Sha256::new(), &mkey);
|
||||
hmac.input(cipher_with_iv);
|
||||
hmac.input(auth_data);
|
||||
let mut mac = [0u8; 32];
|
||||
hmac.raw_result(&mut mac);
|
||||
|
||||
// constant time compare to avoid timing attack.
|
||||
if ::subtle::slices_equal(&mac[..], msg_mac) != 1 {
|
||||
return Err(Error::InvalidMessage);
|
||||
}
|
||||
|
||||
let mut msg = vec![0u8; clen];
|
||||
aes::decrypt(ekey, cipher_iv, cipher_no_iv, &mut msg[..]);
|
||||
Ok(msg)
|
||||
}
|
||||
|
||||
fn kdf(secret: &Secret, s1: &[u8], dest: &mut [u8]) {
|
||||
let mut hasher = Sha256::new();
|
||||
// SEC/ISO/Shoup specify counter size SHOULD be equivalent
|
||||
// to size of hash output, however, it also notes that
|
||||
// the 4 bytes is okay. NIST specifies 4 bytes.
|
||||
let mut ctr = 1u32;
|
||||
let mut written = 0usize;
|
||||
while written < dest.len() {
|
||||
let ctrs = [(ctr >> 24) as u8, (ctr >> 16) as u8, (ctr >> 8) as u8, ctr as u8];
|
||||
hasher.input(&ctrs);
|
||||
hasher.input(secret);
|
||||
hasher.input(s1);
|
||||
hasher.result(&mut dest[written..(written + 32)]);
|
||||
hasher.reset();
|
||||
written += 32;
|
||||
ctr += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ethkey::{Random, Generator};
|
||||
use ecies;
|
||||
|
||||
#[test]
|
||||
fn ecies_shared() {
|
||||
let kp = Random.generate().unwrap();
|
||||
let message = b"So many books, so little time";
|
||||
|
||||
let shared = b"shared";
|
||||
let wrong_shared = b"incorrect";
|
||||
let encrypted = ecies::encrypt(kp.public(), shared, message).unwrap();
|
||||
assert!(encrypted[..] != message[..]);
|
||||
assert_eq!(encrypted[0], 0x04);
|
||||
|
||||
assert!(ecies::decrypt(kp.secret(), wrong_shared, &encrypted).is_err());
|
||||
let decrypted = ecies::decrypt(kp.secret(), shared, &encrypted).unwrap();
|
||||
assert_eq!(decrypted[..message.len()], message[..]);
|
||||
}
|
||||
pub fn is_equal(a: &[u8], b: &[u8]) -> bool {
|
||||
ring::constant_time::verify_slices_are_equal(a, b).is_ok()
|
||||
}
|
||||
|
||||
|
29
ethcore/crypto/src/pbkdf2.rs
Normal file
29
ethcore/crypto/src/pbkdf2.rs
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2018 Parity Technologies (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/>.
|
||||
|
||||
use ring;
|
||||
|
||||
pub struct Salt<'a>(pub &'a [u8]);
|
||||
pub struct Secret<'a>(pub &'a [u8]);
|
||||
|
||||
pub fn sha256(iter: u32, salt: Salt, sec: Secret, out: &mut [u8; 32]) {
|
||||
ring::pbkdf2::derive(&ring::digest::SHA256, iter, salt.0, sec.0, &mut out[..])
|
||||
}
|
||||
|
||||
pub fn sha512(iter: u32, salt: Salt, sec: Secret, out: &mut [u8; 64]) {
|
||||
ring::pbkdf2::derive(&ring::digest::SHA512, iter, salt.0, sec.0, &mut out[..])
|
||||
}
|
||||
|
39
ethcore/crypto/src/scrypt.rs
Normal file
39
ethcore/crypto/src/scrypt.rs
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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/>.
|
||||
|
||||
use error::ScryptError;
|
||||
use rcrypto::scrypt::{scrypt, ScryptParams};
|
||||
use super::{KEY_LENGTH_AES, KEY_LENGTH};
|
||||
|
||||
pub fn derive_key(pass: &str, salt: &[u8; 32], n: u32, p: u32, r: u32) -> Result<(Vec<u8>, Vec<u8>), ScryptError> {
|
||||
// sanity checks
|
||||
let log_n = (32 - n.leading_zeros() - 1) as u8;
|
||||
if log_n as u32 >= r * 16 {
|
||||
return Err(ScryptError::InvalidN);
|
||||
}
|
||||
|
||||
if p as u64 > ((u32::max_value() as u64 - 1) * 32)/(128 * (r as u64)) {
|
||||
return Err(ScryptError::InvalidP);
|
||||
}
|
||||
|
||||
let mut derived_key = vec![0u8; KEY_LENGTH];
|
||||
let scrypt_params = ScryptParams::new(log_n, r, p);
|
||||
scrypt(pass.as_bytes(), salt, &scrypt_params, &mut derived_key);
|
||||
let derived_right_bits = &derived_key[0..KEY_LENGTH_AES];
|
||||
let derived_left_bits = &derived_key[KEY_LENGTH_AES..KEY_LENGTH];
|
||||
Ok((derived_right_bits.to_vec(), derived_left_bits.to_vec()))
|
||||
}
|
||||
|
@ -217,7 +217,8 @@ impl Encryptor for SecretStoreEncryptor {
|
||||
// encrypt data
|
||||
let mut cypher = Vec::with_capacity(plain_data.len() + initialisation_vector.len());
|
||||
cypher.extend(repeat(0).take(plain_data.len()));
|
||||
crypto::aes::encrypt(&key, initialisation_vector, plain_data, &mut cypher);
|
||||
crypto::aes::encrypt_128_ctr(&key, initialisation_vector, plain_data, &mut cypher)
|
||||
.map_err(|e| ErrorKind::Encrypt(e.to_string()))?;
|
||||
cypher.extend_from_slice(&initialisation_vector);
|
||||
|
||||
Ok(cypher)
|
||||
@ -243,8 +244,8 @@ impl Encryptor for SecretStoreEncryptor {
|
||||
let (cypher, iv) = cypher.split_at(cypher_len - INIT_VEC_LEN);
|
||||
let mut plain_data = Vec::with_capacity(cypher_len - INIT_VEC_LEN);
|
||||
plain_data.extend(repeat(0).take(cypher_len - INIT_VEC_LEN));
|
||||
crypto::aes::decrypt(&key, &iv, cypher, &mut plain_data);
|
||||
|
||||
crypto::aes::decrypt_128_ctr(&key, &iv, cypher, &mut plain_data)
|
||||
.map_err(|e| ErrorKind::Decrypt(e.to_string()))?;
|
||||
Ok(plain_data)
|
||||
}
|
||||
}
|
||||
|
@ -18,9 +18,7 @@ use std::cmp::{max, min};
|
||||
use std::io::{self, Read};
|
||||
|
||||
use byteorder::{ByteOrder, BigEndian};
|
||||
use crypto::sha2::Sha256 as Sha256Digest;
|
||||
use crypto::ripemd160::Ripemd160 as Ripemd160Digest;
|
||||
use crypto::digest::Digest;
|
||||
use ethcore_crypto::digest;
|
||||
use num::{BigUint, Zero, One};
|
||||
|
||||
use hash::keccak;
|
||||
@ -295,28 +293,17 @@ impl Impl for EcRecover {
|
||||
|
||||
impl Impl for Sha256 {
|
||||
fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> {
|
||||
let mut sha = Sha256Digest::new();
|
||||
sha.input(input);
|
||||
|
||||
let mut out = [0; 32];
|
||||
sha.result(&mut out);
|
||||
|
||||
output.write(0, &out);
|
||||
|
||||
let d = digest::sha256(input);
|
||||
output.write(0, &*d);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Impl for Ripemd160 {
|
||||
fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> {
|
||||
let mut sha = Ripemd160Digest::new();
|
||||
sha.input(input);
|
||||
|
||||
let mut out = [0; 32];
|
||||
sha.result(&mut out[12..32]);
|
||||
|
||||
output.write(0, &out);
|
||||
|
||||
let hash = digest::ripemd160(input);
|
||||
output.write(0, &[0; 12][..]);
|
||||
output.write(12, &hash);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -63,9 +63,9 @@ extern crate bn;
|
||||
extern crate byteorder;
|
||||
extern crate crossbeam;
|
||||
extern crate common_types as types;
|
||||
extern crate crypto;
|
||||
extern crate ethash;
|
||||
extern crate ethcore_bloom_journal as bloom_journal;
|
||||
extern crate ethcore_crypto;
|
||||
extern crate ethcore_io as io;
|
||||
extern crate ethcore_bytes as bytes;
|
||||
extern crate ethcore_logger;
|
||||
|
@ -6,13 +6,14 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
[dependencies]
|
||||
byteorder = "1.0"
|
||||
edit-distance = "2.0"
|
||||
ethcore-crypto = { path = "../ethcore/crypto" }
|
||||
eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1" }
|
||||
ethereum-types = "0.3"
|
||||
lazy_static = "1.0"
|
||||
log = "0.3"
|
||||
mem = { path = "../util/mem" }
|
||||
parity-wordlist = "1.2"
|
||||
quick-error = "1.2"
|
||||
rand = "0.4"
|
||||
rust-crypto = "0.2"
|
||||
rustc-hex = "1.0"
|
||||
tiny-keccak = "1.3"
|
||||
|
189
ethkey/src/crypto.rs
Normal file
189
ethkey/src/crypto.rs
Normal file
@ -0,0 +1,189 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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/>.
|
||||
|
||||
use secp256k1;
|
||||
use std::io;
|
||||
use ethcore_crypto::error::SymmError;
|
||||
|
||||
quick_error! {
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Secp(e: secp256k1::Error) {
|
||||
display("secp256k1 error: {}", e)
|
||||
cause(e)
|
||||
from()
|
||||
}
|
||||
Io(e: io::Error) {
|
||||
display("i/o error: {}", e)
|
||||
cause(e)
|
||||
from()
|
||||
}
|
||||
InvalidMessage {
|
||||
display("invalid message")
|
||||
}
|
||||
Symm(e: SymmError) {
|
||||
cause(e)
|
||||
from()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ECDH functions
|
||||
pub mod ecdh {
|
||||
use secp256k1::{self, ecdh, key};
|
||||
use super::Error;
|
||||
use {Secret, Public, SECP256K1};
|
||||
|
||||
/// Agree on a shared secret
|
||||
pub fn agree(secret: &Secret, public: &Public) -> Result<Secret, Error> {
|
||||
let context = &SECP256K1;
|
||||
let pdata = {
|
||||
let mut temp = [4u8; 65];
|
||||
(&mut temp[1..65]).copy_from_slice(&public[0..64]);
|
||||
temp
|
||||
};
|
||||
|
||||
let publ = key::PublicKey::from_slice(context, &pdata)?;
|
||||
let sec = key::SecretKey::from_slice(context, &secret)?;
|
||||
let shared = ecdh::SharedSecret::new_raw(context, &publ, &sec);
|
||||
|
||||
Secret::from_unsafe_slice(&shared[0..32])
|
||||
.map_err(|_| Error::Secp(secp256k1::Error::InvalidSecretKey))
|
||||
}
|
||||
}
|
||||
|
||||
/// ECIES function
|
||||
pub mod ecies {
|
||||
use ethcore_crypto::{aes, digest, hmac, is_equal};
|
||||
use ethereum_types::H128;
|
||||
use super::{ecdh, Error};
|
||||
use {Random, Generator, Public, Secret};
|
||||
|
||||
/// Encrypt a message with a public key, writing an HMAC covering both
|
||||
/// the plaintext and authenticated data.
|
||||
///
|
||||
/// Authenticated data may be empty.
|
||||
pub fn encrypt(public: &Public, auth_data: &[u8], plain: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
let r = Random.generate()?;
|
||||
let z = ecdh::agree(r.secret(), public)?;
|
||||
let mut key = [0u8; 32];
|
||||
kdf(&z, &[0u8; 0], &mut key);
|
||||
|
||||
let ekey = &key[0..16];
|
||||
let mkey = hmac::SigKey::sha256(&digest::sha256(&key[16..32]));
|
||||
|
||||
let mut msg = vec![0u8; 1 + 64 + 16 + plain.len() + 32];
|
||||
msg[0] = 0x04u8;
|
||||
{
|
||||
let msgd = &mut msg[1..];
|
||||
msgd[0..64].copy_from_slice(r.public());
|
||||
let iv = H128::random();
|
||||
msgd[64..80].copy_from_slice(&iv);
|
||||
{
|
||||
let cipher = &mut msgd[(64 + 16)..(64 + 16 + plain.len())];
|
||||
aes::encrypt_128_ctr(ekey, &iv, plain, cipher)?;
|
||||
}
|
||||
let mut hmac = hmac::Signer::with(&mkey);
|
||||
{
|
||||
let cipher_iv = &msgd[64..(64 + 16 + plain.len())];
|
||||
hmac.update(cipher_iv);
|
||||
}
|
||||
hmac.update(auth_data);
|
||||
let sig = hmac.sign();
|
||||
msgd[(64 + 16 + plain.len())..].copy_from_slice(&sig);
|
||||
}
|
||||
Ok(msg)
|
||||
}
|
||||
|
||||
/// Decrypt a message with a secret key, checking HMAC for ciphertext
|
||||
/// and authenticated data validity.
|
||||
pub fn decrypt(secret: &Secret, auth_data: &[u8], encrypted: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
let meta_len = 1 + 64 + 16 + 32;
|
||||
if encrypted.len() < meta_len || encrypted[0] < 2 || encrypted[0] > 4 {
|
||||
return Err(Error::InvalidMessage); //invalid message: publickey
|
||||
}
|
||||
|
||||
let e = &encrypted[1..];
|
||||
let p = Public::from_slice(&e[0..64]);
|
||||
let z = ecdh::agree(secret, &p)?;
|
||||
let mut key = [0u8; 32];
|
||||
kdf(&z, &[0u8; 0], &mut key);
|
||||
|
||||
let ekey = &key[0..16];
|
||||
let mkey = hmac::SigKey::sha256(&digest::sha256(&key[16..32]));
|
||||
|
||||
let clen = encrypted.len() - meta_len;
|
||||
let cipher_with_iv = &e[64..(64+16+clen)];
|
||||
let cipher_iv = &cipher_with_iv[0..16];
|
||||
let cipher_no_iv = &cipher_with_iv[16..];
|
||||
let msg_mac = &e[(64+16+clen)..];
|
||||
|
||||
// Verify tag
|
||||
let mut hmac = hmac::Signer::with(&mkey);
|
||||
hmac.update(cipher_with_iv);
|
||||
hmac.update(auth_data);
|
||||
let mac = hmac.sign();
|
||||
|
||||
if !is_equal(&mac.as_ref()[..], msg_mac) {
|
||||
return Err(Error::InvalidMessage);
|
||||
}
|
||||
|
||||
let mut msg = vec![0u8; clen];
|
||||
aes::decrypt_128_ctr(ekey, cipher_iv, cipher_no_iv, &mut msg[..])?;
|
||||
Ok(msg)
|
||||
}
|
||||
|
||||
fn kdf(secret: &Secret, s1: &[u8], dest: &mut [u8]) {
|
||||
// SEC/ISO/Shoup specify counter size SHOULD be equivalent
|
||||
// to size of hash output, however, it also notes that
|
||||
// the 4 bytes is okay. NIST specifies 4 bytes.
|
||||
let mut ctr = 1u32;
|
||||
let mut written = 0usize;
|
||||
while written < dest.len() {
|
||||
let mut hasher = digest::Hasher::sha256();
|
||||
let ctrs = [(ctr >> 24) as u8, (ctr >> 16) as u8, (ctr >> 8) as u8, ctr as u8];
|
||||
hasher.update(&ctrs);
|
||||
hasher.update(secret);
|
||||
hasher.update(s1);
|
||||
let d = hasher.finish();
|
||||
&mut dest[written..(written + 32)].copy_from_slice(&d);
|
||||
written += 32;
|
||||
ctr += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ecies;
|
||||
use {Random, Generator};
|
||||
|
||||
#[test]
|
||||
fn ecies_shared() {
|
||||
let kp = Random.generate().unwrap();
|
||||
let message = b"So many books, so little time";
|
||||
|
||||
let shared = b"shared";
|
||||
let wrong_shared = b"incorrect";
|
||||
let encrypted = ecies::encrypt(kp.public(), shared, message).unwrap();
|
||||
assert!(encrypted[..] != message[..]);
|
||||
assert_eq!(encrypted[0], 0x04);
|
||||
|
||||
assert!(ecies::decrypt(kp.secret(), wrong_shared, &encrypted).is_err());
|
||||
let decrypted = ecies::decrypt(kp.secret(), shared, &encrypted).unwrap();
|
||||
assert_eq!(decrypted[..message.len()], message[..]);
|
||||
}
|
||||
}
|
@ -207,9 +207,7 @@ impl ExtendedKeyPair {
|
||||
// Work is based on BIP0032
|
||||
// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
||||
mod derivation {
|
||||
use rcrypto::hmac::Hmac;
|
||||
use rcrypto::mac::Mac;
|
||||
use rcrypto::sha2::Sha512;
|
||||
use ethcore_crypto::hmac;
|
||||
use ethereum_types::{U256, U512, H512, H256};
|
||||
use secp256k1::key::{SecretKey, PublicKey};
|
||||
use SECP256K1;
|
||||
@ -242,10 +240,8 @@ mod derivation {
|
||||
let private: U256 = private_key.into();
|
||||
|
||||
// produces 512-bit derived hmac (I)
|
||||
let mut hmac = Hmac::new(Sha512::new(), &*chain_code);
|
||||
let mut i_512 = [0u8; 64];
|
||||
hmac.input(&data[..]);
|
||||
hmac.raw_result(&mut i_512);
|
||||
let skey = hmac::SigKey::sha512(&*chain_code);
|
||||
let i_512 = hmac::sign(&skey, &data[..]);
|
||||
|
||||
// left most 256 bits are later added to original private key
|
||||
let hmac_key: U256 = H256::from_slice(&i_512[0..32]).into();
|
||||
@ -321,10 +317,8 @@ mod derivation {
|
||||
index.store(&mut data[33..(33 + T::len())]);
|
||||
|
||||
// HMAC512SHA produces [derived private(256); new chain code(256)]
|
||||
let mut hmac = Hmac::new(Sha512::new(), &*chain_code);
|
||||
let mut i_512 = [0u8; 64];
|
||||
hmac.input(&data[..]);
|
||||
hmac.raw_result(&mut i_512);
|
||||
let skey = hmac::SigKey::sha512(&*chain_code);
|
||||
let i_512 = hmac::sign(&skey, &data[..]);
|
||||
|
||||
let new_private = H256::from(&i_512[0..32]);
|
||||
let new_chain_code = H256::from(&i_512[32..64]);
|
||||
@ -369,10 +363,8 @@ mod derivation {
|
||||
}
|
||||
|
||||
pub fn seed_pair(seed: &[u8]) -> (H256, H256) {
|
||||
let mut hmac = Hmac::new(Sha512::new(), b"Bitcoin seed");
|
||||
let mut i_512 = [0u8; 64];
|
||||
hmac.input(seed);
|
||||
hmac.raw_result(&mut i_512);
|
||||
let skey = hmac::SigKey::sha512(b"Bitcoin seed");
|
||||
let i_512 = hmac::sign(&skey, seed);
|
||||
|
||||
let master_key = H256::from_slice(&i_512[0..32]);
|
||||
let chain_code = H256::from_slice(&i_512[32..64]);
|
||||
|
@ -17,11 +17,13 @@
|
||||
// #![warn(missing_docs)]
|
||||
|
||||
extern crate byteorder;
|
||||
extern crate crypto as rcrypto;
|
||||
extern crate edit_distance;
|
||||
extern crate ethcore_crypto;
|
||||
extern crate ethereum_types;
|
||||
extern crate mem;
|
||||
extern crate parity_wordlist;
|
||||
#[macro_use]
|
||||
extern crate quick_error;
|
||||
extern crate rand;
|
||||
extern crate rustc_hex;
|
||||
extern crate secp256k1;
|
||||
@ -44,6 +46,7 @@ mod secret;
|
||||
mod extended;
|
||||
|
||||
pub mod brain_recover;
|
||||
pub mod crypto;
|
||||
pub mod math;
|
||||
|
||||
pub use self::parity_wordlist::Error as WordlistError;
|
||||
|
@ -12,7 +12,6 @@ serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
serde_derive = "1.0"
|
||||
rustc-hex = "1.0"
|
||||
rust-crypto = "0.2.36"
|
||||
tiny-keccak = "1.3"
|
||||
time = "0.1.34"
|
||||
itertools = "0.5"
|
||||
@ -22,7 +21,6 @@ ethereum-types = "0.3"
|
||||
dir = { path = "../util/dir" }
|
||||
smallvec = "0.4"
|
||||
parity-wordlist = "1.0"
|
||||
subtle = "0.5"
|
||||
tempdir = "0.3"
|
||||
|
||||
[dev-dependencies]
|
||||
|
@ -21,7 +21,6 @@ use crypto::Keccak256;
|
||||
use random::Random;
|
||||
use smallvec::SmallVec;
|
||||
use account::{Cipher, Kdf, Aes128Ctr, Pbkdf2, Prf};
|
||||
use subtle;
|
||||
|
||||
/// Encrypted data
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
@ -74,12 +73,12 @@ impl From<Crypto> for String {
|
||||
|
||||
impl Crypto {
|
||||
/// Encrypt account secret
|
||||
pub fn with_secret(secret: &Secret, password: &str, iterations: u32) -> Self {
|
||||
pub fn with_secret(secret: &Secret, password: &str, iterations: u32) -> Result<Self, crypto::Error> {
|
||||
Crypto::with_plain(&*secret, password, iterations)
|
||||
}
|
||||
|
||||
/// Encrypt custom plain data
|
||||
pub fn with_plain(plain: &[u8], password: &str, iterations: u32) -> Self {
|
||||
pub fn with_plain(plain: &[u8], password: &str, iterations: u32) -> Result<Self, crypto::Error> {
|
||||
let salt: [u8; 32] = Random::random();
|
||||
let iv: [u8; 16] = Random::random();
|
||||
|
||||
@ -93,12 +92,12 @@ impl Crypto {
|
||||
let mut ciphertext: SmallVec<[u8; 32]> = SmallVec::from_vec(vec![0; plain_len]);
|
||||
|
||||
// aes-128-ctr with initial vector of iv
|
||||
crypto::aes::encrypt(&derived_left_bits, &iv, plain, &mut *ciphertext);
|
||||
crypto::aes::encrypt_128_ctr(&derived_left_bits, &iv, plain, &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 {
|
||||
Ok(Crypto {
|
||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||
iv: iv,
|
||||
}),
|
||||
@ -110,7 +109,7 @@ impl Crypto {
|
||||
prf: Prf::HmacSha256,
|
||||
}),
|
||||
mac: mac,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Try to decrypt and convert result to account secret
|
||||
@ -132,13 +131,13 @@ impl Crypto {
|
||||
fn do_decrypt(&self, password: &str, expected_len: usize) -> Result<Vec<u8>, Error> {
|
||||
let (derived_left_bits, derived_right_bits) = match self.kdf {
|
||||
Kdf::Pbkdf2(ref params) => crypto::derive_key_iterations(password, ¶ms.salt, params.c),
|
||||
Kdf::Scrypt(ref params) => crypto::derive_key_scrypt(password, ¶ms.salt, params.n, params.p, params.r)?,
|
||||
Kdf::Scrypt(ref params) => crypto::scrypt::derive_key(password, ¶ms.salt, params.n, params.p, params.r)?,
|
||||
};
|
||||
|
||||
let mac = crypto::derive_mac(&derived_right_bits, &self.ciphertext).keccak256();
|
||||
|
||||
if subtle::slices_equal(&mac, &self.mac) == 0 {
|
||||
return Err(Error::InvalidPassword);
|
||||
if !crypto::is_equal(&mac, &self.mac) {
|
||||
return Err(Error::InvalidPassword)
|
||||
}
|
||||
|
||||
let mut plain: SmallVec<[u8; 32]> = SmallVec::from_vec(vec![0; expected_len]);
|
||||
@ -149,7 +148,7 @@ impl Crypto {
|
||||
debug_assert!(expected_len >= self.ciphertext.len());
|
||||
|
||||
let from = expected_len - self.ciphertext.len();
|
||||
crypto::aes::decrypt(&derived_left_bits, ¶ms.iv, &self.ciphertext, &mut plain[from..]);
|
||||
crypto::aes::decrypt_128_ctr(&derived_left_bits, ¶ms.iv, &self.ciphertext, &mut plain[from..])?;
|
||||
Ok(plain.into_iter().collect())
|
||||
},
|
||||
}
|
||||
@ -164,7 +163,7 @@ mod tests {
|
||||
#[test]
|
||||
fn crypto_with_secret_create() {
|
||||
let keypair = Random.generate().unwrap();
|
||||
let crypto = Crypto::with_secret(keypair.secret(), "this is sparta", 10240);
|
||||
let crypto = Crypto::with_secret(keypair.secret(), "this is sparta", 10240).unwrap();
|
||||
let secret = crypto.secret("this is sparta").unwrap();
|
||||
assert_eq!(keypair.secret(), &secret);
|
||||
}
|
||||
@ -172,14 +171,14 @@ mod tests {
|
||||
#[test]
|
||||
fn crypto_with_secret_invalid_password() {
|
||||
let keypair = Random.generate().unwrap();
|
||||
let crypto = Crypto::with_secret(keypair.secret(), "this is sparta", 10240);
|
||||
let crypto = Crypto::with_secret(keypair.secret(), "this is sparta", 10240).unwrap();
|
||||
assert_matches!(crypto.secret("this is sparta!"), Err(Error::InvalidPassword))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crypto_with_null_plain_data() {
|
||||
let original_data = b"";
|
||||
let crypto = Crypto::with_plain(&original_data[..], "this is sparta", 10240);
|
||||
let crypto = Crypto::with_plain(&original_data[..], "this is sparta", 10240).unwrap();
|
||||
let decrypted_data = crypto.decrypt("this is sparta").unwrap();
|
||||
assert_eq!(original_data[..], *decrypted_data);
|
||||
}
|
||||
@ -187,7 +186,7 @@ mod tests {
|
||||
#[test]
|
||||
fn crypto_with_tiny_plain_data() {
|
||||
let original_data = b"{}";
|
||||
let crypto = Crypto::with_plain(&original_data[..], "this is sparta", 10240);
|
||||
let crypto = Crypto::with_plain(&original_data[..], "this is sparta", 10240).unwrap();
|
||||
let decrypted_data = crypto.decrypt("this is sparta").unwrap();
|
||||
assert_eq!(original_data[..], *decrypted_data);
|
||||
}
|
||||
@ -195,7 +194,7 @@ mod tests {
|
||||
#[test]
|
||||
fn crypto_with_huge_plain_data() {
|
||||
let original_data: Vec<_> = (1..65536).map(|i| (i % 256) as u8).collect();
|
||||
let crypto = Crypto::with_plain(&original_data, "this is sparta", 10240);
|
||||
let crypto = Crypto::with_plain(&original_data, "this is sparta", 10240).unwrap();
|
||||
let decrypted_data = crypto.decrypt("this is sparta").unwrap();
|
||||
assert_eq!(&original_data, &decrypted_data);
|
||||
}
|
||||
|
@ -14,10 +14,11 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use ethkey::{KeyPair, sign, Address, Signature, Message, Public, Secret};
|
||||
use crypto::ecdh::agree;
|
||||
use {json, Error, crypto};
|
||||
use ethkey::{self, KeyPair, sign, Address, Signature, Message, Public, Secret};
|
||||
use ethkey::crypto::ecdh::agree;
|
||||
use {json, Error};
|
||||
use account::Version;
|
||||
use crypto;
|
||||
use super::crypto::Crypto;
|
||||
|
||||
/// Account representation.
|
||||
@ -61,16 +62,16 @@ impl SafeAccount {
|
||||
iterations: u32,
|
||||
name: String,
|
||||
meta: String
|
||||
) -> Self {
|
||||
SafeAccount {
|
||||
) -> Result<Self, crypto::Error> {
|
||||
Ok(SafeAccount {
|
||||
id: id,
|
||||
version: Version::V3,
|
||||
crypto: Crypto::with_secret(keypair.secret(), password, iterations),
|
||||
crypto: Crypto::with_secret(keypair.secret(), password, iterations)?,
|
||||
address: keypair.address(),
|
||||
filename: None,
|
||||
name: name,
|
||||
meta: meta,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a new `SafeAccount` from the given `json`; if it was read from a
|
||||
@ -114,7 +115,7 @@ impl SafeAccount {
|
||||
meta: Some(self.meta),
|
||||
};
|
||||
let meta_plain = meta_plain.write().map_err(|e| Error::Custom(format!("{:?}", e)))?;
|
||||
let meta_crypto = Crypto::with_plain(&meta_plain, password, iterations);
|
||||
let meta_crypto = Crypto::with_plain(&meta_plain, password, iterations)?;
|
||||
|
||||
Ok(json::VaultKeyFile {
|
||||
id: self.id.into(),
|
||||
@ -133,7 +134,7 @@ impl SafeAccount {
|
||||
/// Decrypt a message.
|
||||
pub fn decrypt(&self, password: &str, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
let secret = self.crypto.secret(password)?;
|
||||
crypto::ecies::decrypt(&secret, shared_mac, message).map_err(From::from)
|
||||
ethkey::crypto::ecies::decrypt(&secret, shared_mac, message).map_err(From::from)
|
||||
}
|
||||
|
||||
/// Agree on shared key.
|
||||
@ -154,7 +155,7 @@ impl SafeAccount {
|
||||
let result = SafeAccount {
|
||||
id: self.id.clone(),
|
||||
version: self.version.clone(),
|
||||
crypto: Crypto::with_secret(&secret, new_password, iterations),
|
||||
crypto: Crypto::with_secret(&secret, new_password, iterations)?,
|
||||
address: self.address.clone(),
|
||||
filename: self.filename.clone(),
|
||||
name: self.name.clone(),
|
||||
@ -180,7 +181,7 @@ mod tests {
|
||||
let password = "hello world";
|
||||
let message = Message::default();
|
||||
let account = SafeAccount::create(&keypair, [0u8; 16], password, 10240, "Test".to_owned(), "{}".to_owned());
|
||||
let signature = account.sign(password, &message).unwrap();
|
||||
let signature = account.unwrap().sign(password, &message).unwrap();
|
||||
assert!(verify_public(keypair.public(), &signature, &message).unwrap());
|
||||
}
|
||||
|
||||
@ -191,7 +192,7 @@ mod tests {
|
||||
let sec_password = "this is sparta";
|
||||
let i = 10240;
|
||||
let message = Message::default();
|
||||
let account = SafeAccount::create(&keypair, [0u8; 16], first_password, i, "Test".to_owned(), "{}".to_owned());
|
||||
let account = SafeAccount::create(&keypair, [0u8; 16], first_password, i, "Test".to_owned(), "{}".to_owned()).unwrap();
|
||||
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());
|
||||
|
@ -319,7 +319,7 @@ mod test {
|
||||
|
||||
// when
|
||||
let account = SafeAccount::create(&keypair, [0u8; 16], password, 1024, "Test".to_owned(), "{}".to_owned());
|
||||
let res = directory.insert(account);
|
||||
let res = directory.insert(account.unwrap());
|
||||
|
||||
// then
|
||||
assert!(res.is_ok(), "Should save account succesfuly.");
|
||||
@ -339,7 +339,7 @@ mod test {
|
||||
let directory = RootDiskDirectory::create(dir.clone()).unwrap();
|
||||
|
||||
// when
|
||||
let account = SafeAccount::create(&keypair, [0u8; 16], password, 1024, "Test".to_owned(), "{}".to_owned());
|
||||
let account = SafeAccount::create(&keypair, [0u8; 16], password, 1024, "Test".to_owned(), "{}".to_owned()).unwrap();
|
||||
let filename = "test".to_string();
|
||||
let dedup = true;
|
||||
|
||||
@ -424,7 +424,7 @@ mod test {
|
||||
let keypair = Random.generate().unwrap();
|
||||
let password = "test pass";
|
||||
let account = SafeAccount::create(&keypair, [0u8; 16], password, 1024, "Test".to_owned(), "{}".to_owned());
|
||||
directory.insert(account).expect("Account should be inserted ok");
|
||||
directory.insert(account.unwrap()).expect("Account should be inserted ok");
|
||||
|
||||
let new_hash = directory.files_hash().expect("New files hash should be calculated ok");
|
||||
|
||||
|
@ -235,7 +235,7 @@ fn check_vault_name(name: &str) -> bool {
|
||||
/// Vault can be empty, but still must be pluggable => we store vault password in separate file
|
||||
fn create_vault_file<P>(vault_dir_path: P, key: &VaultKey, meta: &str) -> Result<(), Error> where P: AsRef<Path> {
|
||||
let password_hash = key.password.keccak256();
|
||||
let crypto = Crypto::with_plain(&password_hash, &key.password, key.iterations);
|
||||
let crypto = Crypto::with_plain(&password_hash, &key.password, key.iterations)?;
|
||||
|
||||
let mut vault_file_path: PathBuf = vault_dir_path.as_ref().into();
|
||||
vault_file_path.push(VAULT_FILE_NAME);
|
||||
|
@ -16,8 +16,8 @@
|
||||
|
||||
use std::fmt;
|
||||
use std::io::Error as IoError;
|
||||
use ethkey::Error as EthKeyError;
|
||||
use crypto::Error as EthCryptoError;
|
||||
use ethkey::{self, Error as EthKeyError};
|
||||
use crypto::{self, Error as EthCryptoError};
|
||||
use ethkey::DerivationError;
|
||||
|
||||
/// Account-related errors.
|
||||
@ -49,6 +49,8 @@ pub enum Error {
|
||||
CreationFailed,
|
||||
/// `EthKey` error
|
||||
EthKey(EthKeyError),
|
||||
/// `ethkey::crypto::Error`
|
||||
EthKeyCrypto(ethkey::crypto::Error),
|
||||
/// `EthCrypto` error
|
||||
EthCrypto(EthCryptoError),
|
||||
/// Derivation error
|
||||
@ -73,6 +75,7 @@ impl fmt::Display for Error {
|
||||
Error::VaultNotFound => "Vault not found".into(),
|
||||
Error::CreationFailed => "Account creation failed".into(),
|
||||
Error::EthKey(ref err) => err.to_string(),
|
||||
Error::EthKeyCrypto(ref err) => err.to_string(),
|
||||
Error::EthCrypto(ref err) => err.to_string(),
|
||||
Error::Derivation(ref err) => format!("Derivation error: {:?}", err),
|
||||
Error::Custom(ref s) => s.clone(),
|
||||
@ -94,12 +97,30 @@ impl From<EthKeyError> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ethkey::crypto::Error> for Error {
|
||||
fn from(err: ethkey::crypto::Error) -> Self {
|
||||
Error::EthKeyCrypto(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EthCryptoError> for Error {
|
||||
fn from(err: EthCryptoError) -> Self {
|
||||
Error::EthCrypto(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crypto::error::ScryptError> for Error {
|
||||
fn from(err: crypto::error::ScryptError) -> Self {
|
||||
Error::EthCrypto(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crypto::error::SymmError> for Error {
|
||||
fn from(err: crypto::error::SymmError) -> Self {
|
||||
Error::EthCrypto(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DerivationError> for Error {
|
||||
fn from(err: DerivationError) -> Self {
|
||||
Error::Derivation(err)
|
||||
|
@ -458,7 +458,7 @@ impl SimpleSecretStore for EthMultiStore {
|
||||
fn insert_account(&self, vault: SecretVaultRef, secret: Secret, password: &str) -> Result<StoreAccountRef, Error> {
|
||||
let keypair = KeyPair::from_secret(secret).map_err(|_| Error::CreationFailed)?;
|
||||
let id: [u8; 16] = Random::random();
|
||||
let account = SafeAccount::create(&keypair, id, password, self.iterations, "".to_owned(), "{}".to_owned());
|
||||
let account = SafeAccount::create(&keypair, id, password, self.iterations, "".to_owned(), "{}".to_owned())?;
|
||||
self.import(vault, account)
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,6 @@
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
extern crate crypto as rcrypto;
|
||||
extern crate dir;
|
||||
extern crate itertools;
|
||||
extern crate libc;
|
||||
@ -28,7 +27,6 @@ extern crate rustc_hex;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
extern crate smallvec;
|
||||
extern crate subtle;
|
||||
extern crate time;
|
||||
extern crate tiny_keccak;
|
||||
extern crate tempdir;
|
||||
|
@ -1,11 +1,8 @@
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use rcrypto::pbkdf2::pbkdf2;
|
||||
use rcrypto::sha2::Sha256;
|
||||
use rcrypto::hmac::Hmac;
|
||||
use json;
|
||||
use ethkey::{Address, Secret, KeyPair};
|
||||
use crypto::Keccak256;
|
||||
use crypto::{Keccak256, pbkdf2};
|
||||
use {crypto, Error};
|
||||
|
||||
/// Pre-sale wallet.
|
||||
@ -42,12 +39,14 @@ impl PresaleWallet {
|
||||
|
||||
/// Decrypt the wallet.
|
||||
pub fn decrypt(&self, password: &str) -> Result<KeyPair, Error> {
|
||||
let mut h_mac = Hmac::new(Sha256::new(), password.as_bytes());
|
||||
let mut derived_key = vec![0u8; 16];
|
||||
pbkdf2(&mut h_mac, password.as_bytes(), 2000, &mut derived_key);
|
||||
let mut derived_key = [0u8; 32];
|
||||
let salt = pbkdf2::Salt(password.as_bytes());
|
||||
let sec = pbkdf2::Secret(password.as_bytes());
|
||||
pbkdf2::sha256(2000, salt, sec, &mut derived_key);
|
||||
|
||||
let mut key = vec![0; self.ciphertext.len()];
|
||||
let len = crypto::aes::decrypt_cbc(&derived_key, &self.iv, &self.ciphertext, &mut key).map_err(|_| Error::InvalidPassword)?;
|
||||
let len = crypto::aes::decrypt_128_cbc(&derived_key[0..16], &self.iv, &self.ciphertext, &mut key)
|
||||
.map_err(|_| Error::InvalidPassword)?;
|
||||
let unpadded = &key[..len];
|
||||
|
||||
let secret = Secret::from_unsafe_slice(&unpadded.keccak256())?;
|
||||
|
@ -17,7 +17,6 @@ multihash ="0.7"
|
||||
order-stat = "0.1"
|
||||
parking_lot = "0.5"
|
||||
rand = "0.4"
|
||||
rust-crypto = "0.2"
|
||||
rustc-hex = "1.0"
|
||||
semver = "0.9"
|
||||
serde = "1.0"
|
||||
|
@ -23,7 +23,6 @@ extern crate futures;
|
||||
|
||||
extern crate ansi_term;
|
||||
extern crate cid;
|
||||
extern crate crypto as rust_crypto;
|
||||
extern crate futures_cpupool;
|
||||
extern crate itertools;
|
||||
extern crate multihash;
|
||||
|
@ -18,21 +18,15 @@
|
||||
|
||||
use multihash;
|
||||
use cid::{Cid, Codec, Version};
|
||||
use rust_crypto::sha2::Sha256;
|
||||
use rust_crypto::digest::Digest;
|
||||
use crypto::digest;
|
||||
use jsonrpc_core::Error;
|
||||
use v1::types::Bytes;
|
||||
use super::errors;
|
||||
|
||||
/// Compute CIDv0 from protobuf encoded bytes.
|
||||
pub fn cid(content: Bytes) -> Result<String, Error> {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.input(&content.0);
|
||||
let len = hasher.output_bytes();
|
||||
let mut buf = Vec::with_capacity(len);
|
||||
buf.resize(len, 0);
|
||||
hasher.result(&mut buf);
|
||||
let mh = multihash::encode(multihash::Hash::SHA2256, &buf).map_err(errors::encoding)?;
|
||||
let hash = digest::sha256(&content.0);
|
||||
let mh = multihash::encode(multihash::Hash::SHA2256, &*hash).map_err(errors::encoding)?;
|
||||
let cid = Cid::new(Codec::DagProtobuf, Version::V0, &mh);
|
||||
Ok(cid.to_string().into())
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
use rand::{Rng, OsRng};
|
||||
use ethkey::{Public, Secret, Random, Generator, math};
|
||||
use ethkey::{self, Public, Secret, Random, Generator, math};
|
||||
use crypto;
|
||||
use bytes::Bytes;
|
||||
use jsonrpc_core::Error;
|
||||
@ -36,7 +36,7 @@ pub fn generate_document_key(account_public: Public, server_key_public: Public)
|
||||
let (common_point, encrypted_point) = encrypt_secret(document_key.public(), &server_key_public)?;
|
||||
|
||||
// ..and now encrypt document key with account public
|
||||
let encrypted_key = crypto::ecies::encrypt(&account_public, &crypto::DEFAULT_MAC, document_key.public())
|
||||
let encrypted_key = ethkey::crypto::ecies::encrypt(&account_public, &crypto::DEFAULT_MAC, document_key.public())
|
||||
.map_err(errors::encryption)?;
|
||||
|
||||
Ok(EncryptedDocumentKey {
|
||||
@ -57,7 +57,7 @@ pub fn encrypt_document(key: Bytes, document: Bytes) -> Result<Bytes, Error> {
|
||||
{
|
||||
let (mut encryption_buffer, iv_buffer) = encrypted_document.split_at_mut(document.len());
|
||||
|
||||
crypto::aes::encrypt(&key, &iv, &document, &mut encryption_buffer);
|
||||
crypto::aes::encrypt_128_ctr(&key, &iv, &document, &mut encryption_buffer).map_err(errors::encryption)?;
|
||||
iv_buffer.copy_from_slice(&iv);
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ pub fn decrypt_document(key: Bytes, mut encrypted_document: Bytes) -> Result<Byt
|
||||
// use symmetric decryption to decrypt document
|
||||
let iv = encrypted_document.split_off(encrypted_document_len - INIT_VEC_LEN);
|
||||
let mut document = vec![0; encrypted_document_len - INIT_VEC_LEN];
|
||||
crypto::aes::decrypt(&key, &iv, &encrypted_document, &mut document);
|
||||
crypto::aes::decrypt_128_ctr(&key, &iv, &encrypted_document, &mut document).map_err(errors::encryption)?;
|
||||
|
||||
Ok(document)
|
||||
}
|
||||
|
@ -20,8 +20,8 @@ use std::collections::{BTreeMap, HashSet};
|
||||
|
||||
use version::version_data;
|
||||
|
||||
use crypto::{ecies, DEFAULT_MAC};
|
||||
use ethkey::{Brain, Generator};
|
||||
use crypto::DEFAULT_MAC;
|
||||
use ethkey::{crypto::ecies, Brain, Generator};
|
||||
use ethstore::random_phrase;
|
||||
use sync::LightSyncProvider;
|
||||
use ethcore::account_provider::AccountProvider;
|
||||
|
@ -22,8 +22,8 @@ use std::collections::{BTreeMap, HashSet};
|
||||
use ethereum_types::Address;
|
||||
use version::version_data;
|
||||
|
||||
use crypto::{DEFAULT_MAC, ecies};
|
||||
use ethkey::{Brain, Generator};
|
||||
use crypto::DEFAULT_MAC;
|
||||
use ethkey::{crypto::ecies, Brain, Generator};
|
||||
use ethstore::random_phrase;
|
||||
use sync::{SyncProvider, ManageNetwork};
|
||||
use ethcore::account_provider::AccountProvider;
|
||||
|
@ -21,7 +21,8 @@ use std::sync::mpsc;
|
||||
use futures::{self, Future};
|
||||
use parking_lot::Mutex;
|
||||
use tokio_core::reactor::Core;
|
||||
use crypto;
|
||||
use crypto::DEFAULT_MAC;
|
||||
use ethkey::crypto;
|
||||
use super::acl_storage::AclStorage;
|
||||
use super::key_storage::KeyStorage;
|
||||
use super::key_server_set::KeyServerSet;
|
||||
@ -105,7 +106,7 @@ impl DocumentKeyServer for KeyServerImpl {
|
||||
self.store_document_key(key_id, author, encrypted_document_key.common_point, encrypted_document_key.encrypted_point)?;
|
||||
|
||||
// encrypt document key with requestor public key
|
||||
let document_key = crypto::ecies::encrypt(&public, &crypto::DEFAULT_MAC, &document_key)
|
||||
let document_key = crypto::ecies::encrypt(&public, &DEFAULT_MAC, &document_key)
|
||||
.map_err(|err| Error::Internal(format!("Error encrypting document key: {}", err)))?;
|
||||
Ok(document_key)
|
||||
}
|
||||
@ -122,7 +123,7 @@ impl DocumentKeyServer for KeyServerImpl {
|
||||
.decrypted_secret;
|
||||
|
||||
// encrypt document key with requestor public key
|
||||
let document_key = crypto::ecies::encrypt(&public, &crypto::DEFAULT_MAC, &document_key)
|
||||
let document_key = crypto::ecies::encrypt(&public, &DEFAULT_MAC, &document_key)
|
||||
.map_err(|err| Error::Internal(format!("Error encrypting document key: {}", err)))?;
|
||||
Ok(document_key)
|
||||
}
|
||||
@ -152,7 +153,7 @@ impl MessageSigner for KeyServerImpl {
|
||||
combined_signature[32..].clone_from_slice(&**message_signature.1);
|
||||
|
||||
// encrypt combined signature with requestor public key
|
||||
let message_signature = crypto::ecies::encrypt(&public, &crypto::DEFAULT_MAC, &combined_signature)
|
||||
let message_signature = crypto::ecies::encrypt(&public, &DEFAULT_MAC, &combined_signature)
|
||||
.map_err(|err| Error::Internal(format!("Error encrypting message signature: {}", err)))?;
|
||||
Ok(message_signature)
|
||||
}
|
||||
@ -167,7 +168,7 @@ impl MessageSigner for KeyServerImpl {
|
||||
let message_signature = signing_session.wait()?;
|
||||
|
||||
// encrypt combined signature with requestor public key
|
||||
let message_signature = crypto::ecies::encrypt(&public, &crypto::DEFAULT_MAC, &*message_signature)
|
||||
let message_signature = crypto::ecies::encrypt(&public, &DEFAULT_MAC, &*message_signature)
|
||||
.map_err(|err| Error::Internal(format!("Error encrypting message signature: {}", err)))?;
|
||||
Ok(message_signature)
|
||||
}
|
||||
@ -229,8 +230,8 @@ pub mod tests {
|
||||
use std::sync::Arc;
|
||||
use std::net::SocketAddr;
|
||||
use std::collections::BTreeMap;
|
||||
use crypto;
|
||||
use ethkey::{self, Secret, Random, Generator, verify_public};
|
||||
use crypto::DEFAULT_MAC;
|
||||
use ethkey::{self, crypto, Secret, Random, Generator, verify_public};
|
||||
use acl_storage::DummyAclStorage;
|
||||
use key_storage::KeyStorage;
|
||||
use key_storage::tests::DummyKeyStorage;
|
||||
@ -358,12 +359,12 @@ pub mod tests {
|
||||
let secret = Random.generate().unwrap().secret().clone();
|
||||
let signature = ethkey::sign(&secret, &document).unwrap();
|
||||
let generated_key = key_servers[0].generate_document_key(&document, &signature.clone().into(), threshold).unwrap();
|
||||
let generated_key = crypto::ecies::decrypt(&secret, &crypto::DEFAULT_MAC, &generated_key).unwrap();
|
||||
let generated_key = crypto::ecies::decrypt(&secret, &DEFAULT_MAC, &generated_key).unwrap();
|
||||
|
||||
// now let's try to retrieve key back
|
||||
for key_server in key_servers.iter() {
|
||||
let retrieved_key = key_server.restore_document_key(&document, &signature.clone().into()).unwrap();
|
||||
let retrieved_key = crypto::ecies::decrypt(&secret, &crypto::DEFAULT_MAC, &retrieved_key).unwrap();
|
||||
let retrieved_key = crypto::ecies::decrypt(&secret, &DEFAULT_MAC, &retrieved_key).unwrap();
|
||||
assert_eq!(retrieved_key, generated_key);
|
||||
}
|
||||
}
|
||||
@ -380,12 +381,12 @@ pub mod tests {
|
||||
let secret = Random.generate().unwrap().secret().clone();
|
||||
let signature = ethkey::sign(&secret, &document).unwrap();
|
||||
let generated_key = key_servers[0].generate_document_key(&document, &signature.clone().into(), *threshold).unwrap();
|
||||
let generated_key = crypto::ecies::decrypt(&secret, &crypto::DEFAULT_MAC, &generated_key).unwrap();
|
||||
let generated_key = crypto::ecies::decrypt(&secret, &DEFAULT_MAC, &generated_key).unwrap();
|
||||
|
||||
// now let's try to retrieve key back
|
||||
for (i, key_server) in key_servers.iter().enumerate() {
|
||||
let retrieved_key = key_server.restore_document_key(&document, &signature.clone().into()).unwrap();
|
||||
let retrieved_key = crypto::ecies::decrypt(&secret, &crypto::DEFAULT_MAC, &retrieved_key).unwrap();
|
||||
let retrieved_key = crypto::ecies::decrypt(&secret, &DEFAULT_MAC, &retrieved_key).unwrap();
|
||||
assert_eq!(retrieved_key, generated_key);
|
||||
|
||||
let key_share = key_storages[i].get(&document).unwrap().unwrap();
|
||||
@ -419,7 +420,7 @@ pub mod tests {
|
||||
// now let's try to retrieve key back
|
||||
for key_server in key_servers.iter() {
|
||||
let retrieved_key = key_server.restore_document_key(&server_key_id, &signature.clone().into()).unwrap();
|
||||
let retrieved_key = crypto::ecies::decrypt(&requestor_secret, &crypto::DEFAULT_MAC, &retrieved_key).unwrap();
|
||||
let retrieved_key = crypto::ecies::decrypt(&requestor_secret, &DEFAULT_MAC, &retrieved_key).unwrap();
|
||||
let retrieved_key = Public::from_slice(&retrieved_key);
|
||||
assert_eq!(retrieved_key, generated_key);
|
||||
}
|
||||
@ -442,7 +443,7 @@ pub mod tests {
|
||||
// sign message
|
||||
let message_hash = H256::from(42);
|
||||
let combined_signature = key_servers[0].sign_message_schnorr(&server_key_id, &signature.into(), message_hash.clone()).unwrap();
|
||||
let combined_signature = crypto::ecies::decrypt(&requestor_secret, &crypto::DEFAULT_MAC, &combined_signature).unwrap();
|
||||
let combined_signature = crypto::ecies::decrypt(&requestor_secret, &DEFAULT_MAC, &combined_signature).unwrap();
|
||||
let signature_c = Secret::from_slice(&combined_signature[..32]).unwrap();
|
||||
let signature_s = Secret::from_slice(&combined_signature[32..]).unwrap();
|
||||
|
||||
@ -462,14 +463,14 @@ pub mod tests {
|
||||
let secret = Random.generate().unwrap().secret().clone();
|
||||
let signature = ethkey::sign(&secret, &document).unwrap();
|
||||
let generated_key = key_servers[0].generate_document_key(&document, &signature.clone().into(), threshold).unwrap();
|
||||
let generated_key = crypto::ecies::decrypt(&secret, &crypto::DEFAULT_MAC, &generated_key).unwrap();
|
||||
let generated_key = crypto::ecies::decrypt(&secret, &DEFAULT_MAC, &generated_key).unwrap();
|
||||
|
||||
// remove key from node0
|
||||
key_servers[0].cluster().key_storage().remove(&document).unwrap();
|
||||
|
||||
// now let's try to retrieve key back by requesting it from node0, so that session must be delegated
|
||||
let retrieved_key = key_servers[0].restore_document_key(&document, &signature.into()).unwrap();
|
||||
let retrieved_key = crypto::ecies::decrypt(&secret, &crypto::DEFAULT_MAC, &retrieved_key).unwrap();
|
||||
let retrieved_key = crypto::ecies::decrypt(&secret, &DEFAULT_MAC, &retrieved_key).unwrap();
|
||||
assert_eq!(retrieved_key, generated_key);
|
||||
}
|
||||
|
||||
@ -491,7 +492,7 @@ pub mod tests {
|
||||
// sign message
|
||||
let message_hash = H256::from(42);
|
||||
let combined_signature = key_servers[0].sign_message_schnorr(&server_key_id, &signature.into(), message_hash.clone()).unwrap();
|
||||
let combined_signature = crypto::ecies::decrypt(&requestor_secret, &crypto::DEFAULT_MAC, &combined_signature).unwrap();
|
||||
let combined_signature = crypto::ecies::decrypt(&requestor_secret, &DEFAULT_MAC, &combined_signature).unwrap();
|
||||
let signature_c = Secret::from_slice(&combined_signature[..32]).unwrap();
|
||||
let signature_s = Secret::from_slice(&combined_signature[32..]).unwrap();
|
||||
|
||||
@ -517,7 +518,7 @@ pub mod tests {
|
||||
// sign message
|
||||
let message_hash = H256::random();
|
||||
let signature = key_servers[0].sign_message_ecdsa(&server_key_id, &signature.into(), message_hash.clone()).unwrap();
|
||||
let signature = crypto::ecies::decrypt(&requestor_secret, &crypto::DEFAULT_MAC, &signature).unwrap();
|
||||
let signature = crypto::ecies::decrypt(&requestor_secret, &DEFAULT_MAC, &signature).unwrap();
|
||||
let signature: H520 = signature[0..65].into();
|
||||
|
||||
// check signature
|
||||
|
@ -1279,7 +1279,7 @@ mod tests {
|
||||
assert!(decrypted_secret.decrypt_shadows.is_some());
|
||||
// check that KS client is able to restore original secret
|
||||
use crypto::DEFAULT_MAC;
|
||||
use crypto::ecies::decrypt;
|
||||
use ethkey::crypto::ecies::decrypt;
|
||||
let decrypt_shadows: Vec<_> = decrypted_secret.decrypt_shadows.unwrap().into_iter()
|
||||
.map(|c| Secret::from_slice(&decrypt(key_pair.secret(), &DEFAULT_MAC, &c).unwrap()).unwrap())
|
||||
.collect();
|
||||
@ -1423,7 +1423,7 @@ mod tests {
|
||||
|
||||
// 4 nodes must be able to recover original secret
|
||||
use crypto::DEFAULT_MAC;
|
||||
use crypto::ecies::decrypt;
|
||||
use ethkey::crypto::ecies::decrypt;
|
||||
let result = sessions[0].decrypted_secret().unwrap().unwrap();
|
||||
assert_eq!(3, sessions.iter().skip(1).filter(|s| s.decrypted_secret() == Some(Ok(result.clone()))).count());
|
||||
let decrypt_shadows: Vec<_> = result.decrypt_shadows.unwrap().into_iter()
|
||||
|
@ -37,7 +37,7 @@ use std::sync::Arc;
|
||||
use std::collections::BTreeSet;
|
||||
use futures::{Future, Poll, Async};
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
use crypto::ecdh::agree;
|
||||
use ethkey::crypto::ecdh::agree;
|
||||
use ethkey::{Random, Generator, KeyPair, Public, Signature, verify_public, sign, recover};
|
||||
use ethereum_types::H256;
|
||||
use key_server_cluster::{NodeId, Error, NodeKeyPair};
|
||||
|
@ -19,7 +19,7 @@ use std::u16;
|
||||
use std::ops::Deref;
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
use serde_json;
|
||||
use crypto::ecies;
|
||||
use ethkey::crypto::ecies;
|
||||
use ethkey::{Secret, KeyPair};
|
||||
use ethkey::math::curve_order;
|
||||
use ethereum_types::{H256, U256};
|
||||
@ -306,7 +306,7 @@ pub mod tests {
|
||||
use futures::Poll;
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
use ethkey::{Random, Generator, KeyPair};
|
||||
use crypto::ecdh::agree;
|
||||
use ethkey::crypto::ecdh::agree;
|
||||
use key_server_cluster::Error;
|
||||
use key_server_cluster::message::Message;
|
||||
use super::{MESSAGE_HEADER_SIZE, CURRENT_HEADER_VERSION, MessageHeader, fix_shared_key, encrypt_message,
|
||||
|
@ -17,8 +17,8 @@
|
||||
use std::collections::{BTreeSet, BTreeMap};
|
||||
use ethereum_types::H256;
|
||||
use ethkey::{Public, Secret};
|
||||
use crypto::ecies::encrypt;
|
||||
use crypto::DEFAULT_MAC;
|
||||
use ethkey::crypto::ecies::encrypt;
|
||||
use key_server_cluster::{Error, NodeId, DocumentKeyShare, EncryptedDocumentKeyShadow};
|
||||
use key_server_cluster::math;
|
||||
use key_server_cluster::jobs::job_session::{JobPartialRequestAction, JobPartialResponseAction, JobExecutor};
|
||||
|
@ -15,7 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::sync::Arc;
|
||||
use crypto::ecdh::agree;
|
||||
use ethkey::crypto::ecdh::agree;
|
||||
use ethkey::{KeyPair, Public, Signature, Error as EthKeyError, sign, public_to_address};
|
||||
use ethcore::account_provider::AccountProvider;
|
||||
use ethereum_types::{H256, Address};
|
||||
@ -54,7 +54,8 @@ impl NodeKeyPair for PlainNodeKeyPair {
|
||||
}
|
||||
|
||||
fn compute_shared_key(&self, peer_public: &Public) -> Result<KeyPair, EthKeyError> {
|
||||
agree(self.key_pair.secret(), peer_public).map_err(|e| EthKeyError::Custom(e.into()))
|
||||
agree(self.key_pair.secret(), peer_public)
|
||||
.map_err(|e| EthKeyError::Custom(e.to_string()))
|
||||
.and_then(KeyPair::from_secret)
|
||||
}
|
||||
}
|
||||
|
@ -168,6 +168,12 @@ impl From<ethkey::Error> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ethkey::crypto::Error> for Error {
|
||||
fn from(err: ethkey::crypto::Error) -> Self {
|
||||
Error::EthKey(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<kvdb::Error> for Error {
|
||||
fn from(err: kvdb::Error) -> Self {
|
||||
Error::Database(err.to_string())
|
||||
@ -176,7 +182,7 @@ impl From<kvdb::Error> for Error {
|
||||
|
||||
impl From<crypto::Error> for Error {
|
||||
fn from(err: crypto::Error) -> Self {
|
||||
Error::EthKey(err.into())
|
||||
Error::EthKey(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ use rcrypto::symmetriccipher::*;
|
||||
use rcrypto::buffer::*;
|
||||
use tiny_keccak::Keccak;
|
||||
use bytes::{Buf, BufMut};
|
||||
use crypto;
|
||||
use ethkey::crypto;
|
||||
use network::{Error, ErrorKind};
|
||||
|
||||
const ENCRYPTED_HEADER_LEN: usize = 32;
|
||||
|
@ -25,7 +25,7 @@ use connection::{Connection};
|
||||
use node_table::NodeId;
|
||||
use io::{IoContext, StreamToken};
|
||||
use ethkey::{KeyPair, Public, Secret, recover, sign, Generator, Random};
|
||||
use crypto::{ecdh, ecies};
|
||||
use ethkey::crypto::{ecdh, ecies};
|
||||
use network::{Error, ErrorKind, HostInfo};
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
|
@ -151,8 +151,14 @@ impl From<ethkey::Error> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crypto::Error> for Error {
|
||||
fn from(_err: crypto::Error) -> Self {
|
||||
impl From<ethkey::crypto::Error> for Error {
|
||||
fn from(_err: ethkey::crypto::Error) -> Self {
|
||||
ErrorKind::Auth.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crypto::error::SymmError> for Error {
|
||||
fn from(_err: crypto::error::SymmError) -> Self {
|
||||
ErrorKind::Auth.into()
|
||||
}
|
||||
}
|
||||
@ -168,11 +174,11 @@ fn test_errors() {
|
||||
|
||||
match *<Error as From<rlp::DecoderError>>::from(rlp::DecoderError::RlpIsTooBig).kind() {
|
||||
ErrorKind::Auth => {},
|
||||
_ => panic!("Unexpeceted error"),
|
||||
_ => panic!("Unexpected error"),
|
||||
}
|
||||
|
||||
match *<Error as From<crypto::Error>>::from(crypto::Error::InvalidMessage).kind() {
|
||||
match *<Error as From<ethkey::crypto::Error>>::from(ethkey::crypto::Error::InvalidMessage).kind() {
|
||||
ErrorKind::Auth => {},
|
||||
_ => panic!("Unexpeceted error"),
|
||||
_ => panic!("Unexpected error"),
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ mem = { path = "../util/mem" }
|
||||
ordered-float = "0.5"
|
||||
parking_lot = "0.5"
|
||||
rand = "0.4"
|
||||
ring = "0.12"
|
||||
rlp = { path = "../util/rlp" }
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
|
@ -28,7 +28,6 @@ extern crate ordered_float;
|
||||
extern crate parking_lot;
|
||||
extern crate rand;
|
||||
extern crate rlp;
|
||||
extern crate ring;
|
||||
extern crate serde;
|
||||
extern crate slab;
|
||||
extern crate smallvec;
|
||||
|
@ -16,11 +16,11 @@
|
||||
|
||||
//! Encryption schemes supported by RPC layer.
|
||||
|
||||
use crypto;
|
||||
use crypto::aes_gcm::{Encryptor, Decryptor};
|
||||
use ethkey::crypto::ecies;
|
||||
use ethereum_types::H256;
|
||||
use ethkey::{self, Public, Secret};
|
||||
use mem::Memzero;
|
||||
use ring::aead::{self, AES_256_GCM, SealingKey, OpeningKey};
|
||||
|
||||
/// Length of AES key
|
||||
pub const AES_KEY_LEN: usize = 32;
|
||||
@ -72,38 +72,15 @@ impl EncryptionInstance {
|
||||
}
|
||||
|
||||
/// Encrypt the supplied plaintext
|
||||
pub fn encrypt(self, plain: &[u8]) -> Vec<u8> {
|
||||
pub fn encrypt(self, plain: &[u8]) -> Option<Vec<u8>> {
|
||||
match self.0 {
|
||||
EncryptionInner::AES(key, nonce, encode) => {
|
||||
let sealing_key = SealingKey::new(&AES_256_GCM, &*key)
|
||||
.expect("key is of correct len; qed");
|
||||
|
||||
let encrypt_plain = move |buf: &mut Vec<u8>| {
|
||||
let out_suffix_capacity = AES_256_GCM.tag_len();
|
||||
|
||||
let prepend_len = buf.len();
|
||||
buf.extend(plain);
|
||||
|
||||
buf.resize(prepend_len + plain.len() + out_suffix_capacity, 0);
|
||||
|
||||
let out_size = aead::seal_in_place(
|
||||
&sealing_key,
|
||||
&nonce,
|
||||
&[], // no authenticated data.
|
||||
&mut buf[prepend_len..],
|
||||
out_suffix_capacity,
|
||||
).expect("key, nonce, buf are valid and out suffix large enough; qed");
|
||||
|
||||
// truncate to the output size and return.
|
||||
buf.truncate(prepend_len + out_size);
|
||||
};
|
||||
|
||||
match encode {
|
||||
AesEncode::AppendedNonce => {
|
||||
let mut buf = Vec::new();
|
||||
encrypt_plain(&mut buf);
|
||||
let mut enc = Encryptor::aes_256_gcm(&*key).ok()?;
|
||||
let mut buf = enc.encrypt(&nonce, plain.to_vec()).ok()?;
|
||||
buf.extend(&nonce[..]);
|
||||
buf
|
||||
Some(buf)
|
||||
}
|
||||
AesEncode::OnTopics(topics) => {
|
||||
let mut buf = Vec::new();
|
||||
@ -111,14 +88,16 @@ impl EncryptionInstance {
|
||||
xor(&mut t.0, &key);
|
||||
buf.extend(&t.0);
|
||||
}
|
||||
encrypt_plain(&mut buf);
|
||||
buf
|
||||
let mut enc = Encryptor::aes_256_gcm(&*key).ok()?;
|
||||
enc.offset(buf.len());
|
||||
buf.extend(plain);
|
||||
let ciphertext = enc.encrypt(&nonce, buf).ok()?;
|
||||
Some(ciphertext)
|
||||
}
|
||||
}
|
||||
}
|
||||
EncryptionInner::ECIES(valid_public) => {
|
||||
crypto::ecies::encrypt(&valid_public, &[], plain)
|
||||
.expect("validity of public key an invariant of the type; qed")
|
||||
ecies::encrypt(&valid_public, &[], plain).ok()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -169,58 +148,36 @@ impl DecryptionInstance {
|
||||
pub fn decrypt(self, ciphertext: &[u8]) -> Option<Vec<u8>> {
|
||||
match self.0 {
|
||||
DecryptionInner::AES(extract) => {
|
||||
let decrypt = |
|
||||
key: Memzero<[u8; AES_KEY_LEN]>,
|
||||
nonce: [u8; AES_NONCE_LEN],
|
||||
ciphertext: &[u8]
|
||||
| {
|
||||
if ciphertext.len() < AES_256_GCM.tag_len() { return None }
|
||||
|
||||
let opening_key = OpeningKey::new(&AES_256_GCM, &*key)
|
||||
.expect("key length is valid for mode; qed");
|
||||
|
||||
let mut buf = ciphertext.to_vec();
|
||||
|
||||
// decrypted plaintext always ends up at the
|
||||
// front of the buffer.
|
||||
let maybe_decrypted = aead::open_in_place(
|
||||
&opening_key,
|
||||
&nonce,
|
||||
&[], // no authenticated data
|
||||
0, // no header.
|
||||
&mut buf,
|
||||
).ok().map(|plain_slice| plain_slice.len());
|
||||
|
||||
maybe_decrypted.map(move |len| { buf.truncate(len); buf })
|
||||
};
|
||||
|
||||
match extract {
|
||||
AesExtract::AppendedNonce(key) => {
|
||||
if ciphertext.len() < AES_NONCE_LEN { return None }
|
||||
|
||||
if ciphertext.len() < AES_NONCE_LEN {
|
||||
return None
|
||||
}
|
||||
// nonce is the suffix of ciphertext.
|
||||
let mut nonce = [0; AES_NONCE_LEN];
|
||||
let nonce_offset = ciphertext.len() - AES_NONCE_LEN;
|
||||
|
||||
nonce.copy_from_slice(&ciphertext[nonce_offset..]);
|
||||
decrypt(key, nonce, &ciphertext[..nonce_offset])
|
||||
Decryptor::aes_256_gcm(&*key).ok()?
|
||||
.decrypt(&nonce, Vec::from(&ciphertext[..nonce_offset]))
|
||||
.ok()
|
||||
}
|
||||
AesExtract::OnTopics(num_topics, known_index, known_topic) => {
|
||||
if ciphertext.len() < num_topics * 32 { return None }
|
||||
|
||||
if ciphertext.len() < num_topics * 32 {
|
||||
return None
|
||||
}
|
||||
let mut salted_topic = H256::new();
|
||||
salted_topic.copy_from_slice(&ciphertext[(known_index * 32)..][..32]);
|
||||
|
||||
let key = Memzero::from((salted_topic ^ known_topic).0);
|
||||
|
||||
let offset = num_topics * 32;
|
||||
decrypt(key, BROADCAST_IV, &ciphertext[offset..])
|
||||
Decryptor::aes_256_gcm(&*key).ok()?
|
||||
.decrypt(&BROADCAST_IV, Vec::from(&ciphertext[offset..]))
|
||||
.ok()
|
||||
}
|
||||
}
|
||||
}
|
||||
DecryptionInner::ECIES(secret) => {
|
||||
// secret is checked for validity, so only fails on invalid message.
|
||||
crypto::ecies::decrypt(&secret, &[], ciphertext).ok()
|
||||
ecies::decrypt(&secret, &[], ciphertext).ok()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -230,16 +187,6 @@ impl DecryptionInstance {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn aes_key_len_should_be_equal_to_constant() {
|
||||
assert_eq!(::ring::aead::AES_256_GCM.key_len(), AES_KEY_LEN);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aes_nonce_len_should_be_equal_to_constant() {
|
||||
assert_eq!(::ring::aead::AES_256_GCM.nonce_len(), AES_NONCE_LEN);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encrypt_asymmetric() {
|
||||
use ethkey::{Generator, Random};
|
||||
@ -247,7 +194,7 @@ mod tests {
|
||||
let key_pair = Random.generate().unwrap();
|
||||
let test_message = move |message: &[u8]| {
|
||||
let instance = EncryptionInstance::ecies(key_pair.public().clone()).unwrap();
|
||||
let ciphertext = instance.encrypt(&message);
|
||||
let ciphertext = instance.encrypt(&message).unwrap();
|
||||
|
||||
if !message.is_empty() {
|
||||
assert!(&ciphertext[..message.len()] != message)
|
||||
@ -273,7 +220,7 @@ mod tests {
|
||||
let key = Memzero::from(rng.gen::<[u8; 32]>());
|
||||
|
||||
let instance = EncryptionInstance::aes(key.clone(), rng.gen());
|
||||
let ciphertext = instance.encrypt(message);
|
||||
let ciphertext = instance.encrypt(message).unwrap();
|
||||
|
||||
if !message.is_empty() {
|
||||
assert!(&ciphertext[..message.len()] != message)
|
||||
@ -303,7 +250,7 @@ mod tests {
|
||||
let key = Memzero::from(rng.gen::<[u8; 32]>());
|
||||
|
||||
let instance = EncryptionInstance::broadcast(key, all_topics);
|
||||
let ciphertext = instance.encrypt(message);
|
||||
let ciphertext = instance.encrypt(message).unwrap();
|
||||
|
||||
if !message.is_empty() {
|
||||
assert!(&ciphertext[..message.len()] != message)
|
||||
|
@ -402,7 +402,7 @@ mod tests {
|
||||
sign_with: Some(signing_pair.secret().unwrap())
|
||||
}).unwrap();
|
||||
|
||||
let encrypted = encryption_instance.encrypt(&payload);
|
||||
let encrypted = encryption_instance.encrypt(&payload).unwrap();
|
||||
|
||||
let message = Message::create(CreateParams {
|
||||
ttl: 100,
|
||||
|
@ -25,7 +25,6 @@ use ethereum_types::H256;
|
||||
use ethkey::{KeyPair, Public, Secret};
|
||||
use mem::Memzero;
|
||||
use rand::{Rng, OsRng};
|
||||
use ring::error::Unspecified;
|
||||
|
||||
use rpc::crypto::{AES_KEY_LEN, EncryptionInstance, DecryptionInstance};
|
||||
|
||||
@ -54,10 +53,8 @@ impl Key {
|
||||
}
|
||||
|
||||
/// From secret asymmetric key. Fails if secret is invalid.
|
||||
pub fn from_secret(secret: Secret) -> Result<Self, Unspecified> {
|
||||
KeyPair::from_secret(secret)
|
||||
.map(Key::Asymmetric)
|
||||
.map_err(|_| Unspecified)
|
||||
pub fn from_secret(secret: Secret) -> Option<Self> {
|
||||
KeyPair::from_secret(secret).map(Key::Asymmetric).ok()
|
||||
}
|
||||
|
||||
/// From raw symmetric key.
|
||||
@ -179,7 +176,7 @@ mod tests {
|
||||
#[test]
|
||||
fn rejects_invalid_secret() {
|
||||
let bad_secret = ::ethkey::Secret::from([0xff; 32]);
|
||||
assert!(Key::from_secret(bad_secret).is_err());
|
||||
assert!(Key::from_secret(bad_secret).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -226,7 +226,7 @@ impl<P: PoolHandle + 'static, M: Send + Sync + 'static> Whisper for WhisperClien
|
||||
|
||||
fn add_private_key(&self, private: types::Private) -> Result<types::Identity, Error> {
|
||||
let key_pair = Key::from_secret(private.into_inner().into())
|
||||
.map_err(|_| whisper_error("Invalid private key"))?;
|
||||
.ok_or_else(|| whisper_error("Invalid private key"))?;
|
||||
|
||||
Ok(HexEncode(self.store.write().insert(key_pair)))
|
||||
}
|
||||
@ -317,7 +317,7 @@ impl<P: PoolHandle + 'static, M: Send + Sync + 'static> Whisper for WhisperClien
|
||||
sign_with: sign_with.as_ref(),
|
||||
}).map_err(whisper_error)?;
|
||||
|
||||
encryption.encrypt(&payload)
|
||||
encryption.encrypt(&payload).ok_or(whisper_error("encryption error"))?
|
||||
};
|
||||
|
||||
// mining the packet is the heaviest item of work by far.
|
||||
|
Loading…
Reference in New Issue
Block a user