reth_ecies/
algorithm.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
#![allow(missing_docs)]

use crate::{
    error::ECIESErrorImpl,
    mac::{HeaderBytes, MAC},
    util::{hmac_sha256, sha256},
    ECIESError,
};
use aes::{cipher::StreamCipher, Aes128, Aes256};
use alloy_primitives::{
    bytes::{BufMut, Bytes, BytesMut},
    B128, B256, B512 as PeerId,
};
use alloy_rlp::{Encodable, Rlp, RlpEncodable, RlpMaxEncodedLen};
use byteorder::{BigEndian, ByteOrder, ReadBytesExt};
use ctr::Ctr64BE;
use digest::{crypto_common::KeyIvInit, Digest};
use rand::{thread_rng, Rng};
use reth_network_peers::{id2pk, pk2id};
use secp256k1::{
    ecdsa::{RecoverableSignature, RecoveryId},
    PublicKey, SecretKey, SECP256K1,
};
use sha2::Sha256;
use sha3::Keccak256;

const PROTOCOL_VERSION: usize = 4;

/// Computes the shared secret with ECDH and strips the y coordinate after computing the shared
/// secret.
///
/// This uses the given remote public key and local (ephemeral) secret key to [compute a shared
/// secp256k1 point](secp256k1::ecdh::shared_secret_point) and slices off the y coordinate from the
/// returned pair, returning only the bytes of the x coordinate as a [`B256`].
fn ecdh_x(public_key: &PublicKey, secret_key: &SecretKey) -> B256 {
    B256::from_slice(&secp256k1::ecdh::shared_secret_point(public_key, secret_key)[..32])
}

/// This is the NIST SP 800-56A Concatenation Key Derivation Function (KDF) using SHA-256.
///
/// Internally this uses [`concat_kdf::derive_key_into`] to derive a key into the given `dest`
/// slice.
///
/// # Panics
/// * If the `dest` is empty
/// * If the `dest` len is greater than or equal to the hash output len * the max counter value. In
///   this case, the hash output len is 32 bytes, and the max counter value is 2^32 - 1. So the dest
///   cannot have a len greater than 32 * 2^32 - 1.
fn kdf(secret: B256, s1: &[u8], dest: &mut [u8]) {
    concat_kdf::derive_key_into::<Sha256>(secret.as_slice(), s1, dest).unwrap();
}

pub struct ECIES {
    secret_key: SecretKey,
    public_key: PublicKey,
    remote_public_key: Option<PublicKey>,

    pub(crate) remote_id: Option<PeerId>,

    ephemeral_secret_key: SecretKey,
    ephemeral_public_key: PublicKey,
    ephemeral_shared_secret: Option<B256>,
    remote_ephemeral_public_key: Option<PublicKey>,

    nonce: B256,
    remote_nonce: Option<B256>,

    ingress_aes: Option<Ctr64BE<Aes256>>,
    egress_aes: Option<Ctr64BE<Aes256>>,
    ingress_mac: Option<MAC>,
    egress_mac: Option<MAC>,

    init_msg: Option<Bytes>,
    remote_init_msg: Option<Bytes>,

    body_size: Option<usize>,
}

impl core::fmt::Debug for ECIES {
    #[inline]
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.debug_struct("ECIES")
            .field("public_key", &self.public_key)
            .field("remote_public_key", &self.remote_public_key)
            .field("remote_id", &self.remote_id)
            .field("ephemeral_public_key", &self.ephemeral_public_key)
            .field("ephemeral_shared_secret", &self.ephemeral_shared_secret)
            .field("remote_ephemeral_public_key", &self.remote_ephemeral_public_key)
            .field("nonce", &self.nonce)
            .field("remote_nonce", &self.remote_nonce)
            .field("ingress_mac", &self.ingress_mac)
            .field("egress_mac", &self.egress_mac)
            .field("init_msg", &self.init_msg)
            .field("remote_init_msg", &self.remote_init_msg)
            .field("body_size", &self.body_size)
            .finish()
    }
}

fn split_at_mut<T>(arr: &mut [T], idx: usize) -> Result<(&mut [T], &mut [T]), ECIESError> {
    if idx > arr.len() {
        return Err(ECIESErrorImpl::OutOfBounds { idx, len: arr.len() }.into())
    }
    Ok(arr.split_at_mut(idx))
}

/// A parsed `RLPx` encrypted message
///
/// From the devp2p spec, this should help perform the following operations:
///
/// For Bob to decrypt the message `R || iv || c || d`, he derives the shared secret `S = Px` where
/// `(Px, Py) = kB * R` as well as the encryption and authentication keys `kE || kM = KDF(S, 32)`.
///
/// Bob verifies the authenticity of the message by checking whether `d == MAC(sha256(kM), iv ||
/// c)` then obtains the plaintext as `m = AES(kE, iv || c)`.
#[derive(Debug)]
pub struct EncryptedMessage<'a> {
    /// The auth data, used when checking the `tag` with HMAC-SHA256.
    ///
    /// This is not mentioned in the `RLPx` spec, but included in implementations.
    ///
    /// See source comments of [`Self::check_integrity`] for more information.
    auth_data: [u8; 2],
    /// The remote secp256k1 public key
    public_key: PublicKey,
    /// The IV, for use in AES during decryption, in the tag check
    iv: B128,
    /// The encrypted data
    encrypted_data: &'a mut [u8],
    /// The message tag
    tag: B256,
}

impl<'a> EncryptedMessage<'a> {
    /// Parse the given `data` into an [`EncryptedMessage`].
    ///
    /// If the data is not long enough to contain the expected fields, this returns an error.
    pub fn parse(data: &mut [u8]) -> Result<EncryptedMessage<'_>, ECIESError> {
        // Auth data is 2 bytes, public key is 65 bytes
        if data.len() < 65 + 2 {
            return Err(ECIESErrorImpl::EncryptedDataTooSmall.into())
        }
        let (auth_data, encrypted) = data.split_at_mut(2);

        // convert the auth data to a fixed size array
        //
        // NOTE: this will not panic because we've already checked that the data is long enough
        let auth_data = auth_data.try_into().unwrap();

        let (pubkey_bytes, encrypted) = encrypted.split_at_mut(65);
        let public_key = PublicKey::from_slice(pubkey_bytes)?;

        // return an error if the encrypted len is currently less than 32
        let tag_index =
            encrypted.len().checked_sub(32).ok_or(ECIESErrorImpl::EncryptedDataTooSmall)?;

        // NOTE: we've already checked that the encrypted data is long enough to contain the
        // encrypted data and tag
        let (data_iv, tag_bytes) = encrypted.split_at_mut(tag_index);

        // NOTE: this will not panic because we are splitting at length minus 32 bytes, which
        // causes tag_bytes to be 32 bytes long
        let tag = B256::from_slice(tag_bytes);

        // now we can check if the encrypted data is long enough to contain the IV
        if data_iv.len() < 16 {
            return Err(ECIESErrorImpl::EncryptedDataTooSmall.into())
        }
        let (iv, encrypted_data) = data_iv.split_at_mut(16);

        // NOTE: this will not panic because we are splitting at 16 bytes
        let iv = B128::from_slice(iv);

        Ok(EncryptedMessage { auth_data, public_key, iv, encrypted_data, tag })
    }

    /// Use the given secret and this encrypted message to derive the shared secret, and use the
    /// shared secret to derive the mac and encryption keys.
    pub fn derive_keys(&self, secret_key: &SecretKey) -> RLPxSymmetricKeys {
        // perform ECDH to get the shared secret, using the remote public key from the message and
        // the given secret key
        let x = ecdh_x(&self.public_key, secret_key);
        let mut key = [0u8; 32];

        // The RLPx spec describes the key derivation process as:
        //
        // kE || kM = KDF(S, 32)
        //
        // where kE is the encryption key, and kM is used to determine the MAC key (see below)
        //
        // NOTE: The RLPx spec does not define an `OtherInfo` parameter, and this is unused in
        // other implementations, so we use an empty slice.
        kdf(x, &[], &mut key);

        let enc_key = B128::from_slice(&key[..16]);

        // The MAC tag check operation described is:
        //
        // d == MAC(sha256(kM), iv || c)
        //
        // where kM is the result of the above KDF, iv is the IV, and c is the encrypted data.
        // Because the hash of kM is ultimately used as the mac key, we perform that hashing here.
        let mac_key = sha256(&key[16..32]);

        RLPxSymmetricKeys { enc_key, mac_key }
    }

    /// Use the given ECIES keys to check the message integrity using the contained tag.
    pub fn check_integrity(&self, keys: &RLPxSymmetricKeys) -> Result<(), ECIESError> {
        // The MAC tag check operation described is:
        //
        // d == MAC(sha256(kM), iv || c)
        //
        // NOTE: The RLPx spec does not show here that the `auth_data` is required for checking the
        // tag.
        //
        // Geth refers to SEC 1's definition of ECIES:
        //
        // Encrypt encrypts a message using ECIES as specified in SEC 1, section 5.1.
        //
        // s1 and s2 contain shared information that is not part of the resulting
        // ciphertext. s1 is fed into key derivation, s2 is fed into the MAC. If the
        // shared information parameters aren't being used, they should be nil.
        //
        // ```
        // prefix := make([]byte, 2)
        // binary.BigEndian.PutUint16(prefix, uint16(len(h.wbuf.data)+eciesOverhead))
        //
        // enc, err := ecies.Encrypt(rand.Reader, h.remote, h.wbuf.data, nil, prefix)
        // ```
        let check_tag = hmac_sha256(
            keys.mac_key.as_ref(),
            &[self.iv.as_slice(), self.encrypted_data],
            &self.auth_data,
        );
        if check_tag != self.tag {
            return Err(ECIESErrorImpl::TagCheckDecryptFailed.into())
        }

        Ok(())
    }

    /// Use the given ECIES keys to decrypt the contained encrypted data, consuming the message and
    /// returning the decrypted data.
    pub fn decrypt(self, keys: &RLPxSymmetricKeys) -> &'a mut [u8] {
        let Self { iv, encrypted_data, .. } = self;

        // rename for clarity once it's decrypted
        let decrypted_data = encrypted_data;

        let mut decryptor = Ctr64BE::<Aes128>::new((&keys.enc_key.0).into(), (&*iv).into());
        decryptor.apply_keystream(decrypted_data);
        decrypted_data
    }

    /// Use the given ECIES keys to check the integrity of the message, returning an error if the
    /// tag check fails, and then decrypt the message, returning the decrypted data.
    pub fn check_and_decrypt(self, keys: RLPxSymmetricKeys) -> Result<&'a mut [u8], ECIESError> {
        self.check_integrity(&keys)?;
        Ok(self.decrypt(&keys))
    }
}

/// The symmetric keys derived from an ECIES message.
#[derive(Debug)]
pub struct RLPxSymmetricKeys {
    /// The key used for decryption, specifically with AES-128 in CTR mode, using a 64-bit big
    /// endian counter.
    pub enc_key: B128,

    /// The key used for verifying message integrity, specifically with the NIST SP 800-56A Concat
    /// KDF.
    pub mac_key: B256,
}

impl ECIES {
    /// Create a new client with the given static secret key, remote peer id, nonce, and ephemeral
    /// secret key.
    fn new_static_client(
        secret_key: SecretKey,
        remote_id: PeerId,
        nonce: B256,
        ephemeral_secret_key: SecretKey,
    ) -> Result<Self, ECIESError> {
        let public_key = PublicKey::from_secret_key(SECP256K1, &secret_key);
        let remote_public_key = id2pk(remote_id)?;
        let ephemeral_public_key = PublicKey::from_secret_key(SECP256K1, &ephemeral_secret_key);

        Ok(Self {
            secret_key,
            public_key,
            ephemeral_secret_key,
            ephemeral_public_key,
            nonce,

            remote_public_key: Some(remote_public_key),
            remote_ephemeral_public_key: None,
            remote_nonce: None,
            ephemeral_shared_secret: None,
            init_msg: None,
            remote_init_msg: None,

            remote_id: Some(remote_id),

            body_size: None,
            egress_aes: None,
            ingress_aes: None,
            egress_mac: None,
            ingress_mac: None,
        })
    }

    /// Create a new ECIES client with the given static secret key and remote peer ID.
    pub fn new_client(secret_key: SecretKey, remote_id: PeerId) -> Result<Self, ECIESError> {
        let mut rng = thread_rng();
        let nonce = rng.gen();
        let ephemeral_secret_key = SecretKey::new(&mut rng);
        Self::new_static_client(secret_key, remote_id, nonce, ephemeral_secret_key)
    }

    /// Create a new server with the given static secret key, remote peer id, and ephemeral secret
    /// key.
    pub fn new_static_server(
        secret_key: SecretKey,
        nonce: B256,
        ephemeral_secret_key: SecretKey,
    ) -> Result<Self, ECIESError> {
        let public_key = PublicKey::from_secret_key(SECP256K1, &secret_key);
        let ephemeral_public_key = PublicKey::from_secret_key(SECP256K1, &ephemeral_secret_key);

        Ok(Self {
            secret_key,
            public_key,
            ephemeral_secret_key,
            ephemeral_public_key,
            nonce,

            remote_public_key: None,
            remote_ephemeral_public_key: None,
            remote_nonce: None,
            ephemeral_shared_secret: None,
            init_msg: None,
            remote_init_msg: None,

            remote_id: None,

            body_size: None,
            egress_aes: None,
            ingress_aes: None,
            egress_mac: None,
            ingress_mac: None,
        })
    }

    /// Create a new ECIES server with the given static secret key.
    pub fn new_server(secret_key: SecretKey) -> Result<Self, ECIESError> {
        let mut rng = thread_rng();
        let nonce = rng.gen();
        let ephemeral_secret_key = SecretKey::new(&mut rng);
        Self::new_static_server(secret_key, nonce, ephemeral_secret_key)
    }

    /// Return the contained remote peer ID.
    pub fn remote_id(&self) -> PeerId {
        self.remote_id.unwrap()
    }

    fn encrypt_message(&self, data: &[u8], out: &mut BytesMut) {
        let mut rng = thread_rng();

        out.reserve(secp256k1::constants::UNCOMPRESSED_PUBLIC_KEY_SIZE + 16 + data.len() + 32);

        let secret_key = SecretKey::new(&mut rng);
        out.extend_from_slice(
            &PublicKey::from_secret_key(SECP256K1, &secret_key).serialize_uncompressed(),
        );

        let x = ecdh_x(&self.remote_public_key.unwrap(), &secret_key);
        let mut key = [0u8; 32];
        kdf(x, &[], &mut key);

        let enc_key = B128::from_slice(&key[..16]);
        let mac_key = sha256(&key[16..32]);

        let iv: B128 = rng.gen();
        let mut encryptor = Ctr64BE::<Aes128>::new((&enc_key.0).into(), (&iv.0).into());

        let mut encrypted = data.to_vec();
        encryptor.apply_keystream(&mut encrypted);

        let total_size: u16 = u16::try_from(65 + 16 + data.len() + 32).unwrap();

        let tag =
            hmac_sha256(mac_key.as_ref(), &[iv.as_slice(), &encrypted], &total_size.to_be_bytes());

        out.extend_from_slice(iv.as_slice());
        out.extend_from_slice(&encrypted);
        out.extend_from_slice(tag.as_ref());
    }

    fn decrypt_message<'a>(&self, data: &'a mut [u8]) -> Result<&'a mut [u8], ECIESError> {
        // parse the encrypted message from bytes
        let encrypted_message = EncryptedMessage::parse(data)?;

        // derive keys from the secret key and the encrypted message
        let keys = encrypted_message.derive_keys(&self.secret_key);

        // check message integrity and decrypt the message
        encrypted_message.check_and_decrypt(keys)
    }

    fn create_auth_unencrypted(&self) -> BytesMut {
        let x = ecdh_x(&self.remote_public_key.unwrap(), &self.secret_key);
        let msg = x ^ self.nonce;
        let (rec_id, sig) = SECP256K1
            .sign_ecdsa_recoverable(
                &secp256k1::Message::from_digest(msg.0),
                &self.ephemeral_secret_key,
            )
            .serialize_compact();

        let mut sig_bytes = [0u8; 65];
        sig_bytes[..64].copy_from_slice(&sig);
        sig_bytes[64] = rec_id.to_i32() as u8;

        let id = pk2id(&self.public_key);

        #[derive(RlpEncodable)]
        struct S<'a> {
            sig_bytes: &'a [u8; 65],
            id: &'a PeerId,
            nonce: &'a B256,
            protocol_version: u8,
        }

        let mut out = BytesMut::new();
        S {
            sig_bytes: &sig_bytes,
            id: &id,
            nonce: &self.nonce,
            protocol_version: PROTOCOL_VERSION as u8,
        }
        .encode(&mut out);

        out.resize(out.len() + thread_rng().gen_range(100..=300), 0);
        out
    }

    #[cfg(test)]
    fn create_auth(&mut self) -> BytesMut {
        let mut buf = BytesMut::new();
        self.write_auth(&mut buf);
        buf
    }

    /// Write an auth message to the given buffer.
    pub fn write_auth(&mut self, buf: &mut BytesMut) {
        let unencrypted = self.create_auth_unencrypted();

        let mut out = buf.split_off(buf.len());
        out.put_u16(0);

        let mut encrypted = out.split_off(out.len());
        self.encrypt_message(&unencrypted, &mut encrypted);

        let len_bytes = u16::try_from(encrypted.len()).unwrap().to_be_bytes();
        out[..len_bytes.len()].copy_from_slice(&len_bytes);

        out.unsplit(encrypted);

        self.init_msg = Some(Bytes::copy_from_slice(&out));

        buf.unsplit(out);
    }

    fn parse_auth_unencrypted(&mut self, data: &[u8]) -> Result<(), ECIESError> {
        let mut data = Rlp::new(data)?;

        let sigdata = data.get_next::<[u8; 65]>()?.ok_or(ECIESErrorImpl::InvalidAuthData)?;
        let signature = RecoverableSignature::from_compact(
            &sigdata[..64],
            RecoveryId::from_i32(sigdata[64] as i32)?,
        )?;
        let remote_id = data.get_next()?.ok_or(ECIESErrorImpl::InvalidAuthData)?;
        self.remote_id = Some(remote_id);
        self.remote_public_key = Some(id2pk(remote_id)?);
        self.remote_nonce = Some(data.get_next()?.ok_or(ECIESErrorImpl::InvalidAuthData)?);

        let x = ecdh_x(&self.remote_public_key.unwrap(), &self.secret_key);
        self.remote_ephemeral_public_key = Some(SECP256K1.recover_ecdsa(
            &secp256k1::Message::from_digest((x ^ self.remote_nonce.unwrap()).0),
            &signature,
        )?);
        self.ephemeral_shared_secret =
            Some(ecdh_x(&self.remote_ephemeral_public_key.unwrap(), &self.ephemeral_secret_key));

        Ok(())
    }

    /// Read and verify an auth message from the input data.
    #[tracing::instrument(skip_all)]
    pub fn read_auth(&mut self, data: &mut [u8]) -> Result<(), ECIESError> {
        self.remote_init_msg = Some(Bytes::copy_from_slice(data));
        let unencrypted = self.decrypt_message(data)?;
        self.parse_auth_unencrypted(unencrypted)
    }

    /// Create an `ack` message using the internal nonce, local ephemeral public key, and `RLPx`
    /// ECIES protocol version.
    fn create_ack_unencrypted(&self) -> impl AsRef<[u8]> {
        #[derive(RlpEncodable, RlpMaxEncodedLen)]
        struct S {
            id: PeerId,
            nonce: B256,
            protocol_version: u8,
        }

        alloy_rlp::encode_fixed_size(&S {
            id: pk2id(&self.ephemeral_public_key),
            nonce: self.nonce,
            protocol_version: PROTOCOL_VERSION as u8,
        })
    }

    #[cfg(test)]
    pub fn create_ack(&mut self) -> BytesMut {
        let mut buf = BytesMut::new();
        self.write_ack(&mut buf);
        buf
    }

    /// Write an `ack` message to the given buffer.
    pub fn write_ack(&mut self, out: &mut BytesMut) {
        let unencrypted = self.create_ack_unencrypted();

        let mut buf = out.split_off(out.len());

        // reserve space for length
        buf.put_u16(0);

        // encrypt and append
        let mut encrypted = buf.split_off(buf.len());
        self.encrypt_message(unencrypted.as_ref(), &mut encrypted);
        let len_bytes = u16::try_from(encrypted.len()).unwrap().to_be_bytes();
        buf.unsplit(encrypted);

        // write length
        buf[..len_bytes.len()].copy_from_slice(&len_bytes[..]);

        self.init_msg = Some(buf.clone().freeze());
        out.unsplit(buf);

        self.setup_frame(true);
    }

    /// Parse the incoming `ack` message from the given `data` bytes, which are assumed to be
    /// unencrypted. This parses the remote ephemeral pubkey and nonce from the message, and uses
    /// ECDH to compute the shared secret. The shared secret is the x coordinate of the point
    /// returned by ECDH.
    ///
    /// This sets the `remote_ephemeral_public_key` and `remote_nonce`, and
    /// `ephemeral_shared_secret` fields in the ECIES state.
    fn parse_ack_unencrypted(&mut self, data: &[u8]) -> Result<(), ECIESError> {
        let mut data = Rlp::new(data)?;
        self.remote_ephemeral_public_key =
            Some(id2pk(data.get_next()?.ok_or(ECIESErrorImpl::InvalidAckData)?)?);
        self.remote_nonce = Some(data.get_next()?.ok_or(ECIESErrorImpl::InvalidAckData)?);

        self.ephemeral_shared_secret =
            Some(ecdh_x(&self.remote_ephemeral_public_key.unwrap(), &self.ephemeral_secret_key));
        Ok(())
    }

    /// Read and verify an ack message from the input data.
    #[tracing::instrument(skip_all)]
    pub fn read_ack(&mut self, data: &mut [u8]) -> Result<(), ECIESError> {
        self.remote_init_msg = Some(Bytes::copy_from_slice(data));
        let unencrypted = self.decrypt_message(data)?;
        self.parse_ack_unencrypted(unencrypted)?;
        self.setup_frame(false);
        Ok(())
    }

    fn setup_frame(&mut self, incoming: bool) {
        let mut hasher = Keccak256::new();
        for el in &if incoming {
            [self.nonce, self.remote_nonce.unwrap()]
        } else {
            [self.remote_nonce.unwrap(), self.nonce]
        } {
            hasher.update(el);
        }
        let h_nonce = B256::from(hasher.finalize().as_ref());

        let iv = B128::default();
        let shared_secret: B256 = {
            let mut hasher = Keccak256::new();
            hasher.update(self.ephemeral_shared_secret.unwrap().0.as_ref());
            hasher.update(h_nonce.0.as_ref());
            B256::from(hasher.finalize().as_ref())
        };

        let aes_secret: B256 = {
            let mut hasher = Keccak256::new();
            hasher.update(self.ephemeral_shared_secret.unwrap().0.as_ref());
            hasher.update(shared_secret.0.as_ref());
            B256::from(hasher.finalize().as_ref())
        };
        self.ingress_aes = Some(Ctr64BE::<Aes256>::new((&aes_secret.0).into(), (&iv.0).into()));
        self.egress_aes = Some(Ctr64BE::<Aes256>::new((&aes_secret.0).into(), (&iv.0).into()));

        let mac_secret: B256 = {
            let mut hasher = Keccak256::new();
            hasher.update(self.ephemeral_shared_secret.unwrap().0.as_ref());
            hasher.update(aes_secret.0.as_ref());
            B256::from(hasher.finalize().as_ref())
        };
        self.ingress_mac = Some(MAC::new(mac_secret));
        self.ingress_mac.as_mut().unwrap().update((mac_secret ^ self.nonce).as_ref());
        self.ingress_mac.as_mut().unwrap().update(self.remote_init_msg.as_ref().unwrap());
        self.egress_mac = Some(MAC::new(mac_secret));
        self.egress_mac
            .as_mut()
            .unwrap()
            .update((mac_secret ^ self.remote_nonce.unwrap()).as_ref());
        self.egress_mac.as_mut().unwrap().update(self.init_msg.as_ref().unwrap());
    }

    #[cfg(test)]
    fn create_header(&mut self, size: usize) -> BytesMut {
        let mut out = BytesMut::new();
        self.write_header(&mut out, size);
        out
    }

    pub fn write_header(&mut self, out: &mut BytesMut, size: usize) {
        let mut buf = [0u8; 8];
        BigEndian::write_uint(&mut buf, size as u64, 3);
        let mut header = [0u8; 16];
        header[..3].copy_from_slice(&buf[..3]);
        header[3..6].copy_from_slice(&[194, 128, 128]);

        let mut header = HeaderBytes::from(header);
        self.egress_aes.as_mut().unwrap().apply_keystream(&mut header);
        self.egress_mac.as_mut().unwrap().update_header(&header);
        let tag = self.egress_mac.as_mut().unwrap().digest();

        out.reserve(Self::header_len());
        out.extend_from_slice(&header[..]);
        out.extend_from_slice(tag.as_slice());
    }

    /// Reads the `RLPx` header from the slice, setting up the MAC and AES, returning the body
    /// size contained in the header.
    pub fn read_header(&mut self, data: &mut [u8]) -> Result<usize, ECIESError> {
        // If the data is not large enough to fit the header and mac bytes, return an error
        //
        // The header is 16 bytes, and the mac is 16 bytes, so the data must be at least 32 bytes
        if data.len() < 32 {
            return Err(ECIESErrorImpl::InvalidHeader.into())
        }

        let (header_bytes, mac_bytes) = split_at_mut(data, 16)?;
        let header = HeaderBytes::from_mut_slice(header_bytes);
        let mac = B128::from_slice(&mac_bytes[..16]);

        self.ingress_mac.as_mut().unwrap().update_header(header);
        let check_mac = self.ingress_mac.as_mut().unwrap().digest();
        if check_mac != mac {
            return Err(ECIESErrorImpl::TagCheckHeaderFailed.into())
        }

        self.ingress_aes.as_mut().unwrap().apply_keystream(header);
        if header.as_slice().len() < 3 {
            return Err(ECIESErrorImpl::InvalidHeader.into())
        }

        let body_size = usize::try_from(header.as_slice().read_uint::<BigEndian>(3)?)?;

        self.body_size = Some(body_size);

        Ok(body_size)
    }

    pub const fn header_len() -> usize {
        32
    }

    pub fn body_len(&self) -> usize {
        let len = self.body_size.unwrap();
        Self::align_16(len) + 16
    }

    #[cfg(test)]
    fn create_body(&mut self, data: &[u8]) -> BytesMut {
        let mut out = BytesMut::new();
        self.write_body(&mut out, data);
        out
    }

    pub fn write_body(&mut self, out: &mut BytesMut, data: &[u8]) {
        let len = Self::align_16(data.len());
        let old_len = out.len();
        out.resize(old_len + len, 0);

        let encrypted = &mut out[old_len..old_len + len];
        encrypted[..data.len()].copy_from_slice(data);

        self.egress_aes.as_mut().unwrap().apply_keystream(encrypted);
        self.egress_mac.as_mut().unwrap().update_body(encrypted);
        let tag = self.egress_mac.as_mut().unwrap().digest();

        out.extend_from_slice(tag.as_slice());
    }

    pub fn read_body<'a>(&mut self, data: &'a mut [u8]) -> Result<&'a mut [u8], ECIESError> {
        // error if the data is too small to contain the tag
        // TODO: create a custom type similar to EncryptedMessage for parsing, checking MACs, and
        // decrypting the body
        let mac_index = data.len().checked_sub(16).ok_or(ECIESErrorImpl::EncryptedDataTooSmall)?;
        let (body, mac_bytes) = split_at_mut(data, mac_index)?;
        let mac = B128::from_slice(mac_bytes);
        self.ingress_mac.as_mut().unwrap().update_body(body);
        let check_mac = self.ingress_mac.as_mut().unwrap().digest();
        if check_mac != mac {
            return Err(ECIESErrorImpl::TagCheckBodyFailed.into())
        }

        let size = self.body_size.unwrap();
        self.body_size = None;
        let ret = body;
        self.ingress_aes.as_mut().unwrap().apply_keystream(ret);
        Ok(split_at_mut(ret, size)?.0)
    }

    /// Returns `num` aligned to 16.
    ///
    /// `<https://stackoverflow.com/questions/14561402/how-is-this-size-alignment-working>`
    #[inline]
    const fn align_16(num: usize) -> usize {
        (num + (16 - 1)) & !(16 - 1)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use alloy_primitives::{b256, hex};

    #[test]
    fn ecdh() {
        let our_secret_key = SecretKey::from_slice(&hex!(
            "202a36e24c3eb39513335ec99a7619bad0e7dc68d69401b016253c7d26dc92f8"
        ))
        .unwrap();
        let remote_public_key = id2pk(hex!("d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666").into()).unwrap();

        assert_eq!(
            ecdh_x(&remote_public_key, &our_secret_key),
            hex!("821ce7e01ea11b111a52b2dafae8a3031a372d83bdf1a78109fa0783c2b9d5d3")
        )
    }

    #[test]
    fn communicate() {
        let mut rng = thread_rng();
        let server_secret_key = SecretKey::new(&mut rng);
        let server_public_key = PublicKey::from_secret_key(SECP256K1, &server_secret_key);
        let client_secret_key = SecretKey::new(&mut rng);

        let mut server_ecies = ECIES::new_server(server_secret_key).unwrap();
        let mut client_ecies =
            ECIES::new_client(client_secret_key, pk2id(&server_public_key)).unwrap();

        // Handshake
        let mut auth = client_ecies.create_auth();
        server_ecies.read_auth(&mut auth).unwrap();
        let mut ack = server_ecies.create_ack();
        client_ecies.read_ack(&mut ack).unwrap();
        let mut ack = client_ecies.create_ack();
        server_ecies.read_ack(&mut ack).unwrap();

        let server_to_client_data = [0u8, 1u8, 2u8, 3u8, 4u8];
        let client_to_server_data = [5u8, 6u8, 7u8];

        // Test server to client 1
        let mut header = server_ecies.create_header(server_to_client_data.len());
        assert_eq!(header.len(), ECIES::header_len());
        client_ecies.read_header(&mut header).unwrap();
        let mut body = server_ecies.create_body(&server_to_client_data);
        assert_eq!(body.len(), client_ecies.body_len());
        let ret = client_ecies.read_body(&mut body).unwrap();
        assert_eq!(ret, server_to_client_data);

        // Test client to server 1
        server_ecies
            .read_header(&mut client_ecies.create_header(client_to_server_data.len()))
            .unwrap();
        let mut b = client_ecies.create_body(&client_to_server_data);
        let ret = server_ecies.read_body(&mut b).unwrap();
        assert_eq!(ret, client_to_server_data);

        // Test server to client 2
        client_ecies
            .read_header(&mut server_ecies.create_header(server_to_client_data.len()))
            .unwrap();
        let mut b = server_ecies.create_body(&server_to_client_data);
        let ret = client_ecies.read_body(&mut b).unwrap();
        assert_eq!(ret, server_to_client_data);

        // Test server to client 3
        client_ecies
            .read_header(&mut server_ecies.create_header(server_to_client_data.len()))
            .unwrap();
        let mut b = server_ecies.create_body(&server_to_client_data);
        let ret = client_ecies.read_body(&mut b).unwrap();
        assert_eq!(ret, server_to_client_data);

        // Test client to server 2
        server_ecies
            .read_header(&mut client_ecies.create_header(client_to_server_data.len()))
            .unwrap();
        let mut b = client_ecies.create_body(&client_to_server_data);
        let ret = server_ecies.read_body(&mut b).unwrap();
        assert_eq!(ret, client_to_server_data);

        // Test client to server 3
        server_ecies
            .read_header(&mut client_ecies.create_header(client_to_server_data.len()))
            .unwrap();
        let mut b = client_ecies.create_body(&client_to_server_data);
        let ret = server_ecies.read_body(&mut b).unwrap();
        assert_eq!(ret, client_to_server_data);
    }

    fn eip8_test_server_key() -> SecretKey {
        SecretKey::from_slice(&hex!(
            "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"
        ))
        .unwrap()
    }

    fn eip8_test_client() -> ECIES {
        let client_static_key = SecretKey::from_slice(&hex!(
            "49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee"
        ))
        .unwrap();

        let client_ephemeral_key = SecretKey::from_slice(&hex!(
            "869d6ecf5211f1cc60418a13b9d870b22959d0c16f02bec714c960dd2298a32d"
        ))
        .unwrap();

        let client_nonce =
            b256!("7e968bba13b6c50e2c4cd7f241cc0d64d1ac25c7f5952df231ac6a2bda8ee5d6");

        let server_id = pk2id(&PublicKey::from_secret_key(SECP256K1, &eip8_test_server_key()));

        ECIES::new_static_client(client_static_key, server_id, client_nonce, client_ephemeral_key)
            .unwrap()
    }

    fn eip8_test_server() -> ECIES {
        let server_ephemeral_key = SecretKey::from_slice(&hex!(
            "e238eb8e04fee6511ab04c6dd3c89ce097b11f25d584863ac2b6d5b35b1847e4"
        ))
        .unwrap();

        let server_nonce =
            b256!("559aead08264d5795d3909718cdd05abd49572e84fe55590eef31a88a08fdffd");

        ECIES::new_static_server(eip8_test_server_key(), server_nonce, server_ephemeral_key)
            .unwrap()
    }

    #[test]
    /// Test vectors from <https://eips.ethereum.org/EIPS/eip-8>
    fn eip8_test() {
        // EIP-8 format with version 4 and no additional list elements
        let auth2 = hex!(
            "
        01b304ab7578555167be8154d5cc456f567d5ba302662433674222360f08d5f1534499d3678b513b
        0fca474f3a514b18e75683032eb63fccb16c156dc6eb2c0b1593f0d84ac74f6e475f1b8d56116b84
        9634a8c458705bf83a626ea0384d4d7341aae591fae42ce6bd5c850bfe0b999a694a49bbbaf3ef6c
        da61110601d3b4c02ab6c30437257a6e0117792631a4b47c1d52fc0f8f89caadeb7d02770bf999cc
        147d2df3b62e1ffb2c9d8c125a3984865356266bca11ce7d3a688663a51d82defaa8aad69da39ab6
        d5470e81ec5f2a7a47fb865ff7cca21516f9299a07b1bc63ba56c7a1a892112841ca44b6e0034dee
        70c9adabc15d76a54f443593fafdc3b27af8059703f88928e199cb122362a4b35f62386da7caad09
        c001edaeb5f8a06d2b26fb6cb93c52a9fca51853b68193916982358fe1e5369e249875bb8d0d0ec3
        6f917bc5e1eafd5896d46bd61ff23f1a863a8a8dcd54c7b109b771c8e61ec9c8908c733c0263440e
        2aa067241aaa433f0bb053c7b31a838504b148f570c0ad62837129e547678c5190341e4f1693956c
        3bf7678318e2d5b5340c9e488eefea198576344afbdf66db5f51204a6961a63ce072c8926c
        "
        );

        // EIP-8 format with version 56 and 3 additional list elements (sent from A to B)
        let auth3 = hex!(
            "
        01b8044c6c312173685d1edd268aa95e1d495474c6959bcdd10067ba4c9013df9e40ff45f5bfd6f7
        2471f93a91b493f8e00abc4b80f682973de715d77ba3a005a242eb859f9a211d93a347fa64b597bf
        280a6b88e26299cf263b01b8dfdb712278464fd1c25840b995e84d367d743f66c0e54a586725b7bb
        f12acca27170ae3283c1073adda4b6d79f27656993aefccf16e0d0409fe07db2dc398a1b7e8ee93b
        cd181485fd332f381d6a050fba4c7641a5112ac1b0b61168d20f01b479e19adf7fdbfa0905f63352
        bfc7e23cf3357657455119d879c78d3cf8c8c06375f3f7d4861aa02a122467e069acaf513025ff19
        6641f6d2810ce493f51bee9c966b15c5043505350392b57645385a18c78f14669cc4d960446c1757
        1b7c5d725021babbcd786957f3d17089c084907bda22c2b2675b4378b114c601d858802a55345a15
        116bc61da4193996187ed70d16730e9ae6b3bb8787ebcaea1871d850997ddc08b4f4ea668fbf3740
        7ac044b55be0908ecb94d4ed172ece66fd31bfdadf2b97a8bc690163ee11f5b575a4b44e36e2bfb2
        f0fce91676fd64c7773bac6a003f481fddd0bae0a1f31aa27504e2a533af4cef3b623f4791b2cca6
        d490
        "
        );

        // EIP-8 format with version 4 and no additional list elements (sent from B to A)
        let ack2 = hex!(
            "
        01ea0451958701280a56482929d3b0757da8f7fbe5286784beead59d95089c217c9b917788989470
        b0e330cc6e4fb383c0340ed85fab836ec9fb8a49672712aeabbdfd1e837c1ff4cace34311cd7f4de
        05d59279e3524ab26ef753a0095637ac88f2b499b9914b5f64e143eae548a1066e14cd2f4bd7f814
        c4652f11b254f8a2d0191e2f5546fae6055694aed14d906df79ad3b407d94692694e259191cde171
        ad542fc588fa2b7333313d82a9f887332f1dfc36cea03f831cb9a23fea05b33deb999e85489e645f
        6aab1872475d488d7bd6c7c120caf28dbfc5d6833888155ed69d34dbdc39c1f299be1057810f34fb
        e754d021bfca14dc989753d61c413d261934e1a9c67ee060a25eefb54e81a4d14baff922180c395d
        3f998d70f46f6b58306f969627ae364497e73fc27f6d17ae45a413d322cb8814276be6ddd13b885b
        201b943213656cde498fa0e9ddc8e0b8f8a53824fbd82254f3e2c17e8eaea009c38b4aa0a3f306e8
        797db43c25d68e86f262e564086f59a2fc60511c42abfb3057c247a8a8fe4fb3ccbadde17514b7ac
        8000cdb6a912778426260c47f38919a91f25f4b5ffb455d6aaaf150f7e5529c100ce62d6d92826a7
        1778d809bdf60232ae21ce8a437eca8223f45ac37f6487452ce626f549b3b5fdee26afd2072e4bc7
        5833c2464c805246155289f4
        "
        );

        // EIP-8 format with version 57 and 3 additional list elements (sent from B to A)
        let ack3 = hex!(
            "
        01f004076e58aae772bb101ab1a8e64e01ee96e64857ce82b1113817c6cdd52c09d26f7b90981cd7
        ae835aeac72e1573b8a0225dd56d157a010846d888dac7464baf53f2ad4e3d584531fa203658fab0
        3a06c9fd5e35737e417bc28c1cbf5e5dfc666de7090f69c3b29754725f84f75382891c561040ea1d
        dc0d8f381ed1b9d0d4ad2a0ec021421d847820d6fa0ba66eaf58175f1b235e851c7e2124069fbc20
        2888ddb3ac4d56bcbd1b9b7eab59e78f2e2d400905050f4a92dec1c4bdf797b3fc9b2f8e84a482f3
        d800386186712dae00d5c386ec9387a5e9c9a1aca5a573ca91082c7d68421f388e79127a5177d4f8
        590237364fd348c9611fa39f78dcdceee3f390f07991b7b47e1daa3ebcb6ccc9607811cb17ce51f1
        c8c2c5098dbdd28fca547b3f58c01a424ac05f869f49c6a34672ea2cbbc558428aa1fe48bbfd6115
        8b1b735a65d99f21e70dbc020bfdface9f724a0d1fb5895db971cc81aa7608baa0920abb0a565c9c
        436e2fd13323428296c86385f2384e408a31e104670df0791d93e743a3a5194ee6b076fb6323ca59
        3011b7348c16cf58f66b9633906ba54a2ee803187344b394f75dd2e663a57b956cb830dd7a908d4f
        39a2336a61ef9fda549180d4ccde21514d117b6c6fd07a9102b5efe710a32af4eeacae2cb3b1dec0
        35b9593b48b9d3ca4c13d245d5f04169b0b1
        "
        );

        eip8_test_server().read_auth(&mut auth2.to_vec()).unwrap();
        eip8_test_server().read_auth(&mut auth3.to_vec()).unwrap();

        let mut test_client = eip8_test_client();
        let mut test_server = eip8_test_server();

        test_server.read_auth(&mut test_client.create_auth()).unwrap();

        test_client.read_ack(&mut test_server.create_ack()).unwrap();

        test_client.read_ack(&mut ack2.to_vec()).unwrap();
        test_client.read_ack(&mut ack3.to_vec()).unwrap();
    }

    #[test]
    fn kdf_out_of_bounds() {
        // ensures that the kdf method does not panic if the dest is too small
        let len_range = 1..65;
        for len in len_range {
            let mut dest = vec![1u8; len];
            kdf(
                b256!("7000000000000000000000000000000000000000000000000000000000000007"),
                &[0x01, 0x33, 0x70, 0xbe, 0xef],
                &mut dest,
            );
        }
    }
}