1use crate::error::{Error, PatternProblem};
5use std::str::FromStr;
6mod patterns;
7
8pub use self::patterns::{
9 HandshakeChoice, HandshakeModifier, HandshakeModifierList, HandshakePattern,
10 SUPPORTED_HANDSHAKE_PATTERNS,
11};
12
13pub(crate) use self::patterns::{DhToken, HandshakeTokens, MessagePatterns, Token};
14
15#[allow(missing_docs)]
17#[derive(PartialEq, Copy, Clone, Debug)]
18pub enum BaseChoice {
19 Noise,
20}
21
22impl FromStr for BaseChoice {
23 type Err = Error;
24
25 fn from_str(s: &str) -> Result<Self, Self::Err> {
26 use self::BaseChoice::*;
27 match s {
28 "Noise" => Ok(Noise),
29 _ => Err(PatternProblem::UnsupportedBaseType.into()),
30 }
31 }
32}
33
34#[allow(missing_docs)]
36#[derive(PartialEq, Copy, Clone, Debug)]
37pub enum DHChoice {
38 Curve25519,
39 Ed448,
40}
41
42impl FromStr for DHChoice {
43 type Err = Error;
44
45 fn from_str(s: &str) -> Result<Self, Self::Err> {
46 use self::DHChoice::*;
47 match s {
48 "25519" => Ok(Curve25519),
49 "448" => Ok(Ed448),
50 _ => Err(PatternProblem::UnsupportedDhType.into()),
51 }
52 }
53}
54
55#[allow(missing_docs)]
57#[derive(PartialEq, Copy, Clone, Debug)]
58pub enum CipherChoice {
59 ChaChaPoly,
60 #[cfg(feature = "xchachapoly")]
61 XChaChaPoly,
62 AESGCM,
63}
64
65impl FromStr for CipherChoice {
66 type Err = Error;
67
68 fn from_str(s: &str) -> Result<Self, Self::Err> {
69 use self::CipherChoice::*;
70 match s {
71 "ChaChaPoly" => Ok(ChaChaPoly),
72 #[cfg(feature = "xchachapoly")]
73 "XChaChaPoly" => Ok(XChaChaPoly),
74 "AESGCM" => Ok(AESGCM),
75 _ => Err(PatternProblem::UnsupportedCipherType.into()),
76 }
77 }
78}
79
80#[allow(missing_docs)]
82#[derive(PartialEq, Copy, Clone, Debug)]
83pub enum HashChoice {
84 SHA256,
85 SHA512,
86 Blake2s,
87 Blake2b,
88}
89
90impl FromStr for HashChoice {
91 type Err = Error;
92
93 fn from_str(s: &str) -> Result<Self, Self::Err> {
94 use self::HashChoice::*;
95 match s {
96 "SHA256" => Ok(SHA256),
97 "SHA512" => Ok(SHA512),
98 "BLAKE2s" => Ok(Blake2s),
99 "BLAKE2b" => Ok(Blake2b),
100 _ => Err(PatternProblem::UnsupportedHashType.into()),
101 }
102 }
103}
104
105#[cfg(feature = "hfs")]
107#[allow(missing_docs)]
108#[derive(PartialEq, Copy, Clone, Debug)]
109pub enum KemChoice {
110 Kyber1024,
111}
112
113#[cfg(feature = "hfs")]
114impl FromStr for KemChoice {
115 type Err = Error;
116
117 fn from_str(s: &str) -> Result<Self, Self::Err> {
118 use self::KemChoice::*;
119 match s {
120 "Kyber1024" => Ok(Kyber1024),
121 _ => Err(PatternProblem::UnsupportedKemType.into()),
122 }
123 }
124}
125
126#[allow(missing_docs)]
140#[derive(PartialEq, Clone, Debug)]
141pub struct NoiseParams {
142 pub name: String,
143 pub base: BaseChoice,
144 pub handshake: HandshakeChoice,
145 pub dh: DHChoice,
146 #[cfg(feature = "hfs")]
147 pub kem: Option<KemChoice>,
148 pub cipher: CipherChoice,
149 pub hash: HashChoice,
150}
151
152impl NoiseParams {
153 #[cfg(not(feature = "hfs"))]
154 pub fn new(
156 name: String,
157 base: BaseChoice,
158 handshake: HandshakeChoice,
159 dh: DHChoice,
160 cipher: CipherChoice,
161 hash: HashChoice,
162 ) -> Self {
163 NoiseParams { name, base, handshake, dh, cipher, hash }
164 }
165
166 #[cfg(feature = "hfs")]
167 pub fn new(
169 name: String,
170 base: BaseChoice,
171 handshake: HandshakeChoice,
172 dh: DHChoice,
173 kem: Option<KemChoice>,
174 cipher: CipherChoice,
175 hash: HashChoice,
176 ) -> Self {
177 NoiseParams { name, base, handshake, dh, kem, cipher, hash }
178 }
179}
180
181impl FromStr for NoiseParams {
182 type Err = Error;
183
184 #[cfg(not(feature = "hfs"))]
185 fn from_str(s: &str) -> Result<Self, Self::Err> {
186 let mut split = s.split('_');
187 let params = NoiseParams::new(
188 s.to_owned(),
189 split.next().ok_or(PatternProblem::TooFewParameters)?.parse()?,
190 split.next().ok_or(PatternProblem::TooFewParameters)?.parse()?,
191 split.next().ok_or(PatternProblem::TooFewParameters)?.parse()?,
192 split.next().ok_or(PatternProblem::TooFewParameters)?.parse()?,
193 split.next().ok_or(PatternProblem::TooFewParameters)?.parse()?,
194 );
195 if split.next().is_some() {
196 return Err(PatternProblem::UnsupportedModifier.into());
197 }
198 Ok(params)
199 }
200
201 #[cfg(feature = "hfs")]
202 fn from_str(s: &str) -> Result<Self, Self::Err> {
203 let mut split = s.split('_').peekable();
204 let p = NoiseParams::new(
205 s.to_owned(),
206 split.next().ok_or(PatternProblem::TooFewParameters)?.parse()?,
207 split.next().ok_or(PatternProblem::TooFewParameters)?.parse()?,
208 split
209 .peek()
210 .ok_or(PatternProblem::TooFewParameters)?
211 .splitn(2, '+')
212 .nth(0)
213 .ok_or(PatternProblem::TooFewParameters)?
214 .parse()?,
215 split
216 .next()
217 .ok_or(PatternProblem::TooFewParameters)?
218 .splitn(2, '+')
219 .nth(1)
220 .map(|s| s.parse())
221 .transpose()?,
222 split.next().ok_or(PatternProblem::TooFewParameters)?.parse()?,
223 split.next().ok_or(PatternProblem::TooFewParameters)?.parse()?,
224 );
225 if split.next().is_some() {
226 return Err(PatternProblem::UnsupportedModifier.into());
227 }
228
229 if p.handshake.is_hfs() != p.kem.is_some() {
231 return Err(PatternProblem::TooFewParameters.into());
232 }
233 Ok(p)
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240 use std::convert::TryFrom;
241
242 #[test]
243 fn test_simple_handshake() {
244 let _: HandshakePattern = "XX".parse().unwrap();
245 }
246
247 #[test]
248 fn test_basic() {
249 let p: NoiseParams = "Noise_XX_25519_AESGCM_SHA256".parse().unwrap();
250 assert!(p.handshake.modifiers.list.is_empty());
251 }
252
253 #[test]
254 fn test_basic_deferred() {
255 let p: NoiseParams = "Noise_X1X1_25519_AESGCM_SHA256".parse().unwrap();
256 assert!(p.handshake.modifiers.list.is_empty());
257 }
258
259 #[test]
260 fn test_fallback_mod() {
261 let p: NoiseParams = "Noise_XXfallback_25519_AESGCM_SHA256".parse().unwrap();
262 assert!(p.handshake.modifiers.list[0] == HandshakeModifier::Fallback);
263 }
264
265 #[test]
266 fn test_psk_fallback_mod() {
267 let p: NoiseParams = "Noise_XXfallback+psk0_25519_AESGCM_SHA256".parse().unwrap();
268 assert!(p.handshake.modifiers.list.len() == 2);
269 }
270
271 #[test]
272 fn test_single_psk_mod() {
273 let p: NoiseParams = "Noise_XXpsk0_25519_AESGCM_SHA256".parse().unwrap();
274 match p.handshake.modifiers.list[0] {
275 HandshakeModifier::Psk(0) => {},
276 _ => panic!("modifier isn't as expected!"),
277 }
278 }
279
280 #[test]
281 fn test_multi_psk_mod() {
282 use self::HandshakeModifier::*;
283
284 let p: NoiseParams = "Noise_XXpsk0+psk1+psk2_25519_AESGCM_SHA256".parse().unwrap();
285 let mods = p.handshake.modifiers.list;
286 match (mods[0], mods[1], mods[2]) {
287 (Psk(0), Psk(1), Psk(2)) => {},
288 _ => panic!("modifiers weren't as expected! actual: {:?}", mods),
289 }
290 }
291
292 #[test]
293 fn test_duplicate_psk_mod() {
294 assert!("Noise_XXfallback+psk1_25519_AESGCM_SHA256".parse::<NoiseParams>().is_ok());
295 assert_eq!(
296 Error::Pattern(PatternProblem::UnsupportedModifier),
297 "Noise_XXfallback+fallback_25519_AESGCM_SHA256".parse::<NoiseParams>().unwrap_err()
298 );
299 assert_eq!(
300 Error::Pattern(PatternProblem::UnsupportedModifier),
301 "Noise_XXpsk1+psk1_25519_AESGCM_SHA256".parse::<NoiseParams>().unwrap_err()
302 );
303 }
304
305 #[test]
306 fn test_modified_psk_handshake() {
307 let p: NoiseParams = "Noise_XXpsk0_25519_AESGCM_SHA256".parse().unwrap();
308 let tokens = HandshakeTokens::try_from(&p.handshake).unwrap();
309 match tokens.msg_patterns[0][0] {
310 Token::Psk(_) => {},
311 _ => panic!("missing token!"),
312 }
313 }
314
315 #[test]
316 fn test_modified_multi_psk_handshake() {
317 let p: NoiseParams = "Noise_XXpsk0+psk2_25519_AESGCM_SHA256".parse().unwrap();
318
319 let tokens = HandshakeTokens::try_from(&p.handshake).unwrap();
320
321 match tokens.msg_patterns[0][0] {
322 Token::Psk(_) => {},
323 _ => panic!("missing token!"),
324 }
325
326 let second = &tokens.msg_patterns[1];
327 match second[second.len() - 1] {
328 Token::Psk(_) => {},
329 _ => panic!("missing token!"),
330 }
331 }
332
333 #[test]
334 fn test_invalid_psk_handshake() {
335 let p: NoiseParams = "Noise_XXpsk9_25519_AESGCM_SHA256".parse().unwrap();
336
337 assert_eq!(
338 Error::Pattern(PatternProblem::InvalidPsk),
339 HandshakeTokens::try_from(&p.handshake).unwrap_err()
340 );
341 }
342
343 #[test]
344 fn test_extraneous_string_data() {
345 assert_eq!(
346 Error::Pattern(PatternProblem::UnsupportedModifier),
347 "Noise_XXpsk0_25519_AESGCM_SHA256_HackThePlanet".parse::<NoiseParams>().unwrap_err()
348 );
349 }
350}