libpasta 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
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,66 @@
1
+ //! The `key` module is for managing key sources.
2
+ //!
3
+ //! The idea is that a running application can dynamically insert keys into
4
+ //! the key store, which are used for producing and verifying hashes.
5
+ #![allow(dead_code)]
6
+
7
+ use data_encoding::BASE64_NOPAD;
8
+ use ring::digest;
9
+
10
+ use std::collections::HashMap;
11
+ use std::fmt;
12
+ use std::sync::RwLock;
13
+
14
+ lazy_static!{
15
+ static ref LOCAL_STORE: LocalStore = LocalStore::new();
16
+ }
17
+
18
+ pub(crate) fn get_global() -> &'static LocalStore {
19
+ &*LOCAL_STORE
20
+ }
21
+
22
+ /// Structure used as a global store for keys.
23
+ #[derive(Debug)]
24
+ pub struct LocalStore {
25
+ store: RwLock<HashMap<String, Vec<u8>>>,
26
+ }
27
+
28
+
29
+ /// A key storage source. Permits retrieving and storing keys.
30
+ ///
31
+ /// Keys are indexed by a `String` id, and are stored as Vec<u8>.
32
+ pub trait Store: fmt::Debug + Send + Sync {
33
+ /// Insert a new key into the `Store`.
34
+ fn insert(&self, key: &[u8]) -> String;
35
+
36
+ /// Get a key from the `Store`.
37
+ fn get_key(&self, id: &str) -> Option<Vec<u8>>;
38
+ }
39
+
40
+ impl LocalStore {
41
+ fn new() -> Self {
42
+ Self { store: RwLock::new(HashMap::new()) }
43
+ }
44
+ }
45
+
46
+ impl Store for LocalStore {
47
+ /// Insert a new key into the `KeyStore`.
48
+ fn insert(&self, key: &[u8]) -> String {
49
+ let digest = digest::digest(&digest::SHA512_256, key);
50
+ let key_id = BASE64_NOPAD.encode(digest.as_ref());
51
+ let _ = self.store
52
+ .write()
53
+ .expect("could not get write on key store")
54
+ .insert(key_id.clone(), key.to_vec());
55
+ key_id
56
+ }
57
+
58
+ /// Get a key from the `KeyStore`.
59
+ fn get_key(&self, id: &str) -> Option<Vec<u8>> {
60
+ if let Some(v) = self.store.read().expect("could not get read lock on key store").get(id) {
61
+ Some(v.clone())
62
+ } else {
63
+ None
64
+ }
65
+ }
66
+ }
@@ -0,0 +1,468 @@
1
+ // Copyright (c) 2017, Sam Scott
2
+
3
+ // Permission to use, copy, modify, and/or distribute this software for any
4
+ // purpose with or without fee is hereby granted, provided that the above
5
+ // copyright notice and this permission notice appear in all copies.
6
+
7
+ // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
8
+ // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9
+ // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
10
+ // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11
+ // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
12
+ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13
+ // PERFORMANCE OF THIS SOFTWARE.
14
+
15
+ //! # Pasta - Password Storage
16
+ //! _Making passwords painless_
17
+ //!
18
+ //! This is a library designed to make secure password storage easy.
19
+ //!
20
+ //! For a more comprehensive introduction, see [the homepage](https://libpasta.github.io/)
21
+ //!
22
+ //!
23
+ //! ## Examples
24
+ //!
25
+ //! The basic functionality for computing password hashes is:
26
+ //!
27
+ //! ```
28
+ //! extern crate libpasta;
29
+ //! // We re-export the rpassword crate for CLI password input.
30
+ //! use libpasta::rpassword::*;
31
+ //!
32
+ //! fn main() {
33
+ //! # if false {
34
+ //! let password = prompt_password_stdout("Please enter your password:").unwrap();
35
+ //! # }
36
+ //! # let password = "hunter2";
37
+ //! let password_hash = libpasta::hash_password(password);
38
+ //! println!("The stored password is: '{}'", password_hash);
39
+ //! }
40
+ //! ```
41
+ //! ## Supported formats
42
+ //!
43
+ //! `libpasta` attempts to support some legacy formats. For example, the `bcrypt`
44
+ //! format `$2y$...`.
45
+
46
+ #![cfg_attr(all(feature="bench", test), feature(test))]
47
+
48
+
49
+ #![allow(unknown_lints)]
50
+ #![deny(clippy_pedantic)]
51
+ #![allow(
52
+ missing_docs_in_private_items,
53
+ // we use fn new() -> Primitive for convenience
54
+ new_ret_no_self,
55
+ range_plus_one, // `..=end` not yet stable
56
+ use_debug,
57
+ )]
58
+ #![deny(
59
+ const_err,
60
+ dead_code,
61
+ deprecated,
62
+ exceeding_bitshifts,
63
+ improper_ctypes,
64
+ missing_docs,
65
+ mutable_transmutes,
66
+ no_mangle_const_items,
67
+ non_camel_case_types,
68
+ non_shorthand_field_patterns,
69
+ non_snake_case,
70
+ non_upper_case_globals,
71
+ overflowing_literals,
72
+ path_statements,
73
+ plugin_as_library,
74
+ private_no_mangle_fns,
75
+ private_no_mangle_statics,
76
+ stable_features,
77
+ trivial_casts,
78
+ trivial_numeric_casts,
79
+ unconditional_recursion,
80
+ unknown_crate_types,
81
+ unreachable_code,
82
+ unsafe_code,
83
+ unstable_features,
84
+ unused_allocation,
85
+ unused_assignments,
86
+ unused_attributes,
87
+ unused_comparisons,
88
+ unused_extern_crates,
89
+ unused_features,
90
+ unused_imports,
91
+ unused_import_braces,
92
+ unused_must_use,
93
+ unused_mut,
94
+ unused_parens,
95
+ unused_qualifications,
96
+ unused_results,
97
+ unused_unsafe,
98
+ unused_variables,
99
+ variant_size_differences,
100
+ warnings,
101
+ while_true,
102
+ )]
103
+ #![cfg_attr(all(feature="bench", test), allow(unstable_features))]
104
+
105
+ extern crate data_encoding;
106
+ #[macro_use]
107
+ extern crate error_chain;
108
+ extern crate itertools;
109
+ #[macro_use]
110
+ extern crate lazy_static;
111
+ #[macro_use]
112
+ extern crate log;
113
+ extern crate num_traits;
114
+ extern crate ring;
115
+ extern crate ring_pwhash;
116
+ extern crate serde;
117
+ #[macro_use]
118
+ extern crate serde_derive;
119
+ extern crate serde_mcf;
120
+ extern crate serde_yaml;
121
+
122
+ /// Re-export rpassword for convenience.
123
+ pub mod rpassword {
124
+ extern crate rpassword;
125
+ pub use self::rpassword::*;
126
+ }
127
+
128
+ /// `libpasta` errors.
129
+ pub mod errors {
130
+ use ring;
131
+ use serde_mcf;
132
+ use std::{fmt, result};
133
+ // Create the Error, ErrorKind, ResultExt, and Result types
134
+ error_chain!{
135
+ foreign_links {
136
+ Deserialize(serde_mcf::errors::Error) #[doc = "Errors from de/serializing MCF password hashes."] ;
137
+ Ring(ring::error::Unspecified) #[doc = "Errors originating from `ring`"] ;
138
+ }
139
+ }
140
+
141
+ /// Convenience trait for producing detailed error messages on `expect`.
142
+ pub trait ExpectReport {
143
+ /// Return type on successful `expect`
144
+ type Inner;
145
+ /// Wraps `Result::expect` to produce a longer error message with
146
+ /// instructions for submitting a bug report.
147
+ fn expect_report(self, msg: &str) -> Self::Inner;
148
+ }
149
+
150
+ impl<T, E: fmt::Debug> ExpectReport for result::Result<T, E> {
151
+ type Inner = T;
152
+ fn expect_report(self, msg: &str) -> T {
153
+ self.expect(&format!("{}\nIf you are seeing this message, you have encountered \
154
+ a situation we did not think was possible. Please submit a bug \
155
+ report at https://github.com/libpasta/libpasta/issues with this message.\n", msg))
156
+ }
157
+ }
158
+
159
+ impl<T> ExpectReport for Option<T> {
160
+ type Inner = T;
161
+ fn expect_report(self, msg: &str) -> T {
162
+ self.expect(&format!("{}\nIf you are seeing this message, you have encountered\
163
+ a situation we did not think was possible. Please submit a bug\
164
+ report at https://github.com/libpasta/libpasta/issues with this message.\n", msg))
165
+ }
166
+ }
167
+ }
168
+
169
+ use errors::*;
170
+ use ring::rand::SecureRandom;
171
+
172
+ #[macro_use]
173
+ mod bench;
174
+
175
+ pub mod config;
176
+ pub use config::Config;
177
+ pub mod key;
178
+ pub mod hashing;
179
+ use hashing::Output;
180
+
181
+ pub mod primitives;
182
+
183
+ /// Module to define the Static or Dynamic `Sod` enum.
184
+ pub mod sod;
185
+
186
+ /// Generates a default hash for a given password.
187
+ ///
188
+ /// Will automatically generate a random salt. In the extreme case that the
189
+ /// default source of randomness is unavailable, this will fallback to a seed
190
+ /// generated when the library is initialised. An error will be logged when this
191
+ /// happens.
192
+ ///
193
+ /// This is the simplest way to use libpasta, and uses sane defaults.
194
+ /// ## Panics
195
+ /// A panic indicates a problem with the serialization mechanisms, and should
196
+ /// be reported.
197
+ pub fn hash_password(password: &str) -> String {
198
+ config::DEFAULT_CONFIG.hash_password(password)
199
+ }
200
+
201
+ /// Same as `hash_password` but returns `Result` to allow error handling.
202
+ /// TODO: decide on which API is best to use.
203
+ #[doc(hidden)]
204
+ pub fn hash_password_safe(password: &str) -> Result<String> {
205
+ config::DEFAULT_CONFIG.hash_password_safe(password)
206
+
207
+ }
208
+
209
+ /// Verifies the provided password matches the inputted hash string.
210
+ ///
211
+ /// If there is any error in processing the hash or password, this
212
+ /// will simply return `false`.
213
+ pub fn verify_password(hash: &str, password: &str) -> bool {
214
+ verify_password_safe(hash, password).unwrap_or(false)
215
+ }
216
+
217
+ /// Same as `verify_password` but returns `Result` to allow error handling.
218
+ /// TODO: decide on which API is best to use.
219
+ #[doc(hidden)]
220
+ pub fn verify_password_safe(hash: &str, password: &str) -> Result<bool> {
221
+ let pwd_hash: Output = serde_mcf::from_str(hash)?;
222
+ Ok(pwd_hash.verify(password))
223
+ }
224
+
225
+ /// Verifies a supplied password against a previously computed password hash,
226
+ /// and performs an in-place update of the hash value if the password verifies.
227
+ /// Hence this needs to take a mutable `String` reference.
228
+ pub fn verify_password_update_hash(hash: &mut String, password: &str) -> bool {
229
+ config::DEFAULT_CONFIG.verify_password_update_hash(hash, password)
230
+ }
231
+
232
+ /// Same as `verify_password_update_hash`, but returns `Result` to allow error handling.
233
+ #[doc(hidden)]
234
+ pub fn verify_password_update_hash_safe(hash: &mut String, password: &str) -> Result<bool> {
235
+ config::DEFAULT_CONFIG.verify_password_update_hash_safe(hash, password)
236
+
237
+ }
238
+
239
+
240
+ /// Migrate the input hash to the current recommended hash.
241
+ ///
242
+ /// Note that this does *not* require the password. This is for batch updating
243
+ /// of hashes, where the password is not available. This performs an onion
244
+ /// approach, returning `new_hash(old_hash)`.
245
+ ///
246
+ /// If the password is also available, the `verify_password_update_hash` should
247
+ /// instead be used.
248
+ pub fn migrate_hash(hash: &mut String) {
249
+ config::DEFAULT_CONFIG.migrate_hash(hash)
250
+ }
251
+
252
+ /// Same as `migrate_hash` but returns `Result` to allow error handling.
253
+ #[doc(hidden)]
254
+ pub fn migrate_hash_safe(hash: &mut String) -> Result<()> {
255
+ config::DEFAULT_CONFIG.migrate_hash_safe(hash)
256
+
257
+ }
258
+
259
+ fn gen_salt(rng: &SecureRandom) -> Vec<u8> {
260
+ let mut salt = vec![0_u8; 16];
261
+ if rng.fill(&mut salt).is_ok() {
262
+ salt
263
+ } else {
264
+ error!("failed to get fresh randomness, relying on backup seed to generate pseudoranom output");
265
+ config::backup_gen_salt()
266
+ }
267
+ }
268
+
269
+ #[cfg(test)]
270
+ use ring::rand::SystemRandom;
271
+
272
+ #[cfg(test)]
273
+ fn get_salt() -> Vec<u8> {
274
+ gen_salt(&SystemRandom)
275
+ }
276
+
277
+ #[cfg(test)]
278
+ mod api_tests {
279
+ use super::*;
280
+ use config::DEFAULT_PRIM;
281
+ use hashing::{Algorithm, Output};
282
+ use primitives::{Bcrypt, Hmac};
283
+ use sod::Sod;
284
+
285
+ #[test]
286
+ fn sanity_check() {
287
+ let password = "";
288
+ let hash = hash_password(password);
289
+ println!("Hash: {:?}", hash);
290
+
291
+ // can't use password again
292
+ let password = "";
293
+ assert!(verify_password(&hash, password));
294
+ assert!(!verify_password(&hash, "wrong password"));
295
+
296
+ let password = "hunter2";
297
+ let hash = hash_password(password);
298
+
299
+ // can't use password again
300
+ let password = "hunter2";
301
+ assert!(verify_password(&hash, password));
302
+ assert!(!verify_password(&hash, "wrong password"));
303
+ }
304
+
305
+ #[test]
306
+ fn external_check() {
307
+ let password = "hunter2";
308
+ let hash = "$2a$10$u.Fhlm/a1DpHr/z5KrsLG.iZ7iM9r8DInJvZ57VArRKuhlHAoVZOi";
309
+ let pwd_hash: Output = serde_mcf::from_str(hash).unwrap();
310
+ println!("{:?}", pwd_hash);
311
+
312
+ let expected_hash = pwd_hash.alg.hash_with_salt(password.as_bytes(), &pwd_hash.salt);
313
+ assert_eq!(pwd_hash.hash, &expected_hash[..]);
314
+ assert!(verify_password(&hash, password));
315
+ }
316
+
317
+ #[test]
318
+ fn emoji_password() {
319
+ let password = "emojisaregreat💖💖💖";
320
+ let hash = hash_password(password);
321
+ assert!(verify_password(&hash, password));
322
+ }
323
+
324
+ #[test]
325
+ fn nested_hash() {
326
+ let password = "hunter2";
327
+
328
+ let params = Algorithm::Nested {
329
+ inner: Box::new(Algorithm::default()),
330
+ outer: DEFAULT_PRIM.clone(),
331
+ };
332
+ let hash = params.hash(&password);
333
+
334
+ let password = "hunter2";
335
+ println!("{:?}", hash);
336
+ assert!(hash.verify(&password));
337
+
338
+ let password = "hunter2";
339
+ let hash = serde_mcf::to_string(&hash).unwrap();
340
+ println!("{:?}", hash);
341
+ let _hash: Output = serde_mcf::from_str(&hash).unwrap();
342
+ println!("{:?}", _hash);
343
+ assert!(verify_password(&hash, password));
344
+ }
345
+
346
+ #[test]
347
+ fn verify_update() {
348
+ let password = "hunter2";
349
+
350
+ let params = Algorithm::Nested {
351
+ inner: Box::new(Algorithm::default()),
352
+ outer: DEFAULT_PRIM.clone(),
353
+ };
354
+ let hash = params.hash(&password);
355
+
356
+ let password = "hunter2";
357
+ assert!(hash.verify(&password));
358
+
359
+ let password = "hunter2";
360
+ let hash = serde_mcf::to_string(&hash).unwrap();
361
+ assert!(verify_password(&hash, password));
362
+ }
363
+
364
+ #[test]
365
+ fn migrate() {
366
+ let password = "hunter2";
367
+
368
+ let params = Algorithm::Single(Bcrypt::default());
369
+ let mut hash = serde_mcf::to_string(&params.hash(&password)).unwrap();
370
+ println!("Original: {:?}", hash);
371
+ migrate_hash(&mut hash);
372
+ println!("Migrated: {:?}", hash);
373
+
374
+ let password = "hunter2";
375
+ assert!(verify_password(&hash, password));
376
+
377
+ let password = "hunter2";
378
+ assert!(verify_password_update_hash(&mut hash, password));
379
+ println!("Updated: {:?}", hash);
380
+
381
+
382
+ let password = "hunter2";
383
+ let mut pwd_hash: Output = serde_mcf::from_str(&hash).unwrap();
384
+ pwd_hash.alg = Algorithm::default();
385
+ assert!(pwd_hash.verify(&password));
386
+ }
387
+
388
+ #[test]
389
+ fn handles_broken_hashes() {
390
+ // base hash: $$scrypt$ln=14,r=8,p=1$Yw/fI4D7b2PNqpUCg5UzKA$kp6humqf/GUV+6HQ/jND3gd8Zoz4VyBgGqk4DHt+k5c
391
+ let password = "hunter2";
392
+
393
+ // Missing param
394
+ let hash = "$$scrypt$ln=14p=1$Yw/fI4D7b2PNqpUCg5UzKA$kp6humqf/GUV+6HQ/jND3gd8Zoz4VyBgGqk4DHt+k5c";
395
+ assert!(!verify_password(&hash, password));
396
+
397
+ // Incorrect hash-id
398
+ let hash = "$$nocrypt$ln=14p=1$Yw/fI4D7b2PNqpUCg5UzKA$kp6humqf/GUV+6HQ/jND3gd8Zoz4VyBgGqk4DHt+k5c";
399
+ assert!(!verify_password(&hash, password));
400
+
401
+ // Missing salt
402
+ let hash = "$$scrypt$ln=14p=1$$kp6humqf/GUV+6HQ/jND3gd8Zoz4VyBgGqk4DHt+k5c";
403
+ assert!(!verify_password(&hash, password));
404
+
405
+ // Incorrect number of fields
406
+ let hash = "$$scrypt$ln=14p=1$kp6humqf/GUV+6HQ/jND3gd8Zoz4VyBgGqk4DHt+k5c";
407
+ assert!(!verify_password(&hash, password));
408
+
409
+ // Truncated hash
410
+ let hash = "$$scrypt$ln=14,r=8,\
411
+ p=1$Yw/fI4D7b2PNqpUCg5UzKA$kp6humqf/GUV+6HQ/jND3gd8Zoz4VyBgGqk4DHt";
412
+ assert!(!verify_password(&hash, password));
413
+
414
+ // Extended hash
415
+ let hash = "$$scrypt$ln=14,r=8,\
416
+ p=1$Yw/fI4D7b2PNqpUCg5UzKA$kp6humqf/GUV+6HQ/jND3gd8Zoz4VyBgGqk4DHt+k5cAAAA";
417
+ assert!(!verify_password(&hash, password));
418
+ }
419
+
420
+ #[test]
421
+ fn migrate_hash_ok() {
422
+ let mut hash = "$2a$10$175ikf/E6E.73e83.fJRbODnYWBwmfS0ENdzUBZbedUNGO.99wJfa".to_owned();
423
+ migrate_hash(&mut hash);
424
+ }
425
+
426
+ #[test]
427
+ fn hash_and_key() {
428
+ let password = "hunter2";
429
+
430
+ let alg = Algorithm::Single(Bcrypt::default()).into_wrapped(Hmac::default().into());
431
+ let hash = serde_mcf::to_string(&alg.hash(&password)).unwrap();
432
+ assert!(verify_password(&hash, password));
433
+ }
434
+
435
+ use std::result;
436
+ use std::marker::{Send, Sync};
437
+
438
+ struct NoRandomness;
439
+ static NO_RAND_REF: &'static (SecureRandom + Send + Sync) = &NoRandomness;
440
+ impl SecureRandom for NoRandomness {
441
+ fn fill(&self, _dest: &mut [u8]) -> result::Result<(), ring::error::Unspecified> {
442
+ Err(ring::error::Unspecified)
443
+ }
444
+ }
445
+
446
+ #[test]
447
+ fn no_randomness_ok() {
448
+ use std::mem;
449
+
450
+ // Using a broken PRNG still results in distinct salts
451
+ let salt1 = ::gen_salt(&NoRandomness);
452
+ let salt2 = ::gen_salt(&NoRandomness);
453
+ assert!(salt1 != salt2);
454
+
455
+
456
+ #[allow(unsafe_code)]
457
+ unsafe {
458
+ // We break the PRNG by replacing it with one which always fails!
459
+ let rng = mem::transmute::<*const Sod<SecureRandom + Send + Sync>, *mut Sod<SecureRandom + Send + Sync>>(&*config::RANDOMNESS_SOURCE);
460
+ *rng = Sod::Static(NO_RAND_REF);
461
+ }
462
+
463
+ // Yet two passwords differ
464
+ let hash1 = hash_password("hunter2");
465
+ let hash2 = hash_password("hunter2");
466
+ assert!(hash1 != hash2);
467
+ }
468
+ }