Skip to main content

netget/server/libp2p/
noise.rs

1//! libp2p's Noise channel: `Noise_XX_25519_ChaChaPoly_SHA256` with an empty prologue, each
2//! message framed by a 16-bit big-endian length, and the libp2p handshake payload (the
3//! identity key and its signature over the Noise static key) in messages 2 and 3.
4use super::wire::{self, PbValue};
5use anyhow::{bail, ensure, Context, Result};
6use ed25519_dalek::{Signer, SigningKey, Verifier, VerifyingKey};
7use hkdf::Hkdf;
8use ring::aead::{self, LessSafeKey, Nonce, UnboundKey, CHACHA20_POLY1305};
9use sha2::{Digest, Sha256};
10use std::future::Future;
11use tokio::io::{AsyncReadExt, AsyncWriteExt};
12use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
13use tokio::net::TcpStream;
14use x25519_dalek::{PublicKey, StaticSecret};
15
16const PROTOCOL_NAME: &[u8; 32] = b"Noise_XX_25519_ChaChaPoly_SHA256";
17const SIG_PREFIX: &[u8] = b"noise-libp2p-static-key:";
18const TAG: usize = 16;
19/// Largest Noise message (its 16-bit length prefix).
20pub const MAX_NOISE_MESSAGE: usize = 65535;
21/// Largest plaintext one transport message carries.
22pub const MAX_PLAINTEXT: usize = MAX_NOISE_MESSAGE - TAG;
23
24/// This node's libp2p identity.
25pub struct Identity {
26    pub key: SigningKey,
27    pub public_key_proto: Vec<u8>,
28    pub peer_id: Vec<u8>,
29}
30
31impl Identity {
32    pub fn from_seed(seed: [u8; 32]) -> Self {
33        let key = SigningKey::from_bytes(&seed);
34        let public_key_proto = wire::ed25519_public_key_proto(&key.verifying_key().to_bytes());
35        let peer_id = wire::peer_id(&public_key_proto);
36        Self {
37            key,
38            public_key_proto,
39            peer_id,
40        }
41    }
42    pub fn random() -> Self {
43        Self::from_seed(rand::random())
44    }
45    pub fn peer_id_string(&self) -> String {
46        wire::peer_id_string(&self.peer_id)
47    }
48}
49
50struct Cipher {
51    key: Option<LessSafeKey>,
52    n: u64,
53}
54
55impl Cipher {
56    fn empty() -> Self {
57        Self { key: None, n: 0 }
58    }
59    fn new(k: &[u8]) -> Self {
60        let key = UnboundKey::new(&CHACHA20_POLY1305, &k[..32]).expect("32-byte key");
61        Self {
62            key: Some(LessSafeKey::new(key)),
63            n: 0,
64        }
65    }
66    fn nonce(&mut self) -> Result<Nonce> {
67        ensure!(self.n < u64::MAX, "Noise nonce exhausted");
68        let mut n = [0u8; 12];
69        n[4..].copy_from_slice(&self.n.to_le_bytes());
70        self.n += 1;
71        Ok(Nonce::assume_unique_for_key(n))
72    }
73    fn encrypt(&mut self, ad: &[u8], plaintext: &[u8]) -> Result<Vec<u8>> {
74        if self.key.is_none() {
75            return Ok(plaintext.to_vec());
76        }
77        let nonce = self.nonce()?;
78        let mut buf = plaintext.to_vec();
79        self.key
80            .as_ref()
81            .expect("checked above")
82            .seal_in_place_append_tag(nonce, aead::Aad::from(ad), &mut buf)
83            .map_err(|_| anyhow::anyhow!("Noise encryption failed"))?;
84        Ok(buf)
85    }
86    fn decrypt(&mut self, ad: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>> {
87        if self.key.is_none() {
88            return Ok(ciphertext.to_vec());
89        }
90        let nonce = self.nonce()?;
91        let mut buf = ciphertext.to_vec();
92        let n = self
93            .key
94            .as_ref()
95            .expect("checked above")
96            .open_in_place(nonce, aead::Aad::from(ad), &mut buf)
97            .map_err(|_| {
98                anyhow::anyhow!("Noise decryption failed (wrong key or tampered message)")
99            })?
100            .len();
101        buf.truncate(n);
102        Ok(buf)
103    }
104}
105
106struct Symmetric {
107    ck: [u8; 32],
108    h: [u8; 32],
109    cipher: Cipher,
110}
111
112fn hkdf2(ck: &[u8; 32], ikm: &[u8]) -> ([u8; 32], [u8; 32]) {
113    let mut okm = [0u8; 64];
114    Hkdf::<Sha256>::new(Some(ck), ikm)
115        .expand(&[], &mut okm)
116        .expect("64 bytes is a valid HKDF length");
117    let (a, b) = okm.split_at(32);
118    (a.try_into().unwrap(), b.try_into().unwrap())
119}
120
121impl Symmetric {
122    fn new() -> Self {
123        let h = *PROTOCOL_NAME;
124        let mut s = Self {
125            ck: h,
126            h,
127            cipher: Cipher::empty(),
128        };
129        s.mix_hash(&[]); // the empty prologue
130        s
131    }
132    fn mix_hash(&mut self, data: &[u8]) {
133        let mut d = Sha256::new();
134        d.update(self.h);
135        d.update(data);
136        self.h = d.finalize().into();
137    }
138    fn mix_key(&mut self, ikm: &[u8]) {
139        let (ck, k) = hkdf2(&self.ck, ikm);
140        self.ck = ck;
141        self.cipher = Cipher::new(&k);
142    }
143    fn encrypt_and_hash(&mut self, p: &[u8]) -> Result<Vec<u8>> {
144        let c = self.cipher.encrypt(&self.h, p)?;
145        self.mix_hash(&c);
146        Ok(c)
147    }
148    fn decrypt_and_hash(&mut self, c: &[u8]) -> Result<Vec<u8>> {
149        let p = self.cipher.decrypt(&self.h, c)?;
150        self.mix_hash(c);
151        Ok(p)
152    }
153    fn split(&self) -> (Cipher, Cipher) {
154        let (a, b) = hkdf2(&self.ck, &[]);
155        (Cipher::new(&a), Cipher::new(&b))
156    }
157}
158
159fn dh(secret: &StaticSecret, public: &[u8; 32]) -> [u8; 32] {
160    secret.diffie_hellman(&PublicKey::from(*public)).to_bytes()
161}
162
163async fn read_frame(tcp: &mut TcpStream) -> Result<Vec<u8>> {
164    let len = tcp.read_u16().await? as usize;
165    let mut b = vec![0u8; len];
166    tcp.read_exact(&mut b).await?;
167    Ok(b)
168}
169
170async fn write_frame(tcp: &mut TcpStream, b: &[u8]) -> Result<()> {
171    ensure!(b.len() <= MAX_NOISE_MESSAGE, "Noise message too long");
172    let mut out = (b.len() as u16).to_be_bytes().to_vec();
173    out.extend_from_slice(b);
174    tcp.write_all(&out).await?;
175    Ok(())
176}
177
178fn payload(id: &Identity, static_pub: &[u8; 32]) -> Vec<u8> {
179    let mut msg = SIG_PREFIX.to_vec();
180    msg.extend_from_slice(static_pub);
181    let sig = id.key.sign(&msg).to_bytes();
182    let mut out = Vec::new();
183    wire::pb_bytes(&mut out, 1, &id.public_key_proto);
184    wire::pb_bytes(&mut out, 2, &sig);
185    out
186}
187
188/// Check the remote's handshake payload against its Noise static key; its peer id.
189fn verify_payload(p: &[u8], remote_static: &[u8; 32]) -> Result<Vec<u8>> {
190    let mut key = None;
191    let mut sig = None;
192    for (f, v) in wire::pb_fields(p)? {
193        match (f, v) {
194            (1, PbValue::Bytes(b)) => key = Some(b),
195            (2, PbValue::Bytes(b)) => sig = Some(b),
196            _ => {}
197        }
198    }
199    let key_proto = key.context("handshake payload has no identity key")?;
200    let sig = sig.context("handshake payload has no signature")?;
201    let ed = wire::parse_public_key(key_proto)?;
202    let vk = VerifyingKey::from_bytes(&ed).context("invalid Ed25519 identity key")?;
203    let sig = ed25519_dalek::Signature::from_slice(sig).context("signature must be 64 bytes")?;
204    let mut msg = SIG_PREFIX.to_vec();
205    msg.extend_from_slice(remote_static);
206    vk.verify(&msg, &sig)
207        .context("the identity signature over the Noise static key does not verify")?;
208    Ok(wire::peer_id(key_proto))
209}
210
211/// The secured connection, before it is split for the multiplexer.
212pub struct NoiseConn {
213    pub reader: NoiseReader,
214    pub writer: NoiseWriter,
215    pub remote_peer: Vec<u8>,
216}
217
218/// Run the handshake on a TCP connection that has just selected `/noise`.
219pub async fn handshake(
220    mut tcp: TcpStream,
221    id: &Identity,
222    initiator: bool,
223    expected_peer: Option<&[u8]>,
224) -> Result<NoiseConn> {
225    let s = StaticSecret::random_from_rng(rand::rngs::OsRng);
226    let s_pub = PublicKey::from(&s).to_bytes();
227    let e = StaticSecret::random_from_rng(rand::rngs::OsRng);
228    let e_pub = PublicKey::from(&e).to_bytes();
229    let mut st = Symmetric::new();
230    let take32 = |b: &[u8], at: usize| -> Result<[u8; 32]> {
231        b.get(at..at + 32)
232            .and_then(|x| x.try_into().ok())
233            .context("Noise message too short")
234    };
235    let (remote_static, remote_payload) = if initiator {
236        // -> e
237        st.mix_hash(&e_pub);
238        let mut m1 = e_pub.to_vec();
239        m1.extend(st.encrypt_and_hash(&[])?);
240        write_frame(&mut tcp, &m1).await?;
241        // <- e, ee, s, es, payload
242        let m2 = read_frame(&mut tcp).await?;
243        let re = take32(&m2, 0)?;
244        st.mix_hash(&re);
245        st.mix_key(&dh(&e, &re));
246        ensure!(m2.len() >= 32 + 32 + TAG, "Noise message 2 too short");
247        let rs: [u8; 32] = st
248            .decrypt_and_hash(&m2[32..32 + 32 + TAG])?
249            .try_into()
250            .map_err(|_| anyhow::anyhow!("bad static key"))?;
251        st.mix_key(&dh(&e, &rs));
252        let p = st.decrypt_and_hash(&m2[32 + 32 + TAG..])?;
253        // -> s, se, payload
254        let mut m3 = st.encrypt_and_hash(&s_pub)?;
255        st.mix_key(&dh(&s, &re));
256        m3.extend(st.encrypt_and_hash(&payload(id, &s_pub))?);
257        // Verify before sending our identity on, so a wrong peer learns nothing more.
258        let peer = verify_payload(&p, &rs)?;
259        if let Some(want) = expected_peer {
260            ensure!(
261                peer == want,
262                "dialled {} but the remote is {}",
263                wire::peer_id_string(want),
264                wire::peer_id_string(&peer)
265            );
266        }
267        write_frame(&mut tcp, &m3).await?;
268        (rs, peer)
269    } else {
270        // <- e
271        let m1 = read_frame(&mut tcp).await?;
272        let re = take32(&m1, 0)?;
273        st.mix_hash(&re);
274        st.decrypt_and_hash(&m1[32..])?;
275        // -> e, ee, s, es, payload
276        st.mix_hash(&e_pub);
277        let mut m2 = e_pub.to_vec();
278        st.mix_key(&dh(&e, &re));
279        m2.extend(st.encrypt_and_hash(&s_pub)?);
280        st.mix_key(&dh(&s, &re));
281        m2.extend(st.encrypt_and_hash(&payload(id, &s_pub))?);
282        write_frame(&mut tcp, &m2).await?;
283        // <- s, se, payload
284        let m3 = read_frame(&mut tcp).await?;
285        ensure!(m3.len() >= 32 + TAG, "Noise message 3 too short");
286        let rs: [u8; 32] = st
287            .decrypt_and_hash(&m3[..32 + TAG])?
288            .try_into()
289            .map_err(|_| anyhow::anyhow!("bad static key"))?;
290        st.mix_key(&dh(&e, &rs));
291        let p = st.decrypt_and_hash(&m3[32 + TAG..])?;
292        let peer = verify_payload(&p, &rs)?;
293        (rs, peer)
294    };
295    let _ = remote_static;
296    let (c1, c2) = st.split();
297    let (send, recv) = if initiator { (c1, c2) } else { (c2, c1) };
298    let (r, w) = tcp.into_split();
299    Ok(NoiseConn {
300        reader: NoiseReader {
301            tcp: r,
302            cipher: recv,
303            buf: Vec::new(),
304            pos: 0,
305        },
306        writer: NoiseWriter {
307            tcp: w,
308            cipher: send,
309        },
310        remote_peer: remote_payload,
311    })
312}
313
314pub struct NoiseReader {
315    tcp: OwnedReadHalf,
316    cipher: Cipher,
317    buf: Vec<u8>,
318    pos: usize,
319}
320
321impl NoiseReader {
322    async fn fill(&mut self) -> Result<()> {
323        let len = match self.tcp.read_u16().await {
324            Ok(l) => l as usize,
325            Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => bail!("connection closed"),
326            Err(e) => return Err(e.into()),
327        };
328        ensure!(len >= TAG, "Noise transport message shorter than its tag");
329        let mut c = vec![0u8; len];
330        self.tcp.read_exact(&mut c).await?;
331        self.buf = self.cipher.decrypt(&[], &c)?;
332        self.pos = 0;
333        Ok(())
334    }
335    pub async fn read_exact(&mut self, out: &mut [u8]) -> Result<()> {
336        let mut done = 0;
337        while done < out.len() {
338            if self.pos == self.buf.len() {
339                self.fill().await?;
340                continue;
341            }
342            let n = (out.len() - done).min(self.buf.len() - self.pos);
343            out[done..done + n].copy_from_slice(&self.buf[self.pos..self.pos + n]);
344            self.pos += n;
345            done += n;
346        }
347        Ok(())
348    }
349}
350
351pub struct NoiseWriter {
352    tcp: OwnedWriteHalf,
353    cipher: Cipher,
354}
355
356impl NoiseWriter {
357    pub async fn write_all(&mut self, b: &[u8]) -> Result<()> {
358        let mut out = Vec::with_capacity(b.len() + (b.len() / MAX_PLAINTEXT + 1) * (TAG + 2));
359        for chunk in b.chunks(MAX_PLAINTEXT) {
360            let c = self.cipher.encrypt(&[], chunk)?;
361            out.extend_from_slice(&(c.len() as u16).to_be_bytes());
362            out.extend(c);
363        }
364        self.tcp.write_all(&out).await?;
365        Ok(())
366    }
367    pub async fn shutdown(&mut self) {
368        let _ = self.tcp.shutdown().await;
369    }
370}
371
372impl wire::Io for NoiseConn {
373    fn read_exact<'a>(
374        &'a mut self,
375        buf: &'a mut [u8],
376    ) -> impl Future<Output = Result<()>> + Send + 'a {
377        self.reader.read_exact(buf)
378    }
379    fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> impl Future<Output = Result<()>> + Send + 'a {
380        self.writer.write_all(buf)
381    }
382}