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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.gitmodules +3 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +37 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/ext/Rakefile +9 -0
- data/ext/pasta-bindings/.gitmodules +3 -0
- data/ext/pasta-bindings/Makefile +84 -0
- data/ext/pasta-bindings/README.md +54 -0
- data/ext/pasta-bindings/libpasta/.gitignore +2 -0
- data/ext/pasta-bindings/libpasta/.travis.yml +60 -0
- data/ext/pasta-bindings/libpasta/Cargo.toml +42 -0
- data/ext/pasta-bindings/libpasta/LICENSE.md +21 -0
- data/ext/pasta-bindings/libpasta/Makefile +27 -0
- data/ext/pasta-bindings/libpasta/README.md +78 -0
- data/ext/pasta-bindings/libpasta/benches/bench.rs +125 -0
- data/ext/pasta-bindings/libpasta/examples/alternate_key_source.rs +33 -0
- data/ext/pasta-bindings/libpasta/examples/config.rs +10 -0
- data/ext/pasta-bindings/libpasta/examples/config_hmac.rs +25 -0
- data/ext/pasta-bindings/libpasta/examples/hash_password.rs +10 -0
- data/ext/pasta-bindings/libpasta/examples/migrate_password.rs +47 -0
- data/ext/pasta-bindings/libpasta/examples/verify_password.rs +24 -0
- data/ext/pasta-bindings/libpasta/libpasta-capi/Cargo.toml +12 -0
- data/ext/pasta-bindings/libpasta/libpasta-capi/ctest/compile +6 -0
- data/ext/pasta-bindings/libpasta/libpasta-capi/ctest/test +0 -0
- data/ext/pasta-bindings/libpasta/libpasta-capi/ctest/test.c +12 -0
- data/ext/pasta-bindings/libpasta/libpasta-capi/include/pasta.h +6 -0
- data/ext/pasta-bindings/libpasta/libpasta-capi/src/lib.rs +80 -0
- data/ext/pasta-bindings/libpasta/src/bench.rs +39 -0
- data/ext/pasta-bindings/libpasta/src/config.rs +358 -0
- data/ext/pasta-bindings/libpasta/src/hashing/de.rs +284 -0
- data/ext/pasta-bindings/libpasta/src/hashing/mod.rs +161 -0
- data/ext/pasta-bindings/libpasta/src/hashing/ser.rs +67 -0
- data/ext/pasta-bindings/libpasta/src/key/mod.rs +66 -0
- data/ext/pasta-bindings/libpasta/src/lib.rs +468 -0
- data/ext/pasta-bindings/libpasta/src/primitives/argon2.rs +180 -0
- data/ext/pasta-bindings/libpasta/src/primitives/bcrypt.rs +165 -0
- data/ext/pasta-bindings/libpasta/src/primitives/hmac.rs +134 -0
- data/ext/pasta-bindings/libpasta/src/primitives/mod.rs +312 -0
- data/ext/pasta-bindings/libpasta/src/primitives/pbkdf2.rs +272 -0
- data/ext/pasta-bindings/libpasta/src/primitives/scrypt.rs +144 -0
- data/ext/pasta-bindings/libpasta/src/sod.rs +46 -0
- data/ext/pasta-bindings/libpasta/tests/.libpasta.yaml +8 -0
- data/ext/pasta-bindings/libpasta/tests/common/mod.rs +37 -0
- data/ext/pasta-bindings/libpasta/tests/test_argon2.rs +8 -0
- data/ext/pasta-bindings/libpasta/tests/test_bcrypt.rs +8 -0
- data/ext/pasta-bindings/libpasta/tests/test_from_file.rs +13 -0
- data/ext/pasta-bindings/libpasta/tests/test_hmac.rs +26 -0
- data/ext/pasta-bindings/libpasta/tests/test_pbkdf2.rs +8 -0
- data/ext/pasta-bindings/libpasta/tests/test_ringpbkdf2.rs +8 -0
- data/ext/pasta-bindings/libpasta/tests/test_scrypt.rs +8 -0
- data/ext/pasta-bindings/pasta.i +31 -0
- data/ext/pasta-bindings/tests/Makefile +38 -0
- data/ext/pasta-bindings/tests/pasta_test.go +12 -0
- data/ext/pasta-bindings/tests/test.php +7 -0
- data/ext/pasta-bindings/tests/test.py +7 -0
- data/ext/pasta-bindings/tests/test.rb +5 -0
- data/lib/libpasta.rb +2 -0
- data/lib/libpasta/version.rb +3 -0
- data/libpasta.gemspec +52 -0
- data/spec/libpasta_spec.rb +11 -0
- data/spec/spec_helper.rb +11 -0
- metadata +183 -0
@@ -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
|
+
}
|