reth_ecies/
algorithm.rs

1#![allow(missing_docs)]
2
3use crate::{
4    error::ECIESErrorImpl,
5    mac::{HeaderBytes, MAC},
6    util::{hmac_sha256, sha256},
7    ECIESError,
8};
9use aes::{cipher::StreamCipher, Aes128, Aes256};
10use alloy_primitives::{
11    bytes::{BufMut, Bytes, BytesMut},
12    B128, B256, B512 as PeerId,
13};
14use alloy_rlp::{Encodable, Rlp, RlpEncodable, RlpMaxEncodedLen};
15use byteorder::{BigEndian, ByteOrder, ReadBytesExt};
16use ctr::Ctr64BE;
17use digest::{crypto_common::KeyIvInit, Digest};
18use rand::{thread_rng, Rng};
19use reth_network_peers::{id2pk, pk2id};
20use secp256k1::{
21    ecdsa::{RecoverableSignature, RecoveryId},
22    PublicKey, SecretKey, SECP256K1,
23};
24use sha2::Sha256;
25use sha3::Keccak256;
26
27const PROTOCOL_VERSION: usize = 4;
28
29/// Computes the shared secret with ECDH and strips the y coordinate after computing the shared
30/// secret.
31///
32/// This uses the given remote public key and local (ephemeral) secret key to [compute a shared
33/// secp256k1 point](secp256k1::ecdh::shared_secret_point) and slices off the y coordinate from the
34/// returned pair, returning only the bytes of the x coordinate as a [`B256`].
35fn ecdh_x(public_key: &PublicKey, secret_key: &SecretKey) -> B256 {
36    B256::from_slice(&secp256k1::ecdh::shared_secret_point(public_key, secret_key)[..32])
37}
38
39/// This is the NIST SP 800-56A Concatenation Key Derivation Function (KDF) using SHA-256.
40///
41/// Internally this uses [`concat_kdf::derive_key_into`] to derive a key into the given `dest`
42/// slice.
43///
44/// # Panics
45/// * If the `dest` is empty
46/// * If the `dest` len is greater than or equal to the hash output len * the max counter value. In
47///   this case, the hash output len is 32 bytes, and the max counter value is 2^32 - 1. So the dest
48///   cannot have a len greater than 32 * 2^32 - 1.
49fn kdf(secret: B256, s1: &[u8], dest: &mut [u8]) {
50    concat_kdf::derive_key_into::<Sha256>(secret.as_slice(), s1, dest).unwrap();
51}
52
53pub struct ECIES {
54    secret_key: SecretKey,
55    public_key: PublicKey,
56    remote_public_key: Option<PublicKey>,
57
58    pub(crate) remote_id: Option<PeerId>,
59
60    ephemeral_secret_key: SecretKey,
61    ephemeral_public_key: PublicKey,
62    ephemeral_shared_secret: Option<B256>,
63    remote_ephemeral_public_key: Option<PublicKey>,
64
65    nonce: B256,
66    remote_nonce: Option<B256>,
67
68    ingress_aes: Option<Ctr64BE<Aes256>>,
69    egress_aes: Option<Ctr64BE<Aes256>>,
70    ingress_mac: Option<MAC>,
71    egress_mac: Option<MAC>,
72
73    init_msg: Option<Bytes>,
74    remote_init_msg: Option<Bytes>,
75
76    body_size: Option<usize>,
77}
78
79impl core::fmt::Debug for ECIES {
80    #[inline]
81    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
82        f.debug_struct("ECIES")
83            .field("public_key", &self.public_key)
84            .field("remote_public_key", &self.remote_public_key)
85            .field("remote_id", &self.remote_id)
86            .field("ephemeral_public_key", &self.ephemeral_public_key)
87            .field("ephemeral_shared_secret", &self.ephemeral_shared_secret)
88            .field("remote_ephemeral_public_key", &self.remote_ephemeral_public_key)
89            .field("nonce", &self.nonce)
90            .field("remote_nonce", &self.remote_nonce)
91            .field("ingress_mac", &self.ingress_mac)
92            .field("egress_mac", &self.egress_mac)
93            .field("init_msg", &self.init_msg)
94            .field("remote_init_msg", &self.remote_init_msg)
95            .field("body_size", &self.body_size)
96            .finish()
97    }
98}
99
100fn split_at_mut<T>(arr: &mut [T], idx: usize) -> Result<(&mut [T], &mut [T]), ECIESError> {
101    if idx > arr.len() {
102        return Err(ECIESErrorImpl::OutOfBounds { idx, len: arr.len() }.into())
103    }
104    Ok(arr.split_at_mut(idx))
105}
106
107/// A parsed `RLPx` encrypted message
108///
109/// From the devp2p spec, this should help perform the following operations:
110///
111/// For Bob to decrypt the message `R || iv || c || d`, he derives the shared secret `S = Px` where
112/// `(Px, Py) = kB * R` as well as the encryption and authentication keys `kE || kM = KDF(S, 32)`.
113///
114/// Bob verifies the authenticity of the message by checking whether `d == MAC(sha256(kM), iv ||
115/// c)` then obtains the plaintext as `m = AES(kE, iv || c)`.
116#[derive(Debug)]
117pub struct EncryptedMessage<'a> {
118    /// The auth data, used when checking the `tag` with HMAC-SHA256.
119    ///
120    /// This is not mentioned in the `RLPx` spec, but included in implementations.
121    ///
122    /// See source comments of [`Self::check_integrity`] for more information.
123    auth_data: [u8; 2],
124    /// The remote secp256k1 public key
125    public_key: PublicKey,
126    /// The IV, for use in AES during decryption, in the tag check
127    iv: B128,
128    /// The encrypted data
129    encrypted_data: &'a mut [u8],
130    /// The message tag
131    tag: B256,
132}
133
134impl<'a> EncryptedMessage<'a> {
135    /// Parse the given `data` into an [`EncryptedMessage`].
136    ///
137    /// If the data is not long enough to contain the expected fields, this returns an error.
138    pub fn parse(data: &mut [u8]) -> Result<EncryptedMessage<'_>, ECIESError> {
139        // Auth data is 2 bytes, public key is 65 bytes
140        if data.len() < 65 + 2 {
141            return Err(ECIESErrorImpl::EncryptedDataTooSmall.into())
142        }
143        let (auth_data, encrypted) = data.split_at_mut(2);
144
145        // convert the auth data to a fixed size array
146        //
147        // NOTE: this will not panic because we've already checked that the data is long enough
148        let auth_data = auth_data.try_into().unwrap();
149
150        let (pubkey_bytes, encrypted) = encrypted.split_at_mut(65);
151        let public_key = PublicKey::from_slice(pubkey_bytes)?;
152
153        // return an error if the encrypted len is currently less than 32
154        let tag_index =
155            encrypted.len().checked_sub(32).ok_or(ECIESErrorImpl::EncryptedDataTooSmall)?;
156
157        // NOTE: we've already checked that the encrypted data is long enough to contain the
158        // encrypted data and tag
159        let (data_iv, tag_bytes) = encrypted.split_at_mut(tag_index);
160
161        // NOTE: this will not panic because we are splitting at length minus 32 bytes, which
162        // causes tag_bytes to be 32 bytes long
163        let tag = B256::from_slice(tag_bytes);
164
165        // now we can check if the encrypted data is long enough to contain the IV
166        if data_iv.len() < 16 {
167            return Err(ECIESErrorImpl::EncryptedDataTooSmall.into())
168        }
169        let (iv, encrypted_data) = data_iv.split_at_mut(16);
170
171        // NOTE: this will not panic because we are splitting at 16 bytes
172        let iv = B128::from_slice(iv);
173
174        Ok(EncryptedMessage { auth_data, public_key, iv, encrypted_data, tag })
175    }
176
177    /// Use the given secret and this encrypted message to derive the shared secret, and use the
178    /// shared secret to derive the mac and encryption keys.
179    pub fn derive_keys(&self, secret_key: &SecretKey) -> RLPxSymmetricKeys {
180        // perform ECDH to get the shared secret, using the remote public key from the message and
181        // the given secret key
182        let x = ecdh_x(&self.public_key, secret_key);
183        let mut key = [0u8; 32];
184
185        // The RLPx spec describes the key derivation process as:
186        //
187        // kE || kM = KDF(S, 32)
188        //
189        // where kE is the encryption key, and kM is used to determine the MAC key (see below)
190        //
191        // NOTE: The RLPx spec does not define an `OtherInfo` parameter, and this is unused in
192        // other implementations, so we use an empty slice.
193        kdf(x, &[], &mut key);
194
195        let enc_key = B128::from_slice(&key[..16]);
196
197        // The MAC tag check operation described is:
198        //
199        // d == MAC(sha256(kM), iv || c)
200        //
201        // where kM is the result of the above KDF, iv is the IV, and c is the encrypted data.
202        // Because the hash of kM is ultimately used as the mac key, we perform that hashing here.
203        let mac_key = sha256(&key[16..32]);
204
205        RLPxSymmetricKeys { enc_key, mac_key }
206    }
207
208    /// Use the given ECIES keys to check the message integrity using the contained tag.
209    pub fn check_integrity(&self, keys: &RLPxSymmetricKeys) -> Result<(), ECIESError> {
210        // The MAC tag check operation described is:
211        //
212        // d == MAC(sha256(kM), iv || c)
213        //
214        // NOTE: The RLPx spec does not show here that the `auth_data` is required for checking the
215        // tag.
216        //
217        // Geth refers to SEC 1's definition of ECIES:
218        //
219        // Encrypt encrypts a message using ECIES as specified in SEC 1, section 5.1.
220        //
221        // s1 and s2 contain shared information that is not part of the resulting
222        // ciphertext. s1 is fed into key derivation, s2 is fed into the MAC. If the
223        // shared information parameters aren't being used, they should be nil.
224        //
225        // ```
226        // prefix := make([]byte, 2)
227        // binary.BigEndian.PutUint16(prefix, uint16(len(h.wbuf.data)+eciesOverhead))
228        //
229        // enc, err := ecies.Encrypt(rand.Reader, h.remote, h.wbuf.data, nil, prefix)
230        // ```
231        let check_tag = hmac_sha256(
232            keys.mac_key.as_ref(),
233            &[self.iv.as_slice(), self.encrypted_data],
234            &self.auth_data,
235        );
236        if check_tag != self.tag {
237            return Err(ECIESErrorImpl::TagCheckDecryptFailed.into())
238        }
239
240        Ok(())
241    }
242
243    /// Use the given ECIES keys to decrypt the contained encrypted data, consuming the message and
244    /// returning the decrypted data.
245    pub fn decrypt(self, keys: &RLPxSymmetricKeys) -> &'a mut [u8] {
246        let Self { iv, encrypted_data, .. } = self;
247
248        // rename for clarity once it's decrypted
249        let decrypted_data = encrypted_data;
250
251        let mut decryptor = Ctr64BE::<Aes128>::new((&keys.enc_key.0).into(), (&*iv).into());
252        decryptor.apply_keystream(decrypted_data);
253        decrypted_data
254    }
255
256    /// Use the given ECIES keys to check the integrity of the message, returning an error if the
257    /// tag check fails, and then decrypt the message, returning the decrypted data.
258    pub fn check_and_decrypt(self, keys: RLPxSymmetricKeys) -> Result<&'a mut [u8], ECIESError> {
259        self.check_integrity(&keys)?;
260        Ok(self.decrypt(&keys))
261    }
262}
263
264/// The symmetric keys derived from an ECIES message.
265#[derive(Debug)]
266pub struct RLPxSymmetricKeys {
267    /// The key used for decryption, specifically with AES-128 in CTR mode, using a 64-bit big
268    /// endian counter.
269    pub enc_key: B128,
270
271    /// The key used for verifying message integrity, specifically with the NIST SP 800-56A Concat
272    /// KDF.
273    pub mac_key: B256,
274}
275
276impl ECIES {
277    /// Create a new client with the given static secret key, remote peer id, nonce, and ephemeral
278    /// secret key.
279    fn new_static_client(
280        secret_key: SecretKey,
281        remote_id: PeerId,
282        nonce: B256,
283        ephemeral_secret_key: SecretKey,
284    ) -> Result<Self, ECIESError> {
285        let public_key = PublicKey::from_secret_key(SECP256K1, &secret_key);
286        let remote_public_key = id2pk(remote_id)?;
287        let ephemeral_public_key = PublicKey::from_secret_key(SECP256K1, &ephemeral_secret_key);
288
289        Ok(Self {
290            secret_key,
291            public_key,
292            ephemeral_secret_key,
293            ephemeral_public_key,
294            nonce,
295
296            remote_public_key: Some(remote_public_key),
297            remote_ephemeral_public_key: None,
298            remote_nonce: None,
299            ephemeral_shared_secret: None,
300            init_msg: None,
301            remote_init_msg: None,
302
303            remote_id: Some(remote_id),
304
305            body_size: None,
306            egress_aes: None,
307            ingress_aes: None,
308            egress_mac: None,
309            ingress_mac: None,
310        })
311    }
312
313    /// Create a new ECIES client with the given static secret key and remote peer ID.
314    pub fn new_client(secret_key: SecretKey, remote_id: PeerId) -> Result<Self, ECIESError> {
315        let mut rng = thread_rng();
316        let nonce = rng.gen();
317        let ephemeral_secret_key = SecretKey::new(&mut rng);
318        Self::new_static_client(secret_key, remote_id, nonce, ephemeral_secret_key)
319    }
320
321    /// Create a new server with the given static secret key, remote peer id, and ephemeral secret
322    /// key.
323    pub fn new_static_server(
324        secret_key: SecretKey,
325        nonce: B256,
326        ephemeral_secret_key: SecretKey,
327    ) -> Result<Self, ECIESError> {
328        let public_key = PublicKey::from_secret_key(SECP256K1, &secret_key);
329        let ephemeral_public_key = PublicKey::from_secret_key(SECP256K1, &ephemeral_secret_key);
330
331        Ok(Self {
332            secret_key,
333            public_key,
334            ephemeral_secret_key,
335            ephemeral_public_key,
336            nonce,
337
338            remote_public_key: None,
339            remote_ephemeral_public_key: None,
340            remote_nonce: None,
341            ephemeral_shared_secret: None,
342            init_msg: None,
343            remote_init_msg: None,
344
345            remote_id: None,
346
347            body_size: None,
348            egress_aes: None,
349            ingress_aes: None,
350            egress_mac: None,
351            ingress_mac: None,
352        })
353    }
354
355    /// Create a new ECIES server with the given static secret key.
356    pub fn new_server(secret_key: SecretKey) -> Result<Self, ECIESError> {
357        let mut rng = thread_rng();
358        let nonce = rng.gen();
359        let ephemeral_secret_key = SecretKey::new(&mut rng);
360        Self::new_static_server(secret_key, nonce, ephemeral_secret_key)
361    }
362
363    /// Return the contained remote peer ID.
364    pub fn remote_id(&self) -> PeerId {
365        self.remote_id.unwrap()
366    }
367
368    fn encrypt_message(&self, data: &[u8], out: &mut BytesMut) {
369        let mut rng = thread_rng();
370
371        out.reserve(secp256k1::constants::UNCOMPRESSED_PUBLIC_KEY_SIZE + 16 + data.len() + 32);
372
373        let secret_key = SecretKey::new(&mut rng);
374        out.extend_from_slice(
375            &PublicKey::from_secret_key(SECP256K1, &secret_key).serialize_uncompressed(),
376        );
377
378        let x = ecdh_x(&self.remote_public_key.unwrap(), &secret_key);
379        let mut key = [0u8; 32];
380        kdf(x, &[], &mut key);
381
382        let enc_key = B128::from_slice(&key[..16]);
383        let mac_key = sha256(&key[16..32]);
384
385        let iv: B128 = rng.gen();
386        let mut encryptor = Ctr64BE::<Aes128>::new((&enc_key.0).into(), (&iv.0).into());
387
388        let mut encrypted = data.to_vec();
389        encryptor.apply_keystream(&mut encrypted);
390
391        let total_size: u16 = u16::try_from(65 + 16 + data.len() + 32).unwrap();
392
393        let tag =
394            hmac_sha256(mac_key.as_ref(), &[iv.as_slice(), &encrypted], &total_size.to_be_bytes());
395
396        out.extend_from_slice(iv.as_slice());
397        out.extend_from_slice(&encrypted);
398        out.extend_from_slice(tag.as_ref());
399    }
400
401    fn decrypt_message<'a>(&self, data: &'a mut [u8]) -> Result<&'a mut [u8], ECIESError> {
402        // parse the encrypted message from bytes
403        let encrypted_message = EncryptedMessage::parse(data)?;
404
405        // derive keys from the secret key and the encrypted message
406        let keys = encrypted_message.derive_keys(&self.secret_key);
407
408        // check message integrity and decrypt the message
409        encrypted_message.check_and_decrypt(keys)
410    }
411
412    fn create_auth_unencrypted(&self) -> BytesMut {
413        let x = ecdh_x(&self.remote_public_key.unwrap(), &self.secret_key);
414        let msg = x ^ self.nonce;
415        let (rec_id, sig) = SECP256K1
416            .sign_ecdsa_recoverable(
417                &secp256k1::Message::from_digest(msg.0),
418                &self.ephemeral_secret_key,
419            )
420            .serialize_compact();
421
422        let mut sig_bytes = [0u8; 65];
423        sig_bytes[..64].copy_from_slice(&sig);
424        sig_bytes[64] = rec_id.to_i32() as u8;
425
426        let id = pk2id(&self.public_key);
427
428        #[derive(RlpEncodable)]
429        struct S<'a> {
430            sig_bytes: &'a [u8; 65],
431            id: &'a PeerId,
432            nonce: &'a B256,
433            protocol_version: u8,
434        }
435
436        let mut out = BytesMut::new();
437        S {
438            sig_bytes: &sig_bytes,
439            id: &id,
440            nonce: &self.nonce,
441            protocol_version: PROTOCOL_VERSION as u8,
442        }
443        .encode(&mut out);
444
445        out.resize(out.len() + thread_rng().gen_range(100..=300), 0);
446        out
447    }
448
449    #[cfg(test)]
450    fn create_auth(&mut self) -> BytesMut {
451        let mut buf = BytesMut::new();
452        self.write_auth(&mut buf);
453        buf
454    }
455
456    /// Write an auth message to the given buffer.
457    pub fn write_auth(&mut self, buf: &mut BytesMut) {
458        let unencrypted = self.create_auth_unencrypted();
459
460        let mut out = buf.split_off(buf.len());
461        out.put_u16(0);
462
463        let mut encrypted = out.split_off(out.len());
464        self.encrypt_message(&unencrypted, &mut encrypted);
465
466        let len_bytes = u16::try_from(encrypted.len()).unwrap().to_be_bytes();
467        out[..len_bytes.len()].copy_from_slice(&len_bytes);
468
469        out.unsplit(encrypted);
470
471        self.init_msg = Some(Bytes::copy_from_slice(&out));
472
473        buf.unsplit(out);
474    }
475
476    fn parse_auth_unencrypted(&mut self, data: &[u8]) -> Result<(), ECIESError> {
477        let mut data = Rlp::new(data)?;
478
479        let sigdata = data.get_next::<[u8; 65]>()?.ok_or(ECIESErrorImpl::InvalidAuthData)?;
480        let signature = RecoverableSignature::from_compact(
481            &sigdata[..64],
482            RecoveryId::from_i32(sigdata[64] as i32)?,
483        )?;
484        let remote_id = data.get_next()?.ok_or(ECIESErrorImpl::InvalidAuthData)?;
485        self.remote_id = Some(remote_id);
486        self.remote_public_key = Some(id2pk(remote_id)?);
487        self.remote_nonce = Some(data.get_next()?.ok_or(ECIESErrorImpl::InvalidAuthData)?);
488
489        let x = ecdh_x(&self.remote_public_key.unwrap(), &self.secret_key);
490        self.remote_ephemeral_public_key = Some(SECP256K1.recover_ecdsa(
491            &secp256k1::Message::from_digest((x ^ self.remote_nonce.unwrap()).0),
492            &signature,
493        )?);
494        self.ephemeral_shared_secret =
495            Some(ecdh_x(&self.remote_ephemeral_public_key.unwrap(), &self.ephemeral_secret_key));
496
497        Ok(())
498    }
499
500    /// Read and verify an auth message from the input data.
501    #[tracing::instrument(skip_all)]
502    pub fn read_auth(&mut self, data: &mut [u8]) -> Result<(), ECIESError> {
503        self.remote_init_msg = Some(Bytes::copy_from_slice(data));
504        let unencrypted = self.decrypt_message(data)?;
505        self.parse_auth_unencrypted(unencrypted)
506    }
507
508    /// Create an `ack` message using the internal nonce, local ephemeral public key, and `RLPx`
509    /// ECIES protocol version.
510    fn create_ack_unencrypted(&self) -> impl AsRef<[u8]> {
511        #[derive(RlpEncodable, RlpMaxEncodedLen)]
512        struct S {
513            id: PeerId,
514            nonce: B256,
515            protocol_version: u8,
516        }
517
518        alloy_rlp::encode_fixed_size(&S {
519            id: pk2id(&self.ephemeral_public_key),
520            nonce: self.nonce,
521            protocol_version: PROTOCOL_VERSION as u8,
522        })
523    }
524
525    #[cfg(test)]
526    pub fn create_ack(&mut self) -> BytesMut {
527        let mut buf = BytesMut::new();
528        self.write_ack(&mut buf);
529        buf
530    }
531
532    /// Write an `ack` message to the given buffer.
533    pub fn write_ack(&mut self, out: &mut BytesMut) {
534        let unencrypted = self.create_ack_unencrypted();
535
536        let mut buf = out.split_off(out.len());
537
538        // reserve space for length
539        buf.put_u16(0);
540
541        // encrypt and append
542        let mut encrypted = buf.split_off(buf.len());
543        self.encrypt_message(unencrypted.as_ref(), &mut encrypted);
544        let len_bytes = u16::try_from(encrypted.len()).unwrap().to_be_bytes();
545        buf.unsplit(encrypted);
546
547        // write length
548        buf[..len_bytes.len()].copy_from_slice(&len_bytes[..]);
549
550        self.init_msg = Some(buf.clone().freeze());
551        out.unsplit(buf);
552
553        self.setup_frame(true);
554    }
555
556    /// Parse the incoming `ack` message from the given `data` bytes, which are assumed to be
557    /// unencrypted. This parses the remote ephemeral pubkey and nonce from the message, and uses
558    /// ECDH to compute the shared secret. The shared secret is the x coordinate of the point
559    /// returned by ECDH.
560    ///
561    /// This sets the `remote_ephemeral_public_key` and `remote_nonce`, and
562    /// `ephemeral_shared_secret` fields in the ECIES state.
563    fn parse_ack_unencrypted(&mut self, data: &[u8]) -> Result<(), ECIESError> {
564        let mut data = Rlp::new(data)?;
565        self.remote_ephemeral_public_key =
566            Some(id2pk(data.get_next()?.ok_or(ECIESErrorImpl::InvalidAckData)?)?);
567        self.remote_nonce = Some(data.get_next()?.ok_or(ECIESErrorImpl::InvalidAckData)?);
568
569        self.ephemeral_shared_secret =
570            Some(ecdh_x(&self.remote_ephemeral_public_key.unwrap(), &self.ephemeral_secret_key));
571        Ok(())
572    }
573
574    /// Read and verify an ack message from the input data.
575    #[tracing::instrument(skip_all)]
576    pub fn read_ack(&mut self, data: &mut [u8]) -> Result<(), ECIESError> {
577        self.remote_init_msg = Some(Bytes::copy_from_slice(data));
578        let unencrypted = self.decrypt_message(data)?;
579        self.parse_ack_unencrypted(unencrypted)?;
580        self.setup_frame(false);
581        Ok(())
582    }
583
584    fn setup_frame(&mut self, incoming: bool) {
585        let mut hasher = Keccak256::new();
586        for el in &if incoming {
587            [self.nonce, self.remote_nonce.unwrap()]
588        } else {
589            [self.remote_nonce.unwrap(), self.nonce]
590        } {
591            hasher.update(el);
592        }
593        let h_nonce = B256::from(hasher.finalize().as_ref());
594
595        let iv = B128::default();
596        let shared_secret: B256 = {
597            let mut hasher = Keccak256::new();
598            hasher.update(self.ephemeral_shared_secret.unwrap().0.as_ref());
599            hasher.update(h_nonce.0.as_ref());
600            B256::from(hasher.finalize().as_ref())
601        };
602
603        let aes_secret: B256 = {
604            let mut hasher = Keccak256::new();
605            hasher.update(self.ephemeral_shared_secret.unwrap().0.as_ref());
606            hasher.update(shared_secret.0.as_ref());
607            B256::from(hasher.finalize().as_ref())
608        };
609        self.ingress_aes = Some(Ctr64BE::<Aes256>::new((&aes_secret.0).into(), (&iv.0).into()));
610        self.egress_aes = Some(Ctr64BE::<Aes256>::new((&aes_secret.0).into(), (&iv.0).into()));
611
612        let mac_secret: B256 = {
613            let mut hasher = Keccak256::new();
614            hasher.update(self.ephemeral_shared_secret.unwrap().0.as_ref());
615            hasher.update(aes_secret.0.as_ref());
616            B256::from(hasher.finalize().as_ref())
617        };
618        self.ingress_mac = Some(MAC::new(mac_secret));
619        self.ingress_mac.as_mut().unwrap().update((mac_secret ^ self.nonce).as_ref());
620        self.ingress_mac.as_mut().unwrap().update(self.remote_init_msg.as_ref().unwrap());
621        self.egress_mac = Some(MAC::new(mac_secret));
622        self.egress_mac
623            .as_mut()
624            .unwrap()
625            .update((mac_secret ^ self.remote_nonce.unwrap()).as_ref());
626        self.egress_mac.as_mut().unwrap().update(self.init_msg.as_ref().unwrap());
627    }
628
629    #[cfg(test)]
630    fn create_header(&mut self, size: usize) -> BytesMut {
631        let mut out = BytesMut::new();
632        self.write_header(&mut out, size);
633        out
634    }
635
636    pub fn write_header(&mut self, out: &mut BytesMut, size: usize) {
637        let mut buf = [0u8; 8];
638        BigEndian::write_uint(&mut buf, size as u64, 3);
639        let mut header = [0u8; 16];
640        header[..3].copy_from_slice(&buf[..3]);
641        header[3..6].copy_from_slice(&[194, 128, 128]);
642
643        let mut header = HeaderBytes::from(header);
644        self.egress_aes.as_mut().unwrap().apply_keystream(&mut header);
645        self.egress_mac.as_mut().unwrap().update_header(&header);
646        let tag = self.egress_mac.as_mut().unwrap().digest();
647
648        out.reserve(Self::header_len());
649        out.extend_from_slice(&header[..]);
650        out.extend_from_slice(tag.as_slice());
651    }
652
653    /// Reads the `RLPx` header from the slice, setting up the MAC and AES, returning the body
654    /// size contained in the header.
655    pub fn read_header(&mut self, data: &mut [u8]) -> Result<usize, ECIESError> {
656        // If the data is not large enough to fit the header and mac bytes, return an error
657        //
658        // The header is 16 bytes, and the mac is 16 bytes, so the data must be at least 32 bytes
659        if data.len() < 32 {
660            return Err(ECIESErrorImpl::InvalidHeader.into())
661        }
662
663        let (header_bytes, mac_bytes) = split_at_mut(data, 16)?;
664        let header = HeaderBytes::from_mut_slice(header_bytes);
665        let mac = B128::from_slice(&mac_bytes[..16]);
666
667        self.ingress_mac.as_mut().unwrap().update_header(header);
668        let check_mac = self.ingress_mac.as_mut().unwrap().digest();
669        if check_mac != mac {
670            return Err(ECIESErrorImpl::TagCheckHeaderFailed.into())
671        }
672
673        self.ingress_aes.as_mut().unwrap().apply_keystream(header);
674        if header.as_slice().len() < 3 {
675            return Err(ECIESErrorImpl::InvalidHeader.into())
676        }
677
678        let body_size = usize::try_from(header.as_slice().read_uint::<BigEndian>(3)?)?;
679
680        self.body_size = Some(body_size);
681
682        Ok(body_size)
683    }
684
685    pub const fn header_len() -> usize {
686        32
687    }
688
689    pub fn body_len(&self) -> usize {
690        let len = self.body_size.unwrap();
691        Self::align_16(len) + 16
692    }
693
694    #[cfg(test)]
695    fn create_body(&mut self, data: &[u8]) -> BytesMut {
696        let mut out = BytesMut::new();
697        self.write_body(&mut out, data);
698        out
699    }
700
701    pub fn write_body(&mut self, out: &mut BytesMut, data: &[u8]) {
702        let len = Self::align_16(data.len());
703        let old_len = out.len();
704        out.resize(old_len + len, 0);
705
706        let encrypted = &mut out[old_len..old_len + len];
707        encrypted[..data.len()].copy_from_slice(data);
708
709        self.egress_aes.as_mut().unwrap().apply_keystream(encrypted);
710        self.egress_mac.as_mut().unwrap().update_body(encrypted);
711        let tag = self.egress_mac.as_mut().unwrap().digest();
712
713        out.extend_from_slice(tag.as_slice());
714    }
715
716    pub fn read_body<'a>(&mut self, data: &'a mut [u8]) -> Result<&'a mut [u8], ECIESError> {
717        // error if the data is too small to contain the tag
718        // TODO: create a custom type similar to EncryptedMessage for parsing, checking MACs, and
719        // decrypting the body
720        let mac_index = data.len().checked_sub(16).ok_or(ECIESErrorImpl::EncryptedDataTooSmall)?;
721        let (body, mac_bytes) = split_at_mut(data, mac_index)?;
722        let mac = B128::from_slice(mac_bytes);
723        self.ingress_mac.as_mut().unwrap().update_body(body);
724        let check_mac = self.ingress_mac.as_mut().unwrap().digest();
725        if check_mac != mac {
726            return Err(ECIESErrorImpl::TagCheckBodyFailed.into())
727        }
728
729        let size = self.body_size.unwrap();
730        self.body_size = None;
731        let ret = body;
732        self.ingress_aes.as_mut().unwrap().apply_keystream(ret);
733        Ok(split_at_mut(ret, size)?.0)
734    }
735
736    /// Returns `num` aligned to 16.
737    ///
738    /// `<https://stackoverflow.com/questions/14561402/how-is-this-size-alignment-working>`
739    #[inline]
740    const fn align_16(num: usize) -> usize {
741        (num + (16 - 1)) & !(16 - 1)
742    }
743}
744
745#[cfg(test)]
746mod tests {
747    use super::*;
748    use alloy_primitives::{b256, hex};
749
750    #[test]
751    fn ecdh() {
752        let our_secret_key = SecretKey::from_slice(&hex!(
753            "202a36e24c3eb39513335ec99a7619bad0e7dc68d69401b016253c7d26dc92f8"
754        ))
755        .unwrap();
756        let remote_public_key = id2pk(hex!("d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666").into()).unwrap();
757
758        assert_eq!(
759            ecdh_x(&remote_public_key, &our_secret_key),
760            hex!("821ce7e01ea11b111a52b2dafae8a3031a372d83bdf1a78109fa0783c2b9d5d3")
761        )
762    }
763
764    #[test]
765    fn communicate() {
766        let mut rng = thread_rng();
767        let server_secret_key = SecretKey::new(&mut rng);
768        let server_public_key = PublicKey::from_secret_key(SECP256K1, &server_secret_key);
769        let client_secret_key = SecretKey::new(&mut rng);
770
771        let mut server_ecies = ECIES::new_server(server_secret_key).unwrap();
772        let mut client_ecies =
773            ECIES::new_client(client_secret_key, pk2id(&server_public_key)).unwrap();
774
775        // Handshake
776        let mut auth = client_ecies.create_auth();
777        server_ecies.read_auth(&mut auth).unwrap();
778        let mut ack = server_ecies.create_ack();
779        client_ecies.read_ack(&mut ack).unwrap();
780        let mut ack = client_ecies.create_ack();
781        server_ecies.read_ack(&mut ack).unwrap();
782
783        let server_to_client_data = [0u8, 1u8, 2u8, 3u8, 4u8];
784        let client_to_server_data = [5u8, 6u8, 7u8];
785
786        // Test server to client 1
787        let mut header = server_ecies.create_header(server_to_client_data.len());
788        assert_eq!(header.len(), ECIES::header_len());
789        client_ecies.read_header(&mut header).unwrap();
790        let mut body = server_ecies.create_body(&server_to_client_data);
791        assert_eq!(body.len(), client_ecies.body_len());
792        let ret = client_ecies.read_body(&mut body).unwrap();
793        assert_eq!(ret, server_to_client_data);
794
795        // Test client to server 1
796        server_ecies
797            .read_header(&mut client_ecies.create_header(client_to_server_data.len()))
798            .unwrap();
799        let mut b = client_ecies.create_body(&client_to_server_data);
800        let ret = server_ecies.read_body(&mut b).unwrap();
801        assert_eq!(ret, client_to_server_data);
802
803        // Test server to client 2
804        client_ecies
805            .read_header(&mut server_ecies.create_header(server_to_client_data.len()))
806            .unwrap();
807        let mut b = server_ecies.create_body(&server_to_client_data);
808        let ret = client_ecies.read_body(&mut b).unwrap();
809        assert_eq!(ret, server_to_client_data);
810
811        // Test server to client 3
812        client_ecies
813            .read_header(&mut server_ecies.create_header(server_to_client_data.len()))
814            .unwrap();
815        let mut b = server_ecies.create_body(&server_to_client_data);
816        let ret = client_ecies.read_body(&mut b).unwrap();
817        assert_eq!(ret, server_to_client_data);
818
819        // Test client to server 2
820        server_ecies
821            .read_header(&mut client_ecies.create_header(client_to_server_data.len()))
822            .unwrap();
823        let mut b = client_ecies.create_body(&client_to_server_data);
824        let ret = server_ecies.read_body(&mut b).unwrap();
825        assert_eq!(ret, client_to_server_data);
826
827        // Test client to server 3
828        server_ecies
829            .read_header(&mut client_ecies.create_header(client_to_server_data.len()))
830            .unwrap();
831        let mut b = client_ecies.create_body(&client_to_server_data);
832        let ret = server_ecies.read_body(&mut b).unwrap();
833        assert_eq!(ret, client_to_server_data);
834    }
835
836    fn eip8_test_server_key() -> SecretKey {
837        SecretKey::from_slice(&hex!(
838            "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"
839        ))
840        .unwrap()
841    }
842
843    fn eip8_test_client() -> ECIES {
844        let client_static_key = SecretKey::from_slice(&hex!(
845            "49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee"
846        ))
847        .unwrap();
848
849        let client_ephemeral_key = SecretKey::from_slice(&hex!(
850            "869d6ecf5211f1cc60418a13b9d870b22959d0c16f02bec714c960dd2298a32d"
851        ))
852        .unwrap();
853
854        let client_nonce =
855            b256!("7e968bba13b6c50e2c4cd7f241cc0d64d1ac25c7f5952df231ac6a2bda8ee5d6");
856
857        let server_id = pk2id(&PublicKey::from_secret_key(SECP256K1, &eip8_test_server_key()));
858
859        ECIES::new_static_client(client_static_key, server_id, client_nonce, client_ephemeral_key)
860            .unwrap()
861    }
862
863    fn eip8_test_server() -> ECIES {
864        let server_ephemeral_key = SecretKey::from_slice(&hex!(
865            "e238eb8e04fee6511ab04c6dd3c89ce097b11f25d584863ac2b6d5b35b1847e4"
866        ))
867        .unwrap();
868
869        let server_nonce =
870            b256!("559aead08264d5795d3909718cdd05abd49572e84fe55590eef31a88a08fdffd");
871
872        ECIES::new_static_server(eip8_test_server_key(), server_nonce, server_ephemeral_key)
873            .unwrap()
874    }
875
876    #[test]
877    /// Test vectors from <https://eips.ethereum.org/EIPS/eip-8>
878    fn eip8_test() {
879        // EIP-8 format with version 4 and no additional list elements
880        let auth2 = hex!(
881            "
882        01b304ab7578555167be8154d5cc456f567d5ba302662433674222360f08d5f1534499d3678b513b
883        0fca474f3a514b18e75683032eb63fccb16c156dc6eb2c0b1593f0d84ac74f6e475f1b8d56116b84
884        9634a8c458705bf83a626ea0384d4d7341aae591fae42ce6bd5c850bfe0b999a694a49bbbaf3ef6c
885        da61110601d3b4c02ab6c30437257a6e0117792631a4b47c1d52fc0f8f89caadeb7d02770bf999cc
886        147d2df3b62e1ffb2c9d8c125a3984865356266bca11ce7d3a688663a51d82defaa8aad69da39ab6
887        d5470e81ec5f2a7a47fb865ff7cca21516f9299a07b1bc63ba56c7a1a892112841ca44b6e0034dee
888        70c9adabc15d76a54f443593fafdc3b27af8059703f88928e199cb122362a4b35f62386da7caad09
889        c001edaeb5f8a06d2b26fb6cb93c52a9fca51853b68193916982358fe1e5369e249875bb8d0d0ec3
890        6f917bc5e1eafd5896d46bd61ff23f1a863a8a8dcd54c7b109b771c8e61ec9c8908c733c0263440e
891        2aa067241aaa433f0bb053c7b31a838504b148f570c0ad62837129e547678c5190341e4f1693956c
892        3bf7678318e2d5b5340c9e488eefea198576344afbdf66db5f51204a6961a63ce072c8926c
893        "
894        );
895
896        // EIP-8 format with version 56 and 3 additional list elements (sent from A to B)
897        let auth3 = hex!(
898            "
899        01b8044c6c312173685d1edd268aa95e1d495474c6959bcdd10067ba4c9013df9e40ff45f5bfd6f7
900        2471f93a91b493f8e00abc4b80f682973de715d77ba3a005a242eb859f9a211d93a347fa64b597bf
901        280a6b88e26299cf263b01b8dfdb712278464fd1c25840b995e84d367d743f66c0e54a586725b7bb
902        f12acca27170ae3283c1073adda4b6d79f27656993aefccf16e0d0409fe07db2dc398a1b7e8ee93b
903        cd181485fd332f381d6a050fba4c7641a5112ac1b0b61168d20f01b479e19adf7fdbfa0905f63352
904        bfc7e23cf3357657455119d879c78d3cf8c8c06375f3f7d4861aa02a122467e069acaf513025ff19
905        6641f6d2810ce493f51bee9c966b15c5043505350392b57645385a18c78f14669cc4d960446c1757
906        1b7c5d725021babbcd786957f3d17089c084907bda22c2b2675b4378b114c601d858802a55345a15
907        116bc61da4193996187ed70d16730e9ae6b3bb8787ebcaea1871d850997ddc08b4f4ea668fbf3740
908        7ac044b55be0908ecb94d4ed172ece66fd31bfdadf2b97a8bc690163ee11f5b575a4b44e36e2bfb2
909        f0fce91676fd64c7773bac6a003f481fddd0bae0a1f31aa27504e2a533af4cef3b623f4791b2cca6
910        d490
911        "
912        );
913
914        // EIP-8 format with version 4 and no additional list elements (sent from B to A)
915        let ack2 = hex!(
916            "
917        01ea0451958701280a56482929d3b0757da8f7fbe5286784beead59d95089c217c9b917788989470
918        b0e330cc6e4fb383c0340ed85fab836ec9fb8a49672712aeabbdfd1e837c1ff4cace34311cd7f4de
919        05d59279e3524ab26ef753a0095637ac88f2b499b9914b5f64e143eae548a1066e14cd2f4bd7f814
920        c4652f11b254f8a2d0191e2f5546fae6055694aed14d906df79ad3b407d94692694e259191cde171
921        ad542fc588fa2b7333313d82a9f887332f1dfc36cea03f831cb9a23fea05b33deb999e85489e645f
922        6aab1872475d488d7bd6c7c120caf28dbfc5d6833888155ed69d34dbdc39c1f299be1057810f34fb
923        e754d021bfca14dc989753d61c413d261934e1a9c67ee060a25eefb54e81a4d14baff922180c395d
924        3f998d70f46f6b58306f969627ae364497e73fc27f6d17ae45a413d322cb8814276be6ddd13b885b
925        201b943213656cde498fa0e9ddc8e0b8f8a53824fbd82254f3e2c17e8eaea009c38b4aa0a3f306e8
926        797db43c25d68e86f262e564086f59a2fc60511c42abfb3057c247a8a8fe4fb3ccbadde17514b7ac
927        8000cdb6a912778426260c47f38919a91f25f4b5ffb455d6aaaf150f7e5529c100ce62d6d92826a7
928        1778d809bdf60232ae21ce8a437eca8223f45ac37f6487452ce626f549b3b5fdee26afd2072e4bc7
929        5833c2464c805246155289f4
930        "
931        );
932
933        // EIP-8 format with version 57 and 3 additional list elements (sent from B to A)
934        let ack3 = hex!(
935            "
936        01f004076e58aae772bb101ab1a8e64e01ee96e64857ce82b1113817c6cdd52c09d26f7b90981cd7
937        ae835aeac72e1573b8a0225dd56d157a010846d888dac7464baf53f2ad4e3d584531fa203658fab0
938        3a06c9fd5e35737e417bc28c1cbf5e5dfc666de7090f69c3b29754725f84f75382891c561040ea1d
939        dc0d8f381ed1b9d0d4ad2a0ec021421d847820d6fa0ba66eaf58175f1b235e851c7e2124069fbc20
940        2888ddb3ac4d56bcbd1b9b7eab59e78f2e2d400905050f4a92dec1c4bdf797b3fc9b2f8e84a482f3
941        d800386186712dae00d5c386ec9387a5e9c9a1aca5a573ca91082c7d68421f388e79127a5177d4f8
942        590237364fd348c9611fa39f78dcdceee3f390f07991b7b47e1daa3ebcb6ccc9607811cb17ce51f1
943        c8c2c5098dbdd28fca547b3f58c01a424ac05f869f49c6a34672ea2cbbc558428aa1fe48bbfd6115
944        8b1b735a65d99f21e70dbc020bfdface9f724a0d1fb5895db971cc81aa7608baa0920abb0a565c9c
945        436e2fd13323428296c86385f2384e408a31e104670df0791d93e743a3a5194ee6b076fb6323ca59
946        3011b7348c16cf58f66b9633906ba54a2ee803187344b394f75dd2e663a57b956cb830dd7a908d4f
947        39a2336a61ef9fda549180d4ccde21514d117b6c6fd07a9102b5efe710a32af4eeacae2cb3b1dec0
948        35b9593b48b9d3ca4c13d245d5f04169b0b1
949        "
950        );
951
952        eip8_test_server().read_auth(&mut auth2.to_vec()).unwrap();
953        eip8_test_server().read_auth(&mut auth3.to_vec()).unwrap();
954
955        let mut test_client = eip8_test_client();
956        let mut test_server = eip8_test_server();
957
958        test_server.read_auth(&mut test_client.create_auth()).unwrap();
959
960        test_client.read_ack(&mut test_server.create_ack()).unwrap();
961
962        test_client.read_ack(&mut ack2.to_vec()).unwrap();
963        test_client.read_ack(&mut ack3.to_vec()).unwrap();
964    }
965
966    #[test]
967    fn kdf_out_of_bounds() {
968        // ensures that the kdf method does not panic if the dest is too small
969        let len_range = 1..65;
970        for len in len_range {
971            let mut dest = vec![1u8; len];
972            kdf(
973                b256!("7000000000000000000000000000000000000000000000000000000000000007"),
974                &[0x01, 0x33, 0x70, 0xbe, 0xef],
975                &mut dest,
976            );
977        }
978        std::hint::black_box(());
979    }
980}