/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rustls-0.23.46/src/crypto/cipher.rs
Line | Count | Source |
1 | | use alloc::boxed::Box; |
2 | | use alloc::string::ToString; |
3 | | use core::fmt; |
4 | | |
5 | | use zeroize::Zeroize; |
6 | | |
7 | | use crate::enums::{ContentType, ProtocolVersion}; |
8 | | use crate::error::Error; |
9 | | use crate::msgs::codec; |
10 | | pub use crate::msgs::message::{ |
11 | | BorrowedPayload, InboundOpaqueMessage, InboundPlainMessage, OutboundChunks, |
12 | | OutboundOpaqueMessage, OutboundPlainMessage, PlainMessage, PrefixedPayload, |
13 | | }; |
14 | | use crate::suites::ConnectionTrafficSecrets; |
15 | | |
16 | | /// Factory trait for building `MessageEncrypter` and `MessageDecrypter` for a TLS1.3 cipher suite. |
17 | | pub trait Tls13AeadAlgorithm: Send + Sync { |
18 | | /// Build a `MessageEncrypter` for the given key/iv. |
19 | | fn encrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageEncrypter>; |
20 | | |
21 | | /// Build a `MessageDecrypter` for the given key/iv. |
22 | | fn decrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageDecrypter>; |
23 | | |
24 | | /// The length of key in bytes required by `encrypter()` and `decrypter()`. |
25 | | fn key_len(&self) -> usize; |
26 | | |
27 | | /// Convert the key material from `key`/`iv`, into a `ConnectionTrafficSecrets` item. |
28 | | /// |
29 | | /// May return [`UnsupportedOperationError`] if the AEAD algorithm is not a supported |
30 | | /// variant of `ConnectionTrafficSecrets`. |
31 | | fn extract_keys( |
32 | | &self, |
33 | | key: AeadKey, |
34 | | iv: Iv, |
35 | | ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError>; |
36 | | |
37 | | /// Return `true` if this is backed by a FIPS-approved implementation. |
38 | 0 | fn fips(&self) -> bool { |
39 | 0 | false |
40 | 0 | } Unexecuted instantiation: <_ as rustls::crypto::cipher::Tls13AeadAlgorithm>::fips Unexecuted instantiation: <_ as rustls::crypto::cipher::Tls13AeadAlgorithm>::fips Unexecuted instantiation: <_ as rustls::crypto::cipher::Tls13AeadAlgorithm>::fips |
41 | | } |
42 | | |
43 | | /// Factory trait for building `MessageEncrypter` and `MessageDecrypter` for a TLS1.2 cipher suite. |
44 | | pub trait Tls12AeadAlgorithm: Send + Sync + 'static { |
45 | | /// Build a `MessageEncrypter` for the given key/iv and extra key block (which can be used for |
46 | | /// improving explicit nonce size security, if needed). |
47 | | /// |
48 | | /// The length of `key` is set by [`KeyBlockShape::enc_key_len`]. |
49 | | /// |
50 | | /// The length of `iv` is set by [`KeyBlockShape::fixed_iv_len`]. |
51 | | /// |
52 | | /// The length of `extra` is set by [`KeyBlockShape::explicit_nonce_len`]. |
53 | | fn encrypter(&self, key: AeadKey, iv: &[u8], extra: &[u8]) -> Box<dyn MessageEncrypter>; |
54 | | |
55 | | /// Build a `MessageDecrypter` for the given key/iv. |
56 | | /// |
57 | | /// The length of `key` is set by [`KeyBlockShape::enc_key_len`]. |
58 | | /// |
59 | | /// The length of `iv` is set by [`KeyBlockShape::fixed_iv_len`]. |
60 | | fn decrypter(&self, key: AeadKey, iv: &[u8]) -> Box<dyn MessageDecrypter>; |
61 | | |
62 | | /// Return a `KeyBlockShape` that defines how large the `key_block` is and how it |
63 | | /// is split up prior to calling `encrypter()`, `decrypter()` and/or `extract_keys()`. |
64 | | fn key_block_shape(&self) -> KeyBlockShape; |
65 | | |
66 | | /// Convert the key material from `key`/`iv`, into a `ConnectionTrafficSecrets` item. |
67 | | /// |
68 | | /// The length of `key` is set by [`KeyBlockShape::enc_key_len`]. |
69 | | /// |
70 | | /// The length of `iv` is set by [`KeyBlockShape::fixed_iv_len`]. |
71 | | /// |
72 | | /// The length of `extra` is set by [`KeyBlockShape::explicit_nonce_len`]. |
73 | | /// |
74 | | /// May return [`UnsupportedOperationError`] if the AEAD algorithm is not a supported |
75 | | /// variant of `ConnectionTrafficSecrets`. |
76 | | fn extract_keys( |
77 | | &self, |
78 | | key: AeadKey, |
79 | | iv: &[u8], |
80 | | explicit: &[u8], |
81 | | ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError>; |
82 | | |
83 | | /// Return `true` if this is backed by a FIPS-approved implementation. |
84 | 0 | fn fips(&self) -> bool { |
85 | 0 | false |
86 | 0 | } Unexecuted instantiation: <_ as rustls::crypto::cipher::Tls12AeadAlgorithm>::fips Unexecuted instantiation: <_ as rustls::crypto::cipher::Tls12AeadAlgorithm>::fips Unexecuted instantiation: <_ as rustls::crypto::cipher::Tls12AeadAlgorithm>::fips |
87 | | } |
88 | | |
89 | | /// An error indicating that the AEAD algorithm does not support the requested operation. |
90 | | #[derive(Debug, Eq, PartialEq, Clone, Copy)] |
91 | | pub struct UnsupportedOperationError; |
92 | | |
93 | | impl From<UnsupportedOperationError> for Error { |
94 | 0 | fn from(value: UnsupportedOperationError) -> Self { |
95 | 0 | Self::General(value.to_string()) |
96 | 0 | } Unexecuted instantiation: <rustls::error::Error as core::convert::From<rustls::crypto::cipher::UnsupportedOperationError>>::from Unexecuted instantiation: <rustls::error::Error as core::convert::From<rustls::crypto::cipher::UnsupportedOperationError>>::from Unexecuted instantiation: <rustls::error::Error as core::convert::From<rustls::crypto::cipher::UnsupportedOperationError>>::from |
97 | | } |
98 | | |
99 | | impl fmt::Display for UnsupportedOperationError { |
100 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
101 | 0 | write!(f, "operation not supported") |
102 | 0 | } Unexecuted instantiation: <rustls::crypto::cipher::UnsupportedOperationError as core::fmt::Display>::fmt Unexecuted instantiation: <rustls::crypto::cipher::UnsupportedOperationError as core::fmt::Display>::fmt Unexecuted instantiation: <rustls::crypto::cipher::UnsupportedOperationError as core::fmt::Display>::fmt |
103 | | } |
104 | | |
105 | | #[cfg(feature = "std")] |
106 | | impl std::error::Error for UnsupportedOperationError {} |
107 | | |
108 | | /// How a TLS1.2 `key_block` is partitioned. |
109 | | /// |
110 | | /// Note: ciphersuites with non-zero `mac_key_length` are not currently supported. |
111 | | pub struct KeyBlockShape { |
112 | | /// How long keys are. |
113 | | /// |
114 | | /// `enc_key_length` terminology is from the standard ([RFC5246 A.6]). |
115 | | /// |
116 | | /// [RFC5246 A.6]: <https://www.rfc-editor.org/rfc/rfc5246#appendix-A.6> |
117 | | pub enc_key_len: usize, |
118 | | |
119 | | /// How long the fixed part of the 'IV' is. |
120 | | /// |
121 | | /// `fixed_iv_length` terminology is from the standard ([RFC5246 A.6]). |
122 | | /// |
123 | | /// This isn't usually an IV, but we continue the |
124 | | /// terminology misuse to match the standard. |
125 | | /// |
126 | | /// [RFC5246 A.6]: <https://www.rfc-editor.org/rfc/rfc5246#appendix-A.6> |
127 | | pub fixed_iv_len: usize, |
128 | | |
129 | | /// This is a non-standard extension which extends the |
130 | | /// key block to provide an initial explicit nonce offset, |
131 | | /// in a deterministic and safe way. GCM needs this, |
132 | | /// chacha20poly1305 works this way by design. |
133 | | pub explicit_nonce_len: usize, |
134 | | } |
135 | | |
136 | | /// Objects with this trait can decrypt TLS messages. |
137 | | pub trait MessageDecrypter: Send + Sync { |
138 | | /// Decrypt the given TLS message `msg`, using the sequence number |
139 | | /// `seq` which can be used to derive a unique [`Nonce`]. |
140 | | fn decrypt<'a>( |
141 | | &mut self, |
142 | | msg: InboundOpaqueMessage<'a>, |
143 | | seq: u64, |
144 | | ) -> Result<InboundPlainMessage<'a>, Error>; |
145 | | } |
146 | | |
147 | | /// Objects with this trait can encrypt TLS messages. |
148 | | pub trait MessageEncrypter: Send + Sync { |
149 | | /// Encrypt the given TLS message `msg`, using the sequence number |
150 | | /// `seq` which can be used to derive a unique [`Nonce`]. |
151 | | fn encrypt( |
152 | | &mut self, |
153 | | msg: OutboundPlainMessage<'_>, |
154 | | seq: u64, |
155 | | ) -> Result<OutboundOpaqueMessage, Error>; |
156 | | |
157 | | /// Return the length of the ciphertext that results from encrypting plaintext of |
158 | | /// length `payload_len` |
159 | | fn encrypted_payload_len(&self, payload_len: usize) -> usize; |
160 | | } |
161 | | |
162 | | impl dyn MessageEncrypter { |
163 | 0 | pub(crate) fn invalid() -> Box<dyn MessageEncrypter> { |
164 | 0 | Box::new(InvalidMessageEncrypter {}) |
165 | 0 | } Unexecuted instantiation: <dyn rustls::crypto::cipher::MessageEncrypter>::invalid Unexecuted instantiation: <dyn rustls::crypto::cipher::MessageEncrypter>::invalid Unexecuted instantiation: <dyn rustls::crypto::cipher::MessageEncrypter>::invalid |
166 | | } |
167 | | |
168 | | impl dyn MessageDecrypter { |
169 | 0 | pub(crate) fn invalid() -> Box<dyn MessageDecrypter> { |
170 | 0 | Box::new(InvalidMessageDecrypter {}) |
171 | 0 | } Unexecuted instantiation: <dyn rustls::crypto::cipher::MessageDecrypter>::invalid Unexecuted instantiation: <dyn rustls::crypto::cipher::MessageDecrypter>::invalid Unexecuted instantiation: <dyn rustls::crypto::cipher::MessageDecrypter>::invalid |
172 | | } |
173 | | |
174 | | /// A write or read IV. |
175 | | #[derive(Default)] |
176 | | pub struct Iv([u8; NONCE_LEN]); |
177 | | |
178 | | impl Iv { |
179 | | /// Create a new `Iv` from a byte array, of precisely `NONCE_LEN` bytes. |
180 | | #[cfg(feature = "tls12")] |
181 | | pub fn new(value: [u8; NONCE_LEN]) -> Self { |
182 | | Self(value) |
183 | | } |
184 | | |
185 | | /// Create a new `Iv` from a byte slice, of precisely `NONCE_LEN` bytes. |
186 | | #[cfg(feature = "tls12")] |
187 | | pub fn copy(value: &[u8]) -> Self { |
188 | | debug_assert_eq!(value.len(), NONCE_LEN); |
189 | | let mut iv = Self::new(Default::default()); |
190 | | iv.0.copy_from_slice(value); |
191 | | iv |
192 | | } |
193 | | } |
194 | | |
195 | | impl From<[u8; NONCE_LEN]> for Iv { |
196 | 0 | fn from(bytes: [u8; NONCE_LEN]) -> Self { |
197 | 0 | Self(bytes) |
198 | 0 | } Unexecuted instantiation: <rustls::crypto::cipher::Iv as core::convert::From<[u8; 12]>>::from Unexecuted instantiation: <rustls::crypto::cipher::Iv as core::convert::From<[u8; 12]>>::from Unexecuted instantiation: <rustls::crypto::cipher::Iv as core::convert::From<[u8; 12]>>::from |
199 | | } |
200 | | |
201 | | impl AsRef<[u8]> for Iv { |
202 | 0 | fn as_ref(&self) -> &[u8] { |
203 | 0 | self.0.as_ref() |
204 | 0 | } Unexecuted instantiation: <rustls::crypto::cipher::Iv as core::convert::AsRef<[u8]>>::as_ref Unexecuted instantiation: <rustls::crypto::cipher::Iv as core::convert::AsRef<[u8]>>::as_ref Unexecuted instantiation: <rustls::crypto::cipher::Iv as core::convert::AsRef<[u8]>>::as_ref |
205 | | } |
206 | | |
207 | | /// A nonce. This is unique for all messages on a connection. |
208 | | pub struct Nonce(pub [u8; NONCE_LEN]); |
209 | | |
210 | | impl Nonce { |
211 | | /// Combine an `Iv` and sequence number to produce a unique nonce. |
212 | | /// |
213 | | /// This is `iv ^ seq` where `seq` is encoded as a 96-bit big-endian integer. |
214 | | #[inline] |
215 | 0 | pub fn new(iv: &Iv, seq: u64) -> Self { |
216 | 0 | let mut seq_bytes = [0u8; NONCE_LEN]; |
217 | 0 | codec::put_u64(seq, &mut seq_bytes[4..]); |
218 | 0 | Self::new_from_seq(iv, seq_bytes) |
219 | 0 | } Unexecuted instantiation: <rustls::crypto::cipher::Nonce>::new Unexecuted instantiation: <rustls::crypto::cipher::Nonce>::new Unexecuted instantiation: <rustls::crypto::cipher::Nonce>::new |
220 | | |
221 | | /// Creates a unique nonce based on the `iv`, the packet number `pn` and multipath `path_id`. |
222 | | /// |
223 | | /// The nonce is computed as the XOR between the `iv` and the 96-bit big-ending integer formed |
224 | | /// by concatenating `path_id` and `pn`. |
225 | 0 | pub fn for_path(path_id: u32, iv: &Iv, pn: u64) -> Self { |
226 | 0 | let mut seq_bytes = [0u8; NONCE_LEN]; |
227 | 0 | seq_bytes[0..4].copy_from_slice(&path_id.to_be_bytes()); |
228 | 0 | codec::put_u64(pn, &mut seq_bytes[4..]); |
229 | 0 | Self::new_from_seq(iv, seq_bytes) |
230 | 0 | } Unexecuted instantiation: <rustls::crypto::cipher::Nonce>::for_path Unexecuted instantiation: <rustls::crypto::cipher::Nonce>::for_path Unexecuted instantiation: <rustls::crypto::cipher::Nonce>::for_path |
231 | | |
232 | | /// Creates a unique nonce based on the `iv` and sequence number `seq`. |
233 | | #[inline] |
234 | 0 | fn new_from_seq(iv: &Iv, mut seq: [u8; NONCE_LEN]) -> Self { |
235 | 0 | seq.iter_mut() |
236 | 0 | .zip(iv.0.iter()) |
237 | 0 | .for_each(|(s, iv)| { |
238 | 0 | *s ^= *iv; |
239 | 0 | }); Unexecuted instantiation: <rustls::crypto::cipher::Nonce>::new_from_seq::{closure#0}Unexecuted instantiation: <rustls::crypto::cipher::Nonce>::new_from_seq::{closure#0}Unexecuted instantiation: <rustls::crypto::cipher::Nonce>::new_from_seq::{closure#0} |
240 | | |
241 | 0 | Self(seq) |
242 | 0 | } Unexecuted instantiation: <rustls::crypto::cipher::Nonce>::new_from_seq Unexecuted instantiation: <rustls::crypto::cipher::Nonce>::new_from_seq Unexecuted instantiation: <rustls::crypto::cipher::Nonce>::new_from_seq |
243 | | } |
244 | | |
245 | | /// Size of TLS nonces (incorrectly termed "IV" in standard) for all supported ciphersuites |
246 | | /// (AES-GCM, Chacha20Poly1305) |
247 | | pub const NONCE_LEN: usize = 12; |
248 | | |
249 | | /// Returns a TLS1.3 `additional_data` encoding. |
250 | | /// |
251 | | /// See RFC8446 s5.2 for the `additional_data` definition. |
252 | | #[inline] |
253 | 0 | pub fn make_tls13_aad(payload_len: usize) -> [u8; 5] { |
254 | 0 | let version = ProtocolVersion::TLSv1_2.to_array(); |
255 | 0 | [ |
256 | 0 | ContentType::ApplicationData.into(), |
257 | 0 | // Note: this is `legacy_record_version`, i.e. TLS1.2 even for TLS1.3. |
258 | 0 | version[0], |
259 | 0 | version[1], |
260 | 0 | (payload_len >> 8) as u8, |
261 | 0 | (payload_len & 0xff) as u8, |
262 | 0 | ] |
263 | 0 | } Unexecuted instantiation: rustls::crypto::cipher::make_tls13_aad Unexecuted instantiation: rustls::crypto::cipher::make_tls13_aad Unexecuted instantiation: rustls::crypto::cipher::make_tls13_aad |
264 | | |
265 | | /// Returns a TLS1.2 `additional_data` encoding. |
266 | | /// |
267 | | /// See RFC5246 s6.2.3.3 for the `additional_data` definition. |
268 | | #[inline] |
269 | 0 | pub fn make_tls12_aad( |
270 | 0 | seq: u64, |
271 | 0 | typ: ContentType, |
272 | 0 | vers: ProtocolVersion, |
273 | 0 | len: usize, |
274 | 0 | ) -> [u8; TLS12_AAD_SIZE] { |
275 | 0 | let mut out = [0; TLS12_AAD_SIZE]; |
276 | 0 | codec::put_u64(seq, &mut out[0..]); |
277 | 0 | out[8] = typ.into(); |
278 | 0 | codec::put_u16(vers.into(), &mut out[9..]); |
279 | 0 | codec::put_u16(len as u16, &mut out[11..]); |
280 | 0 | out |
281 | 0 | } Unexecuted instantiation: rustls::crypto::cipher::make_tls12_aad Unexecuted instantiation: rustls::crypto::cipher::make_tls12_aad Unexecuted instantiation: rustls::crypto::cipher::make_tls12_aad |
282 | | |
283 | | const TLS12_AAD_SIZE: usize = 8 + 1 + 2 + 2; |
284 | | |
285 | | /// A key for an AEAD algorithm. |
286 | | /// |
287 | | /// This is a value type for a byte string up to `AeadKey::MAX_LEN` bytes in length. |
288 | | pub struct AeadKey { |
289 | | buf: [u8; Self::MAX_LEN], |
290 | | used: usize, |
291 | | } |
292 | | |
293 | | impl AeadKey { |
294 | | #[cfg(feature = "tls12")] |
295 | | pub(crate) fn new(buf: &[u8]) -> Self { |
296 | | debug_assert!(buf.len() <= Self::MAX_LEN); |
297 | | let mut key = Self::from([0u8; Self::MAX_LEN]); |
298 | | key.buf[..buf.len()].copy_from_slice(buf); |
299 | | key.used = buf.len(); |
300 | | key |
301 | | } |
302 | | |
303 | 0 | pub(crate) fn with_length(self, len: usize) -> Self { |
304 | 0 | assert!(len <= self.used); |
305 | 0 | Self { |
306 | 0 | buf: self.buf, |
307 | 0 | used: len, |
308 | 0 | } |
309 | 0 | } Unexecuted instantiation: <rustls::crypto::cipher::AeadKey>::with_length Unexecuted instantiation: <rustls::crypto::cipher::AeadKey>::with_length Unexecuted instantiation: <rustls::crypto::cipher::AeadKey>::with_length |
310 | | |
311 | | /// Largest possible AEAD key in the ciphersuites we support. |
312 | | pub(crate) const MAX_LEN: usize = 32; |
313 | | } |
314 | | |
315 | | impl Drop for AeadKey { |
316 | 0 | fn drop(&mut self) { |
317 | 0 | self.buf.zeroize(); |
318 | 0 | } Unexecuted instantiation: <rustls::crypto::cipher::AeadKey as core::ops::drop::Drop>::drop Unexecuted instantiation: <rustls::crypto::cipher::AeadKey as core::ops::drop::Drop>::drop Unexecuted instantiation: <rustls::crypto::cipher::AeadKey as core::ops::drop::Drop>::drop |
319 | | } |
320 | | |
321 | | impl AsRef<[u8]> for AeadKey { |
322 | 0 | fn as_ref(&self) -> &[u8] { |
323 | 0 | &self.buf[..self.used] |
324 | 0 | } Unexecuted instantiation: <rustls::crypto::cipher::AeadKey as core::convert::AsRef<[u8]>>::as_ref Unexecuted instantiation: <rustls::crypto::cipher::AeadKey as core::convert::AsRef<[u8]>>::as_ref Unexecuted instantiation: <rustls::crypto::cipher::AeadKey as core::convert::AsRef<[u8]>>::as_ref |
325 | | } |
326 | | |
327 | | impl From<[u8; Self::MAX_LEN]> for AeadKey { |
328 | 0 | fn from(bytes: [u8; Self::MAX_LEN]) -> Self { |
329 | 0 | Self { |
330 | 0 | buf: bytes, |
331 | 0 | used: Self::MAX_LEN, |
332 | 0 | } |
333 | 0 | } Unexecuted instantiation: <rustls::crypto::cipher::AeadKey as core::convert::From<[u8; 32]>>::from Unexecuted instantiation: <rustls::crypto::cipher::AeadKey as core::convert::From<[u8; 32]>>::from Unexecuted instantiation: <rustls::crypto::cipher::AeadKey as core::convert::From<[u8; 32]>>::from |
334 | | } |
335 | | |
336 | | /// A `MessageEncrypter` which doesn't work. |
337 | | struct InvalidMessageEncrypter {} |
338 | | |
339 | | impl MessageEncrypter for InvalidMessageEncrypter { |
340 | 0 | fn encrypt( |
341 | 0 | &mut self, |
342 | 0 | _m: OutboundPlainMessage<'_>, |
343 | 0 | _seq: u64, |
344 | 0 | ) -> Result<OutboundOpaqueMessage, Error> { |
345 | 0 | Err(Error::EncryptError) |
346 | 0 | } Unexecuted instantiation: <rustls::crypto::cipher::InvalidMessageEncrypter as rustls::crypto::cipher::MessageEncrypter>::encrypt Unexecuted instantiation: <rustls::crypto::cipher::InvalidMessageEncrypter as rustls::crypto::cipher::MessageEncrypter>::encrypt Unexecuted instantiation: <rustls::crypto::cipher::InvalidMessageEncrypter as rustls::crypto::cipher::MessageEncrypter>::encrypt |
347 | | |
348 | 0 | fn encrypted_payload_len(&self, payload_len: usize) -> usize { |
349 | 0 | payload_len |
350 | 0 | } Unexecuted instantiation: <rustls::crypto::cipher::InvalidMessageEncrypter as rustls::crypto::cipher::MessageEncrypter>::encrypted_payload_len Unexecuted instantiation: <rustls::crypto::cipher::InvalidMessageEncrypter as rustls::crypto::cipher::MessageEncrypter>::encrypted_payload_len Unexecuted instantiation: <rustls::crypto::cipher::InvalidMessageEncrypter as rustls::crypto::cipher::MessageEncrypter>::encrypted_payload_len |
351 | | } |
352 | | |
353 | | /// A `MessageDecrypter` which doesn't work. |
354 | | struct InvalidMessageDecrypter {} |
355 | | |
356 | | impl MessageDecrypter for InvalidMessageDecrypter { |
357 | 0 | fn decrypt<'a>( |
358 | 0 | &mut self, |
359 | 0 | _m: InboundOpaqueMessage<'a>, |
360 | 0 | _seq: u64, |
361 | 0 | ) -> Result<InboundPlainMessage<'a>, Error> { |
362 | 0 | Err(Error::DecryptError) |
363 | 0 | } Unexecuted instantiation: <rustls::crypto::cipher::InvalidMessageDecrypter as rustls::crypto::cipher::MessageDecrypter>::decrypt Unexecuted instantiation: <rustls::crypto::cipher::InvalidMessageDecrypter as rustls::crypto::cipher::MessageDecrypter>::decrypt Unexecuted instantiation: <rustls::crypto::cipher::InvalidMessageDecrypter as rustls::crypto::cipher::MessageDecrypter>::decrypt |
364 | | } |