libpasta 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
}
|