kpdb/types/
composite_key.rs

1// Copyright (c) 2016-2017 Martijn Rijkeboer <mrr@sru-systems.com>
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 super::KeyFile;
10use crate::crypto::sha256;
11use secstr::SecStr;
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 = File::open("database.key")?;
32    /// let key_file = 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 = File::open("database.key")?;
54    /// let key_file = 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 super::*;
98    use crate::types::{KeyFile, KeyFileType};
99    use secstr::SecStr;
100
101    #[test]
102    fn test_from_both_returns_correct_instance() {
103        let array = [
104            184, 53, 98, 70, 154, 211, 44, 121, 45, 59, 104, 22, 210, 47, 92, 167, 10, 193, 98,
105            121, 81, 174, 1, 128, 96, 122, 3, 12, 5, 33, 202, 40,
106        ];
107        let key = KeyFile {
108            key: SecStr::new(vec![0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64]),
109            file_type: KeyFileType::Xml,
110        };
111        let password = "secret";
112        let expected = CompositeKey::secure(array);
113        let actual = CompositeKey::from_both(password, key);
114        assert_eq!(actual, expected);
115    }
116
117    #[test]
118    fn test_from_key_file_returns_correct_instance() {
119        let array = [
120            94, 136, 72, 152, 218, 40, 4, 113, 81, 208, 229, 111, 141, 198, 41, 39, 115, 96, 61,
121            13, 106, 171, 189, 214, 42, 17, 239, 114, 29, 21, 66, 216,
122        ];
123        let key = KeyFile {
124            key: SecStr::new(vec![0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64]),
125            file_type: KeyFileType::Xml,
126        };
127        let expected = CompositeKey::secure(array);
128        let actual = CompositeKey::from_key_file(key);
129        assert_eq!(actual, expected);
130    }
131
132    #[test]
133    fn test_from_password_returns_correct_instance() {
134        let array = [
135            56, 129, 33, 157, 8, 125, 217, 198, 52, 55, 63, 211, 61, 250, 51, 162, 203, 107, 252,
136            108, 82, 11, 100, 184, 187, 96, 239, 44, 235, 83, 74, 231,
137        ];
138        let password = "secret";
139        let expected = CompositeKey::secure(array);
140        let actual = CompositeKey::from_password(password);
141        assert_eq!(actual, expected);
142    }
143
144    #[test]
145    fn test_unsecure_inverses_secure() {
146        let array = [
147            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
148            25, 26, 27, 28, 29, 30, 31, 32,
149        ];
150        let expected = array.clone();
151        let actual = CompositeKey::unsecure(&CompositeKey::secure(array));
152        assert_eq!(actual, expected);
153    }
154}