cfx_crypto/
crypto.rs

1// Copyright 2015-2019 Parity Technologies (UK) Ltd.
2// This file is part of Parity Ethereum.
3//
4// Parity Ethereum is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Parity Ethereum is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Parity Ethereum.  If not, see <http://www.gnu.org/licenses/>.
16
17// Copyright 2020 Parity Technologies
18//
19// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
20// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
21// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
22// option. This file may not be copied, modified, or distributed
23// except according to those terms.
24
25use crate::{KeyPair, RandomKeyPairGenerator, SecretKey};
26use std::io;
27use subtle::ConstantTimeEq;
28
29pub const KEY_LENGTH: usize = 32;
30pub const KEY_ITERATIONS: usize = 10240;
31pub const KEY_LENGTH_AES: usize = KEY_LENGTH / 2;
32
33#[derive(Debug, thiserror::Error)]
34pub enum Error {
35    #[error("secp256k1 error: {0}")]
36    Secp(#[from] secp256k1::Error),
37    #[error("i/o error: {0}")]
38    Io(#[from] io::Error),
39    #[error("invalid message")]
40    InvalidMessage,
41    #[error("aes error")]
42    Aes,
43}
44
45/// ECDH functions
46pub mod ecdh {
47    use super::{Error, SecretKey};
48    use secp256k1::{self, ecdh, PublicKey};
49
50    /// Agree on a shared secret.
51    ///
52    /// Returns the raw X-coordinate of the EC-DH point (first 32 bytes of
53    /// `shared_secret_point`). Matches the legacy `parity-secp256k1`
54    /// `SharedSecret::new_raw()[0..32]` semantics, which Conflux keystore
55    /// ECIES relies on. Do NOT switch to `ecdh::SharedSecret::new()` — that
56    /// returns SHA-256 of the compressed point, a different value that would
57    /// break decryption of existing keystore files.
58    pub fn agree<S, P>(secret: &S, public: &P) -> Result<S, Error>
59    where
60        S: SecretKey,
61        P: AsRef<[u8]>,
62    {
63        let pdata = {
64            let mut temp = [4u8; 65];
65            let pub_bytes = public.as_ref();
66            if pub_bytes.len() < 64 {
67                return Err(Error::Secp(secp256k1::Error::InvalidPublicKey));
68            }
69            (&mut temp[1..65]).copy_from_slice(&pub_bytes[0..64]);
70            temp
71        };
72
73        let publ = PublicKey::from_slice(&pdata)?;
74        let sec = secp256k1::SecretKey::from_slice(secret.as_ref())?;
75        let xy = ecdh::shared_secret_point(&publ, &sec);
76
77        S::from_unsafe_slice(&xy[0..32])
78            .map_err(|_| Error::Secp(secp256k1::Error::InvalidSecretKey))
79    }
80}
81
82/// ECIES function
83pub mod ecies {
84    use super::{
85        ecdh, is_equal, Error, KeyPair, RandomKeyPairGenerator, SecretKey,
86    };
87    use cfx_types::H128;
88    use hmac::{Hmac, Mac};
89    use sha2::{Digest, Sha256};
90
91    type HmacSha256 = Hmac<Sha256>;
92
93    /// Encrypt a message with a public key, writing an HMAC covering both
94    /// the plaintext and authenticated data.
95    ///
96    /// Authenticated data may be empty.
97    pub fn encrypt<G, KP, S, P>(
98        generator: &mut G, public: &P, auth_data: &[u8], plain: &[u8],
99    ) -> Result<Vec<u8>, Error>
100    where
101        G: RandomKeyPairGenerator<KeyPair = KP, Error = std::io::Error>,
102        KP: KeyPair<Secret = S, Public = P>,
103        S: SecretKey,
104        P: AsRef<[u8]>,
105    {
106        let r = generator.generate()?;
107        let z = ecdh::agree(r.secret(), public)?;
108        let mut key = [0u8; 32];
109        kdf(&z, &[0u8; 0], &mut key);
110
111        let ekey = &key[0..16];
112        let mkey = Sha256::digest(&key[16..32]);
113
114        let mut msg = vec![0u8; 1 + 64 + 16 + plain.len() + 32];
115        msg[0] = 0x04u8;
116        {
117            let msgd = &mut msg[1..];
118            let pub_bytes = r.public().as_ref();
119            if pub_bytes.len() < 64 {
120                return Err(Error::InvalidMessage);
121            }
122            msgd[0..64].copy_from_slice(&pub_bytes[0..64]);
123            let iv = H128::random();
124            msgd[64..80].copy_from_slice(iv.as_bytes());
125            {
126                let cipher = &mut msgd[(64 + 16)..(64 + 16 + plain.len())];
127                super::aes::encrypt_128_ctr(
128                    ekey,
129                    iv.as_bytes(),
130                    plain,
131                    cipher,
132                )?;
133            }
134            let mut hmac = HmacSha256::new_from_slice(mkey.as_slice())
135                .expect("output of Sha256 has invalid length");
136            {
137                let cipher_iv = &msgd[64..(64 + 16 + plain.len())];
138                hmac.update(cipher_iv);
139            }
140            hmac.update(auth_data);
141            let sig = hmac.finalize().into_bytes();
142            msgd[(64 + 16 + plain.len())..].copy_from_slice(&sig);
143        }
144        Ok(msg)
145    }
146
147    /// Decrypt a message with a secret key, checking HMAC for ciphertext
148    /// and authenticated data validity.
149    pub fn decrypt<S>(
150        secret: &S, auth_data: &[u8], encrypted: &[u8],
151    ) -> Result<Vec<u8>, Error>
152    where S: SecretKey {
153        let meta_len = 1 + 64 + 16 + 32;
154        if encrypted.len() < meta_len || encrypted[0] < 2 || encrypted[0] > 4 {
155            return Err(Error::InvalidMessage); //invalid message: publickey
156        }
157
158        let e = &encrypted[1..];
159        // Extract public key bytes directly - use a fixed-size array reference
160        let mut pub_array = [0u8; 64];
161        pub_array.copy_from_slice(&e[0..64]);
162        let z = ecdh::agree(secret, &pub_array)?;
163        let mut key = [0u8; 32];
164        kdf(&z, &[0u8; 0], &mut key);
165
166        let ekey = &key[0..16];
167        let mkey = Sha256::digest(&key[16..32]);
168
169        let clen = encrypted.len() - meta_len;
170        let cipher_with_iv = &e[64..(64 + 16 + clen)];
171        let cipher_iv = &cipher_with_iv[0..16];
172        let cipher_no_iv = &cipher_with_iv[16..];
173        let msg_mac = &e[(64 + 16 + clen)..];
174
175        // Verify tag
176        let mut hmac = HmacSha256::new_from_slice(mkey.as_slice())
177            .expect("output of Sha256 has invalid length");
178        hmac.update(cipher_with_iv);
179        hmac.update(auth_data);
180        let mac = hmac.finalize().into_bytes();
181
182        if !is_equal(mac.as_slice(), msg_mac) {
183            return Err(Error::InvalidMessage);
184        }
185
186        let mut msg = vec![0u8; clen];
187        super::aes::decrypt_128_ctr(
188            ekey,
189            cipher_iv,
190            cipher_no_iv,
191            &mut msg[..],
192        )?;
193        Ok(msg)
194    }
195
196    fn kdf<S: SecretKey>(secret: &S, s1: &[u8], dest: &mut [u8]) {
197        // SEC/ISO/Shoup specify counter size SHOULD be equivalent
198        // to size of hash output, however, it also notes that
199        // the 4 bytes is okay. NIST specifies 4 bytes.
200        let mut ctr = 1u32;
201        let mut written = 0usize;
202        while written < dest.len() {
203            let mut hasher = Sha256::new();
204            let ctrs = [
205                (ctr >> 24) as u8,
206                (ctr >> 16) as u8,
207                (ctr >> 8) as u8,
208                ctr as u8,
209            ];
210            hasher.update(&ctrs);
211            hasher.update(secret.as_ref());
212            hasher.update(s1);
213            let d = hasher.finalize();
214            dest[written..(written + 32)].copy_from_slice(&d);
215            written += 32;
216            ctr += 1;
217        }
218    }
219}
220
221pub mod aes {
222    use super::Error;
223    use aes::Aes128;
224    use cbc::{cipher::BlockDecryptMut, Decryptor};
225    use ctr::{
226        cipher::{KeyIvInit as CtrKeyIvInit, StreamCipher},
227        Ctr128BE,
228    };
229
230    type Aes128Ctr = Ctr128BE<Aes128>;
231    type Aes128CbcDec = Decryptor<Aes128>;
232
233    pub fn encrypt_128_ctr(
234        key: &[u8], iv: &[u8], plain: &[u8], ciphertext: &mut [u8],
235    ) -> Result<(), Error> {
236        let mut cipher = Aes128Ctr::new(key.into(), iv.into());
237        ciphertext[..plain.len()].copy_from_slice(plain);
238        cipher
239            .try_apply_keystream(ciphertext)
240            .map_err(|_| Error::Aes)?;
241        Ok(())
242    }
243
244    pub fn decrypt_128_ctr(
245        key: &[u8], iv: &[u8], ciphertext: &[u8], plain: &mut [u8],
246    ) -> Result<(), Error> {
247        let mut cipher = Aes128Ctr::new(key.into(), iv.into());
248        plain[..ciphertext.len()].copy_from_slice(ciphertext);
249        cipher.try_apply_keystream(plain).map_err(|_| Error::Aes)?;
250        Ok(())
251    }
252
253    pub fn decrypt_128_cbc<'a>(
254        key: &[u8], iv: &[u8], data: &[u8], dest: &'a mut [u8],
255    ) -> Result<&'a [u8], String> {
256        if key.len() != 16 {
257            return Err("Key must be exactly 16 bytes for AES-128".to_string());
258        }
259
260        if iv.len() != 16 {
261            return Err("IV must be exactly 16 bytes".to_string());
262        }
263
264        if data.is_empty() {
265            return Err("Data cannot be empty".to_string());
266        }
267
268        if data.len() % 16 != 0 {
269            return Err(
270                "Data length must be a multiple of 16 bytes".to_string()
271            );
272        }
273
274        // Create decryptor
275        let decryptor = Aes128CbcDec::new(key.into(), iv.into());
276        dest[..data.len()].copy_from_slice(data);
277
278        // Decrypt and remove PKCS7 padding
279        let plaintext = decryptor
280            .decrypt_padded_mut::<cbc::cipher::block_padding::Pkcs7>(dest)
281            .map_err(|e| format!("Decryption failed: {}", e))?;
282
283        Ok(plaintext)
284    }
285}
286
287pub mod keccak {
288    use tiny_keccak::{Hasher, Keccak};
289
290    pub trait Keccak256<T> {
291        fn keccak256(&self) -> T
292        where T: Sized;
293    }
294
295    impl<T> Keccak256<[u8; 32]> for T
296    where T: AsRef<[u8]>
297    {
298        fn keccak256(&self) -> [u8; 32] {
299            let mut keccak = Keccak::v256();
300            let mut result = [0u8; 32];
301            keccak.update(self.as_ref());
302            keccak.finalize(&mut result);
303            result
304        }
305    }
306}
307
308pub mod scrypt {
309    use super::{KEY_LENGTH, KEY_LENGTH_AES};
310    use scrypt::{errors, scrypt, Params};
311
312    pub fn derive_key(
313        pass: &[u8], salt: &[u8], n: u32, p: u32, r: u32,
314    ) -> Result<(Vec<u8>, Vec<u8>), String> {
315        // sanity checks
316        let log_n = (32 - n.leading_zeros() - 1) as u8;
317        if log_n as u32 >= r * 16 {
318            return Err(errors::InvalidParams.to_string());
319        }
320
321        if p as u64 > ((u32::MAX as u64 - 1) * 32) / (128 * (r as u64)) {
322            return Err(errors::InvalidParams.to_string());
323        }
324
325        let mut derived_key = vec![0u8; KEY_LENGTH];
326        let scrypt_params =
327            Params::new(log_n, r, p, KEY_LENGTH).map_err(|e| e.to_string())?;
328        scrypt(pass, salt, &scrypt_params, &mut derived_key)
329            .map_err(|e| e.to_string())?;
330        let derived_right_bits = &derived_key[0..KEY_LENGTH_AES];
331        let derived_left_bits = &derived_key[KEY_LENGTH_AES..KEY_LENGTH];
332        Ok((derived_right_bits.to_vec(), derived_left_bits.to_vec()))
333    }
334}
335
336pub mod pbkdf2 {
337    use super::{KEY_LENGTH, KEY_LENGTH_AES};
338    use hmac;
339    use pbkdf2;
340    use sha2;
341
342    pub struct Salt<'a>(pub &'a [u8]);
343    pub struct Secret<'a>(pub &'a [u8]);
344
345    pub fn sha256(
346        iter: u32, salt: Salt<'_>, sec: Secret<'_>, out: &mut [u8; 32],
347    ) -> Result<(), String> {
348        pbkdf2::pbkdf2::<hmac::Hmac<sha2::Sha256>>(sec.0, salt.0, iter, out)
349            .map_err(|e| e.to_string())
350    }
351
352    pub fn sha512(
353        iter: u32, salt: Salt<'_>, sec: Secret<'_>, out: &mut [u8; 64],
354    ) -> Result<(), String> {
355        pbkdf2::pbkdf2::<hmac::Hmac<sha2::Sha512>>(sec.0, salt.0, iter, out)
356            .map_err(|e| e.to_string())
357    }
358
359    pub fn derive_key_iterations(
360        password: &[u8], salt: &[u8], c: u32,
361    ) -> Result<(Vec<u8>, Vec<u8>), String> {
362        let mut derived_key = [0u8; KEY_LENGTH];
363        sha256(c, Salt(salt), Secret(password), &mut derived_key)?;
364        let derived_right_bits = &derived_key[0..KEY_LENGTH_AES];
365        let derived_left_bits = &derived_key[KEY_LENGTH_AES..KEY_LENGTH];
366        Ok((derived_right_bits.to_vec(), derived_left_bits.to_vec()))
367    }
368}
369
370pub mod digest {
371    use std::{marker::PhantomData, ops::Deref};
372
373    use digest::generic_array::{
374        typenum::{U20, U32, U64},
375        GenericArray,
376    };
377    use ripemd;
378    use sha2::Digest as RDigest;
379
380    /// The message digest.
381    pub struct Digest<T>(InnerDigest, PhantomData<T>);
382
383    enum InnerDigest {
384        Sha256(GenericArray<u8, U32>),
385        Sha512(GenericArray<u8, U64>),
386        Ripemd160(GenericArray<u8, U20>),
387    }
388
389    impl<T> Deref for Digest<T> {
390        type Target = [u8];
391
392        fn deref(&self) -> &Self::Target {
393            match self.0 {
394                InnerDigest::Sha256(ref d) => &d[..],
395                InnerDigest::Sha512(ref d) => &d[..],
396                InnerDigest::Ripemd160(ref d) => &d[..],
397            }
398        }
399    }
400
401    /// Single-step sha256 digest computation.
402    pub fn sha256(data: &[u8]) -> Digest<Sha256> {
403        let mut hasher = Hasher::sha256();
404        hasher.update(data);
405        hasher.finish()
406    }
407
408    /// Single-step sha512 digest computation.
409    pub fn sha512(data: &[u8]) -> Digest<Sha512> {
410        let mut hasher = Hasher::sha512();
411        hasher.update(data);
412        hasher.finish()
413    }
414
415    /// Single-step ripemd160 digest computation.
416    pub fn ripemd160(data: &[u8]) -> Digest<Ripemd160> {
417        let mut hasher = Hasher::ripemd160();
418        hasher.update(data);
419        hasher.finish()
420    }
421
422    #[derive(Debug)]
423    pub enum Sha256 {}
424    #[derive(Debug)]
425    pub enum Sha512 {}
426    #[derive(Debug)]
427    pub enum Ripemd160 {}
428
429    /// Stateful digest computation.
430    pub struct Hasher<T>(Inner, PhantomData<T>);
431
432    enum Inner {
433        Sha256(sha2::Sha256),
434        Sha512(sha2::Sha512),
435        Ripemd160(ripemd::Ripemd160),
436    }
437
438    impl Hasher<Sha256> {
439        pub fn sha256() -> Hasher<Sha256> {
440            Hasher(Inner::Sha256(sha2::Sha256::default()), PhantomData)
441        }
442    }
443
444    impl Hasher<Sha512> {
445        pub fn sha512() -> Hasher<Sha512> {
446            Hasher(Inner::Sha512(sha2::Sha512::default()), PhantomData)
447        }
448    }
449
450    impl Hasher<Ripemd160> {
451        pub fn ripemd160() -> Hasher<Ripemd160> {
452            Hasher(Inner::Ripemd160(ripemd::Ripemd160::default()), PhantomData)
453        }
454    }
455
456    impl<T> Hasher<T> {
457        pub fn update(&mut self, data: &[u8]) {
458            match self.0 {
459                Inner::Sha256(ref mut ctx) => ctx.update(data),
460                Inner::Sha512(ref mut ctx) => ctx.update(data),
461                Inner::Ripemd160(ref mut ctx) => ctx.update(data),
462            }
463        }
464
465        pub fn finish(self) -> Digest<T> {
466            match self.0 {
467                Inner::Sha256(ctx) => {
468                    Digest(InnerDigest::Sha256(ctx.finalize()), PhantomData)
469                }
470                Inner::Sha512(ctx) => {
471                    Digest(InnerDigest::Sha512(ctx.finalize()), PhantomData)
472                }
473                Inner::Ripemd160(ctx) => {
474                    Digest(InnerDigest::Ripemd160(ctx.finalize()), PhantomData)
475                }
476            }
477        }
478    }
479}
480
481pub fn derive_mac(derived_left_bits: &[u8], cipher_text: &[u8]) -> Vec<u8> {
482    let mut mac = vec![0u8; KEY_LENGTH_AES + cipher_text.len()];
483    mac[0..KEY_LENGTH_AES].copy_from_slice(derived_left_bits);
484    mac[KEY_LENGTH_AES..cipher_text.len() + KEY_LENGTH_AES]
485        .copy_from_slice(cipher_text);
486    mac
487}
488
489pub fn is_equal(a: &[u8], b: &[u8]) -> bool { a.ct_eq(b).into() }