libpasta 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.gitmodules +3 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +5 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +41 -0
  9. data/Rakefile +37 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/ext/Rakefile +9 -0
  13. data/ext/pasta-bindings/.gitmodules +3 -0
  14. data/ext/pasta-bindings/Makefile +84 -0
  15. data/ext/pasta-bindings/README.md +54 -0
  16. data/ext/pasta-bindings/libpasta/.gitignore +2 -0
  17. data/ext/pasta-bindings/libpasta/.travis.yml +60 -0
  18. data/ext/pasta-bindings/libpasta/Cargo.toml +42 -0
  19. data/ext/pasta-bindings/libpasta/LICENSE.md +21 -0
  20. data/ext/pasta-bindings/libpasta/Makefile +27 -0
  21. data/ext/pasta-bindings/libpasta/README.md +78 -0
  22. data/ext/pasta-bindings/libpasta/benches/bench.rs +125 -0
  23. data/ext/pasta-bindings/libpasta/examples/alternate_key_source.rs +33 -0
  24. data/ext/pasta-bindings/libpasta/examples/config.rs +10 -0
  25. data/ext/pasta-bindings/libpasta/examples/config_hmac.rs +25 -0
  26. data/ext/pasta-bindings/libpasta/examples/hash_password.rs +10 -0
  27. data/ext/pasta-bindings/libpasta/examples/migrate_password.rs +47 -0
  28. data/ext/pasta-bindings/libpasta/examples/verify_password.rs +24 -0
  29. data/ext/pasta-bindings/libpasta/libpasta-capi/Cargo.toml +12 -0
  30. data/ext/pasta-bindings/libpasta/libpasta-capi/ctest/compile +6 -0
  31. data/ext/pasta-bindings/libpasta/libpasta-capi/ctest/test +0 -0
  32. data/ext/pasta-bindings/libpasta/libpasta-capi/ctest/test.c +12 -0
  33. data/ext/pasta-bindings/libpasta/libpasta-capi/include/pasta.h +6 -0
  34. data/ext/pasta-bindings/libpasta/libpasta-capi/src/lib.rs +80 -0
  35. data/ext/pasta-bindings/libpasta/src/bench.rs +39 -0
  36. data/ext/pasta-bindings/libpasta/src/config.rs +358 -0
  37. data/ext/pasta-bindings/libpasta/src/hashing/de.rs +284 -0
  38. data/ext/pasta-bindings/libpasta/src/hashing/mod.rs +161 -0
  39. data/ext/pasta-bindings/libpasta/src/hashing/ser.rs +67 -0
  40. data/ext/pasta-bindings/libpasta/src/key/mod.rs +66 -0
  41. data/ext/pasta-bindings/libpasta/src/lib.rs +468 -0
  42. data/ext/pasta-bindings/libpasta/src/primitives/argon2.rs +180 -0
  43. data/ext/pasta-bindings/libpasta/src/primitives/bcrypt.rs +165 -0
  44. data/ext/pasta-bindings/libpasta/src/primitives/hmac.rs +134 -0
  45. data/ext/pasta-bindings/libpasta/src/primitives/mod.rs +312 -0
  46. data/ext/pasta-bindings/libpasta/src/primitives/pbkdf2.rs +272 -0
  47. data/ext/pasta-bindings/libpasta/src/primitives/scrypt.rs +144 -0
  48. data/ext/pasta-bindings/libpasta/src/sod.rs +46 -0
  49. data/ext/pasta-bindings/libpasta/tests/.libpasta.yaml +8 -0
  50. data/ext/pasta-bindings/libpasta/tests/common/mod.rs +37 -0
  51. data/ext/pasta-bindings/libpasta/tests/test_argon2.rs +8 -0
  52. data/ext/pasta-bindings/libpasta/tests/test_bcrypt.rs +8 -0
  53. data/ext/pasta-bindings/libpasta/tests/test_from_file.rs +13 -0
  54. data/ext/pasta-bindings/libpasta/tests/test_hmac.rs +26 -0
  55. data/ext/pasta-bindings/libpasta/tests/test_pbkdf2.rs +8 -0
  56. data/ext/pasta-bindings/libpasta/tests/test_ringpbkdf2.rs +8 -0
  57. data/ext/pasta-bindings/libpasta/tests/test_scrypt.rs +8 -0
  58. data/ext/pasta-bindings/pasta.i +31 -0
  59. data/ext/pasta-bindings/tests/Makefile +38 -0
  60. data/ext/pasta-bindings/tests/pasta_test.go +12 -0
  61. data/ext/pasta-bindings/tests/test.php +7 -0
  62. data/ext/pasta-bindings/tests/test.py +7 -0
  63. data/ext/pasta-bindings/tests/test.rb +5 -0
  64. data/lib/libpasta.rb +2 -0
  65. data/lib/libpasta/version.rb +3 -0
  66. data/libpasta.gemspec +52 -0
  67. data/spec/libpasta_spec.rb +11 -0
  68. data/spec/spec_helper.rb +11 -0
  69. metadata +183 -0
@@ -0,0 +1,312 @@
1
+ //! `Primitive` in `libpasta` refers to the raw hashing algorithms as
2
+ //! implemented in many libraries.
3
+ //!
4
+ //! The main algorithms here are re-exported for general use.
5
+ //! Each algorithm has a `new` and `default` function. The former can
6
+ //! be provided parameters and creates a new dynamic instance of that
7
+ //! parameter set. Whereas the latter refers to a statically referenced
8
+ //! parameter set.
9
+ //!
10
+ //! All implementations are wrapped in a `Primitive` struct,
11
+ //! which in effect works like a trait, since it derefs to a `PrimitiveImpl`.
12
+ //! This means that whether using a new or default parameter set, the overall
13
+ //! behaviour is equivalent.
14
+
15
+
16
+ /// `Argon2` implementations
17
+ ///
18
+ /// Currently only a native Rust implementation through `argon2rs`.
19
+ mod argon2;
20
+ pub use self::argon2::Argon2;
21
+
22
+ /// `Bcrypt` implementations
23
+ ///
24
+ /// Currently uses `rust_crypto`s `bcrypt` algorithm.
25
+ mod bcrypt;
26
+ pub use self::bcrypt::Bcrypt;
27
+
28
+ /// `HMAC` implementations
29
+ ///
30
+ /// Uses `ring::hmac` to provide an HMAC implementation. Key must either be
31
+ /// passed using `Hmac::with_key` or will be generated randomly with `Hmac::new`.
32
+ /// Still need to consider the best way to maintain keys for an application.
33
+ /// Perhaps need some kind of "key service" module.
34
+ mod hmac;
35
+ pub use self::hmac::Hmac;
36
+
37
+ /// `PBKDF2` implementations.
38
+ ///
39
+ /// Implementations are from both `ring` and the C `fastpbkdf2` implementations.
40
+ /// The latter is currently in use.
41
+ mod pbkdf2;
42
+ pub use self::pbkdf2::{Pbkdf2, RingPbkdf2};
43
+
44
+ /// `Scrypt` implementations.
45
+ ///
46
+ /// Currently uses `ring_pwhash` for the implementation.
47
+ mod scrypt;
48
+ pub use self::scrypt::Scrypt;
49
+
50
+
51
+ use sod::Sod;
52
+
53
+ use config;
54
+
55
+ use itertools::Itertools;
56
+ use itertools::FoldWhile::{Continue, Done};
57
+ use num_traits;
58
+ use num_traits::FromPrimitive;
59
+ use ring::{constant_time, digest};
60
+ use serde_mcf::{Hashes, Map, Value};
61
+
62
+ use std::cmp::Ordering;
63
+ use std::fmt;
64
+ use std::fmt::Write;
65
+ use std::ops::Deref;
66
+ use std::sync::Arc;
67
+
68
+ /// Password hashing primitives
69
+ ///
70
+ /// Each variant is backed up by different implementation.
71
+ /// Internally, primitives can either be static values, for example,
72
+ /// the `lazy_static` generated value `DEFAULT_PRIM`, or dynamically allocated
73
+ /// variables, which are `Arc<Box<...>>`.
74
+ ///
75
+ /// Most operations are expected to be performed using the static functions,
76
+ /// since most use the default algorithms. However, the flexibilty to support
77
+ /// arbitrary parameter sets is essential.
78
+ #[derive(Clone, PartialEq, PartialOrd)]
79
+ pub struct Primitive(pub Sod<PrimitiveImpl>);
80
+
81
+
82
+ impl fmt::Debug for Primitive {
83
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
84
+ write!(f, "{:?}", self.0.deref())
85
+ }
86
+ }
87
+ /// Trait defining the functionality of a hashing primitive.
88
+ pub trait PrimitiveImpl: fmt::Debug + Send + Sync {
89
+ /// Compute the output of the primitive with input `password` and `salt`.
90
+ fn compute(&self, password: &[u8], salt: &[u8]) -> Vec<u8>;
91
+
92
+ /// Verify the password and salt against the hash.
93
+ ///
94
+ /// In many cases, this just checks whether
95
+ /// `compute(password, salt) == hash`.
96
+ fn verify(&self, password: &[u8], salt: &[u8], hash: &[u8]) -> bool {
97
+ constant_time::verify_slices_are_equal(&self.compute(password, salt), hash).is_ok()
98
+ }
99
+
100
+ /// Output the parameters of the primitive as a list of tuples.
101
+ fn params_as_vec(&self) -> Vec<(&'static str, String)>;
102
+
103
+ /// Return algorithm type as a MCF-compatible hash identifier.
104
+ fn hash_id(&self) -> Hashes;
105
+
106
+ /// Use the supplied `Config` to update the current `Primitive` with
107
+ /// a new key source.
108
+ fn update_key(&self, _config: &config::Config) -> Option<Primitive> {
109
+ None
110
+ }
111
+ }
112
+
113
+ impl<P: PrimitiveImpl + 'static> From<P> for Primitive {
114
+ fn from(other: P) -> Self {
115
+ Primitive(Sod::Dynamic(Arc::new(Box::new(other))))
116
+ }
117
+ }
118
+
119
+ impl PartialEq<PrimitiveImpl> for PrimitiveImpl {
120
+ fn eq(&self, other: &PrimitiveImpl) -> bool {
121
+ self.hash_id() == other.hash_id() && self.params_as_vec() == other.params_as_vec()
122
+ }
123
+ }
124
+
125
+ /// Compare two primitive parameterisations by first checking for equality of
126
+ /// the hash identifiers, and then attempting to compare the parameters
127
+ /// numerically.
128
+ impl PartialOrd<PrimitiveImpl> for PrimitiveImpl {
129
+ fn partial_cmp(&self, other: &PrimitiveImpl) -> Option<Ordering> {
130
+ if self.hash_id() == other.hash_id() {
131
+ self.params_as_vec()
132
+ .iter()
133
+ .zip(other.params_as_vec().iter())
134
+ .map(|(x, y)| if x == y {
135
+ Some(Ordering::Equal)
136
+ } else if x.0 != y.0 {
137
+ None
138
+ } else if let Ok(x) = x.1.parse::<f64>() {
139
+ if let Ok(y) = y.1.parse::<f64>() {
140
+ x.partial_cmp(&y)
141
+ } else {
142
+ None
143
+ }
144
+ } else {
145
+ None
146
+ })
147
+ .fold_while(None, |acc, c| if acc.is_none() {
148
+ Continue(c)
149
+ } else if c == acc || c == Some(Ordering::Equal) {
150
+ Continue(acc)
151
+ } else {
152
+ Done(None)
153
+ })
154
+ .into_inner()
155
+ } else {
156
+ None
157
+ }
158
+ }
159
+ }
160
+
161
+
162
+ impl Deref for Primitive {
163
+ type Target = Sod<PrimitiveImpl>;
164
+
165
+ fn deref(&self) -> &Sod<PrimitiveImpl> {
166
+ &self.0
167
+ }
168
+ }
169
+
170
+ #[derive(Debug, PartialEq, PartialOrd)]
171
+ pub(crate) struct Poisoned;
172
+
173
+ impl PrimitiveImpl for Poisoned {
174
+ fn compute(&self, _password: &[u8], _salt: &[u8]) -> Vec<u8> {
175
+ unreachable!()
176
+ }
177
+
178
+ fn verify(&self, _password: &[u8], _salt: &[u8], _hash: &[u8]) -> bool {
179
+ unreachable!()
180
+ }
181
+
182
+ fn params_as_vec(&self) -> Vec<(&'static str, String)> {
183
+ vec![("poisoned", "".to_string())]
184
+ }
185
+
186
+ fn hash_id(&self) -> Hashes {
187
+ Hashes::Custom
188
+ }
189
+ }
190
+
191
+ /// Helper macro to unwrap the value or early return with `Poisoned`.
192
+ /// Necessary until `TryFrom` stabilises.
193
+ macro_rules! try_or_poisoned {
194
+ ($f:expr) => (
195
+ match $f {
196
+ Some(x) => x,
197
+ None => return Poisoned.into()
198
+ }
199
+ )
200
+ }
201
+
202
+ /// This will be `TryFrom` when it stabilises.
203
+ /// For now we just return a `Poisoned`
204
+ impl<'a> From<(&'a Hashes, &'a Map<String, Value>)> for Primitive {
205
+ fn from(other: (&Hashes, &Map<String, Value>)) -> Self {
206
+ match *other.0 {
207
+ Hashes::Argon2i | Hashes::Argon2d => {
208
+ let passes = try_or_poisoned!(other.1.get("t").and_then(value_as_int));
209
+ let lanes = try_or_poisoned!(other.1.get("p").and_then(value_as_int));
210
+ let kib = try_or_poisoned!(other.1.get("m").and_then(value_as_int));
211
+ Argon2::new(passes, lanes, kib)
212
+ }
213
+ Hashes::BcryptMcf => {
214
+ let cost = try_or_poisoned!(other.1.get("cost").and_then(value_as_int));
215
+ Bcrypt::new(cost)
216
+ }
217
+ Hashes::Hmac => {
218
+ let hash_id = try_or_poisoned!(other.1.get("h").and_then(Value::as_str));
219
+ let key_id = try_or_poisoned!(other.1.get("key_id").and_then(Value::as_str));
220
+ Hmac::with_key_id(hash_from_id(hash_id), key_id)
221
+ }
222
+ ref x @ Hashes::Pbkdf2Sha1 |
223
+ ref x @ Hashes::Pbkdf2Sha256 |
224
+ ref x @ Hashes::Pbkdf2Sha512 => {
225
+ let iterations = try_or_poisoned!(other.1.get("n").and_then(value_as_int));
226
+ match *x {
227
+ Hashes::Pbkdf2Sha1 => pbkdf2::Pbkdf2::new(iterations, &digest::SHA1),
228
+ Hashes::Pbkdf2Sha256 => pbkdf2::Pbkdf2::new(iterations, &digest::SHA256),
229
+ Hashes::Pbkdf2Sha512 => pbkdf2::Pbkdf2::new(iterations, &digest::SHA512),
230
+ _ => Poisoned.into() // not actually be possible due to previous matching,
231
+ }
232
+ }
233
+ Hashes::Scrypt => {
234
+ let log_n = try_or_poisoned!(other.1.get("ln").and_then(value_as_int));
235
+ let r = try_or_poisoned!(other.1.get("r").and_then(value_as_int));
236
+ let p = try_or_poisoned!(other.1.get("p").and_then(value_as_int));
237
+ Scrypt::new(log_n, r, p)
238
+ }
239
+ _ => Poisoned.into(),
240
+ }
241
+ }
242
+ }
243
+
244
+ fn value_as_int<T>(val: &Value) -> Option<T>
245
+ where T: num_traits::Num + FromPrimitive
246
+ {
247
+ match *val {
248
+ Value::Number(ref x) => {
249
+ if let Some(x) = x.as_u64() {
250
+ T::from_u64(x)
251
+ } else {
252
+ None
253
+ }
254
+ }
255
+ Value::String(ref s) => T::from_str_radix(s.as_str(), 10).ok(),
256
+ _ => None,
257
+ }
258
+ }
259
+
260
+ impl<'a> From<&'a Primitive> for (Hashes, Map<String, Value>) {
261
+ fn from(other: &Primitive) -> Self {
262
+ let mut map = Map::new();
263
+ for (key, value) in other.0.params_as_vec() {
264
+ let _ = map.insert(key.to_string(), Value::String(value));
265
+ }
266
+ (other.0.hash_id(), map)
267
+ }
268
+ }
269
+
270
+ fn hash_to_id(algorithm: &'static digest::Algorithm) -> String {
271
+ let mut name = String::new();
272
+ #[allow(use_debug)]
273
+ write!(&mut name, "{:?}", algorithm).expect("error writing to String");
274
+ name
275
+ }
276
+
277
+ fn hash_from_id(id: &str) -> &'static digest::Algorithm {
278
+ match id {
279
+ "SHA1" => &digest::SHA1,
280
+ "SHA256" => &digest::SHA256,
281
+ "SHA384" => &digest::SHA384,
282
+ "SHA512" => &digest::SHA512,
283
+ "SHA512_256" => &digest::SHA512_256,
284
+ _ => panic!("Unknown digest algorithm"),
285
+ }
286
+ }
287
+
288
+
289
+ #[cfg(test)]
290
+ mod test {
291
+ use super::*;
292
+
293
+ #[test]
294
+ fn test_comparisons() {
295
+ let bcrypt = Bcrypt::new(10);
296
+ let bcrypt_better = Bcrypt::new(20);
297
+
298
+ let scrypt = Scrypt::new(10, 8, 1);
299
+ let scrypt_better = Scrypt::new(14, 8, 1);
300
+ let scrypt_diff = Scrypt::new(15, 4, 1);
301
+
302
+ assert_eq!(bcrypt, bcrypt);
303
+ assert_eq!(scrypt, scrypt);
304
+
305
+ assert_eq!(bcrypt.partial_cmp(&bcrypt_better), Some(Ordering::Less));
306
+ assert!(scrypt < scrypt_better);
307
+
308
+ assert_eq!(scrypt.partial_cmp(&scrypt_diff), None);
309
+ assert_eq!(scrypt_better.partial_cmp(&scrypt_diff), None);
310
+ assert_eq!(scrypt.partial_cmp(&bcrypt), None);
311
+ }
312
+ }
@@ -0,0 +1,272 @@
1
+ /// Native Rust implementation of scrypt.
2
+ pub use self::fastpbkdf2::Pbkdf2;
3
+ pub use self::ring_pbkdf2::Pbkdf2 as RingPbkdf2;
4
+
5
+ /// Native Rust implementation of scrypt.
6
+ mod ring_pbkdf2 {
7
+ use primitives::{Primitive, PrimitiveImpl};
8
+ use sod::Sod;
9
+
10
+ use ring::{digest, pbkdf2};
11
+ use serde_mcf::Hashes;
12
+
13
+ use std::fmt;
14
+ use std::sync::Arc;
15
+
16
+ use super::super::hash_to_id;
17
+
18
+ /// Struct holding `PBKDF2` parameters.
19
+ ///
20
+ /// This implementation is backed by `ring`.
21
+ pub struct Pbkdf2 {
22
+ iterations: u32,
23
+ algorithm: &'static digest::Algorithm,
24
+ }
25
+
26
+
27
+ impl Pbkdf2 {
28
+ /// Create a new PBKDF2 instance using defaults.
29
+ pub fn default() -> Primitive {
30
+ Primitive(Sod::Dynamic(Arc::clone(&DEFAULT)))
31
+ }
32
+
33
+ /// Create a new PBKDF2 instance.
34
+ pub fn new(iterations: u32, algorithm: &'static digest::Algorithm) -> Primitive {
35
+ Self::new_impl(iterations, algorithm).into()
36
+ }
37
+
38
+ fn new_impl(iterations: u32, algorithm: &'static digest::Algorithm) -> Self {
39
+ Self {
40
+ iterations: iterations,
41
+ algorithm: algorithm,
42
+ }
43
+ }
44
+ }
45
+
46
+ lazy_static! {
47
+ static ref DEFAULT: Arc<Box<PrimitiveImpl>> = {
48
+ Arc::new(Box::new(Pbkdf2::new_impl(10_000, &digest::SHA256)))
49
+ };
50
+ }
51
+
52
+ impl ::primitives::PrimitiveImpl for Pbkdf2 {
53
+ /// Compute the scrypt hash
54
+ fn compute<'a>(&'a self, password: &[u8], salt: &[u8]) -> Vec<u8> {
55
+ let mut hash = vec![0_u8; 32];
56
+ pbkdf2::derive(self.algorithm, self.iterations, salt, password, &mut hash);
57
+ hash
58
+ }
59
+
60
+ /// Convert parameters into a vector of (key, value) tuples
61
+ /// for serializing.
62
+ fn params_as_vec(&self) -> Vec<(&'static str, String)> {
63
+ vec![("n", self.iterations.to_string())]
64
+ }
65
+
66
+ fn hash_id(&self) -> Hashes {
67
+ match hash_to_id(self.algorithm).as_ref() {
68
+ "SHA1" => Hashes::Pbkdf2Sha1,
69
+ "SHA256" => Hashes::Pbkdf2Sha256,
70
+ "SHA512" => Hashes::Pbkdf2Sha512,
71
+ _ => panic!("unexpected digest algorithm"),
72
+ }
73
+ }
74
+ }
75
+
76
+ impl fmt::Debug for Pbkdf2 {
77
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
78
+ write!(f,
79
+ "PBKDF2-{:?}, iterations: {}",
80
+ self.algorithm,
81
+ self.iterations)
82
+ }
83
+ }
84
+ }
85
+
86
+
87
+ /// Native Rust implementation of scrypt.
88
+ mod fastpbkdf2 {
89
+ extern crate fastpbkdf2;
90
+ use self::fastpbkdf2::*;
91
+
92
+ use primitives::{Primitive, PrimitiveImpl};
93
+ use sod::Sod;
94
+
95
+ use ring::digest;
96
+ use serde_mcf::Hashes;
97
+
98
+ use std::fmt;
99
+ use std::sync::Arc;
100
+
101
+ use super::super::hash_to_id;
102
+
103
+ /// Struct holding `PBKDF2` parameters.
104
+ ///
105
+ /// This implementation is backed by `fastpbkdf2`.
106
+ pub struct Pbkdf2 {
107
+ iterations: u32,
108
+ algorithm: fn(&[u8], &[u8], u32, &mut [u8]),
109
+ alg_id: &'static str,
110
+ }
111
+
112
+ lazy_static! {
113
+ static ref DEFAULT: Arc<Box<PrimitiveImpl>> = {
114
+ Arc::new(Box::new(Pbkdf2::new_impl(10_000, &digest::SHA256)))
115
+ };
116
+ }
117
+
118
+ impl Pbkdf2 {
119
+ /// Create a new PBKDF2 instance using defaults.
120
+ pub fn default() -> Primitive {
121
+ Primitive(Sod::Dynamic(Arc::clone(&DEFAULT)))
122
+ }
123
+
124
+ /// Create a new PBKDF2 instance.
125
+ pub fn new(iterations: u32, algorithm: &'static digest::Algorithm) -> Primitive {
126
+ Self::new_impl(iterations, algorithm).into()
127
+ }
128
+
129
+ fn new_impl(iterations: u32, algorithm: &'static digest::Algorithm) -> Self {
130
+ match hash_to_id(algorithm).as_ref() {
131
+ "SHA1" => {
132
+ Self {
133
+ iterations: iterations,
134
+ algorithm: pbkdf2_hmac_sha1,
135
+ alg_id: "SHA1",
136
+ }
137
+ }
138
+ "SHA256" => {
139
+ Self {
140
+ iterations: iterations,
141
+ algorithm: pbkdf2_hmac_sha256,
142
+ alg_id: "SHA256",
143
+ }
144
+ }
145
+ "SHA512" => {
146
+ Self {
147
+ iterations: iterations,
148
+ algorithm: pbkdf2_hmac_sha512,
149
+ alg_id: "SHA512",
150
+ }
151
+ }
152
+ _ => panic!("unexpected digest algorithm"),
153
+ }
154
+ }
155
+ }
156
+
157
+ impl ::primitives::PrimitiveImpl for Pbkdf2 {
158
+ /// Compute the scrypt hash
159
+ fn compute<'a>(&'a self, password: &[u8], salt: &[u8]) -> Vec<u8> {
160
+ let mut hash = vec![0_u8; 32];
161
+ (self.algorithm)(password, salt, self.iterations, &mut hash);
162
+ hash
163
+ }
164
+
165
+ /// Convert parameters into a vector of (key, value) tuples
166
+ /// for serializing.
167
+ fn params_as_vec(&self) -> Vec<(&'static str, String)> {
168
+ vec![("n", self.iterations.to_string())]
169
+ }
170
+
171
+ fn hash_id(&self) -> Hashes {
172
+ match self.alg_id {
173
+ "SHA1" => Hashes::Pbkdf2Sha1,
174
+ "SHA256" => Hashes::Pbkdf2Sha256,
175
+ "SHA512" => Hashes::Pbkdf2Sha512,
176
+ _ => panic!("unexpected digest algorithm"),
177
+ }
178
+ }
179
+ }
180
+
181
+ impl fmt::Debug for Pbkdf2 {
182
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
183
+ write!(f,
184
+ "PBKDF2-{:?}, iterations: {}",
185
+ self.alg_id,
186
+ self.iterations)
187
+ }
188
+ }
189
+ }
190
+
191
+
192
+ #[cfg(test)]
193
+ mod test {
194
+ use ::hashing::*;
195
+ use ring::digest;
196
+ use serde_mcf;
197
+
198
+ #[test]
199
+ fn sanity_check() {
200
+ let password = "hunter2";
201
+ let params = super::Pbkdf2::default();
202
+ println!("{:?}", params);
203
+ let salt = ::get_salt();
204
+ let hash = params.compute(password.as_bytes(), &salt);
205
+ let hash2 = params.compute(password.as_bytes(), &salt);
206
+ assert_eq!(hash, hash2);
207
+ let out = Output {
208
+ alg: Algorithm::Single(params.into()),
209
+ salt: salt,
210
+ hash: hash,
211
+ };
212
+ println!("{:?}", serde_mcf::to_string(&out).unwrap());
213
+ }
214
+
215
+ #[test]
216
+ fn sanity_check_ring() {
217
+ let password = "hunter2";
218
+ let params = super::RingPbkdf2::default();
219
+ println!("{:?}", params);
220
+ let salt = ::get_salt();
221
+ let hash = params.compute(password.as_bytes(), &salt);
222
+ let hash2 = params.compute(password.as_bytes(), &salt);
223
+ assert_eq!(hash, hash2);
224
+ let out = Output {
225
+ alg: Algorithm::Single(params.into()),
226
+ salt: salt,
227
+ hash: hash,
228
+ };
229
+ println!("{:?}", serde_mcf::to_string(&out).unwrap());
230
+ }
231
+
232
+ macro_rules! primitive_round_trip {
233
+ ($prim:expr) => (
234
+ let hash = serde_mcf::to_string(&$prim.hash(&"hunter2")).unwrap();
235
+ let _output: Output = serde_mcf::from_str(&hash).unwrap();
236
+ )
237
+ }
238
+
239
+ #[test]
240
+ fn pbkdf2_params() {
241
+ let params = Algorithm::Single(super::Pbkdf2::new(1_000, &digest::SHA1));
242
+ primitive_round_trip!(params);
243
+
244
+ let params = Algorithm::Single(super::Pbkdf2::new(1_000, &digest::SHA256));
245
+ primitive_round_trip!(params);
246
+
247
+ let params = Algorithm::Single(super::Pbkdf2::new(1_000, &digest::SHA512));
248
+ primitive_round_trip!(params);
249
+
250
+ let params = Algorithm::Single(super::RingPbkdf2::new(1_000, &digest::SHA1));
251
+ primitive_round_trip!(params);
252
+
253
+ let params = Algorithm::Single(super::RingPbkdf2::new(1_000, &digest::SHA256));
254
+ primitive_round_trip!(params);
255
+
256
+ let params = Algorithm::Single(super::RingPbkdf2::new(1_000, &digest::SHA512));
257
+ primitive_round_trip!(params);
258
+
259
+ }
260
+ }
261
+
262
+ #[cfg(features = "bench")]
263
+ mod ring_bench {
264
+ use super::*;
265
+ benches!(RingPbkdf2);
266
+ }
267
+
268
+ #[cfg(features = "bench")]
269
+ mod fast_bench {
270
+ use super::*;
271
+ benches!(Pbkdf2);
272
+ }