1use super::crypto::{self, NodeKey, ECIES_OVERHEAD};
30use super::rlp::{self, Rlp};
31use aes::cipher::{BlockEncrypt, KeyInit, KeyIvInit, StreamCipher};
32use anyhow::{ensure, Context, Result};
33use rand::Rng;
34use secp256k1::PublicKey;
35use sha3::{Digest, Keccak256};
36use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
37
38pub const MAX_HANDSHAKE_BYTES: usize = 2048;
40pub const MAX_FRAME_SIZE: usize = 0xff_ffff;
42const HEADER_DATA: [u8; 3] = [0xc2, 0x80, 0x80];
44
45type Aes256Ctr = ctr::Ctr128BE<aes::Aes256>;
46
47pub struct Secrets {
49 pub aes: [u8; 32],
50 pub mac: [u8; 32],
51 pub egress_mac: Keccak256,
52 pub ingress_mac: Keccak256,
53 pub remote: PublicKey,
55}
56
57#[derive(Debug, Clone, PartialEq, Eq)]
59pub struct AuthMsg {
60 pub signature: [u8; 65],
61 pub initiator_id: [u8; 64],
62 pub nonce: [u8; 32],
63 pub version: u64,
64}
65
66#[derive(Debug, Clone, PartialEq, Eq)]
68pub struct AckMsg {
69 pub ephemeral_id: [u8; 64],
70 pub nonce: [u8; 32],
71 pub version: u64,
72}
73
74fn xor32(a: &[u8; 32], b: &[u8; 32]) -> [u8; 32] {
75 let mut out = [0u8; 32];
76 for i in 0..32 {
77 out[i] = a[i] ^ b[i];
78 }
79 out
80}
81
82pub fn open_packet(key: &NodeKey, packet: &[u8]) -> Result<Vec<u8>> {
84 ensure!(packet.len() > 2, "handshake packet is empty");
85 crypto::ecies_decrypt(key.secret(), &packet[2..], &packet[..2])
86}
87
88pub fn parse_auth(plain: &[u8]) -> Result<AuthMsg> {
91 let (item, _) = rlp::decode_prefix(plain).context("auth body is not RLP")?;
92 let l = item.as_list().context("auth body")?;
93 ensure!(l.len() >= 4, "auth body has {} fields, not 4", l.len());
94 Ok(AuthMsg {
95 signature: l[0].as_fixed().context("auth signature")?,
96 initiator_id: l[1].as_fixed().context("auth initiator id")?,
97 nonce: l[2].as_fixed().context("auth nonce")?,
98 version: l[3].as_u64().context("auth version")?,
99 })
100}
101
102pub fn parse_ack(plain: &[u8]) -> Result<AckMsg> {
104 let (item, _) = rlp::decode_prefix(plain).context("ack body is not RLP")?;
105 let l = item.as_list().context("ack body")?;
106 ensure!(l.len() >= 3, "ack body has {} fields, not 3", l.len());
107 Ok(AckMsg {
108 ephemeral_id: l[0].as_fixed().context("ack ephemeral key")?,
109 nonce: l[1].as_fixed().context("ack nonce")?,
110 version: l[2].as_u64().context("ack version")?,
111 })
112}
113
114pub fn seal(remote: &PublicKey, body: Rlp) -> Vec<u8> {
116 let mut plain = body.encode();
117 let pad = rand::thread_rng().gen_range(100..200);
118 plain.resize(plain.len() + pad, 0);
119 let size = (plain.len() + ECIES_OVERHEAD) as u16;
120 let prefix = size.to_be_bytes();
121 let mut out = prefix.to_vec();
122 out.extend_from_slice(&crypto::ecies_encrypt(remote, &plain, &prefix));
123 out
124}
125
126pub async fn read_packet<S: AsyncRead + Unpin>(io: &mut S) -> Result<Vec<u8>> {
128 let mut prefix = [0u8; 2];
129 io.read_exact(&mut prefix)
130 .await
131 .context("reading the handshake size")?;
132 let size = usize::from(u16::from_be_bytes(prefix));
133 ensure!(
134 size <= MAX_HANDSHAKE_BYTES,
135 "handshake packet of {size} bytes is above the {MAX_HANDSHAKE_BYTES}-byte bound"
136 );
137 ensure!(
138 size > ECIES_OVERHEAD,
139 "handshake packet of {size} bytes cannot hold an ECIES message"
140 );
141 let mut packet = vec![0u8; 2 + size];
142 packet[..2].copy_from_slice(&prefix);
143 io.read_exact(&mut packet[2..])
144 .await
145 .context("reading the handshake packet")?;
146 Ok(packet)
147}
148
149#[allow(clippy::too_many_arguments)]
151pub fn derive_secrets(
152 initiator: bool,
153 ephemeral: &NodeKey,
154 remote_ephemeral: &PublicKey,
155 init_nonce: &[u8; 32],
156 resp_nonce: &[u8; 32],
157 auth_packet: &[u8],
158 ack_packet: &[u8],
159 remote: PublicKey,
160) -> Secrets {
161 let ecdhe = crypto::ecdh_x(ephemeral.secret(), remote_ephemeral);
162 let shared = crypto::keccak256(&[&ecdhe, &crypto::keccak256(&[resp_nonce, init_nonce])]);
163 let aes = crypto::keccak256(&[&ecdhe, &shared]);
164 let mac = crypto::keccak256(&[&ecdhe, &aes]);
165 let mut mac1 = Keccak256::new();
166 mac1.update(xor32(&mac, resp_nonce));
167 mac1.update(auth_packet);
168 let mut mac2 = Keccak256::new();
169 mac2.update(xor32(&mac, init_nonce));
170 mac2.update(ack_packet);
171 let (egress_mac, ingress_mac) = if initiator {
172 (mac1, mac2)
173 } else {
174 (mac2, mac1)
175 };
176 Secrets {
177 aes,
178 mac,
179 egress_mac,
180 ingress_mac,
181 remote,
182 }
183}
184
185pub async fn initiate<R: AsyncRead + Unpin, W: AsyncWrite + Unpin>(
187 r: &mut R,
188 w: &mut W,
189 key: &NodeKey,
190 remote: &PublicKey,
191) -> Result<Secrets> {
192 let ephemeral = NodeKey::random();
193 let mut init_nonce = [0u8; 32];
194 rand::thread_rng().fill(&mut init_nonce);
195 let token = crypto::ecdh_x(key.secret(), remote);
196 let signature = crypto::sign_recoverable(&xor32(&token, &init_nonce), ephemeral.secret());
197 let auth = seal(
198 remote,
199 Rlp::List(vec![
200 Rlp::bytes(&signature),
201 Rlp::bytes(&key.id()),
202 Rlp::bytes(&init_nonce),
203 Rlp::uint(4),
204 ]),
205 );
206 w.write_all(&auth).await.context("writing auth")?;
207 w.flush().await?;
208 let ack_packet = read_packet(r).await?;
209 let ack = parse_ack(&open_packet(key, &ack_packet).context("decrypting ack")?)?;
210 let remote_ephemeral = crypto::public_from_id(&ack.ephemeral_id).context("ack ephemeral")?;
211 Ok(derive_secrets(
212 true,
213 &ephemeral,
214 &remote_ephemeral,
215 &init_nonce,
216 &ack.nonce,
217 &auth,
218 &ack_packet,
219 *remote,
220 ))
221}
222
223pub struct AcceptedAuth {
225 pub auth: AuthMsg,
226 pub initiator: PublicKey,
227 pub remote_ephemeral: PublicKey,
228}
229
230pub fn accept_auth(key: &NodeKey, auth: AuthMsg) -> Result<AcceptedAuth> {
232 let initiator = crypto::public_from_id(&auth.initiator_id).context("auth initiator id")?;
233 let token = crypto::ecdh_x(key.secret(), &initiator);
234 let remote_ephemeral =
235 crypto::recover(&xor32(&token, &auth.nonce), &auth.signature).context("auth signature")?;
236 Ok(AcceptedAuth {
237 auth,
238 initiator,
239 remote_ephemeral,
240 })
241}
242
243pub async fn respond<R: AsyncRead + Unpin, W: AsyncWrite + Unpin>(
245 r: &mut R,
246 w: &mut W,
247 key: &NodeKey,
248) -> Result<Secrets> {
249 let auth_packet = read_packet(r).await?;
250 let auth = parse_auth(&open_packet(key, &auth_packet).context("decrypting auth")?)?;
251 let accepted = accept_auth(key, auth)?;
252 let ephemeral = NodeKey::random();
253 let mut resp_nonce = [0u8; 32];
254 rand::thread_rng().fill(&mut resp_nonce);
255 let ack = seal(
256 &accepted.initiator,
257 Rlp::List(vec![
258 Rlp::bytes(&ephemeral.id()),
259 Rlp::bytes(&resp_nonce),
260 Rlp::uint(4),
261 ]),
262 );
263 w.write_all(&ack).await.context("writing ack")?;
264 w.flush().await?;
265 Ok(derive_secrets(
266 false,
267 &ephemeral,
268 &accepted.remote_ephemeral,
269 &accepted.auth.nonce,
270 &resp_nonce,
271 &auth_packet,
272 &ack,
273 accepted.initiator,
274 ))
275}
276
277struct MacState {
279 cipher: aes::Aes256,
280 hash: Keccak256,
281}
282
283impl MacState {
284 fn new(mac_secret: &[u8; 32], hash: Keccak256) -> Self {
285 MacState {
286 cipher: aes::Aes256::new(mac_secret.into()),
287 hash,
288 }
289 }
290
291 fn digest(&self) -> [u8; 32] {
292 self.hash.clone().finalize().into()
293 }
294
295 fn compute(&mut self, sum1: &[u8; 32], seed: &[u8]) -> [u8; 16] {
296 let mut first = [0u8; 16];
297 first.copy_from_slice(&sum1[..16]);
298 let mut block = aes::Block::from(first);
299 self.cipher.encrypt_block(&mut block);
300 for (b, s) in block.iter_mut().zip(seed) {
301 *b ^= s;
302 }
303 self.hash.update(block);
304 let sum2 = self.digest();
305 let mut out = [0u8; 16];
306 out.copy_from_slice(&sum2[..16]);
307 out
308 }
309
310 fn header(&mut self, header_ciphertext: &[u8]) -> [u8; 16] {
311 let sum1 = self.digest();
312 self.compute(&sum1, header_ciphertext)
313 }
314
315 fn frame(&mut self, frame_ciphertext: &[u8]) -> [u8; 16] {
316 self.hash.update(frame_ciphertext);
317 let seed = self.digest();
318 self.compute(&seed, &seed[..16])
319 }
320}
321
322fn equal(a: &[u8], b: &[u8]) -> bool {
323 a.len() == b.len() && a.iter().zip(b).fold(0u8, |d, (x, y)| d | (x ^ y)) == 0
324}
325
326#[derive(Debug)]
328pub enum FrameError {
329 Io(std::io::Error),
330 TooLarge {
332 declared: usize,
333 bound: usize,
334 },
335 Protocol(String),
337}
338
339impl std::fmt::Display for FrameError {
340 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
341 match self {
342 FrameError::Io(e) => write!(f, "{e}"),
343 FrameError::TooLarge { declared, bound } => {
344 write!(f, "{declared} bytes declared, above the {bound}-byte bound")
345 }
346 FrameError::Protocol(m) => write!(f, "{m}"),
347 }
348 }
349}
350
351impl std::error::Error for FrameError {}
352
353impl From<std::io::Error> for FrameError {
354 fn from(e: std::io::Error) -> Self {
355 FrameError::Io(e)
356 }
357}
358
359pub struct FrameWriter {
361 enc: Aes256Ctr,
362 mac: MacState,
363 snappy: bool,
364}
365
366pub struct FrameReader {
368 dec: Aes256Ctr,
369 mac: MacState,
370 snappy: bool,
371 max_message: usize,
374}
375
376pub fn codec(secrets: Secrets, max_message: usize) -> (FrameReader, FrameWriter) {
378 let iv = [0u8; 16];
379 let reader = FrameReader {
380 dec: Aes256Ctr::new(&secrets.aes.into(), &iv.into()),
381 mac: MacState::new(&secrets.mac, secrets.ingress_mac),
382 snappy: false,
383 max_message,
384 };
385 let writer = FrameWriter {
386 enc: Aes256Ctr::new(&secrets.aes.into(), &iv.into()),
387 mac: MacState::new(&secrets.mac, secrets.egress_mac),
388 snappy: false,
389 };
390 (reader, writer)
391}
392
393pub fn max_frame_for(max_message: usize) -> usize {
395 (snap::raw::max_compress_len(max_message) + 16).min(MAX_FRAME_SIZE)
396}
397
398impl FrameWriter {
399 pub fn set_snappy(&mut self, on: bool) {
400 self.snappy = on;
401 }
402
403 pub fn encode(&mut self, code: u64, payload: &[u8]) -> Result<Vec<u8>> {
405 let compressed;
406 let payload = if self.snappy {
407 compressed = snap::raw::Encoder::new()
408 .compress_vec(payload)
409 .context("snappy compression")?;
410 &compressed[..]
411 } else {
412 payload
413 };
414 let mut frame = Rlp::uint(code).encode();
415 frame.extend_from_slice(payload);
416 let fsize = frame.len();
417 ensure!(
418 fsize <= MAX_FRAME_SIZE,
419 "a {fsize}-byte frame does not fit RLPx's 24-bit size"
420 );
421 let mut header = [0u8; 16];
422 header[..3].copy_from_slice(&(fsize as u32).to_be_bytes()[1..]);
423 header[3..6].copy_from_slice(&HEADER_DATA);
424 self.enc.apply_keystream(&mut header);
425 let header_mac = self.mac.header(&header);
426 frame.resize(fsize.div_ceil(16) * 16, 0);
427 self.enc.apply_keystream(&mut frame);
428 let frame_mac = self.mac.frame(&frame);
429 let mut out = Vec::with_capacity(32 + frame.len() + 16);
430 out.extend_from_slice(&header);
431 out.extend_from_slice(&header_mac);
432 out.extend_from_slice(&frame);
433 out.extend_from_slice(&frame_mac);
434 Ok(out)
435 }
436}
437
438impl FrameReader {
439 pub fn set_snappy(&mut self, on: bool) {
440 self.snappy = on;
441 }
442
443 pub async fn read<R: AsyncRead + Unpin>(
445 &mut self,
446 r: &mut R,
447 ) -> std::result::Result<(u64, Vec<u8>), FrameError> {
448 let mut head = [0u8; 32];
449 r.read_exact(&mut head).await?;
450 let want = self.mac.header(&head[..16]);
451 if !equal(&want, &head[16..]) {
452 return Err(FrameError::Protocol("bad frame header MAC".into()));
453 }
454 self.dec.apply_keystream(&mut head[..16]);
455 let fsize =
456 (usize::from(head[0]) << 16) | (usize::from(head[1]) << 8) | usize::from(head[2]);
457 let bound = max_frame_for(self.max_message);
458 if fsize > bound {
459 return Err(FrameError::TooLarge {
460 declared: fsize,
461 bound,
462 });
463 }
464 let padded = fsize.div_ceil(16) * 16;
465 let mut frame = vec![0u8; padded + 16];
466 r.read_exact(&mut frame).await?;
467 let (body, mac) = frame.split_at_mut(padded);
468 let want = self.mac.frame(body);
469 if !equal(&want, mac) {
470 return Err(FrameError::Protocol("bad frame MAC".into()));
471 }
472 self.dec.apply_keystream(body);
473 let body = &body[..fsize];
474 let (code, payload) =
475 rlp::split_code(body).map_err(|e| FrameError::Protocol(format!("{e:#}")))?;
476 if !self.snappy {
477 if payload.len() > self.max_message {
478 return Err(FrameError::TooLarge {
479 declared: payload.len(),
480 bound: self.max_message,
481 });
482 }
483 return Ok((code, payload.to_vec()));
484 }
485 let size = snap::raw::decompress_len(payload)
486 .map_err(|e| FrameError::Protocol(format!("snappy: {e}")))?;
487 if size > self.max_message {
488 return Err(FrameError::TooLarge {
489 declared: size,
490 bound: self.max_message,
491 });
492 }
493 let plain = snap::raw::Decoder::new()
494 .decompress_vec(payload)
495 .map_err(|e| FrameError::Protocol(format!("snappy: {e}")))?;
496 Ok((code, plain))
497 }
498}