1
//! Password configuration.
2

            
3
use std::{
4
	fmt::{self, Debug, Formatter},
5
	num::NonZeroU32,
6
};
7

            
8
use argon2::{Algorithm, Argon2, Params, Version};
9
use deranged::U32;
10
use serde::{Deserialize, Serialize};
11

            
12
#[cfg(feature = "pbkdf2")]
13
use crate::cipher_suite::pbkdf2::Pbkdf2;
14
use crate::{
15
	cipher_suite::{CipherSuite, SlowHashParams},
16
	Error, Result,
17
};
18

            
19
/// Common password configuration between server and client.
20
7288
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
21
pub struct Config {
22
	/// [`CipherSuite`] for this [`Config`].
23
	pub(crate) cipher_suite: CipherSuite,
24
	/// [`Mhf`] configuration.
25
	mhf: Mhf,
26
}
27

            
28
impl Default for Config {
29
29
	fn default() -> Self {
30
29
		Self::new(
31
29
			Ake::default(),
32
29
			Group::default(),
33
29
			Hash::default(),
34
29
			Mhf::default(),
35
29
		)
36
29
	}
37
}
38

            
39
impl Config {
40
	/// Builds new [`Config`].
41
	#[allow(clippy::too_many_lines)]
42
	#[must_use]
43
	pub const fn new(ake: Ake, group: Group, hash: Hash, mhf: Mhf) -> Self {
44
		#[allow(clippy::enum_glob_use)]
45
		use self::{CipherSuite::*, Group::*, Hash::*, Mhf::*};
46

            
47
247
		match (ake, group, hash, mhf) {
48
6
			(Ake::Ristretto255, Ristretto255, Sha2, mhf @ Argon2(_)) => Self {
49
6
				cipher_suite: Ristretto255Sha2Argon2,
50
6
				mhf,
51
6
			},
52
			#[cfg(feature = "pbkdf2")]
53
6
			(Ake::Ristretto255, Ristretto255, Sha2, mhf @ Pbkdf2(_)) => Self {
54
6
				cipher_suite: Ristretto255Sha2Pbkdf2,
55
6
				mhf,
56
6
			},
57
			#[cfg(feature = "sha3")]
58
6
			(Ake::Ristretto255, Ristretto255, Sha3, mhf @ Argon2(_)) => Self {
59
6
				cipher_suite: Ristretto255Sha3Argon2,
60
6
				mhf,
61
6
			},
62
			#[cfg(all(feature = "sha3", feature = "pbkdf2"))]
63
6
			(Ake::Ristretto255, Ristretto255, Sha3, mhf @ Pbkdf2(_)) => Self {
64
6
				cipher_suite: Ristretto255Sha3Pbkdf2,
65
6
				mhf,
66
6
			},
67
			#[cfg(feature = "blake3")]
68
37
			(Ake::Ristretto255, Ristretto255, Blake3, mhf @ Argon2(_)) => Self {
69
37
				cipher_suite: Ristretto255Blake3Argon2,
70
37
				mhf,
71
37
			},
72
			#[cfg(all(feature = "blake3", feature = "pbkdf2"))]
73
6
			(Ake::Ristretto255, Ristretto255, Blake3, mhf @ Pbkdf2(_)) => Self {
74
6
				cipher_suite: Ristretto255Blake3Pbkdf2,
75
6
				mhf,
76
6
			},
77
6
			(Ake::X25519, Ristretto255, Sha2, mhf @ Argon2(_)) => Self {
78
6
				cipher_suite: X25519Ristretto255Sha2Argon2,
79
6
				mhf,
80
6
			},
81
			#[cfg(feature = "pbkdf2")]
82
6
			(Ake::X25519, Ristretto255, Sha2, mhf @ Pbkdf2(_)) => Self {
83
6
				cipher_suite: X25519Ristretto255Sha2Pbkdf2,
84
6
				mhf,
85
6
			},
86
			#[cfg(feature = "sha3")]
87
6
			(Ake::X25519, Ristretto255, Sha3, mhf @ Argon2(_)) => Self {
88
6
				cipher_suite: X25519Ristretto255Sha3Argon2,
89
6
				mhf,
90
6
			},
91
			#[cfg(all(feature = "sha3", feature = "pbkdf2"))]
92
6
			(Ake::X25519, Ristretto255, Sha3, mhf @ Pbkdf2(_)) => Self {
93
6
				cipher_suite: X25519Ristretto255Sha3Pbkdf2,
94
6
				mhf,
95
6
			},
96
			#[cfg(feature = "blake3")]
97
6
			(Ake::X25519, Ristretto255, Blake3, mhf @ Argon2(_)) => Self {
98
6
				cipher_suite: X25519Ristretto255Blake3Argon2,
99
6
				mhf,
100
6
			},
101
			#[cfg(all(feature = "blake3", feature = "pbkdf2"))]
102
6
			(Ake::X25519, Ristretto255, Blake3, mhf @ Pbkdf2(_)) => Self {
103
6
				cipher_suite: X25519Ristretto255Blake3Pbkdf2,
104
6
				mhf,
105
6
			},
106
			#[cfg(feature = "p256")]
107
6
			(Ake::P256, Ristretto255, Sha2, mhf @ Argon2(_)) => Self {
108
6
				cipher_suite: P256Ristretto255Sha2Argon2,
109
6
				mhf,
110
6
			},
111
			#[cfg(all(feature = "p256", feature = "pbkdf2"))]
112
6
			(Ake::P256, Ristretto255, Sha2, mhf @ Pbkdf2(_)) => Self {
113
6
				cipher_suite: P256Ristretto255Sha2Pbkdf2,
114
6
				mhf,
115
6
			},
116
			#[cfg(all(feature = "p256", feature = "sha3"))]
117
6
			(Ake::P256, Ristretto255, Sha3, mhf @ Argon2(_)) => Self {
118
6
				cipher_suite: P256Ristretto255Sha3Argon2,
119
6
				mhf,
120
6
			},
121
			#[cfg(all(feature = "p256", feature = "sha3", feature = "pbkdf2"))]
122
6
			(Ake::P256, Ristretto255, Sha3, mhf @ Pbkdf2(_)) => Self {
123
6
				cipher_suite: P256Ristretto255Sha3Pbkdf2,
124
6
				mhf,
125
6
			},
126
			#[cfg(all(feature = "p256", feature = "blake3"))]
127
6
			(Ake::P256, Ristretto255, Blake3, mhf @ Argon2(_)) => Self {
128
6
				cipher_suite: P256Ristretto255Blake3Argon2,
129
6
				mhf,
130
6
			},
131
			#[cfg(all(feature = "p256", feature = "blake3", feature = "pbkdf2"))]
132
6
			(Ake::P256, Ristretto255, Blake3, mhf @ Pbkdf2(_)) => Self {
133
6
				cipher_suite: P256Ristretto255Blake3Pbkdf2,
134
6
				mhf,
135
6
			},
136
			#[cfg(feature = "p256")]
137
6
			(Ake::P256, P256, Sha2, mhf @ Argon2(_)) => Self {
138
6
				cipher_suite: P256Sha2Argon2,
139
6
				mhf,
140
6
			},
141
			#[cfg(all(feature = "p256", feature = "pbkdf2"))]
142
6
			(Ake::P256, P256, Sha2, mhf @ Pbkdf2(_)) => Self {
143
6
				cipher_suite: P256Sha2Pbkdf2,
144
6
				mhf,
145
6
			},
146
			#[cfg(all(feature = "p256", feature = "sha3"))]
147
6
			(Ake::P256, P256, Sha3, mhf @ Argon2(_)) => Self {
148
6
				cipher_suite: P256Sha3Argon2,
149
6
				mhf,
150
6
			},
151
			#[cfg(all(feature = "p256", feature = "sha3", feature = "pbkdf2"))]
152
6
			(Ake::P256, P256, Sha3, mhf @ Pbkdf2(_)) => Self {
153
6
				cipher_suite: P256Sha3Pbkdf2,
154
6
				mhf,
155
6
			},
156
			#[cfg(all(feature = "p256", feature = "blake3"))]
157
6
			(Ake::P256, P256, Blake3, mhf @ Argon2(_)) => Self {
158
6
				cipher_suite: P256Blake3Argon2,
159
6
				mhf,
160
6
			},
161
			#[cfg(all(feature = "p256", feature = "blake3", feature = "pbkdf2"))]
162
6
			(Ake::P256, P256, Blake3, mhf @ Pbkdf2(_)) => Self {
163
6
				cipher_suite: P256Blake3Pbkdf2,
164
6
				mhf,
165
6
			},
166
			#[cfg(feature = "p256")]
167
6
			(Ake::Ristretto255, P256, Sha2, mhf @ Argon2(_)) => Self {
168
6
				cipher_suite: Ristretto255P256Sha2Argon2,
169
6
				mhf,
170
6
			},
171
			#[cfg(all(feature = "p256", feature = "pbkdf2"))]
172
6
			(Ake::Ristretto255, P256, Sha2, mhf @ Pbkdf2(_)) => Self {
173
6
				cipher_suite: Ristretto255P256Sha2Pbkdf2,
174
6
				mhf,
175
6
			},
176
			#[cfg(all(feature = "p256", feature = "sha3"))]
177
6
			(Ake::Ristretto255, P256, Sha3, mhf @ Argon2(_)) => Self {
178
6
				cipher_suite: Ristretto255P256Sha3Argon2,
179
6
				mhf,
180
6
			},
181
			#[cfg(all(feature = "p256", feature = "sha3", feature = "pbkdf2"))]
182
6
			(Ake::Ristretto255, P256, Sha3, mhf @ Pbkdf2(_)) => Self {
183
6
				cipher_suite: Ristretto255P256Sha3Pbkdf2,
184
6
				mhf,
185
6
			},
186
			#[cfg(all(feature = "p256", feature = "blake3"))]
187
6
			(Ake::Ristretto255, P256, Blake3, mhf @ Argon2(_)) => Self {
188
6
				cipher_suite: Ristretto255P256Blake3Argon2,
189
6
				mhf,
190
6
			},
191
			#[cfg(all(feature = "p256", feature = "blake3", feature = "pbkdf2"))]
192
6
			(Ake::Ristretto255, P256, Blake3, mhf @ Pbkdf2(_)) => Self {
193
6
				cipher_suite: Ristretto255P256Blake3Pbkdf2,
194
6
				mhf,
195
6
			},
196
			#[cfg(feature = "p256")]
197
6
			(Ake::X25519, P256, Sha2, mhf @ Argon2(_)) => Self {
198
6
				cipher_suite: X25519P256Sha2Argon2,
199
6
				mhf,
200
6
			},
201
			#[cfg(all(feature = "p256", feature = "pbkdf2"))]
202
6
			(Ake::X25519, P256, Sha2, mhf @ Pbkdf2(_)) => Self {
203
6
				cipher_suite: X25519P256Sha2Pbkdf2,
204
6
				mhf,
205
6
			},
206
			#[cfg(all(feature = "p256", feature = "sha3"))]
207
6
			(Ake::X25519, P256, Sha3, mhf @ Argon2(_)) => Self {
208
6
				cipher_suite: X25519P256Sha3Argon2,
209
6
				mhf,
210
6
			},
211
			#[cfg(all(feature = "p256", feature = "sha3", feature = "pbkdf2"))]
212
6
			(Ake::X25519, P256, Sha3, mhf @ Pbkdf2(_)) => Self {
213
6
				cipher_suite: X25519P256Sha3Pbkdf2,
214
6
				mhf,
215
6
			},
216
			#[cfg(all(feature = "p256", feature = "blake3"))]
217
6
			(Ake::X25519, P256, Blake3, mhf @ Argon2(_)) => Self {
218
6
				cipher_suite: X25519P256Blake3Argon2,
219
6
				mhf,
220
6
			},
221
			#[cfg(all(feature = "p256", feature = "blake3", feature = "pbkdf2"))]
222
6
			(Ake::X25519, P256, Blake3, mhf @ Pbkdf2(_)) => Self {
223
6
				cipher_suite: X25519P256Blake3Pbkdf2,
224
6
				mhf,
225
6
			},
226
		}
227
247
	}
228

            
229
	/// Returns [`Ake`] of this [`Config`].
230
	#[must_use]
231
72
	pub const fn ake(self) -> Ake {
232
72
		#[allow(clippy::enum_glob_use)]
233
72
		use CipherSuite::*;
234
72

            
235
72
		#[allow(clippy::match_same_arms)]
236
72
		match self.cipher_suite {
237
2
			Ristretto255Sha2Argon2 => Ake::Ristretto255,
238
			#[cfg(feature = "pbkdf2")]
239
2
			Ristretto255Sha2Pbkdf2 => Ake::Ristretto255,
240
			#[cfg(feature = "sha3")]
241
2
			Ristretto255Sha3Argon2 => Ake::Ristretto255,
242
			#[cfg(all(feature = "sha3", feature = "pbkdf2"))]
243
2
			Ristretto255Sha3Pbkdf2 => Ake::Ristretto255,
244
			#[cfg(feature = "blake3")]
245
2
			Ristretto255Blake3Argon2 => Ake::Ristretto255,
246
			#[cfg(all(feature = "blake3", feature = "pbkdf2"))]
247
2
			Ristretto255Blake3Pbkdf2 => Ake::Ristretto255,
248
2
			X25519Ristretto255Sha2Argon2 => Ake::X25519,
249
			#[cfg(feature = "pbkdf2")]
250
2
			X25519Ristretto255Sha2Pbkdf2 => Ake::X25519,
251
			#[cfg(feature = "sha3")]
252
2
			X25519Ristretto255Sha3Argon2 => Ake::X25519,
253
			#[cfg(all(feature = "sha3", feature = "pbkdf2"))]
254
2
			X25519Ristretto255Sha3Pbkdf2 => Ake::X25519,
255
			#[cfg(feature = "blake3")]
256
2
			X25519Ristretto255Blake3Argon2 => Ake::X25519,
257
			#[cfg(all(feature = "blake3", feature = "pbkdf2"))]
258
2
			X25519Ristretto255Blake3Pbkdf2 => Ake::X25519,
259
			#[cfg(feature = "p256")]
260
4
			P256Sha2Argon2 | P256Ristretto255Sha2Argon2 => Ake::P256,
261
			#[cfg(all(feature = "p256", feature = "pbkdf2"))]
262
4
			P256Sha2Pbkdf2 | P256Ristretto255Sha2Pbkdf2 => Ake::P256,
263
			#[cfg(all(feature = "p256", feature = "sha3"))]
264
4
			P256Sha3Argon2 | P256Ristretto255Sha3Argon2 => Ake::P256,
265
			#[cfg(all(feature = "p256", feature = "sha3", feature = "pbkdf2"))]
266
4
			P256Sha3Pbkdf2 | P256Ristretto255Sha3Pbkdf2 => Ake::P256,
267
			#[cfg(all(feature = "p256", feature = "blake3"))]
268
4
			P256Blake3Argon2 | P256Ristretto255Blake3Argon2 => Ake::P256,
269
			#[cfg(all(feature = "p256", feature = "blake3", feature = "pbkdf2"))]
270
4
			P256Blake3Pbkdf2 | P256Ristretto255Blake3Pbkdf2 => Ake::P256,
271
			#[cfg(feature = "p256")]
272
2
			Ristretto255P256Sha2Argon2 => Ake::Ristretto255,
273
			#[cfg(all(feature = "p256", feature = "pbkdf2"))]
274
2
			Ristretto255P256Sha2Pbkdf2 => Ake::Ristretto255,
275
			#[cfg(all(feature = "p256", feature = "sha3"))]
276
2
			Ristretto255P256Sha3Argon2 => Ake::Ristretto255,
277
			#[cfg(all(feature = "p256", feature = "sha3", feature = "pbkdf2"))]
278
2
			Ristretto255P256Sha3Pbkdf2 => Ake::Ristretto255,
279
			#[cfg(all(feature = "p256", feature = "blake3"))]
280
2
			Ristretto255P256Blake3Argon2 => Ake::Ristretto255,
281
			#[cfg(all(feature = "p256", feature = "blake3", feature = "pbkdf2"))]
282
2
			Ristretto255P256Blake3Pbkdf2 => Ake::Ristretto255,
283
			#[cfg(feature = "p256")]
284
2
			X25519P256Sha2Argon2 => Ake::X25519,
285
			#[cfg(all(feature = "p256", feature = "pbkdf2"))]
286
2
			X25519P256Sha2Pbkdf2 => Ake::X25519,
287
			#[cfg(all(feature = "p256", feature = "sha3"))]
288
2
			X25519P256Sha3Argon2 => Ake::X25519,
289
			#[cfg(all(feature = "p256", feature = "sha3", feature = "pbkdf2"))]
290
2
			X25519P256Sha3Pbkdf2 => Ake::X25519,
291
			#[cfg(all(feature = "p256", feature = "blake3"))]
292
2
			X25519P256Blake3Argon2 => Ake::X25519,
293
			#[cfg(all(feature = "p256", feature = "blake3", feature = "pbkdf2"))]
294
2
			X25519P256Blake3Pbkdf2 => Ake::X25519,
295
		}
296
72
	}
297

            
298
	/// Returns [`Group`] of this [`Config`].
299
	#[must_use]
300
648
	pub const fn group(self) -> Group {
301
648
		#[allow(clippy::enum_glob_use)]
302
648
		use CipherSuite::*;
303
648

            
304
648
		#[allow(clippy::match_same_arms)]
305
648
		match self.cipher_suite {
306
36
			Ristretto255Sha2Argon2 | X25519Ristretto255Sha2Argon2 => Group::Ristretto255,
307
			#[cfg(feature = "pbkdf2")]
308
36
			Ristretto255Sha2Pbkdf2 | X25519Ristretto255Sha2Pbkdf2 => Group::Ristretto255,
309
			#[cfg(feature = "sha3")]
310
36
			Ristretto255Sha3Argon2 | X25519Ristretto255Sha3Argon2 => Group::Ristretto255,
311
			#[cfg(all(feature = "sha3", feature = "pbkdf2"))]
312
36
			Ristretto255Sha3Pbkdf2 | X25519Ristretto255Sha3Pbkdf2 => Group::Ristretto255,
313
			#[cfg(feature = "blake3")]
314
36
			Ristretto255Blake3Argon2 | X25519Ristretto255Blake3Argon2 => Group::Ristretto255,
315
			#[cfg(all(feature = "blake3", feature = "pbkdf2"))]
316
36
			Ristretto255Blake3Pbkdf2 | X25519Ristretto255Blake3Pbkdf2 => Group::Ristretto255,
317
			#[cfg(feature = "p256")]
318
18
			P256Ristretto255Sha2Argon2 => Group::Ristretto255,
319
			#[cfg(all(feature = "p256", feature = "pbkdf2"))]
320
18
			P256Ristretto255Sha2Pbkdf2 => Group::Ristretto255,
321
			#[cfg(all(feature = "p256", feature = "sha3"))]
322
18
			P256Ristretto255Sha3Argon2 => Group::Ristretto255,
323
			#[cfg(all(feature = "p256", feature = "sha3", feature = "pbkdf2"))]
324
18
			P256Ristretto255Sha3Pbkdf2 => Group::Ristretto255,
325
			#[cfg(all(feature = "p256", feature = "blake3"))]
326
18
			P256Ristretto255Blake3Argon2 => Group::Ristretto255,
327
			#[cfg(all(feature = "p256", feature = "blake3", feature = "pbkdf2"))]
328
18
			P256Ristretto255Blake3Pbkdf2 => Group::Ristretto255,
329
			#[cfg(feature = "p256")]
330
54
			P256Sha2Argon2 | Ristretto255P256Sha2Argon2 | X25519P256Sha2Argon2 => Group::P256,
331
			#[cfg(all(feature = "p256", feature = "pbkdf2"))]
332
54
			P256Sha2Pbkdf2 | Ristretto255P256Sha2Pbkdf2 | X25519P256Sha2Pbkdf2 => Group::P256,
333
			#[cfg(all(feature = "p256", feature = "sha3"))]
334
54
			P256Sha3Argon2 | Ristretto255P256Sha3Argon2 | X25519P256Sha3Argon2 => Group::P256,
335
			#[cfg(all(feature = "p256", feature = "sha3", feature = "pbkdf2"))]
336
54
			P256Sha3Pbkdf2 | Ristretto255P256Sha3Pbkdf2 | X25519P256Sha3Pbkdf2 => Group::P256,
337
			#[cfg(all(feature = "p256", feature = "blake3"))]
338
54
			P256Blake3Argon2 | Ristretto255P256Blake3Argon2 | X25519P256Blake3Argon2 => Group::P256,
339
			#[cfg(all(feature = "p256", feature = "blake3", feature = "pbkdf2"))]
340
54
			P256Blake3Pbkdf2 | Ristretto255P256Blake3Pbkdf2 | X25519P256Blake3Pbkdf2 => Group::P256,
341
		}
342
648
	}
343

            
344
	/// Returns [`Hash`](self::Hash) of this [`Config`].
345
	#[must_use]
346
72
	pub const fn crypto_hash(self) -> Hash {
347
72
		#[allow(clippy::enum_glob_use)]
348
72
		use CipherSuite::*;
349
72

            
350
72
		#[allow(clippy::match_same_arms)]
351
72
		match self.cipher_suite {
352
4
			Ristretto255Sha2Argon2 | X25519Ristretto255Sha2Argon2 => Hash::Sha2,
353
			#[cfg(feature = "pbkdf2")]
354
4
			Ristretto255Sha2Pbkdf2 | X25519Ristretto255Sha2Pbkdf2 => Hash::Sha2,
355
			#[cfg(feature = "sha3")]
356
4
			Ristretto255Sha3Argon2 | X25519Ristretto255Sha3Argon2 => Hash::Sha3,
357
			#[cfg(all(feature = "sha3", feature = "pbkdf2"))]
358
4
			Ristretto255Sha3Pbkdf2 | X25519Ristretto255Sha3Pbkdf2 => Hash::Sha3,
359
			#[cfg(feature = "blake3")]
360
4
			Ristretto255Blake3Argon2 | X25519Ristretto255Blake3Argon2 => Hash::Blake3,
361
			#[cfg(all(feature = "blake3", feature = "pbkdf2"))]
362
4
			Ristretto255Blake3Pbkdf2 | X25519Ristretto255Blake3Pbkdf2 => Hash::Blake3,
363
			#[cfg(feature = "p256")]
364
			P256Sha2Argon2
365
			| P256Ristretto255Sha2Argon2
366
			| Ristretto255P256Sha2Argon2
367
8
			| X25519P256Sha2Argon2 => Hash::Sha2,
368
			#[cfg(all(feature = "p256", feature = "pbkdf2"))]
369
			P256Sha2Pbkdf2
370
			| P256Ristretto255Sha2Pbkdf2
371
			| Ristretto255P256Sha2Pbkdf2
372
8
			| X25519P256Sha2Pbkdf2 => Hash::Sha2,
373
			#[cfg(all(feature = "p256", feature = "sha3"))]
374
			P256Sha3Argon2
375
			| P256Ristretto255Sha3Argon2
376
			| Ristretto255P256Sha3Argon2
377
8
			| X25519P256Sha3Argon2 => Hash::Sha3,
378
			#[cfg(all(feature = "p256", feature = "sha3", feature = "pbkdf2"))]
379
			P256Sha3Pbkdf2
380
			| P256Ristretto255Sha3Pbkdf2
381
			| Ristretto255P256Sha3Pbkdf2
382
8
			| X25519P256Sha3Pbkdf2 => Hash::Sha3,
383
			#[cfg(all(feature = "p256", feature = "blake3"))]
384
			P256Blake3Argon2
385
			| P256Ristretto255Blake3Argon2
386
			| Ristretto255P256Blake3Argon2
387
8
			| X25519P256Blake3Argon2 => Hash::Blake3,
388
			#[cfg(all(feature = "p256", feature = "blake3", feature = "pbkdf2"))]
389
			P256Blake3Pbkdf2
390
			| P256Ristretto255Blake3Pbkdf2
391
			| Ristretto255P256Blake3Pbkdf2
392
8
			| X25519P256Blake3Pbkdf2 => Hash::Blake3,
393
		}
394
72
	}
395

            
396
	/// Returns [`Mhf`] of this [`Config`].
397
	#[must_use]
398
529
	pub const fn mhf(self) -> Mhf {
399
529
		self.mhf
400
529
	}
401
}
402

            
403
/// Authenticated Key-Exchange for OPAQUE.
404
216
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
405
pub enum Ake {
406
	/// Ristretto255.
407
	Ristretto255,
408
	/// X25519.
409
	X25519,
410
	/// P256.
411
	#[cfg(feature = "p256")]
412
	P256,
413
}
414

            
415
impl Default for Ake {
416
31
	fn default() -> Self {
417
31
		Self::Ristretto255
418
31
	}
419
}
420

            
421
/// Prime-order group algorithm for OPAQUE.
422
216
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
423
pub enum Group {
424
	/// Ristretto255, uses SHA-512 or SHA3-512 respectively.
425
	Ristretto255,
426
	/// P256, uses SHA-256 or SHA3-256 respectively.
427
	#[cfg(feature = "p256")]
428
	P256,
429
}
430

            
431
impl Default for Group {
432
31
	fn default() -> Self {
433
31
		Self::Ristretto255
434
31
	}
435
}
436

            
437
/// Hash algorithm for OPAQUE.
438
72
#[derive(Clone, Copy, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
439
pub enum Hash {
440
	/// SHA-2, size depends on selected [`Group`].
441
	Sha2,
442
	/// SHA-3, size depends on selected [`Group`].
443
	#[cfg(feature = "sha3")]
444
	Sha3,
445
	/// BLAKE3.
446
	#[cfg(feature = "blake3")]
447
	Blake3,
448
}
449

            
450
impl Debug for Hash {
451
144
	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
452
144
		match self {
453
48
			Self::Sha2 => write!(f, "SHA-2"),
454
			#[cfg(feature = "sha3")]
455
48
			Self::Sha3 => write!(f, "SHA-3"),
456
			#[cfg(feature = "blake3")]
457
48
			Self::Blake3 => write!(f, "BLAKE3"),
458
		}
459
144
	}
460
}
461

            
462
impl Default for Hash {
463
31
	fn default() -> Self {
464
31
		#[cfg(feature = "blake3")]
465
31
		return Self::Blake3;
466
31
		#[cfg(all(not(feature = "blake3"), feature = "sha3"))]
467
31
		return Self::Sha3;
468
31
		#[cfg(all(not(feature = "blake3"), not(feature = "sha3")))]
469
31
		return Self::Sha2;
470
31
	}
471
}
472

            
473
/// Memory-hardening function for OPAQUE.
474
7288
#[derive(Clone, Copy, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
475
pub enum Mhf {
476
	/// Argon2.
477
	Argon2(Argon2Params),
478
	/// PBKDF2.
479
	#[cfg(feature = "pbkdf2")]
480
	Pbkdf2(Pbkdf2Params),
481
}
482

            
483
impl Debug for Mhf {
484
	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
485
144
		match self {
486
			Self::Argon2(Argon2Params {
487
				algorithm: Argon2Algorithm::Argon2id,
488
				..
489
36
			}) => write!(f, "Argon2id"),
490
			Self::Argon2(Argon2Params {
491
				algorithm: Argon2Algorithm::Argon2d,
492
				..
493
36
			}) => write!(f, "Argon2d"),
494
			#[cfg(feature = "pbkdf2")]
495
			Self::Pbkdf2(Pbkdf2Params {
496
				hash: Pbkdf2Hash::Sha256,
497
				..
498
36
			}) => write!(f, "PBKDF2-SHA256"),
499
			#[cfg(feature = "pbkdf2")]
500
			Self::Pbkdf2(Pbkdf2Params {
501
				hash: Pbkdf2Hash::Sha512,
502
				..
503
36
			}) => write!(f, "PBKDF2-SHA512"),
504
		}
505
144
	}
506
}
507

            
508
impl Default for Mhf {
509
29
	fn default() -> Self {
510
29
		Self::Argon2(Argon2Params::default())
511
29
	}
512
}
513

            
514
impl Mhf {
515
	/// Converts [`Mhf`] to [`SlowHashParams`].
516
457
	pub(crate) fn to_slow_hash(self) -> SlowHashParams {
517
457
		match self {
518
241
			Self::Argon2(config) => {
519
241
				let algorithm = match config.algorithm {
520
131
					Argon2Algorithm::Argon2id => Algorithm::Argon2id,
521
110
					Argon2Algorithm::Argon2d => Algorithm::Argon2d,
522
				};
523

            
524
241
				SlowHashParams::Argon2(Argon2::new(
525
241
					algorithm,
526
241
					Version::default(),
527
241
					Params::new(
528
241
						config.m_cost.get(),
529
241
						config.t_cost.get(),
530
241
						config.p_cost.get(),
531
241
						None,
532
241
					)
533
241
					.expect("unexpected parameter"),
534
241
				))
535
			}
536
			#[cfg(feature = "pbkdf2")]
537
216
			Self::Pbkdf2(config) => SlowHashParams::Pbkdf2(Pbkdf2(config)),
538
		}
539
457
	}
540
}
541

            
542
/// Configuration for [`Mhf::Argon2`].
543
3652
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
544
pub struct Argon2Params {
545
	/// Specific algorithm to use.
546
	pub algorithm: Argon2Algorithm,
547
	/// Number of memory blocks.
548
	pub m_cost: U32<{ Params::MIN_M_COST }, { Params::MAX_M_COST }>,
549
	/// Number of passes.
550
	pub t_cost: NonZeroU32,
551
	/// Number of threads.
552
	pub p_cost: U32<{ Params::MIN_P_COST }, { Params::MAX_P_COST }>,
553
}
554

            
555
1
#[test]
556
1
fn argon2_ranges() {
557
1
	assert_eq!(8, Params::MIN_M_COST);
558
1
	assert_eq!(0x0fff_ffff, Params::MAX_M_COST);
559
1
	assert_eq!(1, Params::MIN_T_COST);
560
1
	assert_eq!(0xffff_ffff, Params::MAX_T_COST);
561
1
	assert_eq!(1, Params::MIN_P_COST);
562
1
	assert_eq!(0x00ff_ffff, Params::MAX_P_COST);
563
1
}
564

            
565
impl Argon2Params {
566
	/// Construct a new [`Argon2Params`], checking for correct integer ranges.
567
	///
568
	/// # Errors
569
	/// [`Error::MhfConfig`] if `m_cost`, `t_cost` or `p_cost` are out of range:
570
	/// - `m_cost`: 8 - 0x0fffffff
571
	/// - `t_cost`: 1 - 0xffffffff
572
	/// - `p_cost`: 1 - 0x00ffffff
573
6
	pub fn new<
574
6
		A: Into<Option<Argon2Algorithm>>,
575
6
		M: Into<Option<u32>>,
576
6
		T: Into<Option<u32>>,
577
6
		P: Into<Option<u32>>,
578
6
	>(
579
6
		algorithm: A,
580
6
		m_cost: M,
581
6
		t_cost: T,
582
6
		p_cost: P,
583
6
	) -> Result<Self> {
584
6
		Ok(Self {
585
6
			algorithm: algorithm.into().unwrap_or_default(),
586
6
			m_cost: U32::new(m_cost.into().unwrap_or(Params::DEFAULT_M_COST))
587
6
				.ok_or(Error::MhfConfig)?,
588
6
			t_cost: NonZeroU32::new(t_cost.into().unwrap_or(Params::DEFAULT_T_COST))
589
6
				.ok_or(Error::MhfConfig)?,
590
6
			p_cost: U32::new(p_cost.into().unwrap_or(Params::DEFAULT_P_COST))
591
6
				.ok_or(Error::MhfConfig)?,
592
		})
593
6
	}
594
}
595

            
596
/// Specific algorithm to use for [`Argon2Params`].
597
3690
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
598
pub enum Argon2Algorithm {
599
	/// Argon2id algorithm. Slower then Argon2d, but resists side-channel
600
	/// attacks.
601
	Argon2id,
602
	/// Argon2d algorithm. Faster then Argon2id, but has no resistance against
603
	/// side-channel attacks.
604
	///
605
	/// # Caution
606
	/// Only use this when you trust your host, e.g. not a shared cloud
607
	/// instance.
608
	Argon2d,
609
}
610

            
611
impl Default for Argon2Params {
612
29
	fn default() -> Self {
613
29
		Self {
614
29
			algorithm: Argon2Algorithm::default(),
615
29
			m_cost: U32::new(Params::DEFAULT_M_COST).expect("unexpected cost"),
616
29
			t_cost: NonZeroU32::new(Params::DEFAULT_T_COST).expect("unexpected cost"),
617
29
			p_cost: U32::new(Params::DEFAULT_P_COST).expect("unexpected cost"),
618
29
		}
619
29
	}
620
}
621

            
622
impl Default for Argon2Algorithm {
623
29
	fn default() -> Self {
624
29
		Self::Argon2id
625
29
	}
626
}
627

            
628
/// Configuration for [`Mhf::Pbkdf2`].
629
#[cfg(feature = "pbkdf2")]
630
3636
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
631
pub struct Pbkdf2Params {
632
	/// Specific [hash](Pbkdf2Hash) to use with PBKDF2.
633
	pub hash: Pbkdf2Hash,
634
	/// Number of passes.
635
	pub rounds: NonZeroU32,
636
}
637

            
638
#[cfg(feature = "pbkdf2")]
639
impl Pbkdf2Params {
640
	/// Construct a new [`Pbkdf2Params`], checking for correct integer ranges.
641
	///
642
	/// # Errors
643
	/// [`Error::MhfConfig`] if `rounds` is `0`.
644
4
	pub fn new<H: Into<Option<Pbkdf2Hash>>, R: Into<Option<u32>>>(
645
4
		hash: H,
646
4
		rounds: R,
647
4
	) -> Result<Self> {
648
4
		Ok(Self {
649
4
			hash: hash.into().unwrap_or_default(),
650
4
			rounds: NonZeroU32::new(rounds.into().unwrap_or(10000)).ok_or(Error::MhfConfig)?,
651
		})
652
4
	}
653
}
654

            
655
/// Specific hash to use with PBKDF2.
656
#[cfg(feature = "pbkdf2")]
657
3672
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
658
pub enum Pbkdf2Hash {
659
	/// SHA-256.
660
	Sha256,
661
	/// SHA-512.
662
	Sha512,
663
}
664

            
665
#[cfg(feature = "pbkdf2")]
666
impl Default for Pbkdf2Params {
667
	fn default() -> Self {
668
		Self {
669
			hash: Pbkdf2Hash::default(),
670
			rounds: NonZeroU32::new(10000).expect("unexpected value"),
671
		}
672
	}
673
}
674

            
675
#[cfg(feature = "pbkdf2")]
676
impl Default for Pbkdf2Hash {
677
	fn default() -> Self {
678
		Self::Sha256
679
	}
680
}