Skip to main content

reth_ecies/
mac.rs

1//! # Ethereum MAC Module
2//!
3//! This module provides the implementation of the Ethereum MAC (Message Authentication Code)
4//! construction, as specified in the Ethereum `RLPx` protocol.
5//!
6//! The Ethereum MAC is a nonstandard MAC construction that utilizes AES-256 (as a block cipher)
7//! and Keccak-256. It is specifically designed for messages of 128 bits in length and is not
8//! intended for general MAC use.
9//!
10//! For more information, refer to the [Ethereum MAC specification](https://github.com/ethereum/devp2p/blob/master/rlpx.md#mac).
11
12use aes::{Aes256Enc, Block};
13use alloy_primitives::{Keccak256, B128, B256};
14use cipher::BlockEncrypt;
15use digest::KeyInit;
16
17/// [`Ethereum MAC`](https://github.com/ethereum/devp2p/blob/master/rlpx.md#mac) state.
18///
19/// The ethereum MAC is a cursed MAC construction.
20///
21/// The ethereum MAC is a nonstandard MAC construction that uses AES-256 (without a mode, as a
22/// block cipher) and Keccak-256. However, it only ever encrypts messages that are 128 bits long,
23/// and is not defined as a general MAC.
24#[derive(Debug)]
25pub struct MAC {
26    /// AES-256 block cipher keyed with the MAC secret.
27    ///
28    /// The secret is fixed for the lifetime of the connection, so the key schedule is expanded
29    /// once here instead of on every header/body update.
30    aes: Aes256Enc,
31    hasher: Keccak256,
32}
33
34impl MAC {
35    /// Initialize the MAC with the given secret
36    pub fn new(secret: B256) -> Self {
37        Self {
38            aes: Aes256Enc::new_from_slice(secret.as_ref())
39                .expect("32 bytes is a valid AES-256 key"),
40            hasher: Keccak256::new(),
41        }
42    }
43
44    /// Update the internal keccak256 hasher with the given data
45    pub fn update(&mut self, data: &[u8]) {
46        self.hasher.update(data)
47    }
48
49    /// Accumulate the given header bytes into the MAC's internal state.
50    pub fn update_header(&mut self, data: &[u8; 16]) {
51        let mut encrypted = self.digest();
52
53        self.aes.encrypt_block(Block::from_mut_slice(encrypted.as_mut_slice()));
54        self.hasher.update(encrypted ^ B128::from(data));
55    }
56
57    /// Accumulate the given message body into the MAC's internal state.
58    pub fn update_body(&mut self, data: &[u8]) {
59        self.hasher.update(data);
60        let prev = self.digest();
61        let mut encrypted = prev;
62
63        self.aes.encrypt_block(Block::from_mut_slice(encrypted.as_mut_slice()));
64        self.hasher.update(encrypted ^ prev);
65    }
66
67    /// Produce a digest by finalizing the internal keccak256 hasher and returning the first 128
68    /// bits.
69    pub fn digest(&self) -> B128 {
70        B128::from_slice(&self.hasher.clone().finalize()[..16])
71    }
72}