// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of OpenEthereum. // OpenEthereum 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. // OpenEthereum 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 OpenEthereum. If not, see . #![allow(deprecated)] use parity_crypto::error::SymmError; use secp256k1; use std::io; 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 super::Error; use secp256k1::{self, ecdh, key}; use Public; use Secret; use SECP256K1; /// Agree on a shared secret pub fn agree(secret: &Secret, public: &Public) -> Result { 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 super::{ecdh, Error}; use ethereum_types::H128; use parity_crypto::{aes, digest, hmac, is_equal}; use Generator; use Public; use Random; use 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, 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, 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 Generator; use Random; #[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[..]); } }