chore: document parts of ECIES and MAC (#1260)

This commit is contained in:
Dan Cline
2023-02-09 19:14:53 -05:00
committed by GitHub
parent 3e269714e7
commit b6c9886b09
2 changed files with 31 additions and 1 deletions

View File

@ -24,6 +24,12 @@ use std::convert::TryFrom;
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 [`H256`].
fn ecdh_x(public_key: &PublicKey, secret_key: &SecretKey) -> H256 {
H256::from_slice(&secp256k1::ecdh::shared_secret_point(public_key, secret_key)[..32])
}
@ -332,6 +338,8 @@ impl ECIES {
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 {
@ -354,7 +362,7 @@ impl ECIES {
buf
}
/// Write an ack message to the given buffer.
/// Write an `ack` message to the given buffer.
pub fn write_ack(&mut self, out: &mut BytesMut) {
let unencrypted = self.create_ack_unencrypted();
@ -378,6 +386,13 @@ impl ECIES {
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 =

View File

@ -10,6 +10,13 @@ use typenum::U16;
pub type HeaderBytes = GenericArray<u8, U16>;
/// [`Ethereum MAC`](https://github.com/ethereum/devp2p/blob/master/rlpx.md#mac) state.
///
/// The ethereum MAC is a cursed MAC construction.
///
/// The ethereum MAC is a nonstandard MAC construction that uses AES-256 (without a mode, as a
/// block cipher) and Keccak-256. However, it only ever encrypts messages that are 128 bits long,
/// and is not defined as a general MAC.
#[derive(Debug)]
pub struct MAC {
secret: H256,
@ -17,17 +24,21 @@ pub struct MAC {
}
impl MAC {
/// Initialize the MAC with the given secret
pub fn new(secret: H256) -> Self {
Self { secret, hasher: Keccak256::new() }
}
/// Update the internal keccak256 hasher with the given data
pub fn update(&mut self, data: &[u8]) {
self.hasher.update(data)
}
/// Accumulate the given [`HeaderBytes`] into the MAC's internal state.
pub fn update_header(&mut self, data: &HeaderBytes) {
let aes = Aes256Enc::new_from_slice(self.secret.as_ref()).unwrap();
let mut encrypted = self.digest().to_fixed_bytes();
aes.encrypt_padded::<NoPadding>(&mut encrypted, H128::len_bytes()).unwrap();
for i in 0..data.len() {
encrypted[i] ^= data[i];
@ -35,11 +46,13 @@ impl MAC {
self.hasher.update(encrypted);
}
/// Accumulate the given message body into the MAC's internal state.
pub fn update_body(&mut self, data: &[u8]) {
self.hasher.update(data);
let prev = self.digest();
let aes = Aes256Enc::new_from_slice(self.secret.as_ref()).unwrap();
let mut encrypted = self.digest().to_fixed_bytes();
aes.encrypt_padded::<NoPadding>(&mut encrypted, H128::len_bytes()).unwrap();
for i in 0..16 {
encrypted[i] ^= prev[i];
@ -47,6 +60,8 @@ impl MAC {
self.hasher.update(encrypted);
}
/// Produce a digest by finalizing the internal keccak256 hasher and returning the first 128
/// bits.
pub fn digest(&self) -> H128 {
H128::from_slice(&self.hasher.clone().finalize()[..16])
}