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:
committed by
Marek Kotewicz
parent
a4c7843a07
commit
e30839e85f
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user