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,180 @@
|
|
1
|
+
pub use self::native::Argon2;
|
2
|
+
|
3
|
+
mod native {
|
4
|
+
extern crate argon2rs;
|
5
|
+
|
6
|
+
use primitives::Primitive;
|
7
|
+
use sod::Sod;
|
8
|
+
|
9
|
+
use serde_mcf::Hashes;
|
10
|
+
|
11
|
+
use std::fmt;
|
12
|
+
|
13
|
+
/// Parameter set for Argon2.
|
14
|
+
///
|
15
|
+
/// This implementation is backed by the `argon2rs` crate.
|
16
|
+
pub struct Argon2 {
|
17
|
+
algorithm: argon2rs::Argon2,
|
18
|
+
}
|
19
|
+
|
20
|
+
impl ::primitives::PrimitiveImpl for Argon2 {
|
21
|
+
fn compute<'a>(&'a self, password: &[u8], salt: &[u8]) -> Vec<u8> {
|
22
|
+
let mut hash = [0_u8; 32];
|
23
|
+
self.algorithm.hash(&mut hash, password, salt, &[], &[]);
|
24
|
+
hash.to_vec()
|
25
|
+
}
|
26
|
+
|
27
|
+
fn params_as_vec(&self) -> Vec<(&'static str, String)> {
|
28
|
+
let (_, kib, passes, lanes) = self.algorithm.params();
|
29
|
+
vec![("m", kib.to_string()), ("t", passes.to_string()), ("p", lanes.to_string())]
|
30
|
+
}
|
31
|
+
|
32
|
+
fn hash_id(&self) -> Hashes {
|
33
|
+
Hashes::Argon2i
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
lazy_static! {
|
38
|
+
pub static ref DEFAULT: Argon2 = {
|
39
|
+
Argon2 {
|
40
|
+
algorithm: argon2rs::Argon2::default(argon2rs::Variant::Argon2i)
|
41
|
+
}
|
42
|
+
};
|
43
|
+
}
|
44
|
+
|
45
|
+
impl Argon2 {
|
46
|
+
/// Get the default Argon2i parameter set
|
47
|
+
pub fn default() -> Primitive {
|
48
|
+
Primitive(Sod::Static(&*DEFAULT))
|
49
|
+
}
|
50
|
+
|
51
|
+
fn new_impl(passes: u32, lanes: u32, kib: u32) -> Self {
|
52
|
+
Self {
|
53
|
+
algorithm: argon2rs::Argon2::new(passes, lanes, kib, argon2rs::Variant::Argon2i)
|
54
|
+
.expect("invalid Argon2 parameters"),
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
/// Creates a new Argon2i instance
|
59
|
+
pub fn new(passes: u32, lanes: u32, kib: u32) -> Primitive {
|
60
|
+
Self::new_impl(passes, lanes, kib).into()
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
impl fmt::Debug for Argon2 {
|
65
|
+
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
66
|
+
write!(f, "Argon2i: {:?}", self.algorithm.params())
|
67
|
+
}
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
|
72
|
+
benches!(Argon2);
|
73
|
+
|
74
|
+
#[cfg(test)]
|
75
|
+
mod test {
|
76
|
+
use data_encoding::HEXLOWER;
|
77
|
+
use serde_mcf;
|
78
|
+
use super::*;
|
79
|
+
use hashing::*;
|
80
|
+
|
81
|
+
#[test]
|
82
|
+
fn sanity_check() {
|
83
|
+
let password = "hunter2";
|
84
|
+
let params = super::Argon2::default();
|
85
|
+
println!("{:?}", params);
|
86
|
+
let salt = ::get_salt();
|
87
|
+
let hash = params.compute(password.as_bytes(), &salt);
|
88
|
+
let hash2 = params.compute(password.as_bytes(), &salt);
|
89
|
+
assert_eq!(hash, hash2);
|
90
|
+
let out = Output {
|
91
|
+
alg: Algorithm::Single(params.into()),
|
92
|
+
salt: salt,
|
93
|
+
hash: hash,
|
94
|
+
};
|
95
|
+
println!("{:?}", serde_mcf::to_string(&out).unwrap());
|
96
|
+
}
|
97
|
+
|
98
|
+
fn hashtest(passes: u32,
|
99
|
+
m: u32,
|
100
|
+
lanes: u32,
|
101
|
+
password: &str,
|
102
|
+
salt: &str,
|
103
|
+
hexpected: &str,
|
104
|
+
encoded: &str) {
|
105
|
+
let alg = Argon2::new(passes, lanes, 1 << m);
|
106
|
+
let hash = alg.compute(password.as_bytes(), salt.as_bytes());
|
107
|
+
assert_eq!(HEXLOWER.encode(&hash), hexpected);
|
108
|
+
assert_eq!(serde_mcf::from_str::<Output>(encoded).unwrap().hash, hash);
|
109
|
+
let output = Output {
|
110
|
+
alg: Algorithm::Single(alg.into()),
|
111
|
+
hash: hash,
|
112
|
+
salt: salt.as_bytes().to_vec(),
|
113
|
+
};
|
114
|
+
assert_eq!(&serde_mcf::to_string(&output).unwrap()[1..], encoded);
|
115
|
+
}
|
116
|
+
|
117
|
+
#[test]
|
118
|
+
fn argon2i_ref_tests() {
|
119
|
+
hashtest(2,
|
120
|
+
8,
|
121
|
+
1,
|
122
|
+
"password",
|
123
|
+
"somesalt",
|
124
|
+
"fd4dd83d762c49bdeaf57c47bdcd0c2f1babf863fdeb490df63ede9975fccf06",
|
125
|
+
"$argon2i$m=256,t=2,p=1$c29tZXNhbHQ\
|
126
|
+
$/U3YPXYsSb3q9XxHvc0MLxur+GP960kN9j7emXX8zwY");
|
127
|
+
hashtest(2,
|
128
|
+
8,
|
129
|
+
2,
|
130
|
+
"password",
|
131
|
+
"somesalt",
|
132
|
+
"b6c11560a6a9d61eac706b79a2f97d68b4463aa3ad87e00c07e2b01e90c564fb",
|
133
|
+
"$argon2i$m=256,t=2,p=2$c29tZXNhbHQ\
|
134
|
+
$tsEVYKap1h6scGt5ovl9aLRGOqOth+AMB+KwHpDFZPs");
|
135
|
+
hashtest(1,
|
136
|
+
16,
|
137
|
+
1,
|
138
|
+
"password",
|
139
|
+
"somesalt",
|
140
|
+
"81630552b8f3b1f48cdb1992c4c678643d490b2b5eb4ff6c4b3438b5621724b2",
|
141
|
+
"$argon2i$m=65536,t=1,p=1$c29tZXNhbHQ\
|
142
|
+
$gWMFUrjzsfSM2xmSxMZ4ZD1JCytetP9sSzQ4tWIXJLI");
|
143
|
+
hashtest(4,
|
144
|
+
16,
|
145
|
+
1,
|
146
|
+
"password",
|
147
|
+
"somesalt",
|
148
|
+
"f212f01615e6eb5d74734dc3ef40ade2d51d052468d8c69440a3a1f2c1c2847b",
|
149
|
+
"$argon2i$m=65536,t=4,p=1$c29tZXNhbHQ\
|
150
|
+
$8hLwFhXm6110c03D70Ct4tUdBSRo2MaUQKOh8sHChHs");
|
151
|
+
hashtest(2,
|
152
|
+
16,
|
153
|
+
1,
|
154
|
+
"differentpassword",
|
155
|
+
"somesalt",
|
156
|
+
"e9c902074b6754531a3a0be519e5baf404b30ce69b3f01ac3bf21229960109a3",
|
157
|
+
"$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ\
|
158
|
+
$6ckCB0tnVFMaOgvlGeW69ASzDOabPwGsO/ISKZYBCaM");
|
159
|
+
hashtest(2,
|
160
|
+
16,
|
161
|
+
1,
|
162
|
+
"password",
|
163
|
+
"diffsalt",
|
164
|
+
"79a103b90fe8aef8570cb31fc8b22259778916f8336b7bdac3892569d4f1c497",
|
165
|
+
"$argon2i$m=65536,t=2,p=1$ZGlmZnNhbHQ\
|
166
|
+
$eaEDuQ/orvhXDLMfyLIiWXeJFvgza3vaw4kladTxxJc");
|
167
|
+
}
|
168
|
+
|
169
|
+
#[test] #[cfg(feature = "long_tests")]
|
170
|
+
fn argon2i_ref_tests() {
|
171
|
+
hashtest(2,
|
172
|
+
18,
|
173
|
+
1,
|
174
|
+
"password",
|
175
|
+
"somesalt",
|
176
|
+
"3e689aaa3d28a77cf2bc72a51ac53166761751182f1ee292e3f677a7da4c2467",
|
177
|
+
"$argon2i$m=262144,t=2,p=1$c29tZXNhbHQ\
|
178
|
+
$Pmiaqj0op3zyvHKlGsUxZnYXURgvHuKS4/Z3p9pMJGc");
|
179
|
+
}
|
180
|
+
}
|
@@ -0,0 +1,165 @@
|
|
1
|
+
pub use self::native::Bcrypt;
|
2
|
+
|
3
|
+
mod native {
|
4
|
+
#![allow(shadow_reuse)]
|
5
|
+
|
6
|
+
extern crate crypto;
|
7
|
+
use self::crypto::bcrypt::bcrypt;
|
8
|
+
|
9
|
+
use primitives::{Primitive, PrimitiveImpl};
|
10
|
+
use sod::Sod;
|
11
|
+
|
12
|
+
use serde_mcf::Hashes;
|
13
|
+
|
14
|
+
use std::fmt;
|
15
|
+
use std::sync::Arc;
|
16
|
+
|
17
|
+
/// `bcrypt` parameter set.
|
18
|
+
///
|
19
|
+
/// Holds the cost value.
|
20
|
+
/// This implementation is backed by `rust-crypto`.
|
21
|
+
#[derive(Clone, Deserialize, Serialize)]
|
22
|
+
pub struct Bcrypt {
|
23
|
+
cost: u32,
|
24
|
+
}
|
25
|
+
|
26
|
+
lazy_static! {
|
27
|
+
static ref DEFAULT: Arc<Box<PrimitiveImpl>> = {
|
28
|
+
Arc::new(Box::new(Bcrypt::new_impl(12)))
|
29
|
+
};
|
30
|
+
}
|
31
|
+
|
32
|
+
impl PrimitiveImpl for Bcrypt {
|
33
|
+
fn compute<'a>(&'a self, password: &[u8], salt: &[u8]) -> Vec<u8> {
|
34
|
+
let mut hash = [0_u8; 24];
|
35
|
+
let mut pw = [0_u8; 72];
|
36
|
+
// Bcrypt inputs need to be at most 72 bytes
|
37
|
+
// with a trailing zero byte only if < 72 bytes
|
38
|
+
let pw = if password.len() > 71 {
|
39
|
+
pw[..72].copy_from_slice(&password[..72]);
|
40
|
+
&pw[..]
|
41
|
+
} else {
|
42
|
+
pw[..password.len()].copy_from_slice(password);
|
43
|
+
&pw[..password.len() + 1]
|
44
|
+
};
|
45
|
+
bcrypt(self.cost, salt, pw, &mut hash);
|
46
|
+
hash[..23].to_vec()
|
47
|
+
}
|
48
|
+
|
49
|
+
fn params_as_vec(&self) -> Vec<(&'static str, String)> {
|
50
|
+
vec![("cost", self.cost.to_string())]
|
51
|
+
}
|
52
|
+
|
53
|
+
fn hash_id(&self) -> Hashes {
|
54
|
+
Hashes::BcryptMcf
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
|
59
|
+
impl fmt::Debug for Bcrypt {
|
60
|
+
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
61
|
+
write!(f, "Bcrypt, cost: {:?}", self.cost)
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
impl Bcrypt {
|
66
|
+
/// Construct a new `Bcrypt` parameter set.
|
67
|
+
pub fn new(cost: u32) -> Primitive {
|
68
|
+
Self::new_impl(cost).into()
|
69
|
+
}
|
70
|
+
|
71
|
+
fn new_impl(cost: u32) -> Self {
|
72
|
+
Self { cost: cost }
|
73
|
+
}
|
74
|
+
|
75
|
+
/// Get the default `Bcrypt` parameter set.
|
76
|
+
pub fn default() -> Primitive {
|
77
|
+
Primitive(Sod::Dynamic(Arc::clone(&DEFAULT)))
|
78
|
+
}
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
benches!(Bcrypt);
|
83
|
+
|
84
|
+
#[cfg(test)]
|
85
|
+
mod bcrypt_test {
|
86
|
+
use hashing::*;
|
87
|
+
use serde_mcf as mcf;
|
88
|
+
|
89
|
+
#[test]
|
90
|
+
fn sanity_check() {
|
91
|
+
let password = "hunter2";
|
92
|
+
let params = super::Bcrypt::default();
|
93
|
+
println!("{:?}", params);
|
94
|
+
let salt = ::get_salt();
|
95
|
+
let hash = params.compute(password.as_bytes(), &salt);
|
96
|
+
let hash2 = params.compute(password.as_bytes(), &salt);
|
97
|
+
assert_eq!(hash, hash2);
|
98
|
+
let out = Output {
|
99
|
+
alg: Algorithm::Single(params.into()),
|
100
|
+
salt: salt,
|
101
|
+
hash: hash,
|
102
|
+
};
|
103
|
+
println!("{:?}", mcf::to_string(&out).unwrap());
|
104
|
+
}
|
105
|
+
|
106
|
+
#[test]
|
107
|
+
fn verifies_bcrypt_hashes() {
|
108
|
+
let password = "hunter2";
|
109
|
+
let hash = "$2a$10$ckjEeyTD6estWyoofn4EROM9Ik2PqVcfcrepX.uGp6.aqRdCMN/Oe";
|
110
|
+
assert!(::verify_password(&hash, password));
|
111
|
+
}
|
112
|
+
|
113
|
+
fn openwall_test(hash: &str, password: &[u8]) {
|
114
|
+
let pwd_hash: Output = mcf::from_str(&hash).unwrap();
|
115
|
+
assert_eq!(pwd_hash.hash,
|
116
|
+
pwd_hash.alg.hash_with_salt(password, &pwd_hash.salt));
|
117
|
+
}
|
118
|
+
|
119
|
+
// Test the internal Bcrypt implementation against the openwall test vectors.
|
120
|
+
// Note that we currently are non compatible with "2x" variant hashes.
|
121
|
+
#[test]
|
122
|
+
fn openwall_test_vectors() {
|
123
|
+
openwall_test("$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW",
|
124
|
+
b"U*U");
|
125
|
+
openwall_test("$2a$05$CCCCCCCCCCCCCCCCCCCCC.VGOzA784oUp/Z0DY336zx7pLYAy0lwK",
|
126
|
+
b"U*U*");
|
127
|
+
openwall_test("$2a$05$XXXXXXXXXXXXXXXXXXXXXOAcXxm9kjPGEMsLznoKqmqw7tc8WCx4a",
|
128
|
+
b"U*U*U");
|
129
|
+
openwall_test("$2a$05$CCCCCCCCCCCCCCCCCCCCC.7uG0VCzI2bS7j6ymqJi9CdcdxiRTWNy",
|
130
|
+
b"");
|
131
|
+
openwall_test("$2a$05$abcdefghijklmnopqrstuu5s2v8.iXieOjg/.AySBTTZIIVFJeBui",
|
132
|
+
b"0123456789abcdefghijklmnopqrstuvwxyz\
|
133
|
+
ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\
|
134
|
+
chars after 72 are ignored");
|
135
|
+
// openwall_test("$2x$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e", b"\xa3");
|
136
|
+
openwall_test("$2y$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq",
|
137
|
+
b"\xa3");
|
138
|
+
// openwall_test("$2x$05$6bNw2HLQYeqHYyBfLMsv/OiwqTymGIGzFsA4hOTWebfehXHNprcAS", b"\xd1\x91");
|
139
|
+
// openwall_test("$2x$05$6bNw2HLQYeqHYyBfLMsv/O9LIGgn8OMzuDoHfof8AQimSGfcSWxnS", b"\xd0\xc1\xd2\xcf\xcc\xd8");
|
140
|
+
openwall_test("$2a$05$/OK.fbVrR/bpIqNJ5ianF.swQOIzjOiJ9GHEPuhEkvqrUyvWhEMx6",
|
141
|
+
b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\
|
142
|
+
\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\
|
143
|
+
\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\
|
144
|
+
\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\
|
145
|
+
\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\
|
146
|
+
\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\
|
147
|
+
chars after 72 are ignored as usual");
|
148
|
+
openwall_test("$2a$05$/OK.fbVrR/bpIqNJ5ianF.R9xrDjiycxMbQE2bp.vgqlYpW5wx2yy",
|
149
|
+
b"\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\
|
150
|
+
\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\
|
151
|
+
\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\
|
152
|
+
\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\
|
153
|
+
\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\
|
154
|
+
\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55");
|
155
|
+
openwall_test("$2a$05$CCCCCCCCCCCCCCCCCCCCC.7uG0VCzI2bS7j6ymqJi9CdcdxiRTWNy",
|
156
|
+
b"");
|
157
|
+
openwall_test("$2a$05$/OK.fbVrR/bpIqNJ5ianF.9tQZzcJfm3uj2NvJ/n5xkhpqLrMpWCe",
|
158
|
+
b"\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\
|
159
|
+
\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\
|
160
|
+
\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\
|
161
|
+
\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\
|
162
|
+
\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\
|
163
|
+
\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff");
|
164
|
+
}
|
165
|
+
}
|
@@ -0,0 +1,134 @@
|
|
1
|
+
pub use self::hmac_ring::Hmac;
|
2
|
+
|
3
|
+
mod hmac_ring {
|
4
|
+
use config;
|
5
|
+
use errors::*;
|
6
|
+
use key;
|
7
|
+
use key::Store;
|
8
|
+
use primitives::Primitive;
|
9
|
+
|
10
|
+
use ring::{digest, hkdf, hmac, rand};
|
11
|
+
use serde_mcf::Hashes;
|
12
|
+
|
13
|
+
use std::fmt;
|
14
|
+
|
15
|
+
/// Password storage strengthening using HMAC.
|
16
|
+
///
|
17
|
+
/// This struct holds the parameters used.
|
18
|
+
/// Represents the `ring` implementation.
|
19
|
+
pub struct Hmac {
|
20
|
+
h: &'static digest::Algorithm,
|
21
|
+
key: Option<hmac::SigningKey>,
|
22
|
+
key_id: String,
|
23
|
+
}
|
24
|
+
|
25
|
+
impl Hmac {
|
26
|
+
/// Construct a new `Hmac` instance with a specified key identifier
|
27
|
+
pub fn with_key_id(h: &'static digest::Algorithm, key_id: &str) -> Primitive {
|
28
|
+
Self {
|
29
|
+
h: h,
|
30
|
+
key: key::get_global().get_key(key_id).map(|k| hmac::SigningKey::new(h, &k)),
|
31
|
+
key_id: key_id.to_string(),
|
32
|
+
}.into()
|
33
|
+
}
|
34
|
+
|
35
|
+
/// Gets a default HMAC instance, generating a fresh new key.
|
36
|
+
pub fn default() -> Primitive {
|
37
|
+
Self::new().into()
|
38
|
+
}
|
39
|
+
|
40
|
+
fn new() -> Self {
|
41
|
+
let rng = rand::SystemRandom::new();
|
42
|
+
let mut key_bytes = [0_u8; 32];
|
43
|
+
let key = hmac::SigningKey::generate_serializable(&digest::SHA256, &rng, &mut key_bytes)
|
44
|
+
.expect("could not generate random bytes for key");
|
45
|
+
let key_id = key::get_global().insert(&key_bytes);
|
46
|
+
Self {
|
47
|
+
h: &digest::SHA256,
|
48
|
+
key: Some(key),
|
49
|
+
key_id: key_id,
|
50
|
+
}
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
impl ::primitives::PrimitiveImpl for Hmac {
|
55
|
+
/// Compute the scrypt hash
|
56
|
+
fn compute(&self, password: &[u8], _salt: &[u8]) -> Vec<u8> {
|
57
|
+
let mut hash = vec![0_u8; 32];
|
58
|
+
let key = self.key.as_ref().expect_report("key not found");
|
59
|
+
hkdf::extract_and_expand(key, password, b"libpasta password hashing", &mut hash);
|
60
|
+
hash
|
61
|
+
}
|
62
|
+
|
63
|
+
/// Convert parameters into a vector of (key, value) tuples
|
64
|
+
/// for serializing.
|
65
|
+
fn params_as_vec(&self) -> Vec<(&'static str, String)> {
|
66
|
+
vec![("key_id", self.key_id.clone()),
|
67
|
+
("h", super::super::hash_to_id(self.h))]
|
68
|
+
}
|
69
|
+
|
70
|
+
fn hash_id(&self) -> Hashes {
|
71
|
+
Hashes::Hmac
|
72
|
+
}
|
73
|
+
|
74
|
+
fn update_key(&self, config: &config::Config) -> Option<Primitive> {
|
75
|
+
Some(Self {
|
76
|
+
h: self.h,
|
77
|
+
key: config.get_key(&self.key_id).map(|k| hmac::SigningKey::new(self.h, &k)),
|
78
|
+
key_id: self.key_id.clone(),
|
79
|
+
}.into())
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
impl fmt::Debug for Hmac {
|
84
|
+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
85
|
+
write!(f,
|
86
|
+
"Hmac, KeyID: {}, Hash: {}",
|
87
|
+
self.key_id,
|
88
|
+
super::super::hash_to_id(self.h))
|
89
|
+
}
|
90
|
+
}
|
91
|
+
}
|
92
|
+
|
93
|
+
|
94
|
+
#[cfg(test)]
|
95
|
+
mod test {
|
96
|
+
use ::hashing::*;
|
97
|
+
use serde_mcf;
|
98
|
+
|
99
|
+
#[test]
|
100
|
+
fn sanity_check() {
|
101
|
+
let password = "hunter2";
|
102
|
+
let hmac_params = super::Hmac::default();
|
103
|
+
println!("{:?}", hmac_params);
|
104
|
+
let inner_params = ::primitives::scrypt::Scrypt::default();
|
105
|
+
let salt = ::get_salt();
|
106
|
+
let hash = hmac_params.compute(&inner_params.compute(password.as_bytes(), &salt), &salt);
|
107
|
+
let hash2 = hmac_params.compute(&inner_params.compute(password.as_bytes(), &salt), &salt);
|
108
|
+
let params = Algorithm::Nested {
|
109
|
+
outer: hmac_params.into(),
|
110
|
+
inner: Box::new(Algorithm::Single(inner_params.into())),
|
111
|
+
};
|
112
|
+
assert_eq!(hash, hash2);
|
113
|
+
let out = Output {
|
114
|
+
alg: params,
|
115
|
+
salt: salt,
|
116
|
+
hash: hash,
|
117
|
+
};
|
118
|
+
println!("{:?}", serde_mcf::to_string(&out).unwrap());
|
119
|
+
}
|
120
|
+
|
121
|
+
#[test]
|
122
|
+
fn hash_verify_works() {
|
123
|
+
let password = "hunter2";
|
124
|
+
let algorithm = Algorithm::Nested {
|
125
|
+
outer: super::Hmac::default().into(),
|
126
|
+
inner: Box::new(Algorithm::Single(::primitives::Scrypt::default())),
|
127
|
+
};
|
128
|
+
let hash = algorithm.hash(&password);
|
129
|
+
assert!(hash.verify(&password));
|
130
|
+
}
|
131
|
+
|
132
|
+
}
|
133
|
+
|
134
|
+
benches!(Hmac);
|