keybase_keystore/
keystore.rs

1use crate::error::Error;
2use crate::generation::Generation;
3use crate::types::*;
4use async_std::path::{Path, PathBuf};
5use async_std::prelude::*;
6use async_std::sync::RwLock;
7
8pub struct KeyStore {
9    path: PathBuf,
10    gen: RwLock<Generation>,
11}
12
13impl KeyStore {
14    /// Opens a keystore.
15    pub async fn open<T: AsRef<Path>>(path: T) -> Result<Self, Error> {
16        let path = path.as_ref().to_path_buf();
17        async_std::fs::create_dir_all(&path).await?;
18        let mut gens = vec![];
19        let mut dir = async_std::fs::read_dir(&path).await?;
20        while let Some(entry) = dir.next().await {
21            let gen = entry?
22                .file_name()
23                .to_str()
24                .ok_or(Error::Corrupted)?
25                .parse()
26                .map_err(|_| Error::Corrupted)?;
27            gens.push(Generation::new(&path, gen));
28        }
29        let gen = match gens.len() {
30            0 => Generation::new(&path, 0),
31            1 => gens.pop().unwrap(),
32            _ => {
33                let mut ugen: Option<Generation> = None;
34                let mut rgens = vec![];
35                for gen in gens {
36                    if gen.device_key().await.is_ok() {
37                        if let Some(ugen2) = ugen {
38                            if ugen2.gen() < gen.gen() {
39                                rgens.push(ugen2);
40                                ugen = Some(gen);
41                            } else {
42                                rgens.push(gen);
43                                ugen = Some(ugen2);
44                            }
45                        } else {
46                            ugen = Some(gen);
47                        }
48                    } else {
49                        rgens.push(gen);
50                    }
51                }
52                if let Some(ugen) = ugen {
53                    for rgen in rgens {
54                        rgen.remove().await?;
55                    }
56                    ugen
57                } else {
58                    return Err(Error::Corrupted);
59                }
60            }
61        };
62        Ok(Self {
63            path,
64            gen: RwLock::new(gen),
65        })
66    }
67
68    /// Creates a new generation from a password mask.
69    pub async fn apply_mask(&self, mask: &Mask, next_gen: u16) -> Result<(), Error> {
70        let mut gen = self.gen.write().await;
71        if gen.gen() + 1 != next_gen {
72            return Err(Error::GenMissmatch);
73        }
74        let dk = gen.device_key().await?;
75        let pass = gen.password().await?.apply_mask(mask);
76        let next_gen = Generation::new(&self.path, next_gen);
77        next_gen.initialize(&dk, &pass, true).await?;
78        let old_gen = std::mem::replace(&mut *gen, next_gen);
79        old_gen.remove().await?;
80        Ok(())
81    }
82
83    /// Returns the generation number.
84    pub async fn gen(&self) -> u16 {
85        self.gen.read().await.gen()
86    }
87
88    /// Checks if the keystore is initialized.
89    pub async fn is_initialized(&self) -> bool {
90        self.gen.read().await.is_initialized().await
91    }
92
93    /// Initializes the keystore.
94    pub async fn initialize(
95        &self,
96        dk: &DeviceKey,
97        pass: &Password,
98        force: bool,
99    ) -> Result<(), Error> {
100        self.gen.write().await.initialize(dk, pass, force).await
101    }
102
103    /// Unlocking the keystore makes the random key decryptable.
104    pub async fn unlock(&self, pass: &Password) -> Result<DeviceKey, Error> {
105        self.gen.write().await.unlock(pass).await
106    }
107
108    /// Locks the keystore by zeroizing the noise file. This makes the encrypted
109    /// random key undecryptable without a password.
110    pub async fn lock(&self) -> Result<(), Error> {
111        self.gen.write().await.lock().await
112    }
113
114    /// The random key is used to decrypt the device key.
115    ///
116    /// NOTE: Only works if the keystore was unlocked.
117    pub async fn device_key(&self) -> Result<DeviceKey, Error> {
118        self.gen.read().await.device_key().await
119    }
120
121    /// The random key is used to recover the password.
122    ///
123    /// NOTE: Only works if the keystore was unlocked.
124    pub async fn password(&self) -> Result<Password, Error> {
125        self.gen.read().await.password().await
126    }
127
128    /// Returns the public device key.
129    pub async fn public(&self) -> Result<PublicDeviceKey, Error> {
130        self.gen.read().await.public().await
131    }
132
133    /// Change password.
134    pub async fn change_password_mask(&self, password: &Password) -> Result<Mask, Error> {
135        self.gen.read().await.change_password_mask(password).await
136    }
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142    use crate::types::{DeviceKey, Password};
143    use fail::FailScenario;
144    use tempdir::TempDir;
145
146    #[async_std::test]
147    async fn test_keystore() {
148        fail::cfg("edk-write-fail", "off").unwrap();
149        fail::cfg("gen-rm-fail", "off").unwrap();
150        let tmp = TempDir::new("keystore-").unwrap();
151        let store = KeyStore::open(tmp.path()).await.unwrap();
152
153        // generate
154        let key = DeviceKey::generate().await;
155        let p1 = Password::from("password".to_string());
156        store.initialize(&key, &p1, false).await.unwrap();
157
158        // check reading the device key.
159        let key2 = store.device_key().await.unwrap();
160        assert_eq!(key.expose_secret(), key2.expose_secret());
161
162        // check reading the password.
163        let rp1 = store.password().await.unwrap();
164        assert_eq!(p1.expose_secret(), rp1.expose_secret());
165
166        // make sure key is the same after lock/unlock
167        store.lock().await.unwrap();
168        store.unlock(&p1).await.unwrap();
169        let key2 = store.device_key().await.unwrap();
170        assert_eq!(key.expose_secret(), key2.expose_secret());
171
172        // change password
173        let p2 = Password::from("other password".to_string());
174        let mask = store.change_password_mask(&p2).await.unwrap();
175        store
176            .apply_mask(&mask, store.gen().await + 1)
177            .await
178            .unwrap();
179
180        // make sure key is the same after lock/unlock
181        store.lock().await.unwrap();
182
183        let store = KeyStore::open(tmp.path()).await.unwrap();
184        store.unlock(&p2).await.unwrap();
185        let key2 = store.device_key().await.unwrap();
186        assert_eq!(key.expose_secret(), key2.expose_secret());
187
188        // make sure unlock fails if password is wrong
189        let p3 = Password::from("wrong password".to_string());
190        store.lock().await.unwrap();
191        match store.unlock(&p3).await {
192            Err(Error::Locked) => {}
193            Ok(_) => panic!("should fail"),
194            r => {
195                r.unwrap();
196            }
197        }
198        match store.device_key().await {
199            Err(Error::Locked) => {}
200            Ok(_) => panic!("should fail"),
201            r => {
202                r.unwrap();
203            }
204        }
205    }
206
207    #[async_std::test]
208    #[ignore] // Fail tests can not be run in parallel
209    async fn test_edk_write_fail_unlock() {
210        fail::cfg("edk-write-fail", "off").unwrap();
211        fail::cfg("gen-rm-fail", "off").unwrap();
212        let tmp = TempDir::new("keystore-").unwrap();
213        let store = KeyStore::open(tmp.path()).await.unwrap();
214        let key = DeviceKey::generate().await;
215        let pass = Password::generate().await;
216        store.initialize(&key, &pass, false).await.unwrap();
217
218        let scenario = FailScenario::setup();
219
220        fail::cfg("edk-write-fail", "return(())").unwrap();
221        let npass = Password::generate().await;
222        let mask = store.change_password_mask(&npass).await.unwrap();
223        store.apply_mask(&mask, store.gen().await + 1).await.ok();
224        store.lock().await.unwrap();
225        store.unlock(&pass).await.unwrap();
226
227        scenario.teardown();
228    }
229
230    #[async_std::test]
231    #[ignore] // Fail tests can not be run in parallel
232    async fn test_edk_write_fail_recovery() {
233        fail::cfg("edk-write-fail", "off").unwrap();
234        fail::cfg("gen-rm-fail", "off").unwrap();
235        let tmp = TempDir::new("keystore-").unwrap();
236        let store = KeyStore::open(tmp.path()).await.unwrap();
237        let key = DeviceKey::generate().await;
238        let pass = Password::generate().await;
239        store.initialize(&key, &pass, false).await.unwrap();
240
241        let scenario = FailScenario::setup();
242
243        fail::cfg("edk-write-fail", "return(())").unwrap();
244        let npass = Password::generate().await;
245        let mask = store.change_password_mask(&npass).await.unwrap();
246        store.apply_mask(&mask, store.gen().await + 1).await.ok();
247
248        let key2 = store.device_key().await.unwrap();
249        assert_eq!(key.expose_secret(), key2.expose_secret());
250
251        let store = KeyStore::open(tmp.path()).await.unwrap();
252        let key2 = store.device_key().await.unwrap();
253        assert_eq!(key.expose_secret(), key2.expose_secret());
254
255        scenario.teardown();
256    }
257
258    #[async_std::test]
259    #[ignore] // Fail tests can not be run in parallel
260    async fn test_gen_remove_fail_recovery() {
261        fail::cfg("edk-write-fail", "off").unwrap();
262        fail::cfg("gen-rm-fail", "off").unwrap();
263        let tmp = TempDir::new("keystore-").unwrap();
264        let store = KeyStore::open(tmp.path()).await.unwrap();
265        let key = DeviceKey::generate().await;
266        let pass = Password::generate().await;
267        store.initialize(&key, &pass, false).await.unwrap();
268
269        let scenario = FailScenario::setup();
270
271        fail::cfg("gen-rm-fail", "return(())").unwrap();
272        let npass = Password::generate().await;
273        let mask = store.change_password_mask(&npass).await.unwrap();
274        store.apply_mask(&mask, store.gen().await + 1).await.ok();
275
276        let key2 = store.device_key().await.unwrap();
277        assert_eq!(key.expose_secret(), key2.expose_secret());
278
279        let store = KeyStore::open(tmp.path()).await.unwrap();
280        let key2 = store.device_key().await.unwrap();
281        assert_eq!(key.expose_secret(), key2.expose_secret());
282
283        scenario.teardown();
284    }
285}