/__w/smoldot/smoldot/repo/lib/src/libp2p/connection/noise.rs
Line | Count | Source (jump to first uncovered line) |
1 | | // Smoldot |
2 | | // Copyright (C) 2023 Pierre Krieger |
3 | | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 |
4 | | |
5 | | // This program is free software: you can redistribute it and/or modify |
6 | | // it under the terms of the GNU General Public License as published by |
7 | | // the Free Software Foundation, either version 3 of the License, or |
8 | | // (at your option) any later version. |
9 | | |
10 | | // This program is distributed in the hope that it will be useful, |
11 | | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | // GNU General Public License for more details. |
14 | | |
15 | | // You should have received a copy of the GNU General Public License |
16 | | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 | | |
18 | | //! Noise protocol libp2p layer. |
19 | | //! |
20 | | //! The [noise protocol](https://noiseprotocol.org/) is a standard framework for building |
21 | | //! cryptographic protocols. Libp2p uses the noise protocol to provide an encryption layer on |
22 | | //! top of which data is exchanged. |
23 | | //! |
24 | | //! # Protocol details |
25 | | //! |
26 | | //! Libp2p uses [the XX pattern](https://noiseexplorer.com/patterns/XX/). The handshake consists |
27 | | //! of three packets: |
28 | | //! |
29 | | //! - The initiator generates an ephemeral key pair and sends the public key to the responder. |
30 | | //! - The responder generates its own ephemeral key pair and sends the public key to the |
31 | | //! initiator. Afterwards, the responder derives a shared secret and uses it to encrypt all |
32 | | //! further communications. Now encrypted, the responder also sends back its static noise public |
33 | | //! key (represented with the [`NoiseKey`] type of this module), its libp2p public key, and a |
34 | | //! signature of the static noise public key made using its libp2p private key. |
35 | | //! - The initiator, after having received the ephemeral key from the remote, derives the same |
36 | | //! shared secret. It sends its own static noise public key, libp2p public key, and signature. |
37 | | //! |
38 | | //! After these three packets, the initiator and responder derive another shared secret using |
39 | | //! both the static and ephemeral keys, which is then used to encrypt communications. Note that |
40 | | //! the libp2p key isn't used in the key derivation. |
41 | | //! |
42 | | //! # Usage |
43 | | //! |
44 | | //! While this is out of scope of this module, the noise protocol must typically first be |
45 | | //! negotiated using the *multistream-select* protocol. The name of the protocol is given by |
46 | | //! the [`PROTOCOL_NAME`] constant. |
47 | | //! |
48 | | //! In order to use noise on top of a connection which has agreed to use noise, create a |
49 | | //! [`HandshakeInProgress`], passing a [`NoiseKey`]. This [`NoiseKey`] is typically generated at |
50 | | //! startup and doesn't need to be persisted after a restart. |
51 | | //! |
52 | | //! Use [`HandshakeInProgress::read_write`] when data is received from the wire or when the remote |
53 | | //! is ready to receive more data. At every call, a [`NoiseHandshake`] is returned, potentially |
54 | | //! indicating the end of the handshake. |
55 | | //! |
56 | | //! If the handshake is finished, a [`NoiseHandshake::Success`] is returned, containing the |
57 | | //! [`PeerId`] of the remote, which is known to be legitimate, and a [`Noise`] object through |
58 | | //! which all further communications should go through. |
59 | | //! |
60 | | |
61 | | // # Q&A |
62 | | // |
63 | | // ## Why not use a library such as `snow`? |
64 | | // |
65 | | // Snow suffers from a variety of problems: |
66 | | // |
67 | | // - It doesn't support `no_std`. |
68 | | // - It uses outdated versions of dependencies. |
69 | | // - It doesn't allow writing a single Noise message onto two consecutive buffers. |
70 | | // - It doesn't support encoding a Noise message already present in the output buffer. |
71 | | // - It isn't really maintained. |
72 | | // - It doesn't give control over its random number generator. |
73 | | // |
74 | | |
75 | | use crate::{ |
76 | | libp2p::{ |
77 | | peer_id::{PeerId, PublicKey, SignatureVerifyFailed}, |
78 | | read_write::{self, ReadWrite}, |
79 | | }, |
80 | | util::protobuf, |
81 | | }; |
82 | | |
83 | | use alloc::{boxed::Box, collections::VecDeque, vec, vec::Vec}; |
84 | | use core::{cmp, fmt, iter, mem, ops}; |
85 | | |
86 | | /// Name of the protocol, typically used when negotiated it using *multistream-select*. |
87 | | pub const PROTOCOL_NAME: &str = "/noise"; |
88 | | |
89 | | /// The noise key is the key exchanged during the noise handshake. It is **not** the same as the |
90 | | /// libp2p key. The libp2p key is used only to sign the noise public key, while the ECDH is |
91 | | /// performed with the noise key. |
92 | | /// |
93 | | /// From the point of view of the noise protocol specification, this [`NoiseKey`] corresponds to |
94 | | /// the static key. The noise key is typically generated at startup and doesn't have to be |
95 | | /// persisted on disk, contrary to the libp2p key which is typically persisted after a restart. |
96 | | /// |
97 | | /// In order to generate a [`NoiseKey`], two things are needed: |
98 | | /// |
99 | | /// - A public/private key, also represented as [`UnsignedNoiseKey`]. |
100 | | /// - A signature of this public key made using the libp2p private key. |
101 | | /// |
102 | | /// The signature requires access to the libp2p private key. As such, there are two possible |
103 | | /// ways to create a [`NoiseKey`]: |
104 | | /// |
105 | | /// - The easier way, by passing the libp2p private key to [`NoiseKey::new`]. |
106 | | /// - The slightly more complex way, by first creating an [`UnsignedNoiseKey`], then passing a |
107 | | /// a signature. This second method doesn't require direct access to the private key but only |
108 | | /// to a method of signing a message, which makes it for example possible to use a hardware |
109 | | /// device. |
110 | | /// |
111 | | pub struct NoiseKey { |
112 | | private_key: zeroize::Zeroizing<x25519_dalek::StaticSecret>, |
113 | | public_key: x25519_dalek::PublicKey, |
114 | | /// Handshake to encrypt then send on the wire. |
115 | | handshake_message: Vec<u8>, |
116 | | /// Ed25519 public key used for the signature in the handshake message. |
117 | | libp2p_public_ed25519_key: [u8; 32], |
118 | | } |
119 | | |
120 | | impl NoiseKey { |
121 | | /// Turns a libp2p private key and a Noise static private key into a [`NoiseKey`]. |
122 | 45 | pub fn new(libp2p_ed25519_private_key: &[u8; 32], noise_static_private_key: &[u8; 32]) -> Self { |
123 | 45 | let unsigned = UnsignedNoiseKey::from_private_key(noise_static_private_key); |
124 | 45 | |
125 | 45 | let (libp2p_public_key, signature) = { |
126 | 45 | // Creating a `SecretKey` can fail only if the length isn't 32 bytes. |
127 | 45 | let secret = ed25519_zebra::SigningKey::from(*libp2p_ed25519_private_key); |
128 | 45 | let public = ed25519_zebra::VerificationKey::from(&secret); |
129 | 45 | // TODO: use sign_prehashed or sign_vectored (https://github.com/dalek-cryptography/ed25519-dalek/pull/143) to not allocate Vec |
130 | 45 | let signature = secret.sign(&unsigned.payload_to_sign_as_vec()); |
131 | 45 | (public, signature) |
132 | 45 | }; |
133 | 45 | |
134 | 45 | unsigned.sign(libp2p_public_key.into(), signature.into()) |
135 | 45 | } _RNvMNtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB2_8NoiseKey3new Line | Count | Source | 122 | 24 | pub fn new(libp2p_ed25519_private_key: &[u8; 32], noise_static_private_key: &[u8; 32]) -> Self { | 123 | 24 | let unsigned = UnsignedNoiseKey::from_private_key(noise_static_private_key); | 124 | 24 | | 125 | 24 | let (libp2p_public_key, signature) = { | 126 | 24 | // Creating a `SecretKey` can fail only if the length isn't 32 bytes. | 127 | 24 | let secret = ed25519_zebra::SigningKey::from(*libp2p_ed25519_private_key); | 128 | 24 | let public = ed25519_zebra::VerificationKey::from(&secret); | 129 | 24 | // TODO: use sign_prehashed or sign_vectored (https://github.com/dalek-cryptography/ed25519-dalek/pull/143) to not allocate Vec | 130 | 24 | let signature = secret.sign(&unsigned.payload_to_sign_as_vec()); | 131 | 24 | (public, signature) | 132 | 24 | }; | 133 | 24 | | 134 | 24 | unsigned.sign(libp2p_public_key.into(), signature.into()) | 135 | 24 | } |
_RNvMNtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB2_8NoiseKey3new Line | Count | Source | 122 | 21 | pub fn new(libp2p_ed25519_private_key: &[u8; 32], noise_static_private_key: &[u8; 32]) -> Self { | 123 | 21 | let unsigned = UnsignedNoiseKey::from_private_key(noise_static_private_key); | 124 | 21 | | 125 | 21 | let (libp2p_public_key, signature) = { | 126 | 21 | // Creating a `SecretKey` can fail only if the length isn't 32 bytes. | 127 | 21 | let secret = ed25519_zebra::SigningKey::from(*libp2p_ed25519_private_key); | 128 | 21 | let public = ed25519_zebra::VerificationKey::from(&secret); | 129 | 21 | // TODO: use sign_prehashed or sign_vectored (https://github.com/dalek-cryptography/ed25519-dalek/pull/143) to not allocate Vec | 130 | 21 | let signature = secret.sign(&unsigned.payload_to_sign_as_vec()); | 131 | 21 | (public, signature) | 132 | 21 | }; | 133 | 21 | | 134 | 21 | unsigned.sign(libp2p_public_key.into(), signature.into()) | 135 | 21 | } |
|
136 | | |
137 | | /// Returns the libp2p public key associated to the signature contained in this noise key. |
138 | 42 | pub fn libp2p_public_ed25519_key(&self) -> &[u8; 32] { |
139 | 42 | &self.libp2p_public_ed25519_key |
140 | 42 | } Unexecuted instantiation: _RNvMNtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB2_8NoiseKey25libp2p_public_ed25519_key _RNvMNtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB2_8NoiseKey25libp2p_public_ed25519_key Line | Count | Source | 138 | 42 | pub fn libp2p_public_ed25519_key(&self) -> &[u8; 32] { | 139 | 42 | &self.libp2p_public_ed25519_key | 140 | 42 | } |
|
141 | | } |
142 | | |
143 | | /// Prototype for a [`NoiseKey`]. |
144 | | /// |
145 | | /// This type is provided for situations where the user has access to some signing mechanism, |
146 | | /// such as a hardware device, but not directly to the private key. |
147 | | /// |
148 | | /// For simple cases, prefer using [`NoiseKey::new`]. |
149 | | pub struct UnsignedNoiseKey { |
150 | | private_key: Option<zeroize::Zeroizing<x25519_dalek::StaticSecret>>, |
151 | | public_key: x25519_dalek::PublicKey, |
152 | | } |
153 | | |
154 | | impl UnsignedNoiseKey { |
155 | | /// Turns a private key into an [`UnsignedNoiseKey`]. |
156 | 45 | pub fn from_private_key(private_key: &[u8; 32]) -> Self { |
157 | 45 | let private_key = zeroize::Zeroizing::new(x25519_dalek::StaticSecret::from(*private_key)); |
158 | 45 | let public_key = x25519_dalek::PublicKey::from(&*private_key); |
159 | 45 | UnsignedNoiseKey { |
160 | 45 | private_key: Some(private_key), |
161 | 45 | public_key, |
162 | 45 | } |
163 | 45 | } _RNvMs_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB4_16UnsignedNoiseKey16from_private_key Line | Count | Source | 156 | 24 | pub fn from_private_key(private_key: &[u8; 32]) -> Self { | 157 | 24 | let private_key = zeroize::Zeroizing::new(x25519_dalek::StaticSecret::from(*private_key)); | 158 | 24 | let public_key = x25519_dalek::PublicKey::from(&*private_key); | 159 | 24 | UnsignedNoiseKey { | 160 | 24 | private_key: Some(private_key), | 161 | 24 | public_key, | 162 | 24 | } | 163 | 24 | } |
_RNvMs_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB4_16UnsignedNoiseKey16from_private_key Line | Count | Source | 156 | 21 | pub fn from_private_key(private_key: &[u8; 32]) -> Self { | 157 | 21 | let private_key = zeroize::Zeroizing::new(x25519_dalek::StaticSecret::from(*private_key)); | 158 | 21 | let public_key = x25519_dalek::PublicKey::from(&*private_key); | 159 | 21 | UnsignedNoiseKey { | 160 | 21 | private_key: Some(private_key), | 161 | 21 | public_key, | 162 | 21 | } | 163 | 21 | } |
|
164 | | |
165 | | /// Returns the data that has to be signed. |
166 | 45 | pub fn payload_to_sign(&'_ self) -> impl Iterator<Item = impl AsRef<[u8]> + '_> + '_ { |
167 | 45 | [ |
168 | 45 | &b"noise-libp2p-static-key:"[..], |
169 | 45 | &self.public_key.as_bytes()[..], |
170 | 45 | ] |
171 | 45 | .into_iter() |
172 | 45 | } _RNvMs_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB4_16UnsignedNoiseKey15payload_to_sign Line | Count | Source | 166 | 24 | pub fn payload_to_sign(&'_ self) -> impl Iterator<Item = impl AsRef<[u8]> + '_> + '_ { | 167 | 24 | [ | 168 | 24 | &b"noise-libp2p-static-key:"[..], | 169 | 24 | &self.public_key.as_bytes()[..], | 170 | 24 | ] | 171 | 24 | .into_iter() | 172 | 24 | } |
_RNvMs_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB4_16UnsignedNoiseKey15payload_to_sign Line | Count | Source | 166 | 21 | pub fn payload_to_sign(&'_ self) -> impl Iterator<Item = impl AsRef<[u8]> + '_> + '_ { | 167 | 21 | [ | 168 | 21 | &b"noise-libp2p-static-key:"[..], | 169 | 21 | &self.public_key.as_bytes()[..], | 170 | 21 | ] | 171 | 21 | .into_iter() | 172 | 21 | } |
|
173 | | |
174 | | /// Returns the data that has to be signed. |
175 | | /// |
176 | | /// This method is a more convenient equivalent to |
177 | | /// [`UnsignedNoiseKey::payload_to_sign_as_vec`]. |
178 | 45 | pub fn payload_to_sign_as_vec(&self) -> Vec<u8> { |
179 | 90 | self.payload_to_sign().fold(Vec::new(), |mut a, b| { |
180 | 90 | a.extend_from_slice(b.as_ref()); |
181 | 90 | a |
182 | 90 | }) _RNCNvMs_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB6_16UnsignedNoiseKey22payload_to_sign_as_vec0Bc_ Line | Count | Source | 179 | 48 | self.payload_to_sign().fold(Vec::new(), |mut a, b| { | 180 | 48 | a.extend_from_slice(b.as_ref()); | 181 | 48 | a | 182 | 48 | }) |
_RNCNvMs_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB6_16UnsignedNoiseKey22payload_to_sign_as_vec0Bc_ Line | Count | Source | 179 | 42 | self.payload_to_sign().fold(Vec::new(), |mut a, b| { | 180 | 42 | a.extend_from_slice(b.as_ref()); | 181 | 42 | a | 182 | 42 | }) |
|
183 | 45 | } _RNvMs_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB4_16UnsignedNoiseKey22payload_to_sign_as_vec Line | Count | Source | 178 | 24 | pub fn payload_to_sign_as_vec(&self) -> Vec<u8> { | 179 | 24 | self.payload_to_sign().fold(Vec::new(), |mut a, b| { | 180 | | a.extend_from_slice(b.as_ref()); | 181 | | a | 182 | 24 | }) | 183 | 24 | } |
_RNvMs_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB4_16UnsignedNoiseKey22payload_to_sign_as_vec Line | Count | Source | 178 | 21 | pub fn payload_to_sign_as_vec(&self) -> Vec<u8> { | 179 | 21 | self.payload_to_sign().fold(Vec::new(), |mut a, b| { | 180 | | a.extend_from_slice(b.as_ref()); | 181 | | a | 182 | 21 | }) | 183 | 21 | } |
|
184 | | |
185 | | /// Turns this [`UnsignedNoiseKey`] into a [`NoiseKey`] after signing it using the libp2p |
186 | | /// private key. |
187 | 45 | pub fn sign(mut self, libp2p_public_ed25519_key: [u8; 32], signature: [u8; 64]) -> NoiseKey { |
188 | 45 | let libp2p_pubkey_protobuf = |
189 | 45 | PublicKey::Ed25519(libp2p_public_ed25519_key).to_protobuf_encoding(); |
190 | | |
191 | 45 | let handshake_message = { |
192 | | // Protobuf message format can be found here: |
193 | | // https://github.com/libp2p/specs/tree/master/noise#the-libp2p-handshake-payload |
194 | | |
195 | | // The capacity is arbitrary but large enough to avoid Vec reallocations. |
196 | 45 | let mut msg = Vec::with_capacity(32 + libp2p_pubkey_protobuf.len() + signature.len()); |
197 | | |
198 | 135 | for slice in protobuf::bytes_tag_encode(1, &libp2p_pubkey_protobuf45 ) { |
199 | 135 | msg.extend_from_slice(slice.as_ref()); |
200 | 135 | } |
201 | | |
202 | 135 | for slice in protobuf::bytes_tag_encode(2, &signature45 ) { |
203 | 135 | msg.extend_from_slice(slice.as_ref()); |
204 | 135 | } |
205 | | |
206 | 45 | msg |
207 | 45 | }; |
208 | 45 | |
209 | 45 | NoiseKey { |
210 | 45 | public_key: self.public_key, |
211 | 45 | private_key: self.private_key.take().unwrap(), |
212 | 45 | libp2p_public_ed25519_key, |
213 | 45 | handshake_message, |
214 | 45 | } |
215 | 45 | } _RNvMs_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB4_16UnsignedNoiseKey4sign Line | Count | Source | 187 | 24 | pub fn sign(mut self, libp2p_public_ed25519_key: [u8; 32], signature: [u8; 64]) -> NoiseKey { | 188 | 24 | let libp2p_pubkey_protobuf = | 189 | 24 | PublicKey::Ed25519(libp2p_public_ed25519_key).to_protobuf_encoding(); | 190 | | | 191 | 24 | let handshake_message = { | 192 | | // Protobuf message format can be found here: | 193 | | // https://github.com/libp2p/specs/tree/master/noise#the-libp2p-handshake-payload | 194 | | | 195 | | // The capacity is arbitrary but large enough to avoid Vec reallocations. | 196 | 24 | let mut msg = Vec::with_capacity(32 + libp2p_pubkey_protobuf.len() + signature.len()); | 197 | | | 198 | 72 | for slice in protobuf::bytes_tag_encode(1, &libp2p_pubkey_protobuf24 ) { | 199 | 72 | msg.extend_from_slice(slice.as_ref()); | 200 | 72 | } | 201 | | | 202 | 72 | for slice in protobuf::bytes_tag_encode(2, &signature24 ) { | 203 | 72 | msg.extend_from_slice(slice.as_ref()); | 204 | 72 | } | 205 | | | 206 | 24 | msg | 207 | 24 | }; | 208 | 24 | | 209 | 24 | NoiseKey { | 210 | 24 | public_key: self.public_key, | 211 | 24 | private_key: self.private_key.take().unwrap(), | 212 | 24 | libp2p_public_ed25519_key, | 213 | 24 | handshake_message, | 214 | 24 | } | 215 | 24 | } |
_RNvMs_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB4_16UnsignedNoiseKey4sign Line | Count | Source | 187 | 21 | pub fn sign(mut self, libp2p_public_ed25519_key: [u8; 32], signature: [u8; 64]) -> NoiseKey { | 188 | 21 | let libp2p_pubkey_protobuf = | 189 | 21 | PublicKey::Ed25519(libp2p_public_ed25519_key).to_protobuf_encoding(); | 190 | | | 191 | 21 | let handshake_message = { | 192 | | // Protobuf message format can be found here: | 193 | | // https://github.com/libp2p/specs/tree/master/noise#the-libp2p-handshake-payload | 194 | | | 195 | | // The capacity is arbitrary but large enough to avoid Vec reallocations. | 196 | 21 | let mut msg = Vec::with_capacity(32 + libp2p_pubkey_protobuf.len() + signature.len()); | 197 | | | 198 | 63 | for slice in protobuf::bytes_tag_encode(1, &libp2p_pubkey_protobuf21 ) { | 199 | 63 | msg.extend_from_slice(slice.as_ref()); | 200 | 63 | } | 201 | | | 202 | 63 | for slice in protobuf::bytes_tag_encode(2, &signature21 ) { | 203 | 63 | msg.extend_from_slice(slice.as_ref()); | 204 | 63 | } | 205 | | | 206 | 21 | msg | 207 | 21 | }; | 208 | 21 | | 209 | 21 | NoiseKey { | 210 | 21 | public_key: self.public_key, | 211 | 21 | private_key: self.private_key.take().unwrap(), | 212 | 21 | libp2p_public_ed25519_key, | 213 | 21 | handshake_message, | 214 | 21 | } | 215 | 21 | } |
|
216 | | } |
217 | | |
218 | | /// Configuration for a Noise handshake. |
219 | | pub struct Config<'a> { |
220 | | /// Key to use during the handshake. |
221 | | pub key: &'a NoiseKey, |
222 | | |
223 | | /// Secret key to use for that specific handshake. Must be randomly generated. Must never be |
224 | | /// re-used between multiple handshakes. |
225 | | pub ephemeral_secret_key: &'a [u8; 32], |
226 | | |
227 | | /// `true` if this side of the connection must initiate the Noise handshake. `false` if it's |
228 | | /// the remote. |
229 | | pub is_initiator: bool, |
230 | | |
231 | | /// Prologue data. The prologue data must be identical on both sides of the handshake, |
232 | | /// otherwise it will fail. |
233 | | /// |
234 | | /// See <https://noiseprotocol.org/noise.html#prologue>. |
235 | | /// |
236 | | /// > **Note**: If a certain protocol specification doesn't mention any prologue, it probably |
237 | | /// > means that this prologue is empty. |
238 | | pub prologue: &'a [u8], |
239 | | } |
240 | | |
241 | | /// State of the noise encryption/decryption cipher. |
242 | | pub struct Noise { |
243 | | /// See [`Config::is_initiator`]. |
244 | | is_initiator: bool, |
245 | | |
246 | | /// Cipher used to encrypt outgoing data. |
247 | | out_cipher_state: CipherState, |
248 | | |
249 | | /// Cipher used to decrypt incoming data. |
250 | | in_cipher_state: CipherState, |
251 | | |
252 | | /// Size in bytes of the next message to receive. `None` if unknown. If `Some`, the libp2p |
253 | | /// length prefix has already been stripped from the incoming stream. |
254 | | next_in_message_size: Option<u16>, |
255 | | |
256 | | /// Buffer of data containing data that has been decrypted. |
257 | | rx_buffer_decrypted: Vec<u8>, |
258 | | |
259 | | /// Value of [`ReadWrite::expected_incoming_bytes`] of the inner stream the last time that |
260 | | /// [`Noise::read_write`] was called. Encrypted data will be read until the length of |
261 | | /// [`Noise::rx_buffer_decrypted`] reaches the value in this field. |
262 | | inner_stream_expected_incoming_bytes: usize, |
263 | | } |
264 | | |
265 | | impl Noise { |
266 | | /// Returns the value that was provided as [`Config::is_initiator`]. |
267 | 30 | pub fn is_initiator(&self) -> bool { |
268 | 30 | self.is_initiator |
269 | 30 | } _RNvMs0_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB5_5Noise12is_initiator Line | Count | Source | 267 | 30 | pub fn is_initiator(&self) -> bool { | 268 | 30 | self.is_initiator | 269 | 30 | } |
Unexecuted instantiation: _RNvMs0_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB5_5Noise12is_initiator |
270 | | |
271 | | /// Feeds data coming from a socket and outputs data to write to the socket. |
272 | | /// |
273 | | /// Returns an object that implements `Deref<Target = ReadWrite>`. This object represents the |
274 | | /// decrypted stream of data. |
275 | | /// |
276 | | /// An error is returned if the protocol is being violated by the remote or if the nonce |
277 | | /// overflows. When that happens, the connection should be closed altogether. |
278 | 429 | pub fn read_write<'a, TNow: Clone>( |
279 | 429 | &'a mut self, |
280 | 429 | outer_read_write: &'a mut ReadWrite<TNow>, |
281 | 429 | ) -> Result<InnerReadWrite<'a, TNow>, CipherError> { |
282 | | // Try to pull data from `outer_read_write` to decrypt it. |
283 | 611 | while self.rx_buffer_decrypted.is_empty() |
284 | 271 | || self.inner_stream_expected_incoming_bytes > self.rx_buffer_decrypted.len() |
285 | | { |
286 | | // TODO: what if EOF in the middle of a message? |
287 | 340 | if let Some(next_in_message_size91 ) = self.next_in_message_size { |
288 | 91 | if let Ok(Some(encrypted_message)) = |
289 | 91 | outer_read_write.incoming_bytes_take(usize::from(next_in_message_size)) |
290 | | { |
291 | 91 | self.next_in_message_size = None; |
292 | 91 | |
293 | 91 | // Read and decrypt the message. |
294 | 91 | // TODO: decipher progressively, based on the inner `expected_incoming_bytes` value |
295 | 91 | self.in_cipher_state.read_chachapoly_message_to_vec_append( |
296 | 91 | &[], |
297 | 91 | &encrypted_message, |
298 | 91 | &mut self.rx_buffer_decrypted, |
299 | 91 | )?0 ; |
300 | | } else { |
301 | 0 | break; |
302 | | } |
303 | 91 | } else if let Ok(Some(next_frame_length)) = |
304 | 249 | outer_read_write.incoming_bytes_take_array::<2>() |
305 | 91 | { |
306 | 91 | self.next_in_message_size = Some(u16::from_be_bytes(next_frame_length)); |
307 | 91 | } else { |
308 | 158 | break; |
309 | | } |
310 | | } |
311 | | |
312 | | // Check ahead of time if writing out a message would panic. |
313 | 429 | if self.out_cipher_state.nonce_has_overflowed { |
314 | 0 | return Err(CipherError::NonceOverflow); |
315 | 429 | } |
316 | 429 | |
317 | 429 | Ok(InnerReadWrite { |
318 | 429 | inner_read_write: ReadWrite { |
319 | 429 | now: outer_read_write.now.clone(), |
320 | 429 | incoming_buffer: mem::take(&mut self.rx_buffer_decrypted), |
321 | 429 | read_bytes: 0, |
322 | 429 | expected_incoming_bytes: if outer_read_write.expected_incoming_bytes.is_some() |
323 | 0 | || !outer_read_write.incoming_buffer.is_empty() |
324 | | { |
325 | 429 | Some(self.inner_stream_expected_incoming_bytes) |
326 | | } else { |
327 | 0 | None |
328 | | }, |
329 | 429 | write_buffers: Vec::new(), |
330 | 429 | write_bytes_queued: 0, |
331 | 429 | write_bytes_queueable: outer_read_write.write_bytes_queueable.map( |
332 | 429 | |outer_writable| cmp::min(outer_writable.saturating_sub(16 + 2), 65535 - 16), _RNCINvMs0_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB8_5Noise10read_writeNtNtCsaYZPK01V26L_4core4time8DurationE0Be_ Line | Count | Source | 332 | 424 | |outer_writable| cmp::min(outer_writable.saturating_sub(16 + 2), 65535 - 16), |
_RNCINvMs0_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB8_5Noise10read_writelE0Be_ Line | Count | Source | 332 | 5 | |outer_writable| cmp::min(outer_writable.saturating_sub(16 + 2), 65535 - 16), |
Unexecuted instantiation: _RNCINvMs0_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_5Noise10read_writeNtNtCsaYZPK01V26L_4core4time8DurationE0CsDDUKWWCHAU_18smoldot_light_wasm Unexecuted instantiation: _RNCINvMs0_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_5Noise10read_writepE0Be_ Unexecuted instantiation: _RNCINvMs0_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_5Noise10read_writeNtNtCsbpXXxgr6u8g_3std4time7InstantE0CsiUjFBJteJ7x_17smoldot_full_node |
333 | 429 | ), |
334 | 429 | wake_up_after: outer_read_write.wake_up_after.clone(), |
335 | 429 | }, |
336 | 429 | noise: self, |
337 | 429 | outer_read_write, |
338 | | }) |
339 | 429 | } _RINvMs0_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB6_5Noise10read_writeNtNtCsaYZPK01V26L_4core4time8DurationEBc_ Line | Count | Source | 278 | 424 | pub fn read_write<'a, TNow: Clone>( | 279 | 424 | &'a mut self, | 280 | 424 | outer_read_write: &'a mut ReadWrite<TNow>, | 281 | 424 | ) -> Result<InnerReadWrite<'a, TNow>, CipherError> { | 282 | | // Try to pull data from `outer_read_write` to decrypt it. | 283 | 600 | while self.rx_buffer_decrypted.is_empty() | 284 | 268 | || self.inner_stream_expected_incoming_bytes > self.rx_buffer_decrypted.len() | 285 | | { | 286 | | // TODO: what if EOF in the middle of a message? | 287 | 332 | if let Some(next_in_message_size88 ) = self.next_in_message_size { | 288 | 88 | if let Ok(Some(encrypted_message)) = | 289 | 88 | outer_read_write.incoming_bytes_take(usize::from(next_in_message_size)) | 290 | | { | 291 | 88 | self.next_in_message_size = None; | 292 | 88 | | 293 | 88 | // Read and decrypt the message. | 294 | 88 | // TODO: decipher progressively, based on the inner `expected_incoming_bytes` value | 295 | 88 | self.in_cipher_state.read_chachapoly_message_to_vec_append( | 296 | 88 | &[], | 297 | 88 | &encrypted_message, | 298 | 88 | &mut self.rx_buffer_decrypted, | 299 | 88 | )?0 ; | 300 | | } else { | 301 | 0 | break; | 302 | | } | 303 | 88 | } else if let Ok(Some(next_frame_length)) = | 304 | 244 | outer_read_write.incoming_bytes_take_array::<2>() | 305 | 88 | { | 306 | 88 | self.next_in_message_size = Some(u16::from_be_bytes(next_frame_length)); | 307 | 88 | } else { | 308 | 156 | break; | 309 | | } | 310 | | } | 311 | | | 312 | | // Check ahead of time if writing out a message would panic. | 313 | 424 | if self.out_cipher_state.nonce_has_overflowed { | 314 | 0 | return Err(CipherError::NonceOverflow); | 315 | 424 | } | 316 | 424 | | 317 | 424 | Ok(InnerReadWrite { | 318 | 424 | inner_read_write: ReadWrite { | 319 | 424 | now: outer_read_write.now.clone(), | 320 | 424 | incoming_buffer: mem::take(&mut self.rx_buffer_decrypted), | 321 | 424 | read_bytes: 0, | 322 | 424 | expected_incoming_bytes: if outer_read_write.expected_incoming_bytes.is_some() | 323 | 0 | || !outer_read_write.incoming_buffer.is_empty() | 324 | | { | 325 | 424 | Some(self.inner_stream_expected_incoming_bytes) | 326 | | } else { | 327 | 0 | None | 328 | | }, | 329 | 424 | write_buffers: Vec::new(), | 330 | 424 | write_bytes_queued: 0, | 331 | 424 | write_bytes_queueable: outer_read_write.write_bytes_queueable.map( | 332 | 424 | |outer_writable| cmp::min(outer_writable.saturating_sub(16 + 2), 65535 - 16), | 333 | 424 | ), | 334 | 424 | wake_up_after: outer_read_write.wake_up_after.clone(), | 335 | 424 | }, | 336 | 424 | noise: self, | 337 | 424 | outer_read_write, | 338 | | }) | 339 | 424 | } |
_RINvMs0_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB6_5Noise10read_writelEBc_ Line | Count | Source | 278 | 5 | pub fn read_write<'a, TNow: Clone>( | 279 | 5 | &'a mut self, | 280 | 5 | outer_read_write: &'a mut ReadWrite<TNow>, | 281 | 5 | ) -> Result<InnerReadWrite<'a, TNow>, CipherError> { | 282 | | // Try to pull data from `outer_read_write` to decrypt it. | 283 | 11 | while self.rx_buffer_decrypted.is_empty() | 284 | 3 | || self.inner_stream_expected_incoming_bytes > self.rx_buffer_decrypted.len() | 285 | | { | 286 | | // TODO: what if EOF in the middle of a message? | 287 | 8 | if let Some(next_in_message_size3 ) = self.next_in_message_size { | 288 | 3 | if let Ok(Some(encrypted_message)) = | 289 | 3 | outer_read_write.incoming_bytes_take(usize::from(next_in_message_size)) | 290 | | { | 291 | 3 | self.next_in_message_size = None; | 292 | 3 | | 293 | 3 | // Read and decrypt the message. | 294 | 3 | // TODO: decipher progressively, based on the inner `expected_incoming_bytes` value | 295 | 3 | self.in_cipher_state.read_chachapoly_message_to_vec_append( | 296 | 3 | &[], | 297 | 3 | &encrypted_message, | 298 | 3 | &mut self.rx_buffer_decrypted, | 299 | 3 | )?0 ; | 300 | | } else { | 301 | 0 | break; | 302 | | } | 303 | 3 | } else if let Ok(Some(next_frame_length)) = | 304 | 5 | outer_read_write.incoming_bytes_take_array::<2>() | 305 | 3 | { | 306 | 3 | self.next_in_message_size = Some(u16::from_be_bytes(next_frame_length)); | 307 | 3 | } else { | 308 | 2 | break; | 309 | | } | 310 | | } | 311 | | | 312 | | // Check ahead of time if writing out a message would panic. | 313 | 5 | if self.out_cipher_state.nonce_has_overflowed { | 314 | 0 | return Err(CipherError::NonceOverflow); | 315 | 5 | } | 316 | 5 | | 317 | 5 | Ok(InnerReadWrite { | 318 | 5 | inner_read_write: ReadWrite { | 319 | 5 | now: outer_read_write.now.clone(), | 320 | 5 | incoming_buffer: mem::take(&mut self.rx_buffer_decrypted), | 321 | 5 | read_bytes: 0, | 322 | 5 | expected_incoming_bytes: if outer_read_write.expected_incoming_bytes.is_some() | 323 | 0 | || !outer_read_write.incoming_buffer.is_empty() | 324 | | { | 325 | 5 | Some(self.inner_stream_expected_incoming_bytes) | 326 | | } else { | 327 | 0 | None | 328 | | }, | 329 | 5 | write_buffers: Vec::new(), | 330 | 5 | write_bytes_queued: 0, | 331 | 5 | write_bytes_queueable: outer_read_write.write_bytes_queueable.map( | 332 | 5 | |outer_writable| cmp::min(outer_writable.saturating_sub(16 + 2), 65535 - 16), | 333 | 5 | ), | 334 | 5 | wake_up_after: outer_read_write.wake_up_after.clone(), | 335 | 5 | }, | 336 | 5 | noise: self, | 337 | 5 | outer_read_write, | 338 | | }) | 339 | 5 | } |
Unexecuted instantiation: _RINvMs0_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB6_5Noise10read_writeNtNtCsaYZPK01V26L_4core4time8DurationECsDDUKWWCHAU_18smoldot_light_wasm Unexecuted instantiation: _RINvMs0_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB6_5Noise10read_writepEBc_ Unexecuted instantiation: _RINvMs0_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB6_5Noise10read_writeNtNtCsbpXXxgr6u8g_3std4time7InstantECsiUjFBJteJ7x_17smoldot_full_node |
340 | | } |
341 | | |
342 | | impl fmt::Debug for Noise { |
343 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
344 | 0 | f.debug_struct("Noise").finish() |
345 | 0 | } Unexecuted instantiation: _RNvXs1_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB5_5NoiseNtNtCsaYZPK01V26L_4core3fmt5Debug3fmt Unexecuted instantiation: _RNvXs1_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB5_5NoiseNtNtCsaYZPK01V26L_4core3fmt5Debug3fmt |
346 | | } |
347 | | |
348 | | /// Stream of decrypted data. See [`Noise::read_write`]. |
349 | | pub struct InnerReadWrite<'a, TNow: Clone> { |
350 | | noise: &'a mut Noise, |
351 | | outer_read_write: &'a mut ReadWrite<TNow>, |
352 | | inner_read_write: ReadWrite<TNow>, |
353 | | } |
354 | | |
355 | | impl<'a, TNow: Clone> ops::Deref for InnerReadWrite<'a, TNow> { |
356 | | type Target = ReadWrite<TNow>; |
357 | | |
358 | 0 | fn deref(&self) -> &Self::Target { |
359 | 0 | &self.inner_read_write |
360 | 0 | } Unexecuted instantiation: _RNvXININtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noises2_0pEINtB5_14InnerReadWritepENtNtNtCsaYZPK01V26L_4core3ops5deref5Deref5derefBb_ Unexecuted instantiation: _RNvXININtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noises2_0pEINtB5_14InnerReadWritepENtNtNtCsaYZPK01V26L_4core3ops5deref5Deref5derefBb_ |
361 | | } |
362 | | |
363 | | impl<'a, TNow: Clone> ops::DerefMut for InnerReadWrite<'a, TNow> { |
364 | 429 | fn deref_mut(&mut self) -> &mut Self::Target { |
365 | 429 | &mut self.inner_read_write |
366 | 429 | } _RNvXs3_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseINtB5_14InnerReadWriteNtNtCsaYZPK01V26L_4core4time8DurationENtNtNtB1l_3ops5deref8DerefMut9deref_mutBb_ Line | Count | Source | 364 | 424 | fn deref_mut(&mut self) -> &mut Self::Target { | 365 | 424 | &mut self.inner_read_write | 366 | 424 | } |
_RNvXs3_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseINtB5_14InnerReadWritelENtNtNtCsaYZPK01V26L_4core3ops5deref8DerefMut9deref_mutBb_ Line | Count | Source | 364 | 5 | fn deref_mut(&mut self) -> &mut Self::Target { | 365 | 5 | &mut self.inner_read_write | 366 | 5 | } |
Unexecuted instantiation: _RNvXs3_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseINtB5_14InnerReadWriteNtNtCsaYZPK01V26L_4core4time8DurationENtNtNtB1m_3ops5deref8DerefMut9deref_mutCsDDUKWWCHAU_18smoldot_light_wasm Unexecuted instantiation: _RNvXININtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noises3_0pEINtB5_14InnerReadWritepENtNtNtCsaYZPK01V26L_4core3ops5deref8DerefMut9deref_mutBb_ Unexecuted instantiation: _RNvXs3_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseINtB5_14InnerReadWriteNtNtCsbpXXxgr6u8g_3std4time7InstantENtNtNtCsaYZPK01V26L_4core3ops5deref8DerefMut9deref_mutCsiUjFBJteJ7x_17smoldot_full_node |
367 | | } |
368 | | |
369 | | impl<'a, TNow: Clone> Drop for InnerReadWrite<'a, TNow> { |
370 | 429 | fn drop(&mut self) { |
371 | 429 | self.outer_read_write.wake_up_after = self.inner_read_write.wake_up_after.clone(); |
372 | 429 | self.noise.rx_buffer_decrypted = mem::take(&mut self.inner_read_write.incoming_buffer); |
373 | 429 | self.noise.inner_stream_expected_incoming_bytes = |
374 | 429 | self.inner_read_write.expected_incoming_bytes.unwrap_or(0); |
375 | 429 | |
376 | 429 | // It is possible that the inner stream processes some bytes of `self.rx_buffer_decrypted` |
377 | 429 | // and expects to be called again while no bytes was pulled from the outer `ReadWrite`. |
378 | 429 | // If that happens, the API user will not call `read_write` again and we will have a stall. |
379 | 429 | // For this reason, if the inner stream has read some bytes, we make sure that the outer |
380 | 429 | // `ReadWrite` wakes up as soon as possible. |
381 | 429 | if self.inner_read_write.read_bytes != 0 { |
382 | 208 | self.outer_read_write.wake_up_asap(); |
383 | 221 | } |
384 | | |
385 | | // Encrypt the data, transferring it from the inner `ReadWrite` to the outer `ReadWrite`. |
386 | 429 | if self |
387 | 429 | .inner_read_write |
388 | 429 | .write_buffers |
389 | 429 | .iter() |
390 | 429 | .any(|b| !b.is_empty()93 ) _RNCNvXs4_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseINtB7_14InnerReadWriteNtNtCsaYZPK01V26L_4core4time8DurationENtNtNtB1n_3ops4drop4Drop4drop0Bd_ Line | Count | Source | 390 | 90 | .any(|b| !b.is_empty()) |
_RNCNvXs4_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseINtB7_14InnerReadWritelENtNtNtCsaYZPK01V26L_4core3ops4drop4Drop4drop0Bd_ Line | Count | Source | 390 | 3 | .any(|b| !b.is_empty()) |
Unexecuted instantiation: _RNCNvXs4_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseINtB7_14InnerReadWriteNtNtCsaYZPK01V26L_4core4time8DurationENtNtNtB1o_3ops4drop4Drop4drop0CsDDUKWWCHAU_18smoldot_light_wasm Unexecuted instantiation: _RNCNvXININtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noises4_0pEINtB7_14InnerReadWritepENtNtNtCsaYZPK01V26L_4core3ops4drop4Drop4drop0Bd_ Unexecuted instantiation: _RNCNvXs4_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseINtB7_14InnerReadWriteNtNtCsbpXXxgr6u8g_3std4time7InstantENtNtNtCsaYZPK01V26L_4core3ops4drop4Drop4drop0CsiUjFBJteJ7x_17smoldot_full_node |
391 | | { |
392 | 93 | self.outer_read_write |
393 | 93 | .write_buffers |
394 | 93 | .reserve(2 + self.inner_read_write.write_buffers.len() * 2); |
395 | 93 | |
396 | 93 | // We push a dummy buffer to `outer_read_write.write_buffers`. This dummy buffer |
397 | 93 | // will later be overwritten with the actual message length. |
398 | 93 | let message_length_prefix_index = self.outer_read_write.write_buffers.len(); |
399 | 93 | self.outer_read_write.write_buffers.push(Vec::new()); |
400 | 93 | |
401 | 93 | // Encrypt the message. |
402 | 93 | // `write_chachapoly_message` returns an error if the nonce has overflowed. It has |
403 | 93 | // been checked in the body of `read_write` that this can't happen. |
404 | 93 | let mut total_size = 0; |
405 | 279 | for encrypted_buffer in self |
406 | 93 | .noise |
407 | 93 | .out_cipher_state |
408 | 93 | .write_chachapoly_message(&[], self.inner_read_write.write_buffers.drain(..)) |
409 | 93 | .unwrap_or_else(|_| unreachable!()0 ) Unexecuted instantiation: _RNCNvXs4_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseINtB7_14InnerReadWriteNtNtCsaYZPK01V26L_4core4time8DurationENtNtNtB1n_3ops4drop4Drop4drops_0Bd_ Unexecuted instantiation: _RNCNvXs4_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseINtB7_14InnerReadWritelENtNtNtCsaYZPK01V26L_4core3ops4drop4Drop4drops_0Bd_ Unexecuted instantiation: _RNCNvXs4_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseINtB7_14InnerReadWriteNtNtCsaYZPK01V26L_4core4time8DurationENtNtNtB1o_3ops4drop4Drop4drops_0CsDDUKWWCHAU_18smoldot_light_wasm Unexecuted instantiation: _RNCNvXININtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noises4_0pEINtB7_14InnerReadWritepENtNtNtCsaYZPK01V26L_4core3ops4drop4Drop4drops_0Bd_ Unexecuted instantiation: _RNCNvXs4_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseINtB7_14InnerReadWriteNtNtCsbpXXxgr6u8g_3std4time7InstantENtNtNtCsaYZPK01V26L_4core3ops4drop4Drop4drops_0CsiUjFBJteJ7x_17smoldot_full_node |
410 | 279 | { |
411 | 279 | total_size += encrypted_buffer.len(); |
412 | 279 | self.outer_read_write.write_buffers.push(encrypted_buffer); |
413 | 279 | } |
414 | | |
415 | | // Now write the message length. |
416 | 93 | let message_length_prefix = u16::try_from(total_size).unwrap().to_be_bytes().to_vec(); |
417 | 93 | self.outer_read_write.write_buffers[message_length_prefix_index] = |
418 | 93 | message_length_prefix; |
419 | 93 | |
420 | 93 | // Properly update the outer `ReadWrite`. |
421 | 93 | self.outer_read_write.write_bytes_queued += total_size + 2; |
422 | 93 | *self |
423 | 93 | .outer_read_write |
424 | 93 | .write_bytes_queueable |
425 | 93 | .as_mut() |
426 | 93 | .unwrap() -= total_size + 2; |
427 | 336 | } |
428 | 429 | } _RNvXs4_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseINtB5_14InnerReadWriteNtNtCsaYZPK01V26L_4core4time8DurationENtNtNtB1l_3ops4drop4Drop4dropBb_ Line | Count | Source | 370 | 424 | fn drop(&mut self) { | 371 | 424 | self.outer_read_write.wake_up_after = self.inner_read_write.wake_up_after.clone(); | 372 | 424 | self.noise.rx_buffer_decrypted = mem::take(&mut self.inner_read_write.incoming_buffer); | 373 | 424 | self.noise.inner_stream_expected_incoming_bytes = | 374 | 424 | self.inner_read_write.expected_incoming_bytes.unwrap_or(0); | 375 | 424 | | 376 | 424 | // It is possible that the inner stream processes some bytes of `self.rx_buffer_decrypted` | 377 | 424 | // and expects to be called again while no bytes was pulled from the outer `ReadWrite`. | 378 | 424 | // If that happens, the API user will not call `read_write` again and we will have a stall. | 379 | 424 | // For this reason, if the inner stream has read some bytes, we make sure that the outer | 380 | 424 | // `ReadWrite` wakes up as soon as possible. | 381 | 424 | if self.inner_read_write.read_bytes != 0 { | 382 | 205 | self.outer_read_write.wake_up_asap(); | 383 | 219 | } | 384 | | | 385 | | // Encrypt the data, transferring it from the inner `ReadWrite` to the outer `ReadWrite`. | 386 | 424 | if self | 387 | 424 | .inner_read_write | 388 | 424 | .write_buffers | 389 | 424 | .iter() | 390 | 424 | .any(|b| !b.is_empty()) | 391 | | { | 392 | 90 | self.outer_read_write | 393 | 90 | .write_buffers | 394 | 90 | .reserve(2 + self.inner_read_write.write_buffers.len() * 2); | 395 | 90 | | 396 | 90 | // We push a dummy buffer to `outer_read_write.write_buffers`. This dummy buffer | 397 | 90 | // will later be overwritten with the actual message length. | 398 | 90 | let message_length_prefix_index = self.outer_read_write.write_buffers.len(); | 399 | 90 | self.outer_read_write.write_buffers.push(Vec::new()); | 400 | 90 | | 401 | 90 | // Encrypt the message. | 402 | 90 | // `write_chachapoly_message` returns an error if the nonce has overflowed. It has | 403 | 90 | // been checked in the body of `read_write` that this can't happen. | 404 | 90 | let mut total_size = 0; | 405 | 270 | for encrypted_buffer in self | 406 | 90 | .noise | 407 | 90 | .out_cipher_state | 408 | 90 | .write_chachapoly_message(&[], self.inner_read_write.write_buffers.drain(..)) | 409 | 90 | .unwrap_or_else(|_| unreachable!()) | 410 | 270 | { | 411 | 270 | total_size += encrypted_buffer.len(); | 412 | 270 | self.outer_read_write.write_buffers.push(encrypted_buffer); | 413 | 270 | } | 414 | | | 415 | | // Now write the message length. | 416 | 90 | let message_length_prefix = u16::try_from(total_size).unwrap().to_be_bytes().to_vec(); | 417 | 90 | self.outer_read_write.write_buffers[message_length_prefix_index] = | 418 | 90 | message_length_prefix; | 419 | 90 | | 420 | 90 | // Properly update the outer `ReadWrite`. | 421 | 90 | self.outer_read_write.write_bytes_queued += total_size + 2; | 422 | 90 | *self | 423 | 90 | .outer_read_write | 424 | 90 | .write_bytes_queueable | 425 | 90 | .as_mut() | 426 | 90 | .unwrap() -= total_size + 2; | 427 | 334 | } | 428 | 424 | } |
_RNvXs4_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseINtB5_14InnerReadWritelENtNtNtCsaYZPK01V26L_4core3ops4drop4Drop4dropBb_ Line | Count | Source | 370 | 5 | fn drop(&mut self) { | 371 | 5 | self.outer_read_write.wake_up_after = self.inner_read_write.wake_up_after.clone(); | 372 | 5 | self.noise.rx_buffer_decrypted = mem::take(&mut self.inner_read_write.incoming_buffer); | 373 | 5 | self.noise.inner_stream_expected_incoming_bytes = | 374 | 5 | self.inner_read_write.expected_incoming_bytes.unwrap_or(0); | 375 | 5 | | 376 | 5 | // It is possible that the inner stream processes some bytes of `self.rx_buffer_decrypted` | 377 | 5 | // and expects to be called again while no bytes was pulled from the outer `ReadWrite`. | 378 | 5 | // If that happens, the API user will not call `read_write` again and we will have a stall. | 379 | 5 | // For this reason, if the inner stream has read some bytes, we make sure that the outer | 380 | 5 | // `ReadWrite` wakes up as soon as possible. | 381 | 5 | if self.inner_read_write.read_bytes != 0 { | 382 | 3 | self.outer_read_write.wake_up_asap(); | 383 | 3 | }2 | 384 | | | 385 | | // Encrypt the data, transferring it from the inner `ReadWrite` to the outer `ReadWrite`. | 386 | 5 | if self | 387 | 5 | .inner_read_write | 388 | 5 | .write_buffers | 389 | 5 | .iter() | 390 | 5 | .any(|b| !b.is_empty()) | 391 | | { | 392 | 3 | self.outer_read_write | 393 | 3 | .write_buffers | 394 | 3 | .reserve(2 + self.inner_read_write.write_buffers.len() * 2); | 395 | 3 | | 396 | 3 | // We push a dummy buffer to `outer_read_write.write_buffers`. This dummy buffer | 397 | 3 | // will later be overwritten with the actual message length. | 398 | 3 | let message_length_prefix_index = self.outer_read_write.write_buffers.len(); | 399 | 3 | self.outer_read_write.write_buffers.push(Vec::new()); | 400 | 3 | | 401 | 3 | // Encrypt the message. | 402 | 3 | // `write_chachapoly_message` returns an error if the nonce has overflowed. It has | 403 | 3 | // been checked in the body of `read_write` that this can't happen. | 404 | 3 | let mut total_size = 0; | 405 | 9 | for encrypted_buffer in self | 406 | 3 | .noise | 407 | 3 | .out_cipher_state | 408 | 3 | .write_chachapoly_message(&[], self.inner_read_write.write_buffers.drain(..)) | 409 | 3 | .unwrap_or_else(|_| unreachable!()) | 410 | 9 | { | 411 | 9 | total_size += encrypted_buffer.len(); | 412 | 9 | self.outer_read_write.write_buffers.push(encrypted_buffer); | 413 | 9 | } | 414 | | | 415 | | // Now write the message length. | 416 | 3 | let message_length_prefix = u16::try_from(total_size).unwrap().to_be_bytes().to_vec(); | 417 | 3 | self.outer_read_write.write_buffers[message_length_prefix_index] = | 418 | 3 | message_length_prefix; | 419 | 3 | | 420 | 3 | // Properly update the outer `ReadWrite`. | 421 | 3 | self.outer_read_write.write_bytes_queued += total_size + 2; | 422 | 3 | *self | 423 | 3 | .outer_read_write | 424 | 3 | .write_bytes_queueable | 425 | 3 | .as_mut() | 426 | 3 | .unwrap() -= total_size + 2; | 427 | 2 | } | 428 | 5 | } |
Unexecuted instantiation: _RNvXs4_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseINtB5_14InnerReadWriteNtNtCsaYZPK01V26L_4core4time8DurationENtNtNtB1m_3ops4drop4Drop4dropCsDDUKWWCHAU_18smoldot_light_wasm Unexecuted instantiation: _RNvXININtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noises4_0pEINtB5_14InnerReadWritepENtNtNtCsaYZPK01V26L_4core3ops4drop4Drop4dropBb_ Unexecuted instantiation: _RNvXs4_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseINtB5_14InnerReadWriteNtNtCsbpXXxgr6u8g_3std4time7InstantENtNtNtCsaYZPK01V26L_4core3ops4drop4Drop4dropCsiUjFBJteJ7x_17smoldot_full_node |
429 | | } |
430 | | |
431 | | /// State of a Noise handshake. |
432 | | #[derive(Debug)] |
433 | | pub enum NoiseHandshake { |
434 | | /// Handshake still in progress. More data needs to be sent or received. |
435 | | InProgress(HandshakeInProgress), |
436 | | /// Noise handshake has successfully completed. |
437 | | Success { |
438 | | /// Object to use to encrypt and decrypt all further communications. |
439 | | cipher: Noise, |
440 | | /// [`PeerId`] of the remote. |
441 | | remote_peer_id: PeerId, |
442 | | }, |
443 | | } |
444 | | |
445 | | /// Handshake still in progress. More data needs to be sent or received. |
446 | | pub struct HandshakeInProgress(Box<HandshakeInProgressInner>); |
447 | | |
448 | | /// The actual fields are wrapped within a `Box` because we move the `HandshakeInProgress` |
449 | | /// frequently. |
450 | | struct HandshakeInProgressInner { |
451 | | /// See [`Config::is_initiator`]. |
452 | | is_initiator: bool, |
453 | | |
454 | | /// Queued data that should be sent out as soon as possible. |
455 | | pending_out_data: VecDeque<u8>, |
456 | | |
457 | | /// Size of the next message being received, if already known. |
458 | | /// |
459 | | /// If `Some`, the libp2p size prefix has already been extracted from the incoming buffer. |
460 | | /// If `None`, this hasn't been done yet. |
461 | | next_in_message_size: Option<u16>, |
462 | | |
463 | | /// Progression of the handshake. |
464 | | /// |
465 | | /// Every time a message is sent out or buffered in |
466 | | /// [`HandshakeInProgressInner::pending_out_data`], this is increased by 1. |
467 | | num_buffered_or_transmitted_messages: u8, |
468 | | |
469 | | /// A single cipher is used during the handshake, as each side takes turn to send data rather |
470 | | /// than both at the same time. |
471 | | cipher_state: CipherState, |
472 | | |
473 | | /// Product of the diffie-hellmans between the various keys that are exchanged. Used to |
474 | | /// initialize the ciphers once the handshake is over. |
475 | | /// |
476 | | /// Corresponds to the `ck` field of the `SymmetricState` in the Noise specification. |
477 | | /// See <https://noiseprotocol.org/noise.html#the-symmetricstate-object>. |
478 | | chaining_key: zeroize::Zeroizing<[u8; 32]>, |
479 | | |
480 | | /// Hash that maintains the state of the data that we've sent out or received. Used as the |
481 | | /// associated data whenever we produce or verify a frame during the handshake. |
482 | | /// |
483 | | /// Corresponds to the `h` field of the `SymmetricState` in the Noise specification. |
484 | | /// See <https://noiseprotocol.org/noise.html#the-symmetricstate-object>. |
485 | | hash: zeroize::Zeroizing<[u8; 32]>, |
486 | | |
487 | | /// Local ephemeral key. Generate for this handshake specifically. |
488 | | /// |
489 | | /// Corresponds to the `e` field of the `HandshakeState` in the Noise specification. |
490 | | /// See <https://noiseprotocol.org/noise.html#the-handshakestate-object>. |
491 | | local_ephemeral_private_key: zeroize::Zeroizing<x25519_dalek::StaticSecret>, |
492 | | |
493 | | /// Local static key. Corresponds to a [`NoiseKey`]. |
494 | | /// |
495 | | /// Corresponds to the `s` field of the `HandshakeState` in the Noise specification. |
496 | | /// See <https://noiseprotocol.org/noise.html#the-handshakestate-object>. |
497 | | local_static_private_key: zeroize::Zeroizing<x25519_dalek::StaticSecret>, |
498 | | |
499 | | /// Public key corresponding to [`HandshakeInProgressInner::local_static_private_key`]. |
500 | | local_static_public_key: x25519_dalek::PublicKey, |
501 | | |
502 | | /// Ephemeral public key of the remote. Initially set to `0`s, then set to the correct value |
503 | | /// once it's been received. |
504 | | /// |
505 | | /// Corresponds to the `re` field of the `HandshakeState` in the Noise specification. |
506 | | /// See <https://noiseprotocol.org/noise.html#the-handshakestate-object>. |
507 | | remote_ephemeral_public_key: x25519_dalek::PublicKey, |
508 | | |
509 | | /// Static public key of the remote. Initially set to `0`s, then set to the correct value |
510 | | /// once it's been received. |
511 | | /// |
512 | | /// Corresponds to the `rs` field of the `HandshakeState` in the Noise specification. |
513 | | /// See <https://noiseprotocol.org/noise.html#the-handshakestate-object>. |
514 | | remote_static_public_key: x25519_dalek::PublicKey, |
515 | | |
516 | | /// Libp2p public key of the remote. Initially `None`, then set to the correct value once |
517 | | /// we've received it. |
518 | | remote_public_key: Option<PublicKey>, |
519 | | |
520 | | /// Libp2p-specific additional handshake message to encrypt then send to the remote. |
521 | | libp2p_handshake_message: Vec<u8>, |
522 | | } |
523 | | |
524 | | impl NoiseHandshake { |
525 | | /// Shortcut function that calls [`HandshakeInProgress::new`] and wraps it into a |
526 | | /// [`NoiseHandshake`]. |
527 | 8 | pub fn new(config: Config) -> Self { |
528 | 8 | NoiseHandshake::InProgress(HandshakeInProgress::new(config)) |
529 | 8 | } _RNvMs5_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB5_14NoiseHandshake3new Line | Count | Source | 527 | 8 | pub fn new(config: Config) -> Self { | 528 | 8 | NoiseHandshake::InProgress(HandshakeInProgress::new(config)) | 529 | 8 | } |
Unexecuted instantiation: _RNvMs5_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB5_14NoiseHandshake3new |
530 | | } |
531 | | |
532 | | impl HandshakeInProgress { |
533 | | /// Initializes a new noise handshake state machine. |
534 | 24 | pub fn new(config: Config) -> Self { |
535 | 24 | // Generate a new ephemeral key for this handshake. |
536 | 24 | // TODO: is it zeroize-safe to call `from([u8; 32])`? |
537 | 24 | let local_ephemeral_private_key = zeroize::Zeroizing::new( |
538 | 24 | x25519_dalek::StaticSecret::from(*config.ephemeral_secret_key), |
539 | 24 | ); |
540 | 24 | |
541 | 24 | // Initialize the hash. |
542 | 24 | let mut hash = zeroize::Zeroizing::new([0u8; 32]); |
543 | 24 | |
544 | 24 | // InitializeSymmetric(protocol_name). |
545 | 24 | { |
546 | 24 | const PROTOCOL_NAME: &[u8] = b"Noise_XX_25519_ChaChaPoly_SHA256"; |
547 | 24 | if PROTOCOL_NAME.len() <= hash.len() { |
548 | 24 | hash[..PROTOCOL_NAME.len()].copy_from_slice(PROTOCOL_NAME); |
549 | 24 | hash[PROTOCOL_NAME.len()..].fill(0); |
550 | 24 | } else { |
551 | 0 | let mut hasher = <sha2::Sha256 as sha2::Digest>::new(); |
552 | 0 | sha2::Digest::update(&mut hasher, PROTOCOL_NAME); |
553 | 0 | sha2::Digest::finalize_into( |
554 | 0 | hasher, |
555 | 0 | sha2::digest::generic_array::GenericArray::from_mut_slice(&mut *hash), |
556 | 0 | ); |
557 | 0 | } |
558 | | } |
559 | 24 | let chaining_key = hash.clone(); |
560 | 24 | |
561 | 24 | // Perform MixHash(prologue). |
562 | 24 | mix_hash(&mut hash, config.prologue); |
563 | 24 | |
564 | 24 | HandshakeInProgress(Box::new(HandshakeInProgressInner { |
565 | 24 | cipher_state: CipherState { |
566 | 24 | key: zeroize::Zeroizing::new([0; 32]), |
567 | 24 | nonce: 0, |
568 | 24 | nonce_has_overflowed: false, |
569 | 24 | }, |
570 | 24 | chaining_key, |
571 | 24 | hash, |
572 | 24 | local_ephemeral_private_key, |
573 | 24 | local_static_private_key: config.key.private_key.clone(), |
574 | 24 | local_static_public_key: config.key.public_key, |
575 | 24 | remote_ephemeral_public_key: x25519_dalek::PublicKey::from([0; 32]), |
576 | 24 | remote_static_public_key: x25519_dalek::PublicKey::from([0; 32]), |
577 | 24 | remote_public_key: None, |
578 | 24 | is_initiator: config.is_initiator, |
579 | 24 | pending_out_data: VecDeque::with_capacity(usize::from(u16::MAX) + 2), |
580 | 24 | next_in_message_size: None, |
581 | 24 | num_buffered_or_transmitted_messages: 0, |
582 | 24 | libp2p_handshake_message: config.key.handshake_message.clone(), |
583 | 24 | })) |
584 | 24 | } _RNvMs6_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB5_19HandshakeInProgress3new Line | Count | Source | 534 | 24 | pub fn new(config: Config) -> Self { | 535 | 24 | // Generate a new ephemeral key for this handshake. | 536 | 24 | // TODO: is it zeroize-safe to call `from([u8; 32])`? | 537 | 24 | let local_ephemeral_private_key = zeroize::Zeroizing::new( | 538 | 24 | x25519_dalek::StaticSecret::from(*config.ephemeral_secret_key), | 539 | 24 | ); | 540 | 24 | | 541 | 24 | // Initialize the hash. | 542 | 24 | let mut hash = zeroize::Zeroizing::new([0u8; 32]); | 543 | 24 | | 544 | 24 | // InitializeSymmetric(protocol_name). | 545 | 24 | { | 546 | 24 | const PROTOCOL_NAME: &[u8] = b"Noise_XX_25519_ChaChaPoly_SHA256"; | 547 | 24 | if PROTOCOL_NAME.len() <= hash.len() { | 548 | 24 | hash[..PROTOCOL_NAME.len()].copy_from_slice(PROTOCOL_NAME); | 549 | 24 | hash[PROTOCOL_NAME.len()..].fill(0); | 550 | 24 | } else { | 551 | 0 | let mut hasher = <sha2::Sha256 as sha2::Digest>::new(); | 552 | 0 | sha2::Digest::update(&mut hasher, PROTOCOL_NAME); | 553 | 0 | sha2::Digest::finalize_into( | 554 | 0 | hasher, | 555 | 0 | sha2::digest::generic_array::GenericArray::from_mut_slice(&mut *hash), | 556 | 0 | ); | 557 | 0 | } | 558 | | } | 559 | 24 | let chaining_key = hash.clone(); | 560 | 24 | | 561 | 24 | // Perform MixHash(prologue). | 562 | 24 | mix_hash(&mut hash, config.prologue); | 563 | 24 | | 564 | 24 | HandshakeInProgress(Box::new(HandshakeInProgressInner { | 565 | 24 | cipher_state: CipherState { | 566 | 24 | key: zeroize::Zeroizing::new([0; 32]), | 567 | 24 | nonce: 0, | 568 | 24 | nonce_has_overflowed: false, | 569 | 24 | }, | 570 | 24 | chaining_key, | 571 | 24 | hash, | 572 | 24 | local_ephemeral_private_key, | 573 | 24 | local_static_private_key: config.key.private_key.clone(), | 574 | 24 | local_static_public_key: config.key.public_key, | 575 | 24 | remote_ephemeral_public_key: x25519_dalek::PublicKey::from([0; 32]), | 576 | 24 | remote_static_public_key: x25519_dalek::PublicKey::from([0; 32]), | 577 | 24 | remote_public_key: None, | 578 | 24 | is_initiator: config.is_initiator, | 579 | 24 | pending_out_data: VecDeque::with_capacity(usize::from(u16::MAX) + 2), | 580 | 24 | next_in_message_size: None, | 581 | 24 | num_buffered_or_transmitted_messages: 0, | 582 | 24 | libp2p_handshake_message: config.key.handshake_message.clone(), | 583 | 24 | })) | 584 | 24 | } |
Unexecuted instantiation: _RNvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB5_19HandshakeInProgress3new |
585 | | |
586 | | /// Feeds data coming from a socket and outputs data to write to the socket. |
587 | | /// |
588 | | /// On success, returns the new state of the negotiation. |
589 | | /// |
590 | | /// An error is returned if the protocol is being violated by the remote. When that happens, |
591 | | /// the connection should be closed altogether. |
592 | 72 | pub fn read_write<TNow>( |
593 | 72 | mut self, |
594 | 72 | read_write: &mut ReadWrite<TNow>, |
595 | 72 | ) -> Result<NoiseHandshake, HandshakeError> { |
596 | 144 | loop { |
597 | 144 | // Write out the data currently buffered waiting to be written out. |
598 | 144 | // If we didn't finish writing our payload, don't do anything more and return now. |
599 | 144 | // Don't even read the data from the remote. |
600 | 144 | read_write.write_from_vec_deque(&mut self.0.pending_out_data); |
601 | 144 | if !self.0.pending_out_data.is_empty() { |
602 | 8 | if read_write.write_bytes_queueable.is_none() { |
603 | 0 | return Err(HandshakeError::WriteClosed); |
604 | 8 | } |
605 | 8 | return Ok(NoiseHandshake::InProgress(self)); |
606 | 136 | } |
607 | 136 | |
608 | 136 | // If the handshake has finished, we return successfully here. |
609 | 136 | if self.0.num_buffered_or_transmitted_messages == 3 { |
610 | 24 | debug_assert!(self.0.pending_out_data.is_empty()); |
611 | 24 | debug_assert!(self.0.next_in_message_size.is_none()); |
612 | | |
613 | | // Perform the `Split()`. |
614 | | let HkdfOutput { |
615 | 24 | output1: init_to_resp, |
616 | 24 | output2: resp_to_init, |
617 | 24 | } = hkdf(&self.0.chaining_key, &[]); |
618 | 24 | let (out_key, in_key) = match self.0.is_initiator { |
619 | 12 | true => (init_to_resp, resp_to_init), |
620 | 12 | false => (resp_to_init, init_to_resp), |
621 | | }; |
622 | 24 | return Ok(NoiseHandshake::Success { |
623 | 24 | cipher: Noise { |
624 | 24 | is_initiator: self.0.is_initiator, |
625 | 24 | out_cipher_state: CipherState { |
626 | 24 | key: out_key, |
627 | 24 | nonce: 0, |
628 | 24 | nonce_has_overflowed: false, |
629 | 24 | }, |
630 | 24 | in_cipher_state: CipherState { |
631 | 24 | key: in_key, |
632 | 24 | nonce: 0, |
633 | 24 | nonce_has_overflowed: false, |
634 | 24 | }, |
635 | 24 | rx_buffer_decrypted: Vec::with_capacity(65535 - 16), |
636 | 24 | next_in_message_size: None, |
637 | 24 | inner_stream_expected_incoming_bytes: 0, |
638 | 24 | }, |
639 | 24 | remote_peer_id: { |
640 | 24 | // The logic of this module guarantees that `remote_peer_id` has |
641 | 24 | // been set during the handshake. |
642 | 24 | self.0 |
643 | 24 | .remote_public_key |
644 | 24 | .take() |
645 | 24 | .unwrap_or_else(|| unreachable!()0 ) Unexecuted instantiation: _RNCINvMs6_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writeNtNtCsaYZPK01V26L_4core4time8DurationE0Be_ Unexecuted instantiation: _RNCINvMs6_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writelE0Be_ Unexecuted instantiation: _RNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writeNtNtCsaYZPK01V26L_4core4time8DurationE0CsDDUKWWCHAU_18smoldot_light_wasm Unexecuted instantiation: _RNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writepE0Be_ Unexecuted instantiation: _RNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writeNtNtCsbpXXxgr6u8g_3std4time7InstantE0CsiUjFBJteJ7x_17smoldot_full_node |
646 | 24 | .into_peer_id() |
647 | 24 | }, |
648 | 24 | }); |
649 | 112 | } |
650 | 112 | |
651 | 112 | // If the handshake is in a phase where we need to send out more data, queue said |
652 | 112 | // data to `pending_out_data` and continue. |
653 | 112 | match ( |
654 | 112 | self.0.num_buffered_or_transmitted_messages, |
655 | 112 | self.0.is_initiator, |
656 | 112 | ) { |
657 | | (0, true) => { |
658 | | // Send `e`, the ephemeral local public key. |
659 | | |
660 | | // Process `e`. |
661 | 12 | let local_ephemeral_public_key = |
662 | 12 | x25519_dalek::PublicKey::from(&*self.0.local_ephemeral_private_key); |
663 | 12 | self.0 |
664 | 12 | .pending_out_data |
665 | 12 | .extend(local_ephemeral_public_key.as_bytes()); |
666 | 12 | mix_hash(&mut self.0.hash, local_ephemeral_public_key.as_bytes()); |
667 | 12 | |
668 | 12 | // Call MixHash(&[]) to process the empty payload. |
669 | 12 | mix_hash(&mut self.0.hash, &[]); |
670 | 12 | |
671 | 12 | // Add the libp2p message length. |
672 | 12 | let len = u16::try_from(self.0.pending_out_data.len()) |
673 | 12 | .unwrap() |
674 | 12 | .to_be_bytes(); |
675 | 12 | self.0.pending_out_data.push_front(len[1]); |
676 | 12 | self.0.pending_out_data.push_front(len[0]); |
677 | 12 | |
678 | 12 | // Message is now fully queued. |
679 | 12 | self.0.num_buffered_or_transmitted_messages += 1; |
680 | 12 | continue; |
681 | | } |
682 | | (1, false) => { |
683 | | // Send `e, ee, s, es` and the libp2p-specific handshake. |
684 | | |
685 | | // Process `e`. |
686 | 12 | let local_ephemeral_public_key = |
687 | 12 | x25519_dalek::PublicKey::from(&*self.0.local_ephemeral_private_key); |
688 | 12 | self.0 |
689 | 12 | .pending_out_data |
690 | 12 | .extend(local_ephemeral_public_key.as_bytes()); |
691 | 12 | mix_hash(&mut self.0.hash, local_ephemeral_public_key.as_bytes()); |
692 | 12 | |
693 | 12 | // Process `ee`. Call MixKey(DH(e, re)). |
694 | 12 | let HkdfOutput { |
695 | 12 | output1: chaining_key_update, |
696 | 12 | output2: key_update, |
697 | 12 | } = hkdf( |
698 | 12 | &self.0.chaining_key, |
699 | 12 | self.0 |
700 | 12 | .local_ephemeral_private_key |
701 | 12 | .diffie_hellman(&self.0.remote_ephemeral_public_key) |
702 | 12 | .as_bytes(), |
703 | 12 | ); |
704 | 12 | self.0.chaining_key = chaining_key_update; |
705 | 12 | self.0.cipher_state.key = key_update; |
706 | 12 | self.0.cipher_state.nonce = 0; |
707 | 12 | |
708 | 12 | // Process `s`. Append EncryptAndHash(s.public_key) to the buffer. |
709 | 12 | let encrypted_static_public_key = self |
710 | 12 | .0 |
711 | 12 | .cipher_state |
712 | 12 | .write_chachapoly_message_to_vec( |
713 | 12 | &*self.0.hash, |
714 | 12 | self.0.local_static_public_key.as_bytes(), |
715 | 12 | ) |
716 | 12 | .unwrap(); |
717 | 12 | self.0 |
718 | 12 | .pending_out_data |
719 | 12 | .extend(encrypted_static_public_key.iter().copied()); |
720 | 12 | mix_hash(&mut self.0.hash, &encrypted_static_public_key); |
721 | 12 | |
722 | 12 | // Process `es`. Call MixKey(DH(s, re)). |
723 | 12 | let HkdfOutput { |
724 | 12 | output1: chaining_key_update, |
725 | 12 | output2: key_update, |
726 | 12 | } = hkdf( |
727 | 12 | &self.0.chaining_key, |
728 | 12 | self.0 |
729 | 12 | .local_static_private_key |
730 | 12 | .diffie_hellman(&self.0.remote_ephemeral_public_key) |
731 | 12 | .as_bytes(), |
732 | 12 | ); |
733 | 12 | self.0.chaining_key = chaining_key_update; |
734 | 12 | self.0.cipher_state.key = key_update; |
735 | 12 | self.0.cipher_state.nonce = 0; |
736 | 12 | |
737 | 12 | // Add the libp2p handshake message. |
738 | 12 | let encrypted_libp2p_handshake = self |
739 | 12 | .0 |
740 | 12 | .cipher_state |
741 | 12 | .write_chachapoly_message_to_vec( |
742 | 12 | &*self.0.hash, |
743 | 12 | &self.0.libp2p_handshake_message, |
744 | 12 | ) |
745 | 12 | .unwrap(); |
746 | 12 | self.0 |
747 | 12 | .pending_out_data |
748 | 12 | .extend(encrypted_libp2p_handshake.iter().copied()); |
749 | 12 | mix_hash(&mut self.0.hash, &encrypted_libp2p_handshake); |
750 | 12 | |
751 | 12 | // Add the libp2p message length. |
752 | 12 | let len = u16::try_from(self.0.pending_out_data.len()) |
753 | 12 | .unwrap() |
754 | 12 | .to_be_bytes(); |
755 | 12 | self.0.pending_out_data.push_front(len[1]); |
756 | 12 | self.0.pending_out_data.push_front(len[0]); |
757 | 12 | |
758 | 12 | // Message is now fully queued. |
759 | 12 | self.0.num_buffered_or_transmitted_messages += 1; |
760 | 12 | continue; |
761 | | } |
762 | | (2, true) => { |
763 | | // Send `s, se` and the libp2p-specific handshake. |
764 | | |
765 | | // Process `s`. Append EncryptAndHash(s.public_key) to the buffer. |
766 | 12 | let encrypted_static_public_key = self |
767 | 12 | .0 |
768 | 12 | .cipher_state |
769 | 12 | .write_chachapoly_message_to_vec( |
770 | 12 | &*self.0.hash, |
771 | 12 | self.0.local_static_public_key.as_bytes(), |
772 | 12 | ) |
773 | 12 | .unwrap(); |
774 | 12 | self.0 |
775 | 12 | .pending_out_data |
776 | 12 | .extend(encrypted_static_public_key.iter().copied()); |
777 | 12 | mix_hash(&mut self.0.hash, &encrypted_static_public_key); |
778 | 12 | |
779 | 12 | // Process `se`. Call MixKey(DH(s, re)). |
780 | 12 | let HkdfOutput { |
781 | 12 | output1: chaining_key_update, |
782 | 12 | output2: key_update, |
783 | 12 | } = hkdf( |
784 | 12 | &self.0.chaining_key, |
785 | 12 | self.0 |
786 | 12 | .local_static_private_key |
787 | 12 | .diffie_hellman(&self.0.remote_ephemeral_public_key) |
788 | 12 | .as_bytes(), |
789 | 12 | ); |
790 | 12 | self.0.chaining_key = chaining_key_update; |
791 | 12 | self.0.cipher_state.key = key_update; |
792 | 12 | self.0.cipher_state.nonce = 0; |
793 | 12 | |
794 | 12 | // Add the libp2p handshake message. |
795 | 12 | let encrypted_libp2p_handshake = self |
796 | 12 | .0 |
797 | 12 | .cipher_state |
798 | 12 | .write_chachapoly_message_to_vec( |
799 | 12 | &*self.0.hash, |
800 | 12 | &self.0.libp2p_handshake_message, |
801 | 12 | ) |
802 | 12 | .unwrap(); |
803 | 12 | self.0 |
804 | 12 | .pending_out_data |
805 | 12 | .extend(encrypted_libp2p_handshake.iter().copied()); |
806 | 12 | mix_hash(&mut self.0.hash, &encrypted_libp2p_handshake); |
807 | 12 | |
808 | 12 | // Add the libp2p message length. |
809 | 12 | let len = u16::try_from(self.0.pending_out_data.len()) |
810 | 12 | .unwrap() |
811 | 12 | .to_be_bytes(); |
812 | 12 | self.0.pending_out_data.push_front(len[1]); |
813 | 12 | self.0.pending_out_data.push_front(len[0]); |
814 | 12 | |
815 | 12 | // Message is now fully queued. |
816 | 12 | self.0.num_buffered_or_transmitted_messages += 1; |
817 | 12 | continue; |
818 | | } |
819 | 76 | _ => {} |
820 | | } |
821 | | |
822 | | // Since we have no more data to write out, and that the handshake isn't finished yet, |
823 | | // the next step is necessarily receiving a message sent by the remote. |
824 | | |
825 | | // Grab the size of the next message, either from `self` or by extracting 2 bytes from |
826 | | // the incoming buffer. |
827 | 42 | let next_in_message_size = |
828 | 76 | if let Some(next_in_message_size6 ) = self.0.next_in_message_size { |
829 | 6 | next_in_message_size |
830 | | } else { |
831 | 70 | match read_write.incoming_bytes_take(2) { |
832 | 36 | Ok(Some(size_buffer)) => *self.0.next_in_message_size.insert( |
833 | 36 | u16::from_be_bytes(<[u8; 2]>::try_from(&size_buffer[..2]).unwrap()), |
834 | 36 | ), |
835 | | Ok(None) => { |
836 | | // Not enough data in incoming buffer. |
837 | 34 | return Ok(NoiseHandshake::InProgress(self)); |
838 | | } |
839 | | Err(read_write::IncomingBytesTakeError::ReadClosed) => { |
840 | 0 | return Err(HandshakeError::ReadClosed) |
841 | | } |
842 | | } |
843 | | }; |
844 | | |
845 | | // Extract the message from the incoming buffer. |
846 | 36 | let available_message = |
847 | 42 | match read_write.incoming_bytes_take(usize::from(next_in_message_size)) { |
848 | 36 | Ok(Some(available_message)) => { |
849 | 36 | self.0.next_in_message_size = None; |
850 | 36 | available_message |
851 | | } |
852 | | Ok(None) => { |
853 | | // Not enough data in incoming buffer. |
854 | 6 | return Ok(NoiseHandshake::InProgress(self)); |
855 | | } |
856 | | Err(read_write::IncomingBytesTakeError::ReadClosed) => { |
857 | 0 | return Err(HandshakeError::ReadClosed) |
858 | | } |
859 | | }; |
860 | | |
861 | | // How to parse the message depends on the current handshake phase. |
862 | 36 | match ( |
863 | 36 | self.0.num_buffered_or_transmitted_messages, |
864 | 36 | self.0.is_initiator, |
865 | 36 | ) { |
866 | | (0, false) => { |
867 | | // Receive `e` message from the remote. |
868 | | self.0.remote_ephemeral_public_key = x25519_dalek::PublicKey::from(*{ |
869 | | // Because the remote hasn't authenticated us at this point, sending more |
870 | | // data than what the protocol specifies is forbidden. |
871 | 12 | let mut parser = nom::combinator::all_consuming::< |
872 | 12 | _, |
873 | 12 | _, |
874 | 12 | (&[u8], nom::error::ErrorKind), |
875 | 12 | _, |
876 | 12 | >(nom::combinator::map( |
877 | 12 | nom::bytes::streaming::take(32u32), |
878 | 12 | |k| <&[u8; 32]>::try_from(k).unwrap(), _RNCINvMs6_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writeNtNtCsaYZPK01V26L_4core4time8DurationEs_0Be_ Line | Count | Source | 878 | 7 | |k| <&[u8; 32]>::try_from(k).unwrap(), |
_RNCINvMs6_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writelEs_0Be_ Line | Count | Source | 878 | 5 | |k| <&[u8; 32]>::try_from(k).unwrap(), |
Unexecuted instantiation: _RNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writeNtNtCsaYZPK01V26L_4core4time8DurationEs_0CsDDUKWWCHAU_18smoldot_light_wasm Unexecuted instantiation: _RNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writepEs_0Be_ Unexecuted instantiation: _RNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writeNtNtCsbpXXxgr6u8g_3std4time7InstantEs_0CsiUjFBJteJ7x_17smoldot_full_node |
879 | 12 | )); |
880 | 12 | match parser(&available_message) { |
881 | 12 | Ok((_, out)) => out, |
882 | | Err(_) => { |
883 | 0 | return Err(HandshakeError::PayloadDecode(PayloadDecodeError)) |
884 | | } |
885 | | } |
886 | | }); |
887 | 12 | mix_hash( |
888 | 12 | &mut self.0.hash, |
889 | 12 | self.0.remote_ephemeral_public_key.as_bytes(), |
890 | 12 | ); |
891 | 12 | |
892 | 12 | // Call MixHash(&[]) to process the empty payload. |
893 | 12 | mix_hash(&mut self.0.hash, &[]); |
894 | 12 | |
895 | 12 | // Message has been fully processed. |
896 | 12 | self.0.num_buffered_or_transmitted_messages += 1; |
897 | 12 | continue; |
898 | | } |
899 | | (1, true) => { |
900 | | // Receive `e, ee, s, es` and the libp2p-specific handshake from the remote. |
901 | | let ( |
902 | 12 | remote_ephemeral_public_key, |
903 | 12 | remote_static_public_key_encrypted, |
904 | 12 | libp2p_handshake_encrypted, |
905 | | ) = { |
906 | | // Because the remote hasn't fully authenticated us at this point, sending |
907 | | // more data than what the protocol specifies is forbidden. |
908 | 12 | let mut parser = nom::combinator::all_consuming::< |
909 | 12 | _, |
910 | 12 | _, |
911 | 12 | (&[u8], nom::error::ErrorKind), |
912 | 12 | _, |
913 | 12 | >(nom::sequence::tuple(( |
914 | 12 | nom::combinator::map(nom::bytes::streaming::take(32u32), |k| { |
915 | 12 | <&[u8; 32]>::try_from(k).unwrap() |
916 | 12 | }), _RNCINvMs6_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writeNtNtCsaYZPK01V26L_4core4time8DurationEs0_0Be_ Line | Count | Source | 914 | 7 | nom::combinator::map(nom::bytes::streaming::take(32u32), |k| { | 915 | 7 | <&[u8; 32]>::try_from(k).unwrap() | 916 | 7 | }), |
_RNCINvMs6_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writelEs0_0Be_ Line | Count | Source | 914 | 5 | nom::combinator::map(nom::bytes::streaming::take(32u32), |k| { | 915 | 5 | <&[u8; 32]>::try_from(k).unwrap() | 916 | 5 | }), |
Unexecuted instantiation: _RNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writeNtNtCsaYZPK01V26L_4core4time8DurationEs0_0CsDDUKWWCHAU_18smoldot_light_wasm Unexecuted instantiation: _RNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writepEs0_0Be_ Unexecuted instantiation: _RNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writeNtNtCsbpXXxgr6u8g_3std4time7InstantEs0_0CsiUjFBJteJ7x_17smoldot_full_node |
917 | 12 | nom::combinator::map(nom::bytes::streaming::take(48u32), |k| { |
918 | 12 | <&[u8; 48]>::try_from(k).unwrap() |
919 | 12 | }), _RNCINvMs6_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writeNtNtCsaYZPK01V26L_4core4time8DurationEs1_0Be_ Line | Count | Source | 917 | 7 | nom::combinator::map(nom::bytes::streaming::take(48u32), |k| { | 918 | 7 | <&[u8; 48]>::try_from(k).unwrap() | 919 | 7 | }), |
_RNCINvMs6_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writelEs1_0Be_ Line | Count | Source | 917 | 5 | nom::combinator::map(nom::bytes::streaming::take(48u32), |k| { | 918 | 5 | <&[u8; 48]>::try_from(k).unwrap() | 919 | 5 | }), |
Unexecuted instantiation: _RNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writeNtNtCsaYZPK01V26L_4core4time8DurationEs1_0CsDDUKWWCHAU_18smoldot_light_wasm Unexecuted instantiation: _RNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writepEs1_0Be_ Unexecuted instantiation: _RNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writeNtNtCsbpXXxgr6u8g_3std4time7InstantEs1_0CsiUjFBJteJ7x_17smoldot_full_node |
920 | 12 | nom::combinator::rest, |
921 | 12 | ))); |
922 | 12 | match parser(&available_message) { |
923 | 12 | Ok((_, out)) => out, |
924 | | Err(_) => { |
925 | 0 | return Err(HandshakeError::PayloadDecode(PayloadDecodeError)) |
926 | | } |
927 | | } |
928 | | }; |
929 | | |
930 | | // Process `e`. |
931 | 12 | self.0.remote_ephemeral_public_key = |
932 | 12 | x25519_dalek::PublicKey::from(*remote_ephemeral_public_key); |
933 | 12 | mix_hash( |
934 | 12 | &mut self.0.hash, |
935 | 12 | self.0.remote_ephemeral_public_key.as_bytes(), |
936 | 12 | ); |
937 | 12 | |
938 | 12 | // Process `ee`. Call MixKey(DH(e, re)). |
939 | 12 | let HkdfOutput { |
940 | 12 | output1: chaining_key_update, |
941 | 12 | output2: key_update, |
942 | 12 | } = hkdf( |
943 | 12 | &self.0.chaining_key, |
944 | 12 | self.0 |
945 | 12 | .local_ephemeral_private_key |
946 | 12 | .diffie_hellman(&self.0.remote_ephemeral_public_key) |
947 | 12 | .as_bytes(), |
948 | 12 | ); |
949 | 12 | self.0.chaining_key = chaining_key_update; |
950 | 12 | self.0.cipher_state.key = key_update; |
951 | 12 | self.0.cipher_state.nonce = 0; |
952 | 12 | |
953 | 12 | // Process `s`. |
954 | 12 | self.0.remote_static_public_key = x25519_dalek::PublicKey::from( |
955 | 12 | self.0 |
956 | 12 | .cipher_state |
957 | 12 | .read_chachapoly_message_to_array( |
958 | 12 | &*self.0.hash, |
959 | 12 | remote_static_public_key_encrypted, |
960 | 12 | ) |
961 | 12 | .map_err(HandshakeError::Cipher)?0 , |
962 | | ); |
963 | 12 | mix_hash(&mut self.0.hash, remote_static_public_key_encrypted); |
964 | 12 | |
965 | 12 | // Process `es`. Call MixKey(DH(e, rs)). |
966 | 12 | let HkdfOutput { |
967 | 12 | output1: chaining_key_update, |
968 | 12 | output2: key_update, |
969 | 12 | } = hkdf( |
970 | 12 | &self.0.chaining_key, |
971 | 12 | self.0 |
972 | 12 | .local_ephemeral_private_key |
973 | 12 | .diffie_hellman(&self.0.remote_static_public_key) |
974 | 12 | .as_bytes(), |
975 | 12 | ); |
976 | 12 | self.0.chaining_key = chaining_key_update; |
977 | 12 | self.0.cipher_state.key = key_update; |
978 | 12 | self.0.cipher_state.nonce = 0; |
979 | | |
980 | | // Process the libp2p-specific handshake. |
981 | | self.0.remote_public_key = Some({ |
982 | 12 | let libp2p_handshake_decrypted = self |
983 | 12 | .0 |
984 | 12 | .cipher_state |
985 | 12 | .read_chachapoly_message_to_vec( |
986 | 12 | &*self.0.hash, |
987 | 12 | libp2p_handshake_encrypted, |
988 | 12 | ) |
989 | 12 | .map_err(HandshakeError::Cipher)?0 ; |
990 | 12 | let (libp2p_key, libp2p_signature) = { |
991 | 12 | let mut parser = |
992 | 12 | nom::combinator::all_consuming::< |
993 | 12 | _, |
994 | 12 | _, |
995 | 12 | (&[u8], nom::error::ErrorKind), |
996 | 12 | _, |
997 | 12 | >(protobuf::message_decode! { |
998 | 0 | #[required] key = 1 => protobuf::bytes_tag_decode, |
999 | 0 | #[required] sig = 2 => protobuf::bytes_tag_decode, |
1000 | 12 | }); Unexecuted instantiation: _RNCNCINvMs6_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtBa_19HandshakeInProgress10read_writeNtNtCsaYZPK01V26L_4core4time8DurationEs5_00Bg_ Unexecuted instantiation: _RNCNCINvMs6_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtBa_19HandshakeInProgress10read_writeNtNtCsaYZPK01V26L_4core4time8DurationEs5_0s_0Bg_ Unexecuted instantiation: _RNCNCINvMs6_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtBa_19HandshakeInProgress10read_writelEs5_00Bg_ Unexecuted instantiation: _RNCNCINvMs6_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtBa_19HandshakeInProgress10read_writelEs5_0s_0Bg_ Unexecuted instantiation: _RNCNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtBa_19HandshakeInProgress10read_writeNtNtCsaYZPK01V26L_4core4time8DurationEs5_00CsDDUKWWCHAU_18smoldot_light_wasm Unexecuted instantiation: _RNCNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtBa_19HandshakeInProgress10read_writeNtNtCsaYZPK01V26L_4core4time8DurationEs5_0s_0CsDDUKWWCHAU_18smoldot_light_wasm Unexecuted instantiation: _RNCNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtBa_19HandshakeInProgress10read_writepEs5_00Bg_ Unexecuted instantiation: _RNCNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtBa_19HandshakeInProgress10read_writepEs5_0s_0Bg_ Unexecuted instantiation: _RNCNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtBa_19HandshakeInProgress10read_writeNtNtCsbpXXxgr6u8g_3std4time7InstantEs5_00CsiUjFBJteJ7x_17smoldot_full_node Unexecuted instantiation: _RNCNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtBa_19HandshakeInProgress10read_writeNtNtCsbpXXxgr6u8g_3std4time7InstantEs5_0s_0CsiUjFBJteJ7x_17smoldot_full_node |
1001 | 12 | match parser(&libp2p_handshake_decrypted) { |
1002 | 12 | Ok((_, out)) => (out.key, out.sig), |
1003 | | Err(_) => { |
1004 | 0 | return Err(HandshakeError::PayloadDecode(PayloadDecodeError)) |
1005 | | } |
1006 | | } |
1007 | | }; |
1008 | 12 | let remote_public_key = PublicKey::from_protobuf_encoding(libp2p_key) |
1009 | 12 | .map_err(|_| HandshakeError::InvalidKey0 )?0 ; Unexecuted instantiation: _RNCINvMs6_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writeNtNtCsaYZPK01V26L_4core4time8DurationEs2_0Be_ Unexecuted instantiation: _RNCINvMs6_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writelEs2_0Be_ Unexecuted instantiation: _RNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writeNtNtCsaYZPK01V26L_4core4time8DurationEs2_0CsDDUKWWCHAU_18smoldot_light_wasm Unexecuted instantiation: _RNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writepEs2_0Be_ Unexecuted instantiation: _RNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writeNtNtCsbpXXxgr6u8g_3std4time7InstantEs2_0CsiUjFBJteJ7x_17smoldot_full_node |
1010 | 12 | remote_public_key |
1011 | 12 | .verify( |
1012 | 12 | &[ |
1013 | 12 | &b"noise-libp2p-static-key:"[..], |
1014 | 12 | &self.0.remote_static_public_key.as_bytes()[..], |
1015 | 12 | ] |
1016 | 12 | .concat(), |
1017 | 12 | libp2p_signature, |
1018 | 12 | ) |
1019 | 12 | .map_err(HandshakeError::SignatureVerificationFailed)?0 ; |
1020 | 12 | remote_public_key |
1021 | 12 | }); |
1022 | 12 | mix_hash(&mut self.0.hash, libp2p_handshake_encrypted); |
1023 | 12 | |
1024 | 12 | // Message has been fully processed. |
1025 | 12 | self.0.num_buffered_or_transmitted_messages += 1; |
1026 | 12 | continue; |
1027 | | } |
1028 | | (2, false) => { |
1029 | | // Receive `s, se` and the libp2p-specific handshake from the remote. |
1030 | 12 | let (remote_static_public_key_encrypted, libp2p_handshake_encrypted) = { |
1031 | | // The noise and libp2p-noise specifications clearly define a noise |
1032 | | // handshake message and a noise transport message as two different things. |
1033 | | // While the remote could in theory send post-handshake |
1034 | | // application-specific data in this message, in practice it is forbidden. |
1035 | 12 | let mut parser = nom::combinator::all_consuming::< |
1036 | 12 | _, |
1037 | 12 | _, |
1038 | 12 | (&[u8], nom::error::ErrorKind), |
1039 | 12 | _, |
1040 | 12 | >(nom::sequence::tuple(( |
1041 | 12 | nom::combinator::map(nom::bytes::streaming::take(48u32), |k| { |
1042 | 12 | <&[u8; 48]>::try_from(k).unwrap() |
1043 | 12 | }), _RNCINvMs6_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writeNtNtCsaYZPK01V26L_4core4time8DurationEs3_0Be_ Line | Count | Source | 1041 | 7 | nom::combinator::map(nom::bytes::streaming::take(48u32), |k| { | 1042 | 7 | <&[u8; 48]>::try_from(k).unwrap() | 1043 | 7 | }), |
_RNCINvMs6_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writelEs3_0Be_ Line | Count | Source | 1041 | 5 | nom::combinator::map(nom::bytes::streaming::take(48u32), |k| { | 1042 | 5 | <&[u8; 48]>::try_from(k).unwrap() | 1043 | 5 | }), |
Unexecuted instantiation: _RNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writeNtNtCsaYZPK01V26L_4core4time8DurationEs3_0CsDDUKWWCHAU_18smoldot_light_wasm Unexecuted instantiation: _RNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writepEs3_0Be_ Unexecuted instantiation: _RNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writeNtNtCsbpXXxgr6u8g_3std4time7InstantEs3_0CsiUjFBJteJ7x_17smoldot_full_node |
1044 | 12 | nom::combinator::rest, |
1045 | 12 | ))); |
1046 | 12 | match parser(&available_message) { |
1047 | 12 | Ok((_, out)) => out, |
1048 | | Err(_) => { |
1049 | 0 | return Err(HandshakeError::PayloadDecode(PayloadDecodeError)) |
1050 | | } |
1051 | | } |
1052 | | }; |
1053 | | |
1054 | | // Process `s`. |
1055 | | self.0.remote_static_public_key = x25519_dalek::PublicKey::from( |
1056 | 12 | self.0 |
1057 | 12 | .cipher_state |
1058 | 12 | .read_chachapoly_message_to_array( |
1059 | 12 | &*self.0.hash, |
1060 | 12 | remote_static_public_key_encrypted, |
1061 | 12 | ) |
1062 | 12 | .map_err(HandshakeError::Cipher)?0 , |
1063 | | ); |
1064 | 12 | mix_hash(&mut self.0.hash, remote_static_public_key_encrypted); |
1065 | 12 | |
1066 | 12 | // Process `se`. Call MixKey(DH(e, rs)). |
1067 | 12 | let HkdfOutput { |
1068 | 12 | output1: chaining_key_update, |
1069 | 12 | output2: key_update, |
1070 | 12 | } = hkdf( |
1071 | 12 | &self.0.chaining_key, |
1072 | 12 | self.0 |
1073 | 12 | .local_ephemeral_private_key |
1074 | 12 | .clone() |
1075 | 12 | .diffie_hellman(&self.0.remote_static_public_key) |
1076 | 12 | .as_bytes(), |
1077 | 12 | ); |
1078 | 12 | self.0.chaining_key = chaining_key_update; |
1079 | 12 | self.0.cipher_state.key = key_update; |
1080 | 12 | self.0.cipher_state.nonce = 0; |
1081 | | |
1082 | | // Process the libp2p-specific handshake. |
1083 | | self.0.remote_public_key = Some({ |
1084 | 12 | let libp2p_handshake_decrypted = self |
1085 | 12 | .0 |
1086 | 12 | .cipher_state |
1087 | 12 | .read_chachapoly_message_to_vec( |
1088 | 12 | &*self.0.hash, |
1089 | 12 | libp2p_handshake_encrypted, |
1090 | 12 | ) |
1091 | 12 | .map_err(HandshakeError::Cipher)?0 ; |
1092 | 12 | let (libp2p_key, libp2p_signature) = { |
1093 | 12 | let mut parser = |
1094 | 12 | nom::combinator::all_consuming::< |
1095 | 12 | _, |
1096 | 12 | _, |
1097 | 12 | (&[u8], nom::error::ErrorKind), |
1098 | 12 | _, |
1099 | 12 | >(protobuf::message_decode! { |
1100 | 0 | #[required] key = 1 => protobuf::bytes_tag_decode, |
1101 | 0 | #[required] sig = 2 => protobuf::bytes_tag_decode, |
1102 | 12 | }); Unexecuted instantiation: _RNCNCINvMs6_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtBa_19HandshakeInProgress10read_writeNtNtCsaYZPK01V26L_4core4time8DurationEs6_00Bg_ Unexecuted instantiation: _RNCNCINvMs6_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtBa_19HandshakeInProgress10read_writeNtNtCsaYZPK01V26L_4core4time8DurationEs6_0s_0Bg_ Unexecuted instantiation: _RNCNCINvMs6_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtBa_19HandshakeInProgress10read_writelEs6_00Bg_ Unexecuted instantiation: _RNCNCINvMs6_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtBa_19HandshakeInProgress10read_writelEs6_0s_0Bg_ Unexecuted instantiation: _RNCNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtBa_19HandshakeInProgress10read_writeNtNtCsaYZPK01V26L_4core4time8DurationEs6_00CsDDUKWWCHAU_18smoldot_light_wasm Unexecuted instantiation: _RNCNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtBa_19HandshakeInProgress10read_writeNtNtCsaYZPK01V26L_4core4time8DurationEs6_0s_0CsDDUKWWCHAU_18smoldot_light_wasm Unexecuted instantiation: _RNCNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtBa_19HandshakeInProgress10read_writepEs6_00Bg_ Unexecuted instantiation: _RNCNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtBa_19HandshakeInProgress10read_writepEs6_0s_0Bg_ Unexecuted instantiation: _RNCNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtBa_19HandshakeInProgress10read_writeNtNtCsbpXXxgr6u8g_3std4time7InstantEs6_00CsiUjFBJteJ7x_17smoldot_full_node Unexecuted instantiation: _RNCNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtBa_19HandshakeInProgress10read_writeNtNtCsbpXXxgr6u8g_3std4time7InstantEs6_0s_0CsiUjFBJteJ7x_17smoldot_full_node |
1103 | 12 | match parser(&libp2p_handshake_decrypted) { |
1104 | 12 | Ok((_, out)) => (out.key, out.sig), |
1105 | | Err(_) => { |
1106 | 0 | return Err(HandshakeError::PayloadDecode(PayloadDecodeError)) |
1107 | | } |
1108 | | } |
1109 | | }; |
1110 | 12 | let remote_public_key = PublicKey::from_protobuf_encoding(libp2p_key) |
1111 | 12 | .map_err(|_| HandshakeError::InvalidKey0 )?0 ; Unexecuted instantiation: _RNCINvMs6_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writeNtNtCsaYZPK01V26L_4core4time8DurationEs4_0Be_ Unexecuted instantiation: _RNCINvMs6_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writelEs4_0Be_ Unexecuted instantiation: _RNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writeNtNtCsaYZPK01V26L_4core4time8DurationEs4_0CsDDUKWWCHAU_18smoldot_light_wasm Unexecuted instantiation: _RNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writepEs4_0Be_ Unexecuted instantiation: _RNCINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_19HandshakeInProgress10read_writeNtNtCsbpXXxgr6u8g_3std4time7InstantEs4_0CsiUjFBJteJ7x_17smoldot_full_node |
1112 | 12 | remote_public_key |
1113 | 12 | .verify( |
1114 | 12 | &[ |
1115 | 12 | &b"noise-libp2p-static-key:"[..], |
1116 | 12 | &self.0.remote_static_public_key.as_bytes()[..], |
1117 | 12 | ] |
1118 | 12 | .concat(), |
1119 | 12 | libp2p_signature, |
1120 | 12 | ) |
1121 | 12 | .map_err(HandshakeError::SignatureVerificationFailed)?0 ; |
1122 | 12 | remote_public_key |
1123 | 12 | }); |
1124 | 12 | mix_hash(&mut self.0.hash, libp2p_handshake_encrypted); |
1125 | 12 | |
1126 | 12 | // Message has been fully processed. |
1127 | 12 | self.0.num_buffered_or_transmitted_messages += 1; |
1128 | 12 | continue; |
1129 | | } |
1130 | | _ => { |
1131 | | // Any other state was handled earlier in the function. |
1132 | 0 | unreachable!() |
1133 | | } |
1134 | | } |
1135 | | } |
1136 | 72 | } _RINvMs6_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB6_19HandshakeInProgress10read_writeNtNtCsaYZPK01V26L_4core4time8DurationEBc_ Line | Count | Source | 592 | 35 | pub fn read_write<TNow>( | 593 | 35 | mut self, | 594 | 35 | read_write: &mut ReadWrite<TNow>, | 595 | 35 | ) -> Result<NoiseHandshake, HandshakeError> { | 596 | 77 | loop { | 597 | 77 | // Write out the data currently buffered waiting to be written out. | 598 | 77 | // If we didn't finish writing our payload, don't do anything more and return now. | 599 | 77 | // Don't even read the data from the remote. | 600 | 77 | read_write.write_from_vec_deque(&mut self.0.pending_out_data); | 601 | 77 | if !self.0.pending_out_data.is_empty() { | 602 | 0 | if read_write.write_bytes_queueable.is_none() { | 603 | 0 | return Err(HandshakeError::WriteClosed); | 604 | 0 | } | 605 | 0 | return Ok(NoiseHandshake::InProgress(self)); | 606 | 77 | } | 607 | 77 | | 608 | 77 | // If the handshake has finished, we return successfully here. | 609 | 77 | if self.0.num_buffered_or_transmitted_messages == 3 { | 610 | 14 | debug_assert!(self.0.pending_out_data.is_empty()); | 611 | 14 | debug_assert!(self.0.next_in_message_size.is_none()); | 612 | | | 613 | | // Perform the `Split()`. | 614 | | let HkdfOutput { | 615 | 14 | output1: init_to_resp, | 616 | 14 | output2: resp_to_init, | 617 | 14 | } = hkdf(&self.0.chaining_key, &[]); | 618 | 14 | let (out_key, in_key) = match self.0.is_initiator { | 619 | 7 | true => (init_to_resp, resp_to_init), | 620 | 7 | false => (resp_to_init, init_to_resp), | 621 | | }; | 622 | 14 | return Ok(NoiseHandshake::Success { | 623 | 14 | cipher: Noise { | 624 | 14 | is_initiator: self.0.is_initiator, | 625 | 14 | out_cipher_state: CipherState { | 626 | 14 | key: out_key, | 627 | 14 | nonce: 0, | 628 | 14 | nonce_has_overflowed: false, | 629 | 14 | }, | 630 | 14 | in_cipher_state: CipherState { | 631 | 14 | key: in_key, | 632 | 14 | nonce: 0, | 633 | 14 | nonce_has_overflowed: false, | 634 | 14 | }, | 635 | 14 | rx_buffer_decrypted: Vec::with_capacity(65535 - 16), | 636 | 14 | next_in_message_size: None, | 637 | 14 | inner_stream_expected_incoming_bytes: 0, | 638 | 14 | }, | 639 | 14 | remote_peer_id: { | 640 | 14 | // The logic of this module guarantees that `remote_peer_id` has | 641 | 14 | // been set during the handshake. | 642 | 14 | self.0 | 643 | 14 | .remote_public_key | 644 | 14 | .take() | 645 | 14 | .unwrap_or_else(|| unreachable!()) | 646 | 14 | .into_peer_id() | 647 | 14 | }, | 648 | 14 | }); | 649 | 63 | } | 650 | 63 | | 651 | 63 | // If the handshake is in a phase where we need to send out more data, queue said | 652 | 63 | // data to `pending_out_data` and continue. | 653 | 63 | match ( | 654 | 63 | self.0.num_buffered_or_transmitted_messages, | 655 | 63 | self.0.is_initiator, | 656 | 63 | ) { | 657 | | (0, true) => { | 658 | | // Send `e`, the ephemeral local public key. | 659 | | | 660 | | // Process `e`. | 661 | 7 | let local_ephemeral_public_key = | 662 | 7 | x25519_dalek::PublicKey::from(&*self.0.local_ephemeral_private_key); | 663 | 7 | self.0 | 664 | 7 | .pending_out_data | 665 | 7 | .extend(local_ephemeral_public_key.as_bytes()); | 666 | 7 | mix_hash(&mut self.0.hash, local_ephemeral_public_key.as_bytes()); | 667 | 7 | | 668 | 7 | // Call MixHash(&[]) to process the empty payload. | 669 | 7 | mix_hash(&mut self.0.hash, &[]); | 670 | 7 | | 671 | 7 | // Add the libp2p message length. | 672 | 7 | let len = u16::try_from(self.0.pending_out_data.len()) | 673 | 7 | .unwrap() | 674 | 7 | .to_be_bytes(); | 675 | 7 | self.0.pending_out_data.push_front(len[1]); | 676 | 7 | self.0.pending_out_data.push_front(len[0]); | 677 | 7 | | 678 | 7 | // Message is now fully queued. | 679 | 7 | self.0.num_buffered_or_transmitted_messages += 1; | 680 | 7 | continue; | 681 | | } | 682 | | (1, false) => { | 683 | | // Send `e, ee, s, es` and the libp2p-specific handshake. | 684 | | | 685 | | // Process `e`. | 686 | 7 | let local_ephemeral_public_key = | 687 | 7 | x25519_dalek::PublicKey::from(&*self.0.local_ephemeral_private_key); | 688 | 7 | self.0 | 689 | 7 | .pending_out_data | 690 | 7 | .extend(local_ephemeral_public_key.as_bytes()); | 691 | 7 | mix_hash(&mut self.0.hash, local_ephemeral_public_key.as_bytes()); | 692 | 7 | | 693 | 7 | // Process `ee`. Call MixKey(DH(e, re)). | 694 | 7 | let HkdfOutput { | 695 | 7 | output1: chaining_key_update, | 696 | 7 | output2: key_update, | 697 | 7 | } = hkdf( | 698 | 7 | &self.0.chaining_key, | 699 | 7 | self.0 | 700 | 7 | .local_ephemeral_private_key | 701 | 7 | .diffie_hellman(&self.0.remote_ephemeral_public_key) | 702 | 7 | .as_bytes(), | 703 | 7 | ); | 704 | 7 | self.0.chaining_key = chaining_key_update; | 705 | 7 | self.0.cipher_state.key = key_update; | 706 | 7 | self.0.cipher_state.nonce = 0; | 707 | 7 | | 708 | 7 | // Process `s`. Append EncryptAndHash(s.public_key) to the buffer. | 709 | 7 | let encrypted_static_public_key = self | 710 | 7 | .0 | 711 | 7 | .cipher_state | 712 | 7 | .write_chachapoly_message_to_vec( | 713 | 7 | &*self.0.hash, | 714 | 7 | self.0.local_static_public_key.as_bytes(), | 715 | 7 | ) | 716 | 7 | .unwrap(); | 717 | 7 | self.0 | 718 | 7 | .pending_out_data | 719 | 7 | .extend(encrypted_static_public_key.iter().copied()); | 720 | 7 | mix_hash(&mut self.0.hash, &encrypted_static_public_key); | 721 | 7 | | 722 | 7 | // Process `es`. Call MixKey(DH(s, re)). | 723 | 7 | let HkdfOutput { | 724 | 7 | output1: chaining_key_update, | 725 | 7 | output2: key_update, | 726 | 7 | } = hkdf( | 727 | 7 | &self.0.chaining_key, | 728 | 7 | self.0 | 729 | 7 | .local_static_private_key | 730 | 7 | .diffie_hellman(&self.0.remote_ephemeral_public_key) | 731 | 7 | .as_bytes(), | 732 | 7 | ); | 733 | 7 | self.0.chaining_key = chaining_key_update; | 734 | 7 | self.0.cipher_state.key = key_update; | 735 | 7 | self.0.cipher_state.nonce = 0; | 736 | 7 | | 737 | 7 | // Add the libp2p handshake message. | 738 | 7 | let encrypted_libp2p_handshake = self | 739 | 7 | .0 | 740 | 7 | .cipher_state | 741 | 7 | .write_chachapoly_message_to_vec( | 742 | 7 | &*self.0.hash, | 743 | 7 | &self.0.libp2p_handshake_message, | 744 | 7 | ) | 745 | 7 | .unwrap(); | 746 | 7 | self.0 | 747 | 7 | .pending_out_data | 748 | 7 | .extend(encrypted_libp2p_handshake.iter().copied()); | 749 | 7 | mix_hash(&mut self.0.hash, &encrypted_libp2p_handshake); | 750 | 7 | | 751 | 7 | // Add the libp2p message length. | 752 | 7 | let len = u16::try_from(self.0.pending_out_data.len()) | 753 | 7 | .unwrap() | 754 | 7 | .to_be_bytes(); | 755 | 7 | self.0.pending_out_data.push_front(len[1]); | 756 | 7 | self.0.pending_out_data.push_front(len[0]); | 757 | 7 | | 758 | 7 | // Message is now fully queued. | 759 | 7 | self.0.num_buffered_or_transmitted_messages += 1; | 760 | 7 | continue; | 761 | | } | 762 | | (2, true) => { | 763 | | // Send `s, se` and the libp2p-specific handshake. | 764 | | | 765 | | // Process `s`. Append EncryptAndHash(s.public_key) to the buffer. | 766 | 7 | let encrypted_static_public_key = self | 767 | 7 | .0 | 768 | 7 | .cipher_state | 769 | 7 | .write_chachapoly_message_to_vec( | 770 | 7 | &*self.0.hash, | 771 | 7 | self.0.local_static_public_key.as_bytes(), | 772 | 7 | ) | 773 | 7 | .unwrap(); | 774 | 7 | self.0 | 775 | 7 | .pending_out_data | 776 | 7 | .extend(encrypted_static_public_key.iter().copied()); | 777 | 7 | mix_hash(&mut self.0.hash, &encrypted_static_public_key); | 778 | 7 | | 779 | 7 | // Process `se`. Call MixKey(DH(s, re)). | 780 | 7 | let HkdfOutput { | 781 | 7 | output1: chaining_key_update, | 782 | 7 | output2: key_update, | 783 | 7 | } = hkdf( | 784 | 7 | &self.0.chaining_key, | 785 | 7 | self.0 | 786 | 7 | .local_static_private_key | 787 | 7 | .diffie_hellman(&self.0.remote_ephemeral_public_key) | 788 | 7 | .as_bytes(), | 789 | 7 | ); | 790 | 7 | self.0.chaining_key = chaining_key_update; | 791 | 7 | self.0.cipher_state.key = key_update; | 792 | 7 | self.0.cipher_state.nonce = 0; | 793 | 7 | | 794 | 7 | // Add the libp2p handshake message. | 795 | 7 | let encrypted_libp2p_handshake = self | 796 | 7 | .0 | 797 | 7 | .cipher_state | 798 | 7 | .write_chachapoly_message_to_vec( | 799 | 7 | &*self.0.hash, | 800 | 7 | &self.0.libp2p_handshake_message, | 801 | 7 | ) | 802 | 7 | .unwrap(); | 803 | 7 | self.0 | 804 | 7 | .pending_out_data | 805 | 7 | .extend(encrypted_libp2p_handshake.iter().copied()); | 806 | 7 | mix_hash(&mut self.0.hash, &encrypted_libp2p_handshake); | 807 | 7 | | 808 | 7 | // Add the libp2p message length. | 809 | 7 | let len = u16::try_from(self.0.pending_out_data.len()) | 810 | 7 | .unwrap() | 811 | 7 | .to_be_bytes(); | 812 | 7 | self.0.pending_out_data.push_front(len[1]); | 813 | 7 | self.0.pending_out_data.push_front(len[0]); | 814 | 7 | | 815 | 7 | // Message is now fully queued. | 816 | 7 | self.0.num_buffered_or_transmitted_messages += 1; | 817 | 7 | continue; | 818 | | } | 819 | 42 | _ => {} | 820 | | } | 821 | | | 822 | | // Since we have no more data to write out, and that the handshake isn't finished yet, | 823 | | // the next step is necessarily receiving a message sent by the remote. | 824 | | | 825 | | // Grab the size of the next message, either from `self` or by extracting 2 bytes from | 826 | | // the incoming buffer. | 827 | 21 | let next_in_message_size = | 828 | 42 | if let Some(next_in_message_size0 ) = self.0.next_in_message_size { | 829 | 0 | next_in_message_size | 830 | | } else { | 831 | 42 | match read_write.incoming_bytes_take(2) { | 832 | 21 | Ok(Some(size_buffer)) => *self.0.next_in_message_size.insert( | 833 | 21 | u16::from_be_bytes(<[u8; 2]>::try_from(&size_buffer[..2]).unwrap()), | 834 | 21 | ), | 835 | | Ok(None) => { | 836 | | // Not enough data in incoming buffer. | 837 | 21 | return Ok(NoiseHandshake::InProgress(self)); | 838 | | } | 839 | | Err(read_write::IncomingBytesTakeError::ReadClosed) => { | 840 | 0 | return Err(HandshakeError::ReadClosed) | 841 | | } | 842 | | } | 843 | | }; | 844 | | | 845 | | // Extract the message from the incoming buffer. | 846 | 21 | let available_message = | 847 | 21 | match read_write.incoming_bytes_take(usize::from(next_in_message_size)) { | 848 | 21 | Ok(Some(available_message)) => { | 849 | 21 | self.0.next_in_message_size = None; | 850 | 21 | available_message | 851 | | } | 852 | | Ok(None) => { | 853 | | // Not enough data in incoming buffer. | 854 | 0 | return Ok(NoiseHandshake::InProgress(self)); | 855 | | } | 856 | | Err(read_write::IncomingBytesTakeError::ReadClosed) => { | 857 | 0 | return Err(HandshakeError::ReadClosed) | 858 | | } | 859 | | }; | 860 | | | 861 | | // How to parse the message depends on the current handshake phase. | 862 | 21 | match ( | 863 | 21 | self.0.num_buffered_or_transmitted_messages, | 864 | 21 | self.0.is_initiator, | 865 | 21 | ) { | 866 | | (0, false) => { | 867 | | // Receive `e` message from the remote. | 868 | | self.0.remote_ephemeral_public_key = x25519_dalek::PublicKey::from(*{ | 869 | | // Because the remote hasn't authenticated us at this point, sending more | 870 | | // data than what the protocol specifies is forbidden. | 871 | 7 | let mut parser = nom::combinator::all_consuming::< | 872 | 7 | _, | 873 | 7 | _, | 874 | 7 | (&[u8], nom::error::ErrorKind), | 875 | 7 | _, | 876 | 7 | >(nom::combinator::map( | 877 | 7 | nom::bytes::streaming::take(32u32), | 878 | 7 | |k| <&[u8; 32]>::try_from(k).unwrap(), | 879 | 7 | )); | 880 | 7 | match parser(&available_message) { | 881 | 7 | Ok((_, out)) => out, | 882 | | Err(_) => { | 883 | 0 | return Err(HandshakeError::PayloadDecode(PayloadDecodeError)) | 884 | | } | 885 | | } | 886 | | }); | 887 | 7 | mix_hash( | 888 | 7 | &mut self.0.hash, | 889 | 7 | self.0.remote_ephemeral_public_key.as_bytes(), | 890 | 7 | ); | 891 | 7 | | 892 | 7 | // Call MixHash(&[]) to process the empty payload. | 893 | 7 | mix_hash(&mut self.0.hash, &[]); | 894 | 7 | | 895 | 7 | // Message has been fully processed. | 896 | 7 | self.0.num_buffered_or_transmitted_messages += 1; | 897 | 7 | continue; | 898 | | } | 899 | | (1, true) => { | 900 | | // Receive `e, ee, s, es` and the libp2p-specific handshake from the remote. | 901 | | let ( | 902 | 7 | remote_ephemeral_public_key, | 903 | 7 | remote_static_public_key_encrypted, | 904 | 7 | libp2p_handshake_encrypted, | 905 | | ) = { | 906 | | // Because the remote hasn't fully authenticated us at this point, sending | 907 | | // more data than what the protocol specifies is forbidden. | 908 | 7 | let mut parser = nom::combinator::all_consuming::< | 909 | 7 | _, | 910 | 7 | _, | 911 | 7 | (&[u8], nom::error::ErrorKind), | 912 | 7 | _, | 913 | 7 | >(nom::sequence::tuple(( | 914 | 7 | nom::combinator::map(nom::bytes::streaming::take(32u32), |k| { | 915 | | <&[u8; 32]>::try_from(k).unwrap() | 916 | 7 | }), | 917 | 7 | nom::combinator::map(nom::bytes::streaming::take(48u32), |k| { | 918 | | <&[u8; 48]>::try_from(k).unwrap() | 919 | 7 | }), | 920 | 7 | nom::combinator::rest, | 921 | 7 | ))); | 922 | 7 | match parser(&available_message) { | 923 | 7 | Ok((_, out)) => out, | 924 | | Err(_) => { | 925 | 0 | return Err(HandshakeError::PayloadDecode(PayloadDecodeError)) | 926 | | } | 927 | | } | 928 | | }; | 929 | | | 930 | | // Process `e`. | 931 | 7 | self.0.remote_ephemeral_public_key = | 932 | 7 | x25519_dalek::PublicKey::from(*remote_ephemeral_public_key); | 933 | 7 | mix_hash( | 934 | 7 | &mut self.0.hash, | 935 | 7 | self.0.remote_ephemeral_public_key.as_bytes(), | 936 | 7 | ); | 937 | 7 | | 938 | 7 | // Process `ee`. Call MixKey(DH(e, re)). | 939 | 7 | let HkdfOutput { | 940 | 7 | output1: chaining_key_update, | 941 | 7 | output2: key_update, | 942 | 7 | } = hkdf( | 943 | 7 | &self.0.chaining_key, | 944 | 7 | self.0 | 945 | 7 | .local_ephemeral_private_key | 946 | 7 | .diffie_hellman(&self.0.remote_ephemeral_public_key) | 947 | 7 | .as_bytes(), | 948 | 7 | ); | 949 | 7 | self.0.chaining_key = chaining_key_update; | 950 | 7 | self.0.cipher_state.key = key_update; | 951 | 7 | self.0.cipher_state.nonce = 0; | 952 | 7 | | 953 | 7 | // Process `s`. | 954 | 7 | self.0.remote_static_public_key = x25519_dalek::PublicKey::from( | 955 | 7 | self.0 | 956 | 7 | .cipher_state | 957 | 7 | .read_chachapoly_message_to_array( | 958 | 7 | &*self.0.hash, | 959 | 7 | remote_static_public_key_encrypted, | 960 | 7 | ) | 961 | 7 | .map_err(HandshakeError::Cipher)?0 , | 962 | | ); | 963 | 7 | mix_hash(&mut self.0.hash, remote_static_public_key_encrypted); | 964 | 7 | | 965 | 7 | // Process `es`. Call MixKey(DH(e, rs)). | 966 | 7 | let HkdfOutput { | 967 | 7 | output1: chaining_key_update, | 968 | 7 | output2: key_update, | 969 | 7 | } = hkdf( | 970 | 7 | &self.0.chaining_key, | 971 | 7 | self.0 | 972 | 7 | .local_ephemeral_private_key | 973 | 7 | .diffie_hellman(&self.0.remote_static_public_key) | 974 | 7 | .as_bytes(), | 975 | 7 | ); | 976 | 7 | self.0.chaining_key = chaining_key_update; | 977 | 7 | self.0.cipher_state.key = key_update; | 978 | 7 | self.0.cipher_state.nonce = 0; | 979 | | | 980 | | // Process the libp2p-specific handshake. | 981 | | self.0.remote_public_key = Some({ | 982 | 7 | let libp2p_handshake_decrypted = self | 983 | 7 | .0 | 984 | 7 | .cipher_state | 985 | 7 | .read_chachapoly_message_to_vec( | 986 | 7 | &*self.0.hash, | 987 | 7 | libp2p_handshake_encrypted, | 988 | 7 | ) | 989 | 7 | .map_err(HandshakeError::Cipher)?0 ; | 990 | 7 | let (libp2p_key, libp2p_signature) = { | 991 | 7 | let mut parser = | 992 | 7 | nom::combinator::all_consuming::< | 993 | 7 | _, | 994 | 7 | _, | 995 | 7 | (&[u8], nom::error::ErrorKind), | 996 | 7 | _, | 997 | 7 | >(protobuf::message_decode! { | 998 | | #[required] key = 1 => protobuf::bytes_tag_decode, | 999 | | #[required] sig = 2 => protobuf::bytes_tag_decode, | 1000 | 7 | }); | 1001 | 7 | match parser(&libp2p_handshake_decrypted) { | 1002 | 7 | Ok((_, out)) => (out.key, out.sig), | 1003 | | Err(_) => { | 1004 | 0 | return Err(HandshakeError::PayloadDecode(PayloadDecodeError)) | 1005 | | } | 1006 | | } | 1007 | | }; | 1008 | 7 | let remote_public_key = PublicKey::from_protobuf_encoding(libp2p_key) | 1009 | 7 | .map_err(|_| HandshakeError::InvalidKey)?0 ; | 1010 | 7 | remote_public_key | 1011 | 7 | .verify( | 1012 | 7 | &[ | 1013 | 7 | &b"noise-libp2p-static-key:"[..], | 1014 | 7 | &self.0.remote_static_public_key.as_bytes()[..], | 1015 | 7 | ] | 1016 | 7 | .concat(), | 1017 | 7 | libp2p_signature, | 1018 | 7 | ) | 1019 | 7 | .map_err(HandshakeError::SignatureVerificationFailed)?0 ; | 1020 | 7 | remote_public_key | 1021 | 7 | }); | 1022 | 7 | mix_hash(&mut self.0.hash, libp2p_handshake_encrypted); | 1023 | 7 | | 1024 | 7 | // Message has been fully processed. | 1025 | 7 | self.0.num_buffered_or_transmitted_messages += 1; | 1026 | 7 | continue; | 1027 | | } | 1028 | | (2, false) => { | 1029 | | // Receive `s, se` and the libp2p-specific handshake from the remote. | 1030 | 7 | let (remote_static_public_key_encrypted, libp2p_handshake_encrypted) = { | 1031 | | // The noise and libp2p-noise specifications clearly define a noise | 1032 | | // handshake message and a noise transport message as two different things. | 1033 | | // While the remote could in theory send post-handshake | 1034 | | // application-specific data in this message, in practice it is forbidden. | 1035 | 7 | let mut parser = nom::combinator::all_consuming::< | 1036 | 7 | _, | 1037 | 7 | _, | 1038 | 7 | (&[u8], nom::error::ErrorKind), | 1039 | 7 | _, | 1040 | 7 | >(nom::sequence::tuple(( | 1041 | 7 | nom::combinator::map(nom::bytes::streaming::take(48u32), |k| { | 1042 | | <&[u8; 48]>::try_from(k).unwrap() | 1043 | 7 | }), | 1044 | 7 | nom::combinator::rest, | 1045 | 7 | ))); | 1046 | 7 | match parser(&available_message) { | 1047 | 7 | Ok((_, out)) => out, | 1048 | | Err(_) => { | 1049 | 0 | return Err(HandshakeError::PayloadDecode(PayloadDecodeError)) | 1050 | | } | 1051 | | } | 1052 | | }; | 1053 | | | 1054 | | // Process `s`. | 1055 | | self.0.remote_static_public_key = x25519_dalek::PublicKey::from( | 1056 | 7 | self.0 | 1057 | 7 | .cipher_state | 1058 | 7 | .read_chachapoly_message_to_array( | 1059 | 7 | &*self.0.hash, | 1060 | 7 | remote_static_public_key_encrypted, | 1061 | 7 | ) | 1062 | 7 | .map_err(HandshakeError::Cipher)?0 , | 1063 | | ); | 1064 | 7 | mix_hash(&mut self.0.hash, remote_static_public_key_encrypted); | 1065 | 7 | | 1066 | 7 | // Process `se`. Call MixKey(DH(e, rs)). | 1067 | 7 | let HkdfOutput { | 1068 | 7 | output1: chaining_key_update, | 1069 | 7 | output2: key_update, | 1070 | 7 | } = hkdf( | 1071 | 7 | &self.0.chaining_key, | 1072 | 7 | self.0 | 1073 | 7 | .local_ephemeral_private_key | 1074 | 7 | .clone() | 1075 | 7 | .diffie_hellman(&self.0.remote_static_public_key) | 1076 | 7 | .as_bytes(), | 1077 | 7 | ); | 1078 | 7 | self.0.chaining_key = chaining_key_update; | 1079 | 7 | self.0.cipher_state.key = key_update; | 1080 | 7 | self.0.cipher_state.nonce = 0; | 1081 | | | 1082 | | // Process the libp2p-specific handshake. | 1083 | | self.0.remote_public_key = Some({ | 1084 | 7 | let libp2p_handshake_decrypted = self | 1085 | 7 | .0 | 1086 | 7 | .cipher_state | 1087 | 7 | .read_chachapoly_message_to_vec( | 1088 | 7 | &*self.0.hash, | 1089 | 7 | libp2p_handshake_encrypted, | 1090 | 7 | ) | 1091 | 7 | .map_err(HandshakeError::Cipher)?0 ; | 1092 | 7 | let (libp2p_key, libp2p_signature) = { | 1093 | 7 | let mut parser = | 1094 | 7 | nom::combinator::all_consuming::< | 1095 | 7 | _, | 1096 | 7 | _, | 1097 | 7 | (&[u8], nom::error::ErrorKind), | 1098 | 7 | _, | 1099 | 7 | >(protobuf::message_decode! { | 1100 | | #[required] key = 1 => protobuf::bytes_tag_decode, | 1101 | | #[required] sig = 2 => protobuf::bytes_tag_decode, | 1102 | 7 | }); | 1103 | 7 | match parser(&libp2p_handshake_decrypted) { | 1104 | 7 | Ok((_, out)) => (out.key, out.sig), | 1105 | | Err(_) => { | 1106 | 0 | return Err(HandshakeError::PayloadDecode(PayloadDecodeError)) | 1107 | | } | 1108 | | } | 1109 | | }; | 1110 | 7 | let remote_public_key = PublicKey::from_protobuf_encoding(libp2p_key) | 1111 | 7 | .map_err(|_| HandshakeError::InvalidKey)?0 ; | 1112 | 7 | remote_public_key | 1113 | 7 | .verify( | 1114 | 7 | &[ | 1115 | 7 | &b"noise-libp2p-static-key:"[..], | 1116 | 7 | &self.0.remote_static_public_key.as_bytes()[..], | 1117 | 7 | ] | 1118 | 7 | .concat(), | 1119 | 7 | libp2p_signature, | 1120 | 7 | ) | 1121 | 7 | .map_err(HandshakeError::SignatureVerificationFailed)?0 ; | 1122 | 7 | remote_public_key | 1123 | 7 | }); | 1124 | 7 | mix_hash(&mut self.0.hash, libp2p_handshake_encrypted); | 1125 | 7 | | 1126 | 7 | // Message has been fully processed. | 1127 | 7 | self.0.num_buffered_or_transmitted_messages += 1; | 1128 | 7 | continue; | 1129 | | } | 1130 | | _ => { | 1131 | | // Any other state was handled earlier in the function. | 1132 | 0 | unreachable!() | 1133 | | } | 1134 | | } | 1135 | | } | 1136 | 35 | } |
_RINvMs6_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB6_19HandshakeInProgress10read_writelEBc_ Line | Count | Source | 592 | 37 | pub fn read_write<TNow>( | 593 | 37 | mut self, | 594 | 37 | read_write: &mut ReadWrite<TNow>, | 595 | 37 | ) -> Result<NoiseHandshake, HandshakeError> { | 596 | 67 | loop { | 597 | 67 | // Write out the data currently buffered waiting to be written out. | 598 | 67 | // If we didn't finish writing our payload, don't do anything more and return now. | 599 | 67 | // Don't even read the data from the remote. | 600 | 67 | read_write.write_from_vec_deque(&mut self.0.pending_out_data); | 601 | 67 | if !self.0.pending_out_data.is_empty() { | 602 | 8 | if read_write.write_bytes_queueable.is_none() { | 603 | 0 | return Err(HandshakeError::WriteClosed); | 604 | 8 | } | 605 | 8 | return Ok(NoiseHandshake::InProgress(self)); | 606 | 59 | } | 607 | 59 | | 608 | 59 | // If the handshake has finished, we return successfully here. | 609 | 59 | if self.0.num_buffered_or_transmitted_messages == 3 { | 610 | 10 | debug_assert!(self.0.pending_out_data.is_empty()); | 611 | 10 | debug_assert!(self.0.next_in_message_size.is_none()); | 612 | | | 613 | | // Perform the `Split()`. | 614 | | let HkdfOutput { | 615 | 10 | output1: init_to_resp, | 616 | 10 | output2: resp_to_init, | 617 | 10 | } = hkdf(&self.0.chaining_key, &[]); | 618 | 10 | let (out_key, in_key) = match self.0.is_initiator { | 619 | 5 | true => (init_to_resp, resp_to_init), | 620 | 5 | false => (resp_to_init, init_to_resp), | 621 | | }; | 622 | 10 | return Ok(NoiseHandshake::Success { | 623 | 10 | cipher: Noise { | 624 | 10 | is_initiator: self.0.is_initiator, | 625 | 10 | out_cipher_state: CipherState { | 626 | 10 | key: out_key, | 627 | 10 | nonce: 0, | 628 | 10 | nonce_has_overflowed: false, | 629 | 10 | }, | 630 | 10 | in_cipher_state: CipherState { | 631 | 10 | key: in_key, | 632 | 10 | nonce: 0, | 633 | 10 | nonce_has_overflowed: false, | 634 | 10 | }, | 635 | 10 | rx_buffer_decrypted: Vec::with_capacity(65535 - 16), | 636 | 10 | next_in_message_size: None, | 637 | 10 | inner_stream_expected_incoming_bytes: 0, | 638 | 10 | }, | 639 | 10 | remote_peer_id: { | 640 | 10 | // The logic of this module guarantees that `remote_peer_id` has | 641 | 10 | // been set during the handshake. | 642 | 10 | self.0 | 643 | 10 | .remote_public_key | 644 | 10 | .take() | 645 | 10 | .unwrap_or_else(|| unreachable!()) | 646 | 10 | .into_peer_id() | 647 | 10 | }, | 648 | 10 | }); | 649 | 49 | } | 650 | 49 | | 651 | 49 | // If the handshake is in a phase where we need to send out more data, queue said | 652 | 49 | // data to `pending_out_data` and continue. | 653 | 49 | match ( | 654 | 49 | self.0.num_buffered_or_transmitted_messages, | 655 | 49 | self.0.is_initiator, | 656 | 49 | ) { | 657 | | (0, true) => { | 658 | | // Send `e`, the ephemeral local public key. | 659 | | | 660 | | // Process `e`. | 661 | 5 | let local_ephemeral_public_key = | 662 | 5 | x25519_dalek::PublicKey::from(&*self.0.local_ephemeral_private_key); | 663 | 5 | self.0 | 664 | 5 | .pending_out_data | 665 | 5 | .extend(local_ephemeral_public_key.as_bytes()); | 666 | 5 | mix_hash(&mut self.0.hash, local_ephemeral_public_key.as_bytes()); | 667 | 5 | | 668 | 5 | // Call MixHash(&[]) to process the empty payload. | 669 | 5 | mix_hash(&mut self.0.hash, &[]); | 670 | 5 | | 671 | 5 | // Add the libp2p message length. | 672 | 5 | let len = u16::try_from(self.0.pending_out_data.len()) | 673 | 5 | .unwrap() | 674 | 5 | .to_be_bytes(); | 675 | 5 | self.0.pending_out_data.push_front(len[1]); | 676 | 5 | self.0.pending_out_data.push_front(len[0]); | 677 | 5 | | 678 | 5 | // Message is now fully queued. | 679 | 5 | self.0.num_buffered_or_transmitted_messages += 1; | 680 | 5 | continue; | 681 | | } | 682 | | (1, false) => { | 683 | | // Send `e, ee, s, es` and the libp2p-specific handshake. | 684 | | | 685 | | // Process `e`. | 686 | 5 | let local_ephemeral_public_key = | 687 | 5 | x25519_dalek::PublicKey::from(&*self.0.local_ephemeral_private_key); | 688 | 5 | self.0 | 689 | 5 | .pending_out_data | 690 | 5 | .extend(local_ephemeral_public_key.as_bytes()); | 691 | 5 | mix_hash(&mut self.0.hash, local_ephemeral_public_key.as_bytes()); | 692 | 5 | | 693 | 5 | // Process `ee`. Call MixKey(DH(e, re)). | 694 | 5 | let HkdfOutput { | 695 | 5 | output1: chaining_key_update, | 696 | 5 | output2: key_update, | 697 | 5 | } = hkdf( | 698 | 5 | &self.0.chaining_key, | 699 | 5 | self.0 | 700 | 5 | .local_ephemeral_private_key | 701 | 5 | .diffie_hellman(&self.0.remote_ephemeral_public_key) | 702 | 5 | .as_bytes(), | 703 | 5 | ); | 704 | 5 | self.0.chaining_key = chaining_key_update; | 705 | 5 | self.0.cipher_state.key = key_update; | 706 | 5 | self.0.cipher_state.nonce = 0; | 707 | 5 | | 708 | 5 | // Process `s`. Append EncryptAndHash(s.public_key) to the buffer. | 709 | 5 | let encrypted_static_public_key = self | 710 | 5 | .0 | 711 | 5 | .cipher_state | 712 | 5 | .write_chachapoly_message_to_vec( | 713 | 5 | &*self.0.hash, | 714 | 5 | self.0.local_static_public_key.as_bytes(), | 715 | 5 | ) | 716 | 5 | .unwrap(); | 717 | 5 | self.0 | 718 | 5 | .pending_out_data | 719 | 5 | .extend(encrypted_static_public_key.iter().copied()); | 720 | 5 | mix_hash(&mut self.0.hash, &encrypted_static_public_key); | 721 | 5 | | 722 | 5 | // Process `es`. Call MixKey(DH(s, re)). | 723 | 5 | let HkdfOutput { | 724 | 5 | output1: chaining_key_update, | 725 | 5 | output2: key_update, | 726 | 5 | } = hkdf( | 727 | 5 | &self.0.chaining_key, | 728 | 5 | self.0 | 729 | 5 | .local_static_private_key | 730 | 5 | .diffie_hellman(&self.0.remote_ephemeral_public_key) | 731 | 5 | .as_bytes(), | 732 | 5 | ); | 733 | 5 | self.0.chaining_key = chaining_key_update; | 734 | 5 | self.0.cipher_state.key = key_update; | 735 | 5 | self.0.cipher_state.nonce = 0; | 736 | 5 | | 737 | 5 | // Add the libp2p handshake message. | 738 | 5 | let encrypted_libp2p_handshake = self | 739 | 5 | .0 | 740 | 5 | .cipher_state | 741 | 5 | .write_chachapoly_message_to_vec( | 742 | 5 | &*self.0.hash, | 743 | 5 | &self.0.libp2p_handshake_message, | 744 | 5 | ) | 745 | 5 | .unwrap(); | 746 | 5 | self.0 | 747 | 5 | .pending_out_data | 748 | 5 | .extend(encrypted_libp2p_handshake.iter().copied()); | 749 | 5 | mix_hash(&mut self.0.hash, &encrypted_libp2p_handshake); | 750 | 5 | | 751 | 5 | // Add the libp2p message length. | 752 | 5 | let len = u16::try_from(self.0.pending_out_data.len()) | 753 | 5 | .unwrap() | 754 | 5 | .to_be_bytes(); | 755 | 5 | self.0.pending_out_data.push_front(len[1]); | 756 | 5 | self.0.pending_out_data.push_front(len[0]); | 757 | 5 | | 758 | 5 | // Message is now fully queued. | 759 | 5 | self.0.num_buffered_or_transmitted_messages += 1; | 760 | 5 | continue; | 761 | | } | 762 | | (2, true) => { | 763 | | // Send `s, se` and the libp2p-specific handshake. | 764 | | | 765 | | // Process `s`. Append EncryptAndHash(s.public_key) to the buffer. | 766 | 5 | let encrypted_static_public_key = self | 767 | 5 | .0 | 768 | 5 | .cipher_state | 769 | 5 | .write_chachapoly_message_to_vec( | 770 | 5 | &*self.0.hash, | 771 | 5 | self.0.local_static_public_key.as_bytes(), | 772 | 5 | ) | 773 | 5 | .unwrap(); | 774 | 5 | self.0 | 775 | 5 | .pending_out_data | 776 | 5 | .extend(encrypted_static_public_key.iter().copied()); | 777 | 5 | mix_hash(&mut self.0.hash, &encrypted_static_public_key); | 778 | 5 | | 779 | 5 | // Process `se`. Call MixKey(DH(s, re)). | 780 | 5 | let HkdfOutput { | 781 | 5 | output1: chaining_key_update, | 782 | 5 | output2: key_update, | 783 | 5 | } = hkdf( | 784 | 5 | &self.0.chaining_key, | 785 | 5 | self.0 | 786 | 5 | .local_static_private_key | 787 | 5 | .diffie_hellman(&self.0.remote_ephemeral_public_key) | 788 | 5 | .as_bytes(), | 789 | 5 | ); | 790 | 5 | self.0.chaining_key = chaining_key_update; | 791 | 5 | self.0.cipher_state.key = key_update; | 792 | 5 | self.0.cipher_state.nonce = 0; | 793 | 5 | | 794 | 5 | // Add the libp2p handshake message. | 795 | 5 | let encrypted_libp2p_handshake = self | 796 | 5 | .0 | 797 | 5 | .cipher_state | 798 | 5 | .write_chachapoly_message_to_vec( | 799 | 5 | &*self.0.hash, | 800 | 5 | &self.0.libp2p_handshake_message, | 801 | 5 | ) | 802 | 5 | .unwrap(); | 803 | 5 | self.0 | 804 | 5 | .pending_out_data | 805 | 5 | .extend(encrypted_libp2p_handshake.iter().copied()); | 806 | 5 | mix_hash(&mut self.0.hash, &encrypted_libp2p_handshake); | 807 | 5 | | 808 | 5 | // Add the libp2p message length. | 809 | 5 | let len = u16::try_from(self.0.pending_out_data.len()) | 810 | 5 | .unwrap() | 811 | 5 | .to_be_bytes(); | 812 | 5 | self.0.pending_out_data.push_front(len[1]); | 813 | 5 | self.0.pending_out_data.push_front(len[0]); | 814 | 5 | | 815 | 5 | // Message is now fully queued. | 816 | 5 | self.0.num_buffered_or_transmitted_messages += 1; | 817 | 5 | continue; | 818 | | } | 819 | 34 | _ => {} | 820 | | } | 821 | | | 822 | | // Since we have no more data to write out, and that the handshake isn't finished yet, | 823 | | // the next step is necessarily receiving a message sent by the remote. | 824 | | | 825 | | // Grab the size of the next message, either from `self` or by extracting 2 bytes from | 826 | | // the incoming buffer. | 827 | 21 | let next_in_message_size = | 828 | 34 | if let Some(next_in_message_size6 ) = self.0.next_in_message_size { | 829 | 6 | next_in_message_size | 830 | | } else { | 831 | 28 | match read_write.incoming_bytes_take(2) { | 832 | 15 | Ok(Some(size_buffer)) => *self.0.next_in_message_size.insert( | 833 | 15 | u16::from_be_bytes(<[u8; 2]>::try_from(&size_buffer[..2]).unwrap()), | 834 | 15 | ), | 835 | | Ok(None) => { | 836 | | // Not enough data in incoming buffer. | 837 | 13 | return Ok(NoiseHandshake::InProgress(self)); | 838 | | } | 839 | | Err(read_write::IncomingBytesTakeError::ReadClosed) => { | 840 | 0 | return Err(HandshakeError::ReadClosed) | 841 | | } | 842 | | } | 843 | | }; | 844 | | | 845 | | // Extract the message from the incoming buffer. | 846 | 15 | let available_message = | 847 | 21 | match read_write.incoming_bytes_take(usize::from(next_in_message_size)) { | 848 | 15 | Ok(Some(available_message)) => { | 849 | 15 | self.0.next_in_message_size = None; | 850 | 15 | available_message | 851 | | } | 852 | | Ok(None) => { | 853 | | // Not enough data in incoming buffer. | 854 | 6 | return Ok(NoiseHandshake::InProgress(self)); | 855 | | } | 856 | | Err(read_write::IncomingBytesTakeError::ReadClosed) => { | 857 | 0 | return Err(HandshakeError::ReadClosed) | 858 | | } | 859 | | }; | 860 | | | 861 | | // How to parse the message depends on the current handshake phase. | 862 | 15 | match ( | 863 | 15 | self.0.num_buffered_or_transmitted_messages, | 864 | 15 | self.0.is_initiator, | 865 | 15 | ) { | 866 | | (0, false) => { | 867 | | // Receive `e` message from the remote. | 868 | | self.0.remote_ephemeral_public_key = x25519_dalek::PublicKey::from(*{ | 869 | | // Because the remote hasn't authenticated us at this point, sending more | 870 | | // data than what the protocol specifies is forbidden. | 871 | 5 | let mut parser = nom::combinator::all_consuming::< | 872 | 5 | _, | 873 | 5 | _, | 874 | 5 | (&[u8], nom::error::ErrorKind), | 875 | 5 | _, | 876 | 5 | >(nom::combinator::map( | 877 | 5 | nom::bytes::streaming::take(32u32), | 878 | 5 | |k| <&[u8; 32]>::try_from(k).unwrap(), | 879 | 5 | )); | 880 | 5 | match parser(&available_message) { | 881 | 5 | Ok((_, out)) => out, | 882 | | Err(_) => { | 883 | 0 | return Err(HandshakeError::PayloadDecode(PayloadDecodeError)) | 884 | | } | 885 | | } | 886 | | }); | 887 | 5 | mix_hash( | 888 | 5 | &mut self.0.hash, | 889 | 5 | self.0.remote_ephemeral_public_key.as_bytes(), | 890 | 5 | ); | 891 | 5 | | 892 | 5 | // Call MixHash(&[]) to process the empty payload. | 893 | 5 | mix_hash(&mut self.0.hash, &[]); | 894 | 5 | | 895 | 5 | // Message has been fully processed. | 896 | 5 | self.0.num_buffered_or_transmitted_messages += 1; | 897 | 5 | continue; | 898 | | } | 899 | | (1, true) => { | 900 | | // Receive `e, ee, s, es` and the libp2p-specific handshake from the remote. | 901 | | let ( | 902 | 5 | remote_ephemeral_public_key, | 903 | 5 | remote_static_public_key_encrypted, | 904 | 5 | libp2p_handshake_encrypted, | 905 | | ) = { | 906 | | // Because the remote hasn't fully authenticated us at this point, sending | 907 | | // more data than what the protocol specifies is forbidden. | 908 | 5 | let mut parser = nom::combinator::all_consuming::< | 909 | 5 | _, | 910 | 5 | _, | 911 | 5 | (&[u8], nom::error::ErrorKind), | 912 | 5 | _, | 913 | 5 | >(nom::sequence::tuple(( | 914 | 5 | nom::combinator::map(nom::bytes::streaming::take(32u32), |k| { | 915 | | <&[u8; 32]>::try_from(k).unwrap() | 916 | 5 | }), | 917 | 5 | nom::combinator::map(nom::bytes::streaming::take(48u32), |k| { | 918 | | <&[u8; 48]>::try_from(k).unwrap() | 919 | 5 | }), | 920 | 5 | nom::combinator::rest, | 921 | 5 | ))); | 922 | 5 | match parser(&available_message) { | 923 | 5 | Ok((_, out)) => out, | 924 | | Err(_) => { | 925 | 0 | return Err(HandshakeError::PayloadDecode(PayloadDecodeError)) | 926 | | } | 927 | | } | 928 | | }; | 929 | | | 930 | | // Process `e`. | 931 | 5 | self.0.remote_ephemeral_public_key = | 932 | 5 | x25519_dalek::PublicKey::from(*remote_ephemeral_public_key); | 933 | 5 | mix_hash( | 934 | 5 | &mut self.0.hash, | 935 | 5 | self.0.remote_ephemeral_public_key.as_bytes(), | 936 | 5 | ); | 937 | 5 | | 938 | 5 | // Process `ee`. Call MixKey(DH(e, re)). | 939 | 5 | let HkdfOutput { | 940 | 5 | output1: chaining_key_update, | 941 | 5 | output2: key_update, | 942 | 5 | } = hkdf( | 943 | 5 | &self.0.chaining_key, | 944 | 5 | self.0 | 945 | 5 | .local_ephemeral_private_key | 946 | 5 | .diffie_hellman(&self.0.remote_ephemeral_public_key) | 947 | 5 | .as_bytes(), | 948 | 5 | ); | 949 | 5 | self.0.chaining_key = chaining_key_update; | 950 | 5 | self.0.cipher_state.key = key_update; | 951 | 5 | self.0.cipher_state.nonce = 0; | 952 | 5 | | 953 | 5 | // Process `s`. | 954 | 5 | self.0.remote_static_public_key = x25519_dalek::PublicKey::from( | 955 | 5 | self.0 | 956 | 5 | .cipher_state | 957 | 5 | .read_chachapoly_message_to_array( | 958 | 5 | &*self.0.hash, | 959 | 5 | remote_static_public_key_encrypted, | 960 | 5 | ) | 961 | 5 | .map_err(HandshakeError::Cipher)?0 , | 962 | | ); | 963 | 5 | mix_hash(&mut self.0.hash, remote_static_public_key_encrypted); | 964 | 5 | | 965 | 5 | // Process `es`. Call MixKey(DH(e, rs)). | 966 | 5 | let HkdfOutput { | 967 | 5 | output1: chaining_key_update, | 968 | 5 | output2: key_update, | 969 | 5 | } = hkdf( | 970 | 5 | &self.0.chaining_key, | 971 | 5 | self.0 | 972 | 5 | .local_ephemeral_private_key | 973 | 5 | .diffie_hellman(&self.0.remote_static_public_key) | 974 | 5 | .as_bytes(), | 975 | 5 | ); | 976 | 5 | self.0.chaining_key = chaining_key_update; | 977 | 5 | self.0.cipher_state.key = key_update; | 978 | 5 | self.0.cipher_state.nonce = 0; | 979 | | | 980 | | // Process the libp2p-specific handshake. | 981 | | self.0.remote_public_key = Some({ | 982 | 5 | let libp2p_handshake_decrypted = self | 983 | 5 | .0 | 984 | 5 | .cipher_state | 985 | 5 | .read_chachapoly_message_to_vec( | 986 | 5 | &*self.0.hash, | 987 | 5 | libp2p_handshake_encrypted, | 988 | 5 | ) | 989 | 5 | .map_err(HandshakeError::Cipher)?0 ; | 990 | 5 | let (libp2p_key, libp2p_signature) = { | 991 | 5 | let mut parser = | 992 | 5 | nom::combinator::all_consuming::< | 993 | 5 | _, | 994 | 5 | _, | 995 | 5 | (&[u8], nom::error::ErrorKind), | 996 | 5 | _, | 997 | 5 | >(protobuf::message_decode! { | 998 | | #[required] key = 1 => protobuf::bytes_tag_decode, | 999 | | #[required] sig = 2 => protobuf::bytes_tag_decode, | 1000 | 5 | }); | 1001 | 5 | match parser(&libp2p_handshake_decrypted) { | 1002 | 5 | Ok((_, out)) => (out.key, out.sig), | 1003 | | Err(_) => { | 1004 | 0 | return Err(HandshakeError::PayloadDecode(PayloadDecodeError)) | 1005 | | } | 1006 | | } | 1007 | | }; | 1008 | 5 | let remote_public_key = PublicKey::from_protobuf_encoding(libp2p_key) | 1009 | 5 | .map_err(|_| HandshakeError::InvalidKey)?0 ; | 1010 | 5 | remote_public_key | 1011 | 5 | .verify( | 1012 | 5 | &[ | 1013 | 5 | &b"noise-libp2p-static-key:"[..], | 1014 | 5 | &self.0.remote_static_public_key.as_bytes()[..], | 1015 | 5 | ] | 1016 | 5 | .concat(), | 1017 | 5 | libp2p_signature, | 1018 | 5 | ) | 1019 | 5 | .map_err(HandshakeError::SignatureVerificationFailed)?0 ; | 1020 | 5 | remote_public_key | 1021 | 5 | }); | 1022 | 5 | mix_hash(&mut self.0.hash, libp2p_handshake_encrypted); | 1023 | 5 | | 1024 | 5 | // Message has been fully processed. | 1025 | 5 | self.0.num_buffered_or_transmitted_messages += 1; | 1026 | 5 | continue; | 1027 | | } | 1028 | | (2, false) => { | 1029 | | // Receive `s, se` and the libp2p-specific handshake from the remote. | 1030 | 5 | let (remote_static_public_key_encrypted, libp2p_handshake_encrypted) = { | 1031 | | // The noise and libp2p-noise specifications clearly define a noise | 1032 | | // handshake message and a noise transport message as two different things. | 1033 | | // While the remote could in theory send post-handshake | 1034 | | // application-specific data in this message, in practice it is forbidden. | 1035 | 5 | let mut parser = nom::combinator::all_consuming::< | 1036 | 5 | _, | 1037 | 5 | _, | 1038 | 5 | (&[u8], nom::error::ErrorKind), | 1039 | 5 | _, | 1040 | 5 | >(nom::sequence::tuple(( | 1041 | 5 | nom::combinator::map(nom::bytes::streaming::take(48u32), |k| { | 1042 | | <&[u8; 48]>::try_from(k).unwrap() | 1043 | 5 | }), | 1044 | 5 | nom::combinator::rest, | 1045 | 5 | ))); | 1046 | 5 | match parser(&available_message) { | 1047 | 5 | Ok((_, out)) => out, | 1048 | | Err(_) => { | 1049 | 0 | return Err(HandshakeError::PayloadDecode(PayloadDecodeError)) | 1050 | | } | 1051 | | } | 1052 | | }; | 1053 | | | 1054 | | // Process `s`. | 1055 | | self.0.remote_static_public_key = x25519_dalek::PublicKey::from( | 1056 | 5 | self.0 | 1057 | 5 | .cipher_state | 1058 | 5 | .read_chachapoly_message_to_array( | 1059 | 5 | &*self.0.hash, | 1060 | 5 | remote_static_public_key_encrypted, | 1061 | 5 | ) | 1062 | 5 | .map_err(HandshakeError::Cipher)?0 , | 1063 | | ); | 1064 | 5 | mix_hash(&mut self.0.hash, remote_static_public_key_encrypted); | 1065 | 5 | | 1066 | 5 | // Process `se`. Call MixKey(DH(e, rs)). | 1067 | 5 | let HkdfOutput { | 1068 | 5 | output1: chaining_key_update, | 1069 | 5 | output2: key_update, | 1070 | 5 | } = hkdf( | 1071 | 5 | &self.0.chaining_key, | 1072 | 5 | self.0 | 1073 | 5 | .local_ephemeral_private_key | 1074 | 5 | .clone() | 1075 | 5 | .diffie_hellman(&self.0.remote_static_public_key) | 1076 | 5 | .as_bytes(), | 1077 | 5 | ); | 1078 | 5 | self.0.chaining_key = chaining_key_update; | 1079 | 5 | self.0.cipher_state.key = key_update; | 1080 | 5 | self.0.cipher_state.nonce = 0; | 1081 | | | 1082 | | // Process the libp2p-specific handshake. | 1083 | | self.0.remote_public_key = Some({ | 1084 | 5 | let libp2p_handshake_decrypted = self | 1085 | 5 | .0 | 1086 | 5 | .cipher_state | 1087 | 5 | .read_chachapoly_message_to_vec( | 1088 | 5 | &*self.0.hash, | 1089 | 5 | libp2p_handshake_encrypted, | 1090 | 5 | ) | 1091 | 5 | .map_err(HandshakeError::Cipher)?0 ; | 1092 | 5 | let (libp2p_key, libp2p_signature) = { | 1093 | 5 | let mut parser = | 1094 | 5 | nom::combinator::all_consuming::< | 1095 | 5 | _, | 1096 | 5 | _, | 1097 | 5 | (&[u8], nom::error::ErrorKind), | 1098 | 5 | _, | 1099 | 5 | >(protobuf::message_decode! { | 1100 | | #[required] key = 1 => protobuf::bytes_tag_decode, | 1101 | | #[required] sig = 2 => protobuf::bytes_tag_decode, | 1102 | 5 | }); | 1103 | 5 | match parser(&libp2p_handshake_decrypted) { | 1104 | 5 | Ok((_, out)) => (out.key, out.sig), | 1105 | | Err(_) => { | 1106 | 0 | return Err(HandshakeError::PayloadDecode(PayloadDecodeError)) | 1107 | | } | 1108 | | } | 1109 | | }; | 1110 | 5 | let remote_public_key = PublicKey::from_protobuf_encoding(libp2p_key) | 1111 | 5 | .map_err(|_| HandshakeError::InvalidKey)?0 ; | 1112 | 5 | remote_public_key | 1113 | 5 | .verify( | 1114 | 5 | &[ | 1115 | 5 | &b"noise-libp2p-static-key:"[..], | 1116 | 5 | &self.0.remote_static_public_key.as_bytes()[..], | 1117 | 5 | ] | 1118 | 5 | .concat(), | 1119 | 5 | libp2p_signature, | 1120 | 5 | ) | 1121 | 5 | .map_err(HandshakeError::SignatureVerificationFailed)?0 ; | 1122 | 5 | remote_public_key | 1123 | 5 | }); | 1124 | 5 | mix_hash(&mut self.0.hash, libp2p_handshake_encrypted); | 1125 | 5 | | 1126 | 5 | // Message has been fully processed. | 1127 | 5 | self.0.num_buffered_or_transmitted_messages += 1; | 1128 | 5 | continue; | 1129 | | } | 1130 | | _ => { | 1131 | | // Any other state was handled earlier in the function. | 1132 | 0 | unreachable!() | 1133 | | } | 1134 | | } | 1135 | | } | 1136 | 37 | } |
Unexecuted instantiation: _RINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB6_19HandshakeInProgress10read_writeNtNtCsaYZPK01V26L_4core4time8DurationECsDDUKWWCHAU_18smoldot_light_wasm Unexecuted instantiation: _RINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB6_19HandshakeInProgress10read_writepEBc_ Unexecuted instantiation: _RINvMs6_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB6_19HandshakeInProgress10read_writeNtNtCsbpXXxgr6u8g_3std4time7InstantECsiUjFBJteJ7x_17smoldot_full_node |
1137 | | } |
1138 | | |
1139 | | impl fmt::Debug for HandshakeInProgress { |
1140 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
1141 | 0 | f.debug_struct("HandshakeInProgress").finish() |
1142 | 0 | } Unexecuted instantiation: _RNvXs7_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB5_19HandshakeInProgressNtNtCsaYZPK01V26L_4core3fmt5Debug3fmt Unexecuted instantiation: _RNvXs7_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB5_19HandshakeInProgressNtNtCsaYZPK01V26L_4core3fmt5Debug3fmt |
1143 | | } |
1144 | | |
1145 | | /// Potential error during the noise handshake. |
1146 | 0 | #[derive(Debug, derive_more::Display)] Unexecuted instantiation: _RNvXsb_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB5_14HandshakeErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt Unexecuted instantiation: _RNvXsb_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB5_14HandshakeErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt |
1147 | | pub enum HandshakeError { |
1148 | | /// Reading side of the connection is closed. The handshake can't proceed further. |
1149 | | ReadClosed, |
1150 | | /// Writing side of the connection is closed. The handshake can't proceed further. |
1151 | | WriteClosed, |
1152 | | /// Error in the decryption state machine. |
1153 | | #[display(fmt = "Cipher error: {_0}")] |
1154 | | Cipher(CipherError), |
1155 | | /// Failed to decode the payload as the libp2p-extension-to-noise payload. |
1156 | | #[display(fmt = "Failed to decode payload as the libp2p-extension-to-noise payload: {_0}")] |
1157 | | PayloadDecode(PayloadDecodeError), |
1158 | | /// Key passed as part of the payload failed to decode into a libp2p public key. |
1159 | | InvalidKey, |
1160 | | /// Signature of the noise public key by the libp2p key failed. |
1161 | | #[display(fmt = "Signature of the noise public key by the libp2p key failed.")] |
1162 | | SignatureVerificationFailed(SignatureVerifyFailed), |
1163 | | } |
1164 | | |
1165 | | /// Error while encrypting data. |
1166 | 0 | #[derive(Debug, derive_more::Display)] Unexecuted instantiation: _RNvXsd_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB5_12EncryptErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt Unexecuted instantiation: _RNvXsd_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB5_12EncryptErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt |
1167 | | #[display(fmt = "Error while encrypting the Noise payload")] |
1168 | | pub enum EncryptError { |
1169 | | /// The nonce has overflowed because too many messages have been exchanged. This error is a |
1170 | | /// normal situation and will happen given sufficient time. |
1171 | | NonceOverflow, |
1172 | | } |
1173 | | |
1174 | | /// Error while decoding data. |
1175 | 0 | #[derive(Debug, derive_more::Display)] Unexecuted instantiation: _RNvXsf_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB5_11CipherErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt Unexecuted instantiation: _RNvXsf_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB5_11CipherErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt |
1176 | | #[display(fmt = "Error while decrypting the Noise payload")] |
1177 | | pub enum CipherError { |
1178 | | /// Message is too small. This is likely caused by a bug either in this code or in the remote's |
1179 | | /// code. |
1180 | | MissingHmac, |
1181 | | /// Authentication data doesn't match what is expected. |
1182 | | HmacInvalid, |
1183 | | /// The nonce has overflowed because too many messages have been exchanged. This error is a |
1184 | | /// normal situation and will happen given sufficient time. |
1185 | | NonceOverflow, |
1186 | | } |
1187 | | |
1188 | | /// Error while decoding the handshake. |
1189 | 0 | #[derive(Debug, derive_more::Display)] Unexecuted instantiation: _RNvXsh_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB5_18PayloadDecodeErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt Unexecuted instantiation: _RNvXsh_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB5_18PayloadDecodeErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt |
1190 | | pub struct PayloadDecodeError; |
1191 | | |
1192 | | struct CipherState { |
1193 | | key: zeroize::Zeroizing<[u8; 32]>, |
1194 | | nonce: u64, |
1195 | | nonce_has_overflowed: bool, |
1196 | | } |
1197 | | |
1198 | | impl CipherState { |
1199 | | /// Accepts a list of input buffers, and returns output buffers that contain the encrypted data |
1200 | | /// and the HMAC will be written. |
1201 | | /// |
1202 | | /// Does *not* include the libp2p-specific message length prefix. |
1203 | 141 | fn write_chachapoly_message( |
1204 | 141 | &'_ mut self, |
1205 | 141 | associated_data: &[u8], |
1206 | 141 | decrypted_buffers: impl Iterator<Item = Vec<u8>>, |
1207 | 141 | ) -> Result<impl Iterator<Item = Vec<u8>>, EncryptError> { |
1208 | 141 | if self.nonce_has_overflowed { |
1209 | 0 | return Err(EncryptError::NonceOverflow); |
1210 | 141 | } |
1211 | 141 | |
1212 | 141 | let (mut cipher, mac) = self.prepare(associated_data); |
1213 | 141 | let associated_data_len = associated_data.len(); |
1214 | 141 | |
1215 | 141 | // Increment the nonce by 1. This is done ahead of time in order to be sure that the same |
1216 | 141 | // nonce is never re-used even if the API user drops the returned iterator before it ends. |
1217 | 141 | (self.nonce, self.nonce_has_overflowed) = self.nonce.overflowing_add(1); |
1218 | 141 | |
1219 | 141 | // The difficulty in this function implementation is that the cipher operates on 64 bytes |
1220 | 141 | // blocks (in other words, the data passed to them must have a size multiple of 64), and |
1221 | 141 | // unfortunately the input buffers might be weirdly aligned. |
1222 | 141 | // To overcome this, when there's an alignment issue, we copy the data to a contiguous |
1223 | 141 | // slice. |
1224 | 141 | |
1225 | 141 | // Each input buffer is encrypted in place as much as possible. Due to alignment issues, |
1226 | 141 | // each input buffer is split in three parts: the data that is appended to the previous |
1227 | 141 | // buffer's data in order to align it, the data that can be encrypted in place, and the |
1228 | 141 | // data that must be prepanded to the start of the next buffer. |
1229 | 141 | // Furthermore, note that the third part of the last buffer can always be encrypted in |
1230 | 141 | // place. |
1231 | 141 | |
1232 | 141 | // The implementation below requires `decrypted_buffers` to be peekable. |
1233 | 141 | let mut decrypted_buffers = decrypted_buffers.peekable(); |
1234 | 141 | // Counter for total input decrypted data increased when iterating over the input. |
1235 | 141 | // Necessary at the very end of the calculation. |
1236 | 141 | let mut total_decrypted_data = 0; |
1237 | 141 | // Data that was copied from the end of the previous buffer. |
1238 | 141 | // TODO: ideally we would avoid copying the end of the previous buffer, and instead only copy the start of the next one, but this means that we couldn't return concrete `Vec`s anymore, and this API change is complicated |
1239 | 141 | let mut overlapping_data = Vec::new(); |
1240 | 141 | // `None` if the HMAC has already been returned from the iterator. |
1241 | 141 | let mut mac = Some(mac); |
1242 | 141 | |
1243 | 141 | // Iterator being returned. |
1244 | 564 | Ok(iter::from_fn(move || { |
1245 | 720 | loop { |
1246 | 720 | debug_assert!(overlapping_data.len() < 64); |
1247 | | |
1248 | | // Return if iterator has finished. |
1249 | 720 | let Some(mac_deref579 ) = mac.as_mut() else { |
1250 | 141 | return None; |
1251 | | }; |
1252 | | |
1253 | 579 | if !overlapping_data.is_empty() { |
1254 | | // Copy data from the start of the next buffer to the end |
1255 | | // of `overlapping_data`. |
1256 | 297 | if let Some(next_buffer156 ) = decrypted_buffers.peek_mut() { |
1257 | 156 | let missing_data_for_full_frame = 64 - overlapping_data.len(); |
1258 | 156 | if next_buffer.len() >= missing_data_for_full_frame { |
1259 | | // Enough data in next buffer to fill a frame in `overlapping_data`. |
1260 | | // Extract data from the next buffer. |
1261 | 0 | overlapping_data |
1262 | 0 | .extend_from_slice(&next_buffer[..missing_data_for_full_frame]); |
1263 | 0 | next_buffer.copy_within(missing_data_for_full_frame.., 0); |
1264 | 0 | next_buffer.truncate(next_buffer.len() - missing_data_for_full_frame); |
1265 | 0 |
|
1266 | 0 | // Encrypt `overlapping_data` in place and return it. |
1267 | 0 | chacha20::cipher::StreamCipher::apply_keystream( |
1268 | 0 | &mut cipher, |
1269 | 0 | &mut overlapping_data, |
1270 | 0 | ); |
1271 | 0 | poly1305::universal_hash::UniversalHash::update_padded( |
1272 | 0 | mac_deref, |
1273 | 0 | &overlapping_data, |
1274 | 0 | ); |
1275 | 0 | debug_assert_eq!(overlapping_data.len(), 64); |
1276 | 0 | total_decrypted_data += 64; |
1277 | 0 | return Some(mem::take(&mut overlapping_data)); |
1278 | 156 | } else { |
1279 | 156 | // Not enough data in next buffer to fill `overlapping_data`. |
1280 | 156 | // Copy the data and continue looping. |
1281 | 156 | overlapping_data.extend_from_slice(next_buffer); |
1282 | 156 | let _ = decrypted_buffers.next(); |
1283 | 156 | } |
1284 | | } else { |
1285 | | // Input is empty. `overlapping_data` is the last buffer. |
1286 | 141 | chacha20::cipher::StreamCipher::apply_keystream( |
1287 | 141 | &mut cipher, |
1288 | 141 | &mut overlapping_data, |
1289 | 141 | ); |
1290 | 141 | poly1305::universal_hash::UniversalHash::update_padded( |
1291 | 141 | mac_deref, |
1292 | 141 | &overlapping_data, |
1293 | 141 | ); |
1294 | 141 | total_decrypted_data += overlapping_data.len(); |
1295 | 141 | return Some(mem::take(&mut overlapping_data)); |
1296 | | } |
1297 | 282 | } else if let Some(mut buffer141 ) = decrypted_buffers.next() { |
1298 | | // Number of bytes of `next_buffer` that can be encrypted in place. |
1299 | 141 | let encryptable_in_place = 64 * (buffer.len() / 64); |
1300 | 141 | |
1301 | 141 | // Perform the encryption. |
1302 | 141 | chacha20::cipher::StreamCipher::apply_keystream( |
1303 | 141 | &mut cipher, |
1304 | 141 | &mut buffer[..encryptable_in_place], |
1305 | 141 | ); |
1306 | 141 | poly1305::universal_hash::UniversalHash::update_padded( |
1307 | 141 | mac_deref, |
1308 | 141 | &buffer[..encryptable_in_place], |
1309 | 141 | ); |
1310 | 141 | |
1311 | 141 | // Copy the non-encryptable-in-place data to `overlapping_data`. |
1312 | 141 | if encryptable_in_place != buffer.len() { |
1313 | 141 | overlapping_data.reserve(64); |
1314 | 141 | overlapping_data.extend_from_slice(&buffer[encryptable_in_place..]); |
1315 | 141 | buffer.truncate(encryptable_in_place); |
1316 | 141 | }0 |
1317 | | |
1318 | | // And return. |
1319 | 141 | total_decrypted_data += encryptable_in_place; |
1320 | 141 | return Some(buffer); |
1321 | | } else { |
1322 | | // No more encrypted data to return. |
1323 | | |
1324 | | // Update the MAC with the length of the associated data and input data. |
1325 | 141 | let mut block = |
1326 | 141 | poly1305::universal_hash::generic_array::GenericArray::default(); |
1327 | 141 | block[..8].copy_from_slice( |
1328 | 141 | &u64::try_from(associated_data_len).unwrap().to_le_bytes(), |
1329 | 141 | ); |
1330 | 141 | block[8..].copy_from_slice( |
1331 | 141 | &u64::try_from(total_decrypted_data).unwrap().to_le_bytes(), |
1332 | 141 | ); |
1333 | 141 | poly1305::universal_hash::UniversalHash::update(mac_deref, &[block]); |
1334 | 141 | |
1335 | 141 | // Return the HMAC. |
1336 | 141 | let mac_bytes = |
1337 | 141 | poly1305::universal_hash::UniversalHash::finalize(mac.take().unwrap()) |
1338 | 141 | .to_vec(); |
1339 | 141 | return Some(mac_bytes); |
1340 | | } |
1341 | | } |
1342 | 564 | }))141 _RNCINvMs8_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB8_11CipherState24write_chachapoly_messageINtNtNtCsdZExvAaxgia_5alloc3vec5drain5DrainINtB1L_3VechEEE0Be_ Line | Count | Source | 1244 | 372 | Ok(iter::from_fn(move || { | 1245 | 528 | loop { | 1246 | 528 | debug_assert!(overlapping_data.len() < 64); | 1247 | | | 1248 | | // Return if iterator has finished. | 1249 | 528 | let Some(mac_deref435 ) = mac.as_mut() else { | 1250 | 93 | return None; | 1251 | | }; | 1252 | | | 1253 | 435 | if !overlapping_data.is_empty() { | 1254 | | // Copy data from the start of the next buffer to the end | 1255 | | // of `overlapping_data`. | 1256 | 249 | if let Some(next_buffer156 ) = decrypted_buffers.peek_mut() { | 1257 | 156 | let missing_data_for_full_frame = 64 - overlapping_data.len(); | 1258 | 156 | if next_buffer.len() >= missing_data_for_full_frame { | 1259 | | // Enough data in next buffer to fill a frame in `overlapping_data`. | 1260 | | // Extract data from the next buffer. | 1261 | 0 | overlapping_data | 1262 | 0 | .extend_from_slice(&next_buffer[..missing_data_for_full_frame]); | 1263 | 0 | next_buffer.copy_within(missing_data_for_full_frame.., 0); | 1264 | 0 | next_buffer.truncate(next_buffer.len() - missing_data_for_full_frame); | 1265 | 0 |
| 1266 | 0 | // Encrypt `overlapping_data` in place and return it. | 1267 | 0 | chacha20::cipher::StreamCipher::apply_keystream( | 1268 | 0 | &mut cipher, | 1269 | 0 | &mut overlapping_data, | 1270 | 0 | ); | 1271 | 0 | poly1305::universal_hash::UniversalHash::update_padded( | 1272 | 0 | mac_deref, | 1273 | 0 | &overlapping_data, | 1274 | 0 | ); | 1275 | 0 | debug_assert_eq!(overlapping_data.len(), 64); | 1276 | 0 | total_decrypted_data += 64; | 1277 | 0 | return Some(mem::take(&mut overlapping_data)); | 1278 | 156 | } else { | 1279 | 156 | // Not enough data in next buffer to fill `overlapping_data`. | 1280 | 156 | // Copy the data and continue looping. | 1281 | 156 | overlapping_data.extend_from_slice(next_buffer); | 1282 | 156 | let _ = decrypted_buffers.next(); | 1283 | 156 | } | 1284 | | } else { | 1285 | | // Input is empty. `overlapping_data` is the last buffer. | 1286 | 93 | chacha20::cipher::StreamCipher::apply_keystream( | 1287 | 93 | &mut cipher, | 1288 | 93 | &mut overlapping_data, | 1289 | 93 | ); | 1290 | 93 | poly1305::universal_hash::UniversalHash::update_padded( | 1291 | 93 | mac_deref, | 1292 | 93 | &overlapping_data, | 1293 | 93 | ); | 1294 | 93 | total_decrypted_data += overlapping_data.len(); | 1295 | 93 | return Some(mem::take(&mut overlapping_data)); | 1296 | | } | 1297 | 186 | } else if let Some(mut buffer93 ) = decrypted_buffers.next() { | 1298 | | // Number of bytes of `next_buffer` that can be encrypted in place. | 1299 | 93 | let encryptable_in_place = 64 * (buffer.len() / 64); | 1300 | 93 | | 1301 | 93 | // Perform the encryption. | 1302 | 93 | chacha20::cipher::StreamCipher::apply_keystream( | 1303 | 93 | &mut cipher, | 1304 | 93 | &mut buffer[..encryptable_in_place], | 1305 | 93 | ); | 1306 | 93 | poly1305::universal_hash::UniversalHash::update_padded( | 1307 | 93 | mac_deref, | 1308 | 93 | &buffer[..encryptable_in_place], | 1309 | 93 | ); | 1310 | 93 | | 1311 | 93 | // Copy the non-encryptable-in-place data to `overlapping_data`. | 1312 | 93 | if encryptable_in_place != buffer.len() { | 1313 | 93 | overlapping_data.reserve(64); | 1314 | 93 | overlapping_data.extend_from_slice(&buffer[encryptable_in_place..]); | 1315 | 93 | buffer.truncate(encryptable_in_place); | 1316 | 93 | }0 | 1317 | | | 1318 | | // And return. | 1319 | 93 | total_decrypted_data += encryptable_in_place; | 1320 | 93 | return Some(buffer); | 1321 | | } else { | 1322 | | // No more encrypted data to return. | 1323 | | | 1324 | | // Update the MAC with the length of the associated data and input data. | 1325 | 93 | let mut block = | 1326 | 93 | poly1305::universal_hash::generic_array::GenericArray::default(); | 1327 | 93 | block[..8].copy_from_slice( | 1328 | 93 | &u64::try_from(associated_data_len).unwrap().to_le_bytes(), | 1329 | 93 | ); | 1330 | 93 | block[8..].copy_from_slice( | 1331 | 93 | &u64::try_from(total_decrypted_data).unwrap().to_le_bytes(), | 1332 | 93 | ); | 1333 | 93 | poly1305::universal_hash::UniversalHash::update(mac_deref, &[block]); | 1334 | 93 | | 1335 | 93 | // Return the HMAC. | 1336 | 93 | let mac_bytes = | 1337 | 93 | poly1305::universal_hash::UniversalHash::finalize(mac.take().unwrap()) | 1338 | 93 | .to_vec(); | 1339 | 93 | return Some(mac_bytes); | 1340 | | } | 1341 | | } | 1342 | 372 | })) |
_RNCINvMs8_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB8_11CipherState24write_chachapoly_messageINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceINtNtCsdZExvAaxgia_5alloc3vec3VechEEE0Be_ Line | Count | Source | 1244 | 192 | Ok(iter::from_fn(move || { | 1245 | 192 | loop { | 1246 | 192 | debug_assert!(overlapping_data.len() < 64); | 1247 | | | 1248 | | // Return if iterator has finished. | 1249 | 192 | let Some(mac_deref144 ) = mac.as_mut() else { | 1250 | 48 | return None; | 1251 | | }; | 1252 | | | 1253 | 144 | if !overlapping_data.is_empty() { | 1254 | | // Copy data from the start of the next buffer to the end | 1255 | | // of `overlapping_data`. | 1256 | 48 | if let Some(next_buffer0 ) = decrypted_buffers.peek_mut() { | 1257 | 0 | let missing_data_for_full_frame = 64 - overlapping_data.len(); | 1258 | 0 | if next_buffer.len() >= missing_data_for_full_frame { | 1259 | | // Enough data in next buffer to fill a frame in `overlapping_data`. | 1260 | | // Extract data from the next buffer. | 1261 | 0 | overlapping_data | 1262 | 0 | .extend_from_slice(&next_buffer[..missing_data_for_full_frame]); | 1263 | 0 | next_buffer.copy_within(missing_data_for_full_frame.., 0); | 1264 | 0 | next_buffer.truncate(next_buffer.len() - missing_data_for_full_frame); | 1265 | 0 |
| 1266 | 0 | // Encrypt `overlapping_data` in place and return it. | 1267 | 0 | chacha20::cipher::StreamCipher::apply_keystream( | 1268 | 0 | &mut cipher, | 1269 | 0 | &mut overlapping_data, | 1270 | 0 | ); | 1271 | 0 | poly1305::universal_hash::UniversalHash::update_padded( | 1272 | 0 | mac_deref, | 1273 | 0 | &overlapping_data, | 1274 | 0 | ); | 1275 | 0 | debug_assert_eq!(overlapping_data.len(), 64); | 1276 | 0 | total_decrypted_data += 64; | 1277 | 0 | return Some(mem::take(&mut overlapping_data)); | 1278 | 0 | } else { | 1279 | 0 | // Not enough data in next buffer to fill `overlapping_data`. | 1280 | 0 | // Copy the data and continue looping. | 1281 | 0 | overlapping_data.extend_from_slice(next_buffer); | 1282 | 0 | let _ = decrypted_buffers.next(); | 1283 | 0 | } | 1284 | | } else { | 1285 | | // Input is empty. `overlapping_data` is the last buffer. | 1286 | 48 | chacha20::cipher::StreamCipher::apply_keystream( | 1287 | 48 | &mut cipher, | 1288 | 48 | &mut overlapping_data, | 1289 | 48 | ); | 1290 | 48 | poly1305::universal_hash::UniversalHash::update_padded( | 1291 | 48 | mac_deref, | 1292 | 48 | &overlapping_data, | 1293 | 48 | ); | 1294 | 48 | total_decrypted_data += overlapping_data.len(); | 1295 | 48 | return Some(mem::take(&mut overlapping_data)); | 1296 | | } | 1297 | 96 | } else if let Some(mut buffer48 ) = decrypted_buffers.next() { | 1298 | | // Number of bytes of `next_buffer` that can be encrypted in place. | 1299 | 48 | let encryptable_in_place = 64 * (buffer.len() / 64); | 1300 | 48 | | 1301 | 48 | // Perform the encryption. | 1302 | 48 | chacha20::cipher::StreamCipher::apply_keystream( | 1303 | 48 | &mut cipher, | 1304 | 48 | &mut buffer[..encryptable_in_place], | 1305 | 48 | ); | 1306 | 48 | poly1305::universal_hash::UniversalHash::update_padded( | 1307 | 48 | mac_deref, | 1308 | 48 | &buffer[..encryptable_in_place], | 1309 | 48 | ); | 1310 | 48 | | 1311 | 48 | // Copy the non-encryptable-in-place data to `overlapping_data`. | 1312 | 48 | if encryptable_in_place != buffer.len() { | 1313 | 48 | overlapping_data.reserve(64); | 1314 | 48 | overlapping_data.extend_from_slice(&buffer[encryptable_in_place..]); | 1315 | 48 | buffer.truncate(encryptable_in_place); | 1316 | 48 | }0 | 1317 | | | 1318 | | // And return. | 1319 | 48 | total_decrypted_data += encryptable_in_place; | 1320 | 48 | return Some(buffer); | 1321 | | } else { | 1322 | | // No more encrypted data to return. | 1323 | | | 1324 | | // Update the MAC with the length of the associated data and input data. | 1325 | 48 | let mut block = | 1326 | 48 | poly1305::universal_hash::generic_array::GenericArray::default(); | 1327 | 48 | block[..8].copy_from_slice( | 1328 | 48 | &u64::try_from(associated_data_len).unwrap().to_le_bytes(), | 1329 | 48 | ); | 1330 | 48 | block[8..].copy_from_slice( | 1331 | 48 | &u64::try_from(total_decrypted_data).unwrap().to_le_bytes(), | 1332 | 48 | ); | 1333 | 48 | poly1305::universal_hash::UniversalHash::update(mac_deref, &[block]); | 1334 | 48 | | 1335 | 48 | // Return the HMAC. | 1336 | 48 | let mac_bytes = | 1337 | 48 | poly1305::universal_hash::UniversalHash::finalize(mac.take().unwrap()) | 1338 | 48 | .to_vec(); | 1339 | 48 | return Some(mac_bytes); | 1340 | | } | 1341 | | } | 1342 | 192 | })) |
Unexecuted instantiation: _RNCINvMs8_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_11CipherState24write_chachapoly_messageINtNtNtCsdZExvAaxgia_5alloc3vec5drain5DrainINtB1M_3VechEEE0CsDDUKWWCHAU_18smoldot_light_wasm Unexecuted instantiation: _RNCINvMs8_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_11CipherState24write_chachapoly_messageINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceINtNtCsdZExvAaxgia_5alloc3vec3VechEEE0Be_ Unexecuted instantiation: _RNCINvMs8_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB8_11CipherState24write_chachapoly_messageINtNtNtCsdZExvAaxgia_5alloc3vec5drain5DrainINtB1M_3VechEEE0CsiUjFBJteJ7x_17smoldot_full_node |
1343 | 141 | } _RINvMs8_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB6_11CipherState24write_chachapoly_messageINtNtNtCsdZExvAaxgia_5alloc3vec5drain5DrainINtB1J_3VechEEEBc_ Line | Count | Source | 1203 | 93 | fn write_chachapoly_message( | 1204 | 93 | &'_ mut self, | 1205 | 93 | associated_data: &[u8], | 1206 | 93 | decrypted_buffers: impl Iterator<Item = Vec<u8>>, | 1207 | 93 | ) -> Result<impl Iterator<Item = Vec<u8>>, EncryptError> { | 1208 | 93 | if self.nonce_has_overflowed { | 1209 | 0 | return Err(EncryptError::NonceOverflow); | 1210 | 93 | } | 1211 | 93 | | 1212 | 93 | let (mut cipher, mac) = self.prepare(associated_data); | 1213 | 93 | let associated_data_len = associated_data.len(); | 1214 | 93 | | 1215 | 93 | // Increment the nonce by 1. This is done ahead of time in order to be sure that the same | 1216 | 93 | // nonce is never re-used even if the API user drops the returned iterator before it ends. | 1217 | 93 | (self.nonce, self.nonce_has_overflowed) = self.nonce.overflowing_add(1); | 1218 | 93 | | 1219 | 93 | // The difficulty in this function implementation is that the cipher operates on 64 bytes | 1220 | 93 | // blocks (in other words, the data passed to them must have a size multiple of 64), and | 1221 | 93 | // unfortunately the input buffers might be weirdly aligned. | 1222 | 93 | // To overcome this, when there's an alignment issue, we copy the data to a contiguous | 1223 | 93 | // slice. | 1224 | 93 | | 1225 | 93 | // Each input buffer is encrypted in place as much as possible. Due to alignment issues, | 1226 | 93 | // each input buffer is split in three parts: the data that is appended to the previous | 1227 | 93 | // buffer's data in order to align it, the data that can be encrypted in place, and the | 1228 | 93 | // data that must be prepanded to the start of the next buffer. | 1229 | 93 | // Furthermore, note that the third part of the last buffer can always be encrypted in | 1230 | 93 | // place. | 1231 | 93 | | 1232 | 93 | // The implementation below requires `decrypted_buffers` to be peekable. | 1233 | 93 | let mut decrypted_buffers = decrypted_buffers.peekable(); | 1234 | 93 | // Counter for total input decrypted data increased when iterating over the input. | 1235 | 93 | // Necessary at the very end of the calculation. | 1236 | 93 | let mut total_decrypted_data = 0; | 1237 | 93 | // Data that was copied from the end of the previous buffer. | 1238 | 93 | // TODO: ideally we would avoid copying the end of the previous buffer, and instead only copy the start of the next one, but this means that we couldn't return concrete `Vec`s anymore, and this API change is complicated | 1239 | 93 | let mut overlapping_data = Vec::new(); | 1240 | 93 | // `None` if the HMAC has already been returned from the iterator. | 1241 | 93 | let mut mac = Some(mac); | 1242 | 93 | | 1243 | 93 | // Iterator being returned. | 1244 | 93 | Ok(iter::from_fn(move || { | 1245 | | loop { | 1246 | | debug_assert!(overlapping_data.len() < 64); | 1247 | | | 1248 | | // Return if iterator has finished. | 1249 | | let Some(mac_deref) = mac.as_mut() else { | 1250 | | return None; | 1251 | | }; | 1252 | | | 1253 | | if !overlapping_data.is_empty() { | 1254 | | // Copy data from the start of the next buffer to the end | 1255 | | // of `overlapping_data`. | 1256 | | if let Some(next_buffer) = decrypted_buffers.peek_mut() { | 1257 | | let missing_data_for_full_frame = 64 - overlapping_data.len(); | 1258 | | if next_buffer.len() >= missing_data_for_full_frame { | 1259 | | // Enough data in next buffer to fill a frame in `overlapping_data`. | 1260 | | // Extract data from the next buffer. | 1261 | | overlapping_data | 1262 | | .extend_from_slice(&next_buffer[..missing_data_for_full_frame]); | 1263 | | next_buffer.copy_within(missing_data_for_full_frame.., 0); | 1264 | | next_buffer.truncate(next_buffer.len() - missing_data_for_full_frame); | 1265 | | | 1266 | | // Encrypt `overlapping_data` in place and return it. | 1267 | | chacha20::cipher::StreamCipher::apply_keystream( | 1268 | | &mut cipher, | 1269 | | &mut overlapping_data, | 1270 | | ); | 1271 | | poly1305::universal_hash::UniversalHash::update_padded( | 1272 | | mac_deref, | 1273 | | &overlapping_data, | 1274 | | ); | 1275 | | debug_assert_eq!(overlapping_data.len(), 64); | 1276 | | total_decrypted_data += 64; | 1277 | | return Some(mem::take(&mut overlapping_data)); | 1278 | | } else { | 1279 | | // Not enough data in next buffer to fill `overlapping_data`. | 1280 | | // Copy the data and continue looping. | 1281 | | overlapping_data.extend_from_slice(next_buffer); | 1282 | | let _ = decrypted_buffers.next(); | 1283 | | } | 1284 | | } else { | 1285 | | // Input is empty. `overlapping_data` is the last buffer. | 1286 | | chacha20::cipher::StreamCipher::apply_keystream( | 1287 | | &mut cipher, | 1288 | | &mut overlapping_data, | 1289 | | ); | 1290 | | poly1305::universal_hash::UniversalHash::update_padded( | 1291 | | mac_deref, | 1292 | | &overlapping_data, | 1293 | | ); | 1294 | | total_decrypted_data += overlapping_data.len(); | 1295 | | return Some(mem::take(&mut overlapping_data)); | 1296 | | } | 1297 | | } else if let Some(mut buffer) = decrypted_buffers.next() { | 1298 | | // Number of bytes of `next_buffer` that can be encrypted in place. | 1299 | | let encryptable_in_place = 64 * (buffer.len() / 64); | 1300 | | | 1301 | | // Perform the encryption. | 1302 | | chacha20::cipher::StreamCipher::apply_keystream( | 1303 | | &mut cipher, | 1304 | | &mut buffer[..encryptable_in_place], | 1305 | | ); | 1306 | | poly1305::universal_hash::UniversalHash::update_padded( | 1307 | | mac_deref, | 1308 | | &buffer[..encryptable_in_place], | 1309 | | ); | 1310 | | | 1311 | | // Copy the non-encryptable-in-place data to `overlapping_data`. | 1312 | | if encryptable_in_place != buffer.len() { | 1313 | | overlapping_data.reserve(64); | 1314 | | overlapping_data.extend_from_slice(&buffer[encryptable_in_place..]); | 1315 | | buffer.truncate(encryptable_in_place); | 1316 | | } | 1317 | | | 1318 | | // And return. | 1319 | | total_decrypted_data += encryptable_in_place; | 1320 | | return Some(buffer); | 1321 | | } else { | 1322 | | // No more encrypted data to return. | 1323 | | | 1324 | | // Update the MAC with the length of the associated data and input data. | 1325 | | let mut block = | 1326 | | poly1305::universal_hash::generic_array::GenericArray::default(); | 1327 | | block[..8].copy_from_slice( | 1328 | | &u64::try_from(associated_data_len).unwrap().to_le_bytes(), | 1329 | | ); | 1330 | | block[8..].copy_from_slice( | 1331 | | &u64::try_from(total_decrypted_data).unwrap().to_le_bytes(), | 1332 | | ); | 1333 | | poly1305::universal_hash::UniversalHash::update(mac_deref, &[block]); | 1334 | | | 1335 | | // Return the HMAC. | 1336 | | let mac_bytes = | 1337 | | poly1305::universal_hash::UniversalHash::finalize(mac.take().unwrap()) | 1338 | | .to_vec(); | 1339 | | return Some(mac_bytes); | 1340 | | } | 1341 | | } | 1342 | 93 | })) | 1343 | 93 | } |
_RINvMs8_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB6_11CipherState24write_chachapoly_messageINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceINtNtCsdZExvAaxgia_5alloc3vec3VechEEEBc_ Line | Count | Source | 1203 | 48 | fn write_chachapoly_message( | 1204 | 48 | &'_ mut self, | 1205 | 48 | associated_data: &[u8], | 1206 | 48 | decrypted_buffers: impl Iterator<Item = Vec<u8>>, | 1207 | 48 | ) -> Result<impl Iterator<Item = Vec<u8>>, EncryptError> { | 1208 | 48 | if self.nonce_has_overflowed { | 1209 | 0 | return Err(EncryptError::NonceOverflow); | 1210 | 48 | } | 1211 | 48 | | 1212 | 48 | let (mut cipher, mac) = self.prepare(associated_data); | 1213 | 48 | let associated_data_len = associated_data.len(); | 1214 | 48 | | 1215 | 48 | // Increment the nonce by 1. This is done ahead of time in order to be sure that the same | 1216 | 48 | // nonce is never re-used even if the API user drops the returned iterator before it ends. | 1217 | 48 | (self.nonce, self.nonce_has_overflowed) = self.nonce.overflowing_add(1); | 1218 | 48 | | 1219 | 48 | // The difficulty in this function implementation is that the cipher operates on 64 bytes | 1220 | 48 | // blocks (in other words, the data passed to them must have a size multiple of 64), and | 1221 | 48 | // unfortunately the input buffers might be weirdly aligned. | 1222 | 48 | // To overcome this, when there's an alignment issue, we copy the data to a contiguous | 1223 | 48 | // slice. | 1224 | 48 | | 1225 | 48 | // Each input buffer is encrypted in place as much as possible. Due to alignment issues, | 1226 | 48 | // each input buffer is split in three parts: the data that is appended to the previous | 1227 | 48 | // buffer's data in order to align it, the data that can be encrypted in place, and the | 1228 | 48 | // data that must be prepanded to the start of the next buffer. | 1229 | 48 | // Furthermore, note that the third part of the last buffer can always be encrypted in | 1230 | 48 | // place. | 1231 | 48 | | 1232 | 48 | // The implementation below requires `decrypted_buffers` to be peekable. | 1233 | 48 | let mut decrypted_buffers = decrypted_buffers.peekable(); | 1234 | 48 | // Counter for total input decrypted data increased when iterating over the input. | 1235 | 48 | // Necessary at the very end of the calculation. | 1236 | 48 | let mut total_decrypted_data = 0; | 1237 | 48 | // Data that was copied from the end of the previous buffer. | 1238 | 48 | // TODO: ideally we would avoid copying the end of the previous buffer, and instead only copy the start of the next one, but this means that we couldn't return concrete `Vec`s anymore, and this API change is complicated | 1239 | 48 | let mut overlapping_data = Vec::new(); | 1240 | 48 | // `None` if the HMAC has already been returned from the iterator. | 1241 | 48 | let mut mac = Some(mac); | 1242 | 48 | | 1243 | 48 | // Iterator being returned. | 1244 | 48 | Ok(iter::from_fn(move || { | 1245 | | loop { | 1246 | | debug_assert!(overlapping_data.len() < 64); | 1247 | | | 1248 | | // Return if iterator has finished. | 1249 | | let Some(mac_deref) = mac.as_mut() else { | 1250 | | return None; | 1251 | | }; | 1252 | | | 1253 | | if !overlapping_data.is_empty() { | 1254 | | // Copy data from the start of the next buffer to the end | 1255 | | // of `overlapping_data`. | 1256 | | if let Some(next_buffer) = decrypted_buffers.peek_mut() { | 1257 | | let missing_data_for_full_frame = 64 - overlapping_data.len(); | 1258 | | if next_buffer.len() >= missing_data_for_full_frame { | 1259 | | // Enough data in next buffer to fill a frame in `overlapping_data`. | 1260 | | // Extract data from the next buffer. | 1261 | | overlapping_data | 1262 | | .extend_from_slice(&next_buffer[..missing_data_for_full_frame]); | 1263 | | next_buffer.copy_within(missing_data_for_full_frame.., 0); | 1264 | | next_buffer.truncate(next_buffer.len() - missing_data_for_full_frame); | 1265 | | | 1266 | | // Encrypt `overlapping_data` in place and return it. | 1267 | | chacha20::cipher::StreamCipher::apply_keystream( | 1268 | | &mut cipher, | 1269 | | &mut overlapping_data, | 1270 | | ); | 1271 | | poly1305::universal_hash::UniversalHash::update_padded( | 1272 | | mac_deref, | 1273 | | &overlapping_data, | 1274 | | ); | 1275 | | debug_assert_eq!(overlapping_data.len(), 64); | 1276 | | total_decrypted_data += 64; | 1277 | | return Some(mem::take(&mut overlapping_data)); | 1278 | | } else { | 1279 | | // Not enough data in next buffer to fill `overlapping_data`. | 1280 | | // Copy the data and continue looping. | 1281 | | overlapping_data.extend_from_slice(next_buffer); | 1282 | | let _ = decrypted_buffers.next(); | 1283 | | } | 1284 | | } else { | 1285 | | // Input is empty. `overlapping_data` is the last buffer. | 1286 | | chacha20::cipher::StreamCipher::apply_keystream( | 1287 | | &mut cipher, | 1288 | | &mut overlapping_data, | 1289 | | ); | 1290 | | poly1305::universal_hash::UniversalHash::update_padded( | 1291 | | mac_deref, | 1292 | | &overlapping_data, | 1293 | | ); | 1294 | | total_decrypted_data += overlapping_data.len(); | 1295 | | return Some(mem::take(&mut overlapping_data)); | 1296 | | } | 1297 | | } else if let Some(mut buffer) = decrypted_buffers.next() { | 1298 | | // Number of bytes of `next_buffer` that can be encrypted in place. | 1299 | | let encryptable_in_place = 64 * (buffer.len() / 64); | 1300 | | | 1301 | | // Perform the encryption. | 1302 | | chacha20::cipher::StreamCipher::apply_keystream( | 1303 | | &mut cipher, | 1304 | | &mut buffer[..encryptable_in_place], | 1305 | | ); | 1306 | | poly1305::universal_hash::UniversalHash::update_padded( | 1307 | | mac_deref, | 1308 | | &buffer[..encryptable_in_place], | 1309 | | ); | 1310 | | | 1311 | | // Copy the non-encryptable-in-place data to `overlapping_data`. | 1312 | | if encryptable_in_place != buffer.len() { | 1313 | | overlapping_data.reserve(64); | 1314 | | overlapping_data.extend_from_slice(&buffer[encryptable_in_place..]); | 1315 | | buffer.truncate(encryptable_in_place); | 1316 | | } | 1317 | | | 1318 | | // And return. | 1319 | | total_decrypted_data += encryptable_in_place; | 1320 | | return Some(buffer); | 1321 | | } else { | 1322 | | // No more encrypted data to return. | 1323 | | | 1324 | | // Update the MAC with the length of the associated data and input data. | 1325 | | let mut block = | 1326 | | poly1305::universal_hash::generic_array::GenericArray::default(); | 1327 | | block[..8].copy_from_slice( | 1328 | | &u64::try_from(associated_data_len).unwrap().to_le_bytes(), | 1329 | | ); | 1330 | | block[8..].copy_from_slice( | 1331 | | &u64::try_from(total_decrypted_data).unwrap().to_le_bytes(), | 1332 | | ); | 1333 | | poly1305::universal_hash::UniversalHash::update(mac_deref, &[block]); | 1334 | | | 1335 | | // Return the HMAC. | 1336 | | let mac_bytes = | 1337 | | poly1305::universal_hash::UniversalHash::finalize(mac.take().unwrap()) | 1338 | | .to_vec(); | 1339 | | return Some(mac_bytes); | 1340 | | } | 1341 | | } | 1342 | 48 | })) | 1343 | 48 | } |
Unexecuted instantiation: _RINvMs8_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB6_11CipherState24write_chachapoly_messageINtNtNtCsdZExvAaxgia_5alloc3vec5drain5DrainINtB1K_3VechEEECsDDUKWWCHAU_18smoldot_light_wasm Unexecuted instantiation: _RINvMs8_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB6_11CipherState24write_chachapoly_messageINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceINtNtCsdZExvAaxgia_5alloc3vec3VechEEEBc_ Unexecuted instantiation: _RINvMs8_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB6_11CipherState24write_chachapoly_messageINtNtNtCsdZExvAaxgia_5alloc3vec5drain5DrainINtB1K_3VechEEECsiUjFBJteJ7x_17smoldot_full_node |
1344 | | |
1345 | | /// Creates a ChaChaPoly1305 frame as a `Vec`. |
1346 | | /// |
1347 | | /// Does *not* include the libp2p-specific message length prefix. |
1348 | 48 | fn write_chachapoly_message_to_vec( |
1349 | 48 | &'_ mut self, |
1350 | 48 | associated_data: &[u8], |
1351 | 48 | data: &[u8], |
1352 | 48 | ) -> Result<Vec<u8>, EncryptError> { |
1353 | 48 | Ok(self |
1354 | 48 | .write_chachapoly_message(associated_data, iter::once(data.to_vec()))?0 |
1355 | 144 | .fold(Vec::new(), 48 |mut a, b| { |
1356 | 144 | if a.is_empty() { |
1357 | 72 | b |
1358 | | } else { |
1359 | 72 | a.extend_from_slice(&b); |
1360 | 72 | a |
1361 | | } |
1362 | 144 | })48 ) _RNCNvMs8_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB7_11CipherState31write_chachapoly_message_to_vec0Bd_ Line | Count | Source | 1355 | 144 | .fold(Vec::new(), |mut a, b| { | 1356 | 144 | if a.is_empty() { | 1357 | 72 | b | 1358 | | } else { | 1359 | 72 | a.extend_from_slice(&b); | 1360 | 72 | a | 1361 | | } | 1362 | 144 | })) |
Unexecuted instantiation: _RNCNvMs8_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB7_11CipherState31write_chachapoly_message_to_vec0Bd_ |
1363 | 48 | } _RNvMs8_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB5_11CipherState31write_chachapoly_message_to_vec Line | Count | Source | 1348 | 48 | fn write_chachapoly_message_to_vec( | 1349 | 48 | &'_ mut self, | 1350 | 48 | associated_data: &[u8], | 1351 | 48 | data: &[u8], | 1352 | 48 | ) -> Result<Vec<u8>, EncryptError> { | 1353 | 48 | Ok(self | 1354 | 48 | .write_chachapoly_message(associated_data, iter::once(data.to_vec()))?0 | 1355 | 48 | .fold(Vec::new(), |mut a, b| { | 1356 | | if a.is_empty() { | 1357 | | b | 1358 | | } else { | 1359 | | a.extend_from_slice(&b); | 1360 | | a | 1361 | | } | 1362 | 48 | })) | 1363 | 48 | } |
Unexecuted instantiation: _RNvMs8_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB5_11CipherState31write_chachapoly_message_to_vec |
1364 | | |
1365 | | /// Highly-specific function when the message to decode is 32 bytes. |
1366 | 24 | fn read_chachapoly_message_to_array( |
1367 | 24 | &'_ mut self, |
1368 | 24 | associated_data: &[u8], |
1369 | 24 | message_data: &[u8; 48], |
1370 | 24 | ) -> Result<[u8; 32], CipherError> { |
1371 | 24 | let mut out = [0; 32]; |
1372 | 24 | self.read_chachapoly_message_to_slice(associated_data, message_data, &mut out)?0 ; |
1373 | 24 | Ok(out) |
1374 | 24 | } _RNvMs8_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB5_11CipherState32read_chachapoly_message_to_array Line | Count | Source | 1366 | 24 | fn read_chachapoly_message_to_array( | 1367 | 24 | &'_ mut self, | 1368 | 24 | associated_data: &[u8], | 1369 | 24 | message_data: &[u8; 48], | 1370 | 24 | ) -> Result<[u8; 32], CipherError> { | 1371 | 24 | let mut out = [0; 32]; | 1372 | 24 | self.read_chachapoly_message_to_slice(associated_data, message_data, &mut out)?0 ; | 1373 | 24 | Ok(out) | 1374 | 24 | } |
Unexecuted instantiation: _RNvMs8_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB5_11CipherState32read_chachapoly_message_to_array |
1375 | | |
1376 | 24 | fn read_chachapoly_message_to_vec( |
1377 | 24 | &'_ mut self, |
1378 | 24 | associated_data: &[u8], |
1379 | 24 | message_data: &[u8], |
1380 | 24 | ) -> Result<Vec<u8>, CipherError> { |
1381 | 24 | let mut destination = vec![0; message_data.len().saturating_sub(16)]; |
1382 | 24 | self.read_chachapoly_message_to_slice(associated_data, message_data, &mut destination)?0 ; |
1383 | 24 | Ok(destination) |
1384 | 24 | } _RNvMs8_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB5_11CipherState30read_chachapoly_message_to_vec Line | Count | Source | 1376 | 24 | fn read_chachapoly_message_to_vec( | 1377 | 24 | &'_ mut self, | 1378 | 24 | associated_data: &[u8], | 1379 | 24 | message_data: &[u8], | 1380 | 24 | ) -> Result<Vec<u8>, CipherError> { | 1381 | 24 | let mut destination = vec![0; message_data.len().saturating_sub(16)]; | 1382 | 24 | self.read_chachapoly_message_to_slice(associated_data, message_data, &mut destination)?0 ; | 1383 | 24 | Ok(destination) | 1384 | 24 | } |
Unexecuted instantiation: _RNvMs8_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB5_11CipherState30read_chachapoly_message_to_vec |
1385 | | |
1386 | 91 | fn read_chachapoly_message_to_vec_append( |
1387 | 91 | &'_ mut self, |
1388 | 91 | associated_data: &[u8], |
1389 | 91 | message_data: &[u8], |
1390 | 91 | out: &mut Vec<u8>, |
1391 | 91 | ) -> Result<(), CipherError> { |
1392 | 91 | let len_before = out.len(); |
1393 | 91 | out.resize(len_before + message_data.len().saturating_sub(16), 0); |
1394 | 91 | let result = self.read_chachapoly_message_to_slice( |
1395 | 91 | associated_data, |
1396 | 91 | message_data, |
1397 | 91 | &mut out[len_before..], |
1398 | 91 | ); |
1399 | 91 | if result.is_err() { |
1400 | 0 | out.truncate(len_before); |
1401 | 91 | } |
1402 | 91 | result |
1403 | 91 | } _RNvMs8_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB5_11CipherState37read_chachapoly_message_to_vec_append Line | Count | Source | 1386 | 91 | fn read_chachapoly_message_to_vec_append( | 1387 | 91 | &'_ mut self, | 1388 | 91 | associated_data: &[u8], | 1389 | 91 | message_data: &[u8], | 1390 | 91 | out: &mut Vec<u8>, | 1391 | 91 | ) -> Result<(), CipherError> { | 1392 | 91 | let len_before = out.len(); | 1393 | 91 | out.resize(len_before + message_data.len().saturating_sub(16), 0); | 1394 | 91 | let result = self.read_chachapoly_message_to_slice( | 1395 | 91 | associated_data, | 1396 | 91 | message_data, | 1397 | 91 | &mut out[len_before..], | 1398 | 91 | ); | 1399 | 91 | if result.is_err() { | 1400 | 0 | out.truncate(len_before); | 1401 | 91 | } | 1402 | 91 | result | 1403 | 91 | } |
Unexecuted instantiation: _RNvMs8_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB5_11CipherState37read_chachapoly_message_to_vec_append |
1404 | | |
1405 | 139 | fn read_chachapoly_message_to_slice( |
1406 | 139 | &'_ mut self, |
1407 | 139 | associated_data: &[u8], |
1408 | 139 | message_data: &[u8], |
1409 | 139 | destination: &mut [u8], |
1410 | 139 | ) -> Result<(), CipherError> { |
1411 | 139 | debug_assert_eq!(destination.len(), message_data.len() - 16); |
1412 | | |
1413 | 139 | if self.nonce_has_overflowed { |
1414 | 0 | return Err(CipherError::NonceOverflow); |
1415 | 139 | } |
1416 | 139 | |
1417 | 139 | // Messages that are missing a HMAC are invalid. |
1418 | 139 | if message_data.len() < 16 { |
1419 | 0 | return Err(CipherError::MissingHmac); |
1420 | 139 | } |
1421 | 139 | |
1422 | 139 | let (mut cipher, mut mac) = self.prepare(associated_data); |
1423 | 139 | |
1424 | 139 | poly1305::universal_hash::UniversalHash::update_padded( |
1425 | 139 | &mut mac, |
1426 | 139 | &message_data[..message_data.len() - 16], |
1427 | 139 | ); |
1428 | 139 | |
1429 | 139 | // Update the MAC with the length of the associated data and input data. |
1430 | 139 | let mut block = poly1305::universal_hash::generic_array::GenericArray::default(); |
1431 | 139 | block[..8].copy_from_slice(&u64::try_from(associated_data.len()).unwrap().to_le_bytes()); |
1432 | 139 | block[8..].copy_from_slice( |
1433 | 139 | &u64::try_from(message_data.len() - 16) |
1434 | 139 | .unwrap() |
1435 | 139 | .to_le_bytes(), |
1436 | 139 | ); |
1437 | 139 | poly1305::universal_hash::UniversalHash::update(&mut mac, &[block]); |
1438 | 139 | |
1439 | 139 | // Compare the calculated MAC with the one in the payload. |
1440 | 139 | // This is done in constant time. |
1441 | 139 | let obtained_mac_bytes = &message_data[message_data.len() - 16..]; |
1442 | 139 | if poly1305::universal_hash::UniversalHash::verify( |
1443 | 139 | mac, |
1444 | 139 | poly1305::universal_hash::generic_array::GenericArray::from_slice(obtained_mac_bytes), |
1445 | 139 | ) |
1446 | 139 | .is_err() |
1447 | | { |
1448 | 0 | return Err(CipherError::HmacInvalid); |
1449 | 139 | } |
1450 | 139 | |
1451 | 139 | // Only after the MAC has been verified, we copy the data and decrypt it. |
1452 | 139 | // This function returns an error if the cipher stream is exhausted. Because we recreate |
1453 | 139 | // a cipher stream every time we encode a message, and a message is maximum 2^16 bytes, |
1454 | 139 | // this can't happen. |
1455 | 139 | chacha20::cipher::StreamCipher::apply_keystream_b2b( |
1456 | 139 | &mut cipher, |
1457 | 139 | &message_data[..message_data.len() - 16], |
1458 | 139 | destination, |
1459 | 139 | ) |
1460 | 139 | .unwrap_or_else(|_| unreachable!()0 ); Unexecuted instantiation: _RNCNvMs8_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB7_11CipherState32read_chachapoly_message_to_slice0Bd_ Unexecuted instantiation: _RNCNvMs8_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB7_11CipherState32read_chachapoly_message_to_slice0Bd_ |
1461 | 139 | |
1462 | 139 | // Increment the nonce by 1. |
1463 | 139 | (self.nonce, self.nonce_has_overflowed) = self.nonce.overflowing_add(1); |
1464 | 139 | |
1465 | 139 | Ok(()) |
1466 | 139 | } _RNvMs8_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB5_11CipherState32read_chachapoly_message_to_slice Line | Count | Source | 1405 | 139 | fn read_chachapoly_message_to_slice( | 1406 | 139 | &'_ mut self, | 1407 | 139 | associated_data: &[u8], | 1408 | 139 | message_data: &[u8], | 1409 | 139 | destination: &mut [u8], | 1410 | 139 | ) -> Result<(), CipherError> { | 1411 | 139 | debug_assert_eq!(destination.len(), message_data.len() - 16); | 1412 | | | 1413 | 139 | if self.nonce_has_overflowed { | 1414 | 0 | return Err(CipherError::NonceOverflow); | 1415 | 139 | } | 1416 | 139 | | 1417 | 139 | // Messages that are missing a HMAC are invalid. | 1418 | 139 | if message_data.len() < 16 { | 1419 | 0 | return Err(CipherError::MissingHmac); | 1420 | 139 | } | 1421 | 139 | | 1422 | 139 | let (mut cipher, mut mac) = self.prepare(associated_data); | 1423 | 139 | | 1424 | 139 | poly1305::universal_hash::UniversalHash::update_padded( | 1425 | 139 | &mut mac, | 1426 | 139 | &message_data[..message_data.len() - 16], | 1427 | 139 | ); | 1428 | 139 | | 1429 | 139 | // Update the MAC with the length of the associated data and input data. | 1430 | 139 | let mut block = poly1305::universal_hash::generic_array::GenericArray::default(); | 1431 | 139 | block[..8].copy_from_slice(&u64::try_from(associated_data.len()).unwrap().to_le_bytes()); | 1432 | 139 | block[8..].copy_from_slice( | 1433 | 139 | &u64::try_from(message_data.len() - 16) | 1434 | 139 | .unwrap() | 1435 | 139 | .to_le_bytes(), | 1436 | 139 | ); | 1437 | 139 | poly1305::universal_hash::UniversalHash::update(&mut mac, &[block]); | 1438 | 139 | | 1439 | 139 | // Compare the calculated MAC with the one in the payload. | 1440 | 139 | // This is done in constant time. | 1441 | 139 | let obtained_mac_bytes = &message_data[message_data.len() - 16..]; | 1442 | 139 | if poly1305::universal_hash::UniversalHash::verify( | 1443 | 139 | mac, | 1444 | 139 | poly1305::universal_hash::generic_array::GenericArray::from_slice(obtained_mac_bytes), | 1445 | 139 | ) | 1446 | 139 | .is_err() | 1447 | | { | 1448 | 0 | return Err(CipherError::HmacInvalid); | 1449 | 139 | } | 1450 | 139 | | 1451 | 139 | // Only after the MAC has been verified, we copy the data and decrypt it. | 1452 | 139 | // This function returns an error if the cipher stream is exhausted. Because we recreate | 1453 | 139 | // a cipher stream every time we encode a message, and a message is maximum 2^16 bytes, | 1454 | 139 | // this can't happen. | 1455 | 139 | chacha20::cipher::StreamCipher::apply_keystream_b2b( | 1456 | 139 | &mut cipher, | 1457 | 139 | &message_data[..message_data.len() - 16], | 1458 | 139 | destination, | 1459 | 139 | ) | 1460 | 139 | .unwrap_or_else(|_| unreachable!()); | 1461 | 139 | | 1462 | 139 | // Increment the nonce by 1. | 1463 | 139 | (self.nonce, self.nonce_has_overflowed) = self.nonce.overflowing_add(1); | 1464 | 139 | | 1465 | 139 | Ok(()) | 1466 | 139 | } |
Unexecuted instantiation: _RNvMs8_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB5_11CipherState32read_chachapoly_message_to_slice |
1467 | | |
1468 | 280 | fn prepare(&self, associated_data: &[u8]) -> (chacha20::ChaCha20, poly1305::Poly1305) { |
1469 | 280 | let mut cipher = { |
1470 | 280 | let nonce = { |
1471 | 280 | let mut out = [0; 12]; |
1472 | 280 | out[4..].copy_from_slice(&self.nonce.to_le_bytes()); |
1473 | 280 | out |
1474 | 280 | }; |
1475 | 280 | |
1476 | 280 | <chacha20::ChaCha20 as chacha20::cipher::KeyIvInit>::new( |
1477 | 280 | chacha20::cipher::generic_array::GenericArray::from_slice(&self.key[..]), |
1478 | 280 | chacha20::cipher::generic_array::GenericArray::from_slice(&nonce[..]), |
1479 | 280 | ) |
1480 | 280 | }; |
1481 | 280 | |
1482 | 280 | let mut mac = { |
1483 | 280 | let mut mac_key = zeroize::Zeroizing::new([0u8; 32]); |
1484 | 280 | chacha20::cipher::StreamCipher::apply_keystream(&mut cipher, &mut *mac_key); |
1485 | 280 | chacha20::cipher::StreamCipherSeek::seek(&mut cipher, 64); |
1486 | 280 | <poly1305::Poly1305 as poly1305::universal_hash::KeyInit>::new( |
1487 | 280 | poly1305::universal_hash::generic_array::GenericArray::from_slice(&*mac_key), |
1488 | 280 | ) |
1489 | 280 | }; |
1490 | 280 | |
1491 | 280 | poly1305::universal_hash::UniversalHash::update_padded(&mut mac, associated_data); |
1492 | 280 | |
1493 | 280 | (cipher, mac) |
1494 | 280 | } _RNvMs8_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noiseNtB5_11CipherState7prepare Line | Count | Source | 1468 | 280 | fn prepare(&self, associated_data: &[u8]) -> (chacha20::ChaCha20, poly1305::Poly1305) { | 1469 | 280 | let mut cipher = { | 1470 | 280 | let nonce = { | 1471 | 280 | let mut out = [0; 12]; | 1472 | 280 | out[4..].copy_from_slice(&self.nonce.to_le_bytes()); | 1473 | 280 | out | 1474 | 280 | }; | 1475 | 280 | | 1476 | 280 | <chacha20::ChaCha20 as chacha20::cipher::KeyIvInit>::new( | 1477 | 280 | chacha20::cipher::generic_array::GenericArray::from_slice(&self.key[..]), | 1478 | 280 | chacha20::cipher::generic_array::GenericArray::from_slice(&nonce[..]), | 1479 | 280 | ) | 1480 | 280 | }; | 1481 | 280 | | 1482 | 280 | let mut mac = { | 1483 | 280 | let mut mac_key = zeroize::Zeroizing::new([0u8; 32]); | 1484 | 280 | chacha20::cipher::StreamCipher::apply_keystream(&mut cipher, &mut *mac_key); | 1485 | 280 | chacha20::cipher::StreamCipherSeek::seek(&mut cipher, 64); | 1486 | 280 | <poly1305::Poly1305 as poly1305::universal_hash::KeyInit>::new( | 1487 | 280 | poly1305::universal_hash::generic_array::GenericArray::from_slice(&*mac_key), | 1488 | 280 | ) | 1489 | 280 | }; | 1490 | 280 | | 1491 | 280 | poly1305::universal_hash::UniversalHash::update_padded(&mut mac, associated_data); | 1492 | 280 | | 1493 | 280 | (cipher, mac) | 1494 | 280 | } |
Unexecuted instantiation: _RNvMs8_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noiseNtB5_11CipherState7prepare |
1495 | | } |
1496 | | |
1497 | | // Implementation of `MixHash`. See <https://noiseprotocol.org/noise.html#the-symmetricstate-object>. |
1498 | 192 | fn mix_hash(hash: &mut [u8; 32], data: &[u8]) { |
1499 | 192 | let mut hasher = <sha2::Sha256 as sha2::Digest>::new(); |
1500 | 192 | sha2::Digest::update(&mut hasher, *hash); |
1501 | 192 | sha2::Digest::update(&mut hasher, data); |
1502 | 192 | sha2::Digest::finalize_into( |
1503 | 192 | hasher, |
1504 | 192 | sha2::digest::generic_array::GenericArray::from_mut_slice(hash), |
1505 | 192 | ); |
1506 | 192 | } _RNvNtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noise8mix_hash Line | Count | Source | 1498 | 192 | fn mix_hash(hash: &mut [u8; 32], data: &[u8]) { | 1499 | 192 | let mut hasher = <sha2::Sha256 as sha2::Digest>::new(); | 1500 | 192 | sha2::Digest::update(&mut hasher, *hash); | 1501 | 192 | sha2::Digest::update(&mut hasher, data); | 1502 | 192 | sha2::Digest::finalize_into( | 1503 | 192 | hasher, | 1504 | 192 | sha2::digest::generic_array::GenericArray::from_mut_slice(hash), | 1505 | 192 | ); | 1506 | 192 | } |
Unexecuted instantiation: _RNvNtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noise8mix_hash |
1507 | | |
1508 | | // Implementation of `HKDF`. See <https://noiseprotocol.org/noise.html#hash-functions>. |
1509 | | // |
1510 | | // Contrary to the version in the Noise specification, this always returns 2 outputs. The third |
1511 | | // output version is never used in this module. |
1512 | 96 | fn hkdf(chaining_key: &[u8; 32], input_key_material: &[u8]) -> HkdfOutput { |
1513 | 288 | fn hmac_hash<'a>( |
1514 | 288 | key: &[u8; 32], |
1515 | 288 | data: impl IntoIterator<Item = &'a [u8]>, |
1516 | 288 | ) -> zeroize::Zeroizing<[u8; 32]> { |
1517 | 288 | // Formula is: `H(K XOR opad, H(K XOR ipad, text))` |
1518 | 288 | // See <https://www.ietf.org/rfc/rfc2104.txt>. |
1519 | 288 | let mut ipad = [0x36u8; 64]; |
1520 | 288 | let mut opad = [0x5cu8; 64]; |
1521 | 96 | |
1522 | 96 | // Algorithm says that we have to zero-extend `key` to 64 bits, then XOR `ipad` and `opad` |
1523 | 96 | // with that zero-extended `key`. Given that XOR'ing with 0 is a no-op, we don't care with |
1524 | 96 | // that and just XOR the key without zero-extending it. |
1525 | 9.21k | for n in 0..key.len()288 { |
1526 | 9.21k | ipad[n] ^= key[n]; |
1527 | 9.21k | opad[n] ^= key[n]; |
1528 | 9.21k | } |
1529 | 96 | |
1530 | 288 | let intermediary_result = { |
1531 | 288 | let mut hasher = <sha2::Sha256 as sha2::Digest>::new(); |
1532 | 288 | sha2::Digest::update(&mut hasher, ipad); |
1533 | 672 | for data384 in data { |
1534 | 384 | sha2::Digest::update(&mut hasher, data); |
1535 | 384 | } |
1536 | 288 | sha2::Digest::finalize(hasher) |
1537 | 288 | }; |
1538 | 288 | |
1539 | 288 | let mut hasher = <sha2::Sha256 as sha2::Digest>::new(); |
1540 | 288 | sha2::Digest::update(&mut hasher, opad); |
1541 | 288 | sha2::Digest::update(&mut hasher, intermediary_result); |
1542 | 288 | |
1543 | 288 | let mut output = zeroize::Zeroizing::new([0; 32]); |
1544 | 288 | sha2::Digest::finalize_into( |
1545 | 288 | hasher, |
1546 | 288 | sha2::digest::generic_array::GenericArray::from_mut_slice(&mut *output), |
1547 | 288 | ); |
1548 | 288 | output |
1549 | 288 | } _RINvNvNtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noise4hkdf9hmac_hashARShj1_EBa_ Line | Count | Source | 1513 | 192 | fn hmac_hash<'a>( | 1514 | 192 | key: &[u8; 32], | 1515 | 192 | data: impl IntoIterator<Item = &'a [u8]>, | 1516 | 192 | ) -> zeroize::Zeroizing<[u8; 32]> { | 1517 | 192 | // Formula is: `H(K XOR opad, H(K XOR ipad, text))` | 1518 | 192 | // See <https://www.ietf.org/rfc/rfc2104.txt>. | 1519 | 192 | let mut ipad = [0x36u8; 64]; | 1520 | 192 | let mut opad = [0x5cu8; 64]; | 1521 | | | 1522 | | // Algorithm says that we have to zero-extend `key` to 64 bits, then XOR `ipad` and `opad` | 1523 | | // with that zero-extended `key`. Given that XOR'ing with 0 is a no-op, we don't care with | 1524 | | // that and just XOR the key without zero-extending it. | 1525 | 6.14k | for n in 0..key.len()192 { | 1526 | 6.14k | ipad[n] ^= key[n]; | 1527 | 6.14k | opad[n] ^= key[n]; | 1528 | 6.14k | } | 1529 | | | 1530 | 192 | let intermediary_result = { | 1531 | 192 | let mut hasher = <sha2::Sha256 as sha2::Digest>::new(); | 1532 | 192 | sha2::Digest::update(&mut hasher, ipad); | 1533 | 384 | for data192 in data { | 1534 | 192 | sha2::Digest::update(&mut hasher, data); | 1535 | 192 | } | 1536 | 192 | sha2::Digest::finalize(hasher) | 1537 | 192 | }; | 1538 | 192 | | 1539 | 192 | let mut hasher = <sha2::Sha256 as sha2::Digest>::new(); | 1540 | 192 | sha2::Digest::update(&mut hasher, opad); | 1541 | 192 | sha2::Digest::update(&mut hasher, intermediary_result); | 1542 | 192 | | 1543 | 192 | let mut output = zeroize::Zeroizing::new([0; 32]); | 1544 | 192 | sha2::Digest::finalize_into( | 1545 | 192 | hasher, | 1546 | 192 | sha2::digest::generic_array::GenericArray::from_mut_slice(&mut *output), | 1547 | 192 | ); | 1548 | 192 | output | 1549 | 192 | } |
_RINvNvNtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noise4hkdf9hmac_hashARShj2_EBa_ Line | Count | Source | 1513 | 96 | fn hmac_hash<'a>( | 1514 | 96 | key: &[u8; 32], | 1515 | 96 | data: impl IntoIterator<Item = &'a [u8]>, | 1516 | 96 | ) -> zeroize::Zeroizing<[u8; 32]> { | 1517 | 96 | // Formula is: `H(K XOR opad, H(K XOR ipad, text))` | 1518 | 96 | // See <https://www.ietf.org/rfc/rfc2104.txt>. | 1519 | 96 | let mut ipad = [0x36u8; 64]; | 1520 | 96 | let mut opad = [0x5cu8; 64]; | 1521 | | | 1522 | | // Algorithm says that we have to zero-extend `key` to 64 bits, then XOR `ipad` and `opad` | 1523 | | // with that zero-extended `key`. Given that XOR'ing with 0 is a no-op, we don't care with | 1524 | | // that and just XOR the key without zero-extending it. | 1525 | 3.07k | for n in 0..key.len()96 { | 1526 | 3.07k | ipad[n] ^= key[n]; | 1527 | 3.07k | opad[n] ^= key[n]; | 1528 | 3.07k | } | 1529 | | | 1530 | 96 | let intermediary_result = { | 1531 | 96 | let mut hasher = <sha2::Sha256 as sha2::Digest>::new(); | 1532 | 96 | sha2::Digest::update(&mut hasher, ipad); | 1533 | 288 | for data192 in data { | 1534 | 192 | sha2::Digest::update(&mut hasher, data); | 1535 | 192 | } | 1536 | 96 | sha2::Digest::finalize(hasher) | 1537 | 96 | }; | 1538 | 96 | | 1539 | 96 | let mut hasher = <sha2::Sha256 as sha2::Digest>::new(); | 1540 | 96 | sha2::Digest::update(&mut hasher, opad); | 1541 | 96 | sha2::Digest::update(&mut hasher, intermediary_result); | 1542 | 96 | | 1543 | 96 | let mut output = zeroize::Zeroizing::new([0; 32]); | 1544 | 96 | sha2::Digest::finalize_into( | 1545 | 96 | hasher, | 1546 | 96 | sha2::digest::generic_array::GenericArray::from_mut_slice(&mut *output), | 1547 | 96 | ); | 1548 | 96 | output | 1549 | 96 | } |
Unexecuted instantiation: _RINvNvNtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noise4hkdf9hmac_hashARShj1_EBa_ Unexecuted instantiation: _RINvNvNtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noise4hkdf9hmac_hashARShj2_EBa_ |
1550 | 96 | |
1551 | 96 | let temp_key = hmac_hash(chaining_key, [input_key_material]); |
1552 | 96 | let output1 = hmac_hash(&temp_key, [&[0x01][..]]); |
1553 | 96 | let output2 = hmac_hash(&temp_key, [&*output1, &[0x02][..]]); |
1554 | 96 | HkdfOutput { output1, output2 } |
1555 | 96 | } _RNvNtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection5noise4hkdf Line | Count | Source | 1512 | 96 | fn hkdf(chaining_key: &[u8; 32], input_key_material: &[u8]) -> HkdfOutput { | 1513 | 96 | fn hmac_hash<'a>( | 1514 | 96 | key: &[u8; 32], | 1515 | 96 | data: impl IntoIterator<Item = &'a [u8]>, | 1516 | 96 | ) -> zeroize::Zeroizing<[u8; 32]> { | 1517 | 96 | // Formula is: `H(K XOR opad, H(K XOR ipad, text))` | 1518 | 96 | // See <https://www.ietf.org/rfc/rfc2104.txt>. | 1519 | 96 | let mut ipad = [0x36u8; 64]; | 1520 | 96 | let mut opad = [0x5cu8; 64]; | 1521 | 96 | | 1522 | 96 | // Algorithm says that we have to zero-extend `key` to 64 bits, then XOR `ipad` and `opad` | 1523 | 96 | // with that zero-extended `key`. Given that XOR'ing with 0 is a no-op, we don't care with | 1524 | 96 | // that and just XOR the key without zero-extending it. | 1525 | 96 | for n in 0..key.len() { | 1526 | 96 | ipad[n] ^= key[n]; | 1527 | 96 | opad[n] ^= key[n]; | 1528 | 96 | } | 1529 | 96 | | 1530 | 96 | let intermediary_result = { | 1531 | 96 | let mut hasher = <sha2::Sha256 as sha2::Digest>::new(); | 1532 | 96 | sha2::Digest::update(&mut hasher, ipad); | 1533 | 96 | for data in data { | 1534 | 96 | sha2::Digest::update(&mut hasher, data); | 1535 | 96 | } | 1536 | 96 | sha2::Digest::finalize(hasher) | 1537 | 96 | }; | 1538 | 96 | | 1539 | 96 | let mut hasher = <sha2::Sha256 as sha2::Digest>::new(); | 1540 | 96 | sha2::Digest::update(&mut hasher, opad); | 1541 | 96 | sha2::Digest::update(&mut hasher, intermediary_result); | 1542 | 96 | | 1543 | 96 | let mut output = zeroize::Zeroizing::new([0; 32]); | 1544 | 96 | sha2::Digest::finalize_into( | 1545 | 96 | hasher, | 1546 | 96 | sha2::digest::generic_array::GenericArray::from_mut_slice(&mut *output), | 1547 | 96 | ); | 1548 | 96 | output | 1549 | 96 | } | 1550 | 96 | | 1551 | 96 | let temp_key = hmac_hash(chaining_key, [input_key_material]); | 1552 | 96 | let output1 = hmac_hash(&temp_key, [&[0x01][..]]); | 1553 | 96 | let output2 = hmac_hash(&temp_key, [&*output1, &[0x02][..]]); | 1554 | 96 | HkdfOutput { output1, output2 } | 1555 | 96 | } |
Unexecuted instantiation: _RNvNtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection5noise4hkdf |
1556 | | |
1557 | | /// Output of the [`hkdf`] function. |
1558 | | struct HkdfOutput { |
1559 | | output1: zeroize::Zeroizing<[u8; 32]>, |
1560 | | output2: zeroize::Zeroizing<[u8; 32]>, |
1561 | | } |
1562 | | |
1563 | | #[cfg(test)] |
1564 | | mod tests { |
1565 | | use core::{cmp, mem}; |
1566 | | |
1567 | | use super::{Config, NoiseHandshake, NoiseKey, ReadWrite}; |
1568 | | |
1569 | | #[test] |
1570 | 1 | fn handshake_basic_works() { |
1571 | 4 | fn test_with_buffer_sizes(mut size1: usize, mut size2: usize) { |
1572 | 4 | let key1 = NoiseKey::new(&rand::random(), &rand::random()); |
1573 | 4 | let key2 = NoiseKey::new(&rand::random(), &rand::random()); |
1574 | 4 | |
1575 | 4 | let mut handshake1 = NoiseHandshake::new(Config { |
1576 | 4 | key: &key1, |
1577 | 4 | is_initiator: true, |
1578 | 4 | prologue: &[], |
1579 | 4 | ephemeral_secret_key: &rand::random(), |
1580 | 4 | }); |
1581 | 4 | let mut handshake2 = NoiseHandshake::new(Config { |
1582 | 4 | key: &key2, |
1583 | 4 | is_initiator: false, |
1584 | 4 | prologue: &[], |
1585 | 4 | ephemeral_secret_key: &rand::random(), |
1586 | 4 | }); |
1587 | 4 | |
1588 | 4 | let mut buf_1_to_2 = Vec::new(); |
1589 | 4 | let mut buf_2_to_1 = Vec::new(); |
1590 | 1 | |
1591 | 16 | while !matches!( |
1592 | 20 | (&handshake1, &handshake2), |
1593 | 1 | ( |
1594 | 1 | NoiseHandshake::Success { .. }, |
1595 | 1 | NoiseHandshake::Success { .. } |
1596 | 1 | ) |
1597 | 1 | ) { |
1598 | 16 | match handshake1 { |
1599 | 1 | NoiseHandshake::Success { .. } => {}0 |
1600 | 16 | NoiseHandshake::InProgress(nego) => { |
1601 | 16 | let mut read_write = ReadWrite { |
1602 | 16 | now: 0, |
1603 | 16 | incoming_buffer: buf_2_to_1, |
1604 | 16 | expected_incoming_bytes: Some(0), |
1605 | 16 | read_bytes: 0, |
1606 | 16 | write_bytes_queued: buf_1_to_2.len(), |
1607 | 16 | write_bytes_queueable: Some(size1 - buf_1_to_2.len()), |
1608 | 16 | write_buffers: vec![mem::take(&mut buf_1_to_2)], |
1609 | 16 | wake_up_after: None, |
1610 | 16 | }; |
1611 | 16 | handshake1 = nego.read_write(&mut read_write).unwrap(); |
1612 | 16 | buf_2_to_1 = read_write.incoming_buffer; |
1613 | 16 | buf_1_to_2.extend( |
1614 | 16 | read_write |
1615 | 16 | .write_buffers |
1616 | 16 | .drain(..) |
1617 | 44 | .flat_map(|b| b.into_iter()), |
1618 | 16 | ); |
1619 | 16 | size2 = cmp::max(size2, read_write.expected_incoming_bytes.unwrap_or(0)); |
1620 | 16 | } |
1621 | 1 | } |
1622 | 1 | |
1623 | 16 | match handshake2 { |
1624 | 1 | NoiseHandshake::Success { .. } => {}0 |
1625 | 16 | NoiseHandshake::InProgress(nego) => { |
1626 | 16 | let mut read_write = ReadWrite { |
1627 | 16 | now: 0, |
1628 | 16 | incoming_buffer: buf_1_to_2, |
1629 | 16 | expected_incoming_bytes: Some(0), |
1630 | 16 | read_bytes: 0, |
1631 | 16 | write_bytes_queued: buf_2_to_1.len(), |
1632 | 16 | write_bytes_queueable: Some(size2 - buf_2_to_1.len()), |
1633 | 16 | write_buffers: vec![mem::take(&mut buf_2_to_1)], |
1634 | 16 | wake_up_after: None, |
1635 | 16 | }; |
1636 | 16 | handshake2 = nego.read_write(&mut read_write).unwrap(); |
1637 | 16 | buf_1_to_2 = read_write.incoming_buffer; |
1638 | 16 | buf_2_to_1.extend( |
1639 | 16 | read_write |
1640 | 16 | .write_buffers |
1641 | 16 | .drain(..) |
1642 | 28 | .flat_map(|b| b.into_iter()), |
1643 | 16 | ); |
1644 | 16 | size1 = cmp::max(size1, read_write.expected_incoming_bytes.unwrap_or(0)); |
1645 | 16 | } |
1646 | 1 | } |
1647 | 1 | } |
1648 | 4 | } |
1649 | 1 | |
1650 | 1 | test_with_buffer_sizes(256, 256); |
1651 | 1 | test_with_buffer_sizes(1, 1); |
1652 | 1 | test_with_buffer_sizes(1, 2048); |
1653 | 1 | test_with_buffer_sizes(2048, 1); |
1654 | 1 | } |
1655 | | } |