kpdb/types/composite_key.rs
1// Copyright (c) 2016-2017 Martijn Rijkeboer <[email protected]>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use crypto::sha256;
10use secstr::SecStr;
11use super::KeyFile;
12
13/// Composition of the user's key data.
14///
15/// This data type uses secstr's `SecStr` to protect the key data. To
16/// retrieve the protected data use the `unsecure` method.
17#[derive(Clone, Debug, PartialEq)]
18pub struct CompositeKey(SecStr);
19
20impl CompositeKey {
21 /// Create a composite key from both a password and a key file.
22 ///
23 /// # Examples
24 ///
25 /// ```rust,no_run
26 /// # use kpdb::Result;
27 /// use kpdb::{CompositeKey, KeyFile};
28 /// use std::fs::File;
29 ///
30 /// # fn from_both_example() -> Result<()> {
31 /// let mut file = try!(File::open("database.key"));
32 /// let key_file = try!(KeyFile::open(&mut file));
33 /// let key = CompositeKey::from_both("secret", key_file);
34 /// # Ok(())
35 /// # }
36 /// ```
37 pub fn from_both<S: Into<String>>(password: S, key_file: KeyFile) -> CompositeKey {
38 let password = sha256::hash(&[&password.into().into_bytes()]);
39 let combined = sha256::hash(&[&password, &key_file.key.unsecure()]);
40 CompositeKey::secure(combined)
41 }
42
43 /// Create a composite key from a key file.
44 ///
45 /// # Examples
46 ///
47 /// ```rust,no_run
48 /// # use kpdb::Result;
49 /// use kpdb::{CompositeKey, KeyFile};
50 /// use std::fs::File;
51 ///
52 /// # fn from_key_file_example() -> Result<()> {
53 /// let mut file = try!(File::open("database.key"));
54 /// let key_file = try!(KeyFile::open(&mut file));
55 /// let key = CompositeKey::from_key_file(key_file);
56 /// # Ok(())
57 /// # }
58 /// ```
59 pub fn from_key_file(key_file: KeyFile) -> CompositeKey {
60 let combined = sha256::hash(&[&key_file.key.unsecure()]);
61 CompositeKey::secure(combined)
62 }
63
64 /// Create a composite key from a password.
65 ///
66 /// # Examples
67 ///
68 /// ```rust
69 /// use kpdb::CompositeKey;
70 ///
71 /// let key = CompositeKey::from_password("secret");
72 /// ```
73 pub fn from_password<S: Into<String>>(password: S) -> CompositeKey {
74 let password = sha256::hash(&[&password.into().into_bytes()]);
75 let combined = sha256::hash(&[&password]);
76 CompositeKey::secure(combined)
77 }
78
79 /// Gets the protected data from this composite key.
80 pub fn unsecure(&self) -> [u8; 32] {
81 let unsecure = self.0.unsecure();
82 let mut array = [0u8; 32];
83 for (u, a) in unsecure.iter().zip(array.iter_mut()) {
84 *a = *u;
85 }
86 array
87 }
88
89 fn secure(key: [u8; 32]) -> CompositeKey {
90 CompositeKey(SecStr::new(key.to_vec()))
91 }
92}
93
94#[cfg(test)]
95mod tests {
96
97 use secstr::SecStr;
98 use super::*;
99 use types::{KeyFile, KeyFileType};
100
101 #[test]
102 fn test_from_both_returns_correct_instance() {
103 let array = [
104 184,
105 53,
106 98,
107 70,
108 154,
109 211,
110 44,
111 121,
112 45,
113 59,
114 104,
115 22,
116 210,
117 47,
118 92,
119 167,
120 10,
121 193,
122 98,
123 121,
124 81,
125 174,
126 1,
127 128,
128 96,
129 122,
130 3,
131 12,
132 5,
133 33,
134 202,
135 40,
136 ];
137 let key = KeyFile {
138 key: SecStr::new(vec![0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64]),
139 file_type: KeyFileType::Xml,
140 };
141 let password = "secret";
142 let expected = CompositeKey::secure(array);
143 let actual = CompositeKey::from_both(password, key);
144 assert_eq!(actual, expected);
145 }
146
147 #[test]
148 fn test_from_key_file_returns_correct_instance() {
149 let array = [
150 94,
151 136,
152 72,
153 152,
154 218,
155 40,
156 4,
157 113,
158 81,
159 208,
160 229,
161 111,
162 141,
163 198,
164 41,
165 39,
166 115,
167 96,
168 61,
169 13,
170 106,
171 171,
172 189,
173 214,
174 42,
175 17,
176 239,
177 114,
178 29,
179 21,
180 66,
181 216,
182 ];
183 let key = KeyFile {
184 key: SecStr::new(vec![0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64]),
185 file_type: KeyFileType::Xml,
186 };
187 let expected = CompositeKey::secure(array);
188 let actual = CompositeKey::from_key_file(key);
189 assert_eq!(actual, expected);
190 }
191
192 #[test]
193 fn test_from_password_returns_correct_instance() {
194 let array = [
195 56,
196 129,
197 33,
198 157,
199 8,
200 125,
201 217,
202 198,
203 52,
204 55,
205 63,
206 211,
207 61,
208 250,
209 51,
210 162,
211 203,
212 107,
213 252,
214 108,
215 82,
216 11,
217 100,
218 184,
219 187,
220 96,
221 239,
222 44,
223 235,
224 83,
225 74,
226 231,
227 ];
228 let password = "secret";
229 let expected = CompositeKey::secure(array);
230 let actual = CompositeKey::from_password(password);
231 assert_eq!(actual, expected);
232 }
233
234 #[test]
235 fn test_unsecure_inverses_secure() {
236 let array = [
237 1,
238 2,
239 3,
240 4,
241 5,
242 6,
243 7,
244 8,
245 9,
246 10,
247 11,
248 12,
249 13,
250 14,
251 15,
252 16,
253 17,
254 18,
255 19,
256 20,
257 21,
258 22,
259 23,
260 24,
261 25,
262 26,
263 27,
264 28,
265 29,
266 30,
267 31,
268 32,
269 ];
270 let expected = array.clone();
271 let actual = CompositeKey::unsecure(&CompositeKey::secure(array));
272 assert_eq!(actual, expected);
273 }
274}