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,6 @@
1
+ #include <stdbool.h>
2
+
3
+ extern char * hash_password(const char *password);
4
+ extern bool verify_password(const char* hash, const char *password);
5
+ extern void free_string(const char *);
6
+ extern char * read_password(const char *prompt);
@@ -0,0 +1,80 @@
1
+ extern crate libc;
2
+ extern crate libpasta;
3
+ extern crate rpassword;
4
+
5
+ use libc::c_char;
6
+ use rpassword::prompt_password_stdout;
7
+
8
+ use std::ffi::{CStr, CString};
9
+
10
+ #[no_mangle]
11
+ pub extern fn read_password(prompt: *const c_char) -> *mut c_char {
12
+ let prompt = unsafe {
13
+ assert!(!prompt.is_null());
14
+ CStr::from_ptr(prompt).to_str().unwrap()
15
+ };
16
+ let password = prompt_password_stdout(prompt).unwrap();
17
+ CString::new(password).unwrap().into_raw()
18
+ }
19
+
20
+ // Required to free memory properly
21
+ #[no_mangle]
22
+ pub extern fn free_string(s: *mut c_char) {
23
+ unsafe {
24
+ if s.is_null() { return }
25
+ CString::from_raw(s)
26
+ };
27
+ }
28
+
29
+ #[no_mangle]
30
+ pub extern fn hash_password(password: *const c_char) -> *mut c_char {
31
+ let password = unsafe {
32
+ assert!(!password.is_null());
33
+ CStr::from_ptr(password).to_str().unwrap()
34
+ };
35
+ let output = libpasta::hash_password(password);
36
+ CString::new(output).unwrap().into_raw()
37
+ }
38
+
39
+ #[no_mangle]
40
+ pub extern "C" fn verify_password(hash: *const c_char, password: *const c_char) -> bool {
41
+ let hash = unsafe {
42
+ assert!(!hash.is_null());
43
+ CStr::from_ptr(hash).to_str().unwrap()
44
+ };
45
+ let password = unsafe {
46
+ assert!(!password.is_null());
47
+ CStr::from_ptr(password).to_str().unwrap()
48
+ };
49
+ libpasta::verify_password(hash, password)
50
+ }
51
+
52
+ #[no_mangle]
53
+ pub extern "C" fn verify_password_update_hash(hash: *const c_char, password: *const c_char) -> *mut c_char {
54
+ let mut new_hash = unsafe {
55
+ assert!(!hash.is_null());
56
+ CStr::from_ptr(hash).to_str().unwrap().to_owned()
57
+ };
58
+ let password = unsafe {
59
+ assert!(!password.is_null());
60
+ CStr::from_ptr(password).to_str().unwrap()
61
+ };
62
+ if libpasta::verify_password_update_hash(&mut new_hash, password) {
63
+ CString::new(new_hash).unwrap().into_raw()
64
+ } else {
65
+ CString::new("").unwrap().into_raw()
66
+ }
67
+ }
68
+
69
+ #[no_mangle]
70
+ pub extern "C" fn migrate_hash(hash: *const c_char) -> *mut c_char {
71
+ let mut hash = unsafe {
72
+ assert!(!hash.is_null());
73
+ CStr::from_ptr(hash).to_str().unwrap().to_owned()
74
+ };
75
+ libpasta::migrate_hash(&mut hash);
76
+ CString::new(hash).unwrap().into_raw()
77
+ }
78
+
79
+
80
+
@@ -0,0 +1,39 @@
1
+ #[doc(hidden)]
2
+ #[macro_export]
3
+ macro_rules! benches {
4
+ ($params:path) => {
5
+ #[cfg(all(test, feature="bench"))]
6
+ mod bench {
7
+ #![allow(unused_qualifications, unused_imports)]
8
+ extern crate test;
9
+
10
+ use self::test::Bencher;
11
+
12
+ use super::*;
13
+
14
+ use ::hashing::Algorithm;
15
+
16
+ #[bench]
17
+ fn short(b: &mut Bencher) {
18
+ let password = "hunter2*********".to_owned();
19
+ let alg = Algorithm::Single(<$params>::default().into());
20
+ println!("Bench params: {:?}", alg);
21
+ b.iter(|| {
22
+ alg.hash(password.clone().into())
23
+ })
24
+ }
25
+
26
+ #[bench]
27
+ fn long(b: &mut Bencher) {
28
+ let password = "hunter2".to_owned().repeat(10);
29
+ println!("Password: {:?}", &password);
30
+ let alg = Algorithm::Single(<$params>::default().into());
31
+ println!("Bench params: {:?}", alg);
32
+ b.iter(|| {
33
+ alg.hash(password.clone().into())
34
+ })
35
+ }
36
+ }
37
+
38
+ }
39
+ }
@@ -0,0 +1,358 @@
1
+ //! # Configuration
2
+ //!
3
+ //! Included here are methods to setup and configure `libpasta`.
4
+ //! Currently, this refers to the choice of default hashing algorithm.
5
+ //!
6
+ //! Configuration can be specified in two ways: through configuration files,
7
+ //! or programmatically.
8
+ //!
9
+ //! Alternatively, the `set_primitive` function, and others, can be used
10
+ //! to configure the library. However, note that once the library is "in use",
11
+ //! i.e. a function like `hash_password` has been called, then attempting
12
+ //! to configure the library will cause a panic.
13
+ //!
14
+ //! There are a number of ways panics can happen through using the configuration
15
+ //! files. `libpasta` does not try to recover gracefully if
16
+ use lazy_static;
17
+ use ring::{digest, hkdf, hmac, rand};
18
+ use ring::rand::SecureRandom;
19
+ use serde_mcf;
20
+ use serde_yaml;
21
+
22
+ use key;
23
+ use errors::*;
24
+ use hashing::{Algorithm, Output};
25
+ use primitives::{self, Primitive};
26
+ use sod::Sod;
27
+
28
+ use std::default::Default;
29
+ use std::fs::File;
30
+ use std::marker::{Send, Sync};
31
+ use std::path::Path;
32
+ use std::io::BufReader;
33
+ use std::sync::{Arc, Mutex};
34
+
35
+ static RAND_REF: &'static (SecureRandom + Send + Sync) = &rand::SystemRandom;
36
+ lazy_static! {
37
+ /// Global source of randomness for generating salts
38
+ pub static ref RANDOMNESS_SOURCE: Sod<SecureRandom + Send + Sync> = {
39
+ lazy_static::initialize(&RAND_BACKUP);
40
+ Sod::Static(RAND_REF)
41
+ };
42
+
43
+ /// Backup PRNG source for when `SystemRandom`is unavailable
44
+ static ref RAND_BACKUP: Arc<Mutex<BackupPrng>> = {
45
+ let rng = rand::SystemRandom::new();
46
+ let mut seed = [0_u8; 32];
47
+ rng.fill(&mut seed).expect("could not generate any randomness");
48
+ Arc::new(Mutex::new(BackupPrng {
49
+ salt: hmac::SigningKey::generate(&digest::SHA256, &rng).expect("could not generate any randomness"),
50
+ seed: seed,
51
+ }))
52
+ };
53
+
54
+ /// Default primitive used for hash computations
55
+ pub static ref DEFAULT_PRIM: Primitive = {
56
+ primitives::Scrypt::default()
57
+ };
58
+
59
+ /// Default algorithm to use for new hash computations.
60
+ pub static ref DEFAULT_ALG: Algorithm = {
61
+ Algorithm::Single(DEFAULT_PRIM.clone())
62
+ };
63
+
64
+ /// Default configuration set.
65
+ pub static ref DEFAULT_CONFIG: Config = {
66
+ Config::default()
67
+ };
68
+ }
69
+
70
+ /// Configuration presets
71
+ pub enum Presets {
72
+ /// The defaults used, useful to make small tweaks to the default set
73
+ Default,
74
+ /// Suitable values for interactive logins (~0.5s hashing times)
75
+ Interactive,
76
+ /// Stronger values for non-interactive actions, e.g. disk encryption (~3s hashing times)
77
+ NonInteractive,
78
+ /// Combines both `Argon2i` and `scrypt` for side-channel resistance.
79
+ Paranoid,
80
+ }
81
+
82
+ /// Holds possible configuration options
83
+ /// See the [module level documentation](index.html) for more information.
84
+ #[derive(Debug, Deserialize, Serialize)]
85
+ pub struct Config {
86
+ #[serde(skip)]
87
+ algorithm: Algorithm,
88
+ #[serde(default = "primitives::Scrypt::default")]
89
+ primitive: Primitive,
90
+ keyed: Option<Primitive>,
91
+ #[serde(skip, default = "key::get_global")]
92
+ keys: &'static key::Store,
93
+ }
94
+
95
+
96
+
97
+ impl Default for Config {
98
+ fn default() -> Self {
99
+ Self {
100
+ algorithm: DEFAULT_ALG.clone(),
101
+ primitive: DEFAULT_PRIM.clone(),
102
+ keyed: None,
103
+ keys: key::get_global(),
104
+ }
105
+ }
106
+ }
107
+
108
+
109
+ impl Config {
110
+ /// Create a new empty `Config` for setting parameters.
111
+ pub fn with_primitive(primitive: Primitive) -> Self {
112
+ Self {
113
+ algorithm: Algorithm::Single(primitive.clone()),
114
+ primitive: primitive,
115
+ keyed: None,
116
+ keys: key::get_global(),
117
+ }
118
+ }
119
+
120
+ /// Generates a `Config` from a selected preset
121
+ /// configuration.
122
+ pub fn from_preset(preset: &Presets) -> Self {
123
+ match *preset {
124
+ Presets::Default => Self::default(),
125
+ Presets::Interactive => unimplemented!(),
126
+ Presets::NonInteractive => unimplemented!(),
127
+ Presets::Paranoid => unimplemented!(),
128
+ }
129
+ }
130
+
131
+ /// Generates a `Config` from a .toml file.
132
+ /// Config files can be generated using the `Config::to_string` method on
133
+ /// an existing config.
134
+ pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
135
+ let file = File::open(path.as_ref());
136
+ if let Ok(file) = file {
137
+ let reader = BufReader::new(file);
138
+ let mut config: Self = serde_yaml::from_reader(reader).expect("invalid config file");
139
+ config.algorithm = Algorithm::Single(config.primitive.clone());
140
+ if let Some(kh) = config.keyed.clone() {
141
+ config.algorithm = config.algorithm.into_wrapped(kh);
142
+ }
143
+ trace!("imported config as: {:?}", config);
144
+ Ok(config)
145
+
146
+ } else {
147
+ info!("could not open config file {:?}: {:?}", path.as_ref(), file);
148
+ Err("could not open config file".into())
149
+ }
150
+ }
151
+
152
+
153
+ /// Generates hash for a given password.
154
+ ///
155
+ /// Will automatically generate a random salt. In the extreme case that the
156
+ /// default source of randomness is unavailable, this will fallback to a seed
157
+ /// generated when the library is initialised. An error will be logged when this
158
+ /// happens.
159
+ /// /// ## Panics
160
+ /// A panic indicates a problem with the serialization mechanisms, and should
161
+ /// be reported.
162
+ pub fn hash_password(&self, password: &str) -> String {
163
+ self.hash_password_safe(password).expect_report("failed to hash password")
164
+ }
165
+
166
+ /// Same as `hash_password` but returns `Result` to allow error handling.
167
+ /// TODO: decide on which API is best to use.
168
+ #[doc(hidden)]
169
+ pub fn hash_password_safe(&self, password: &str) -> Result<String> {
170
+ let pwd_hash = self.algorithm.hash(password);
171
+ Ok(serde_mcf::to_string(&pwd_hash)?)
172
+ }
173
+
174
+ /// Verifies the provided password matches the inputted hash string.
175
+ ///
176
+ /// If there is any error in processing the hash or password, this
177
+ /// will simply return `false`.
178
+ pub fn verify_password(&self, hash: &str, password: &str) -> bool {
179
+ self.verify_password_safe(hash, password).unwrap_or(false)
180
+ }
181
+
182
+ /// Same as `verify_password` but returns `Result` to allow error handling.
183
+ /// TODO: decide on which API is best to use.
184
+ #[doc(hidden)]
185
+ pub fn verify_password_safe(&self, hash: &str, password: &str) -> Result<bool> {
186
+ let mut pwd_hash: Output = serde_mcf::from_str(hash)?;
187
+ pwd_hash.check_keys(self);
188
+ Ok(pwd_hash.verify(password))
189
+ }
190
+
191
+ /// Verifies a supplied password against a previously computed password hash,
192
+ /// and performs an in-place update of the hash value if the password verifies.
193
+ /// Hence this needs to take a mutable `String` reference.
194
+ pub fn verify_password_update_hash(&self, hash: &mut String, password: &str) -> bool {
195
+ self.verify_password_update_hash_safe(hash, password).unwrap_or(false)
196
+ }
197
+
198
+ /// Same as `verify_password_update_hash`, but returns `Result` to allow error handling.
199
+ #[doc(hidden)]
200
+ pub fn verify_password_update_hash_safe(&self, hash: &mut String, password: &str) -> Result<bool> {
201
+ let pwd_hash: Output = serde_mcf::from_str(hash)?;
202
+ if pwd_hash.verify(password) {
203
+ if pwd_hash.alg != *DEFAULT_ALG {
204
+ let new_hash = serde_mcf::to_string(&self.algorithm.hash(password))?;
205
+ *hash = new_hash;
206
+ }
207
+ Ok(true)
208
+ } else {
209
+ Ok(false)
210
+ }
211
+ }
212
+
213
+
214
+ /// Migrate the input hash to the current recommended hash.
215
+ ///
216
+ /// Note that this does *not* require the password. This is for batch updating
217
+ /// of hashes, where the password is not available. This performs an onion
218
+ /// approach, returning `new_hash(old_hash)`.
219
+ ///
220
+ /// If the password is also available, the `verify_password_update_hash` should
221
+ /// instead be used.
222
+ pub fn migrate_hash(&self, hash: &mut String) {
223
+ self.migrate_hash_safe(hash).expect("failed to migrate password");
224
+ }
225
+
226
+ /// Same as `migrate_hash` but returns `Result` to allow error handling.
227
+ #[doc(hidden)]
228
+ pub fn migrate_hash_safe(&self, hash: &mut String) -> Result<()> {
229
+ let pwd_hash: Output = serde_mcf::from_str(hash)?;
230
+
231
+ if !pwd_hash.alg.needs_migrating() {
232
+ // no need to migrate
233
+ return Ok(());
234
+ }
235
+
236
+ let new_params = pwd_hash.alg.to_wrapped(self.primitive.clone());
237
+ let new_salt = pwd_hash.salt;
238
+
239
+ let new_hash = self.primitive.compute(&pwd_hash.hash, &new_salt);
240
+ let new_hash = Output {
241
+ alg: new_params,
242
+ hash: new_hash,
243
+ salt: new_salt,
244
+ };
245
+
246
+ *hash = serde_mcf::to_string(&new_hash)?;
247
+ Ok(())
248
+ }
249
+
250
+
251
+ /// Add a new key into the list of configured keys
252
+ pub fn add_key(&self, key: &[u8]) -> String {
253
+ self.keys.insert(key)
254
+ }
255
+
256
+ pub(crate) fn get_key(&self, key_id: &str) -> Option<Vec<u8>> {
257
+ self.keys.get_key(key_id)
258
+ }
259
+
260
+ /// Set the default primitive
261
+ pub fn set_primitive(&mut self, primitive: Primitive) {
262
+ self.primitive = primitive.clone();
263
+ self.algorithm = match self.algorithm {
264
+ Algorithm::Single(_) => Algorithm::Single(primitive.clone()),
265
+ Algorithm::Nested { ref outer, .. } => Algorithm::Single(primitive).into_wrapped(outer.clone())
266
+ };
267
+ }
268
+
269
+ /// Set a keyed function to be applied after hashing.
270
+ pub fn set_keyed_hash(&mut self, keyed: Primitive) {
271
+ self.keyed = Some(keyed.clone());
272
+ let mut newalg = match self.algorithm {
273
+ // If just a single algorithm, wrap with the keyed primitive
274
+ Algorithm::Single(_) => self.algorithm.to_wrapped(keyed),
275
+ // Otherwise, replace the outer algorithm with the keyed primitive
276
+ Algorithm::Nested { outer: ref _outer, ref inner } => inner.to_wrapped(keyed)
277
+ };
278
+ newalg.update_key(self);
279
+ self.algorithm = newalg;
280
+ }
281
+
282
+ /// Sets the location of keys for keyed functions.
283
+ pub fn set_key_source(&mut self, store: &'static key::Store) {
284
+ self.keys = store;
285
+ }
286
+
287
+
288
+ /// Serialize the configuration as YAML
289
+ pub fn to_string(&self) -> String {
290
+ serde_yaml::to_string(&self).expect("failed to serialize config")
291
+ }
292
+ }
293
+
294
+ struct BackupPrng {
295
+ salt: hmac::SigningKey,
296
+ seed: [u8; 32],
297
+ }
298
+
299
+ impl BackupPrng {
300
+ fn gen_salt(&mut self) -> Vec<u8> {
301
+ let mut output = vec![0_u8; 48];
302
+ hkdf::extract_and_expand(
303
+ &self.salt,
304
+ &self.seed,
305
+ b"libpasta backup PRNG",
306
+ &mut output
307
+ );
308
+ self.seed.copy_from_slice(&output[16..]);
309
+ output.truncate(16);
310
+ output
311
+ }
312
+ }
313
+
314
+ pub(crate) fn backup_gen_salt() -> Vec<u8> {
315
+ RAND_BACKUP.lock().expect("could not acquire lock on RAND_BACKUP").gen_salt()
316
+ }
317
+
318
+
319
+ #[cfg(test)]
320
+ mod test {
321
+ use super::*;
322
+ use super::super::*;
323
+
324
+ use ring;
325
+ #[test]
326
+ fn use_config() {
327
+ let config = Config::with_primitive(primitives::Argon2::default());
328
+ let hash = config.hash_password("hunter2".into());
329
+ assert!(verify_password(&hash, "hunter2".into()));
330
+ }
331
+
332
+ #[derive(Debug)]
333
+ struct StaticSource(&'static [u8; 16]);
334
+
335
+ impl key::Store for StaticSource {
336
+ /// Insert a new key into the `Store`.
337
+ fn insert(&self, _key: &[u8]) -> String {
338
+ "StaticKey".to_string()
339
+ }
340
+
341
+ /// Get a key from the `Store`.
342
+ fn get_key(&self, _id: &str) -> Option<Vec<u8>> {
343
+ Some(self.0.to_vec())
344
+ }
345
+ }
346
+ static STATIC_SOURCE: StaticSource = StaticSource(b"ThisIsAStaticKey");
347
+
348
+ #[test]
349
+ fn alternate_key_source() {
350
+ let mut config = Config::default();
351
+ config.set_key_source(&STATIC_SOURCE);
352
+ assert_eq!(config.get_key("dummy"), Some(b"ThisIsAStaticKey".to_vec()));
353
+ let hmac = primitives::Hmac::with_key_id(&ring::digest::SHA256, "dummy");
354
+ config.set_keyed_hash(hmac);
355
+ let hash = config.hash_password("hunter2");
356
+ assert!(config.verify_password_safe(&hash, "hunter2").unwrap())
357
+ }
358
+ }