1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 //! Implementation of the `KeyValueStore` trait using Trusty secure storage.
18 //!
19 //! Store each secret in a file of its own (mapping the secret ID to the filename) as there are not
20 //! expected to be many extant secrets. However, use a prefix for the filename to allow easier
21 //! migration to a different scheme in future if this assumption changes.
22
23 use alloc::string::String;
24 use secretkeeper_comm::data_types::error::Error;
25 use secretkeeper_core::store::KeyValueStore;
26 use storage::{OpenMode, Port, Session};
27
28 /// Store each secret in a file named "v1_<hex>", using the hex representation of the key/secret ID.
29 /// Note that IDs are not confidential, so can appear in logs.
30 const PREFIX_V1: &str = "v1_";
31
32 /// Generate the filename corresponding to a key.
filename(key: &[u8]) -> String33 fn filename(key: &[u8]) -> String {
34 let mut result = String::with_capacity(PREFIX_V1.len() + 2 * key.len());
35 result += PREFIX_V1;
36 for byte in key {
37 result += &format!("{:02x}", byte);
38 }
39 result
40 }
41
42 /// Helper macro for emitting an error.
43 macro_rules! ss_err {
44 { $e:expr, $($arg:tt)+ } => {
45 {
46 log::error!("{}: {:?}", format_args!($($arg)+), $e);
47 Error::UnexpectedError
48 }
49 };
50 }
51
52 /// Create a storage session.
create_session() -> Result<Session, Error>53 fn create_session() -> Result<Session, Error> {
54 // Use TD storage, which means that:
55 // - storage is only available after Android has booted
56 // - storage is wiped on factory reset
57 // - size of stored data isn't problematic
58 Session::new(Port::TamperDetect, /* wait_for_port= */ true)
59 .map_err(|e| ss_err!(e, "Couldn't create storage session"))
60 }
61
62 /// An implementation of `KeyValueStore` backed by secure storage.
63 #[derive(Default)]
64 pub struct SecureStore;
65
66 impl KeyValueStore for SecureStore {
store(&mut self, key: &[u8], val: &[u8]) -> Result<(), Error>67 fn store(&mut self, key: &[u8], val: &[u8]) -> Result<(), Error> {
68 let filename = filename(key);
69 let mut session = create_session()?;
70
71 // This will overwrite the value if key is already present.
72 let mut file = session
73 .open_file(&filename, OpenMode::Create)
74 .map_err(|e| ss_err!(e, "Couldn't create file '{filename}'"))?;
75 session.write_all(&mut file, val).map_err(|e| ss_err!(e, "Failed to write data"))?;
76 Ok(())
77 }
78
get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Error>79 fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Error> {
80 let filename = filename(key);
81 let mut session = create_session()?;
82 let file = match session.open_file(&filename, OpenMode::Open) {
83 Ok(f) => f,
84 Err(storage::Error::Code(trusty_sys::Error::NotFound)) => return Ok(None),
85 Err(e) => return Err(ss_err!(e, "Failed to open file '{filename}'")),
86 };
87 let size = session
88 .get_size(&file)
89 .map_err(|e| ss_err!(e, "Failed to get size for '{filename}'"))?;
90 let mut buffer = vec![0; size];
91 let content = session
92 .read_all(&file, buffer.as_mut_slice())
93 .map_err(|e| ss_err!(e, "Failed to read '{filename}'"))?;
94 let total_size = content.len();
95 buffer.resize(total_size, 0);
96 Ok(Some(buffer))
97 }
98
delete(&mut self, key: &[u8]) -> Result<(), Error>99 fn delete(&mut self, key: &[u8]) -> Result<(), Error> {
100 let filename = filename(key);
101 let mut session = create_session()?;
102 match session.remove(&filename) {
103 Ok(_) => Ok(()),
104 Err(storage::Error::Code(trusty_sys::Error::NotFound)) => Ok(()),
105 Err(e) => Err(ss_err!(e, "Failed to delete file '{filename}'")),
106 }
107 }
108
delete_all(&mut self) -> Result<(), Error>109 fn delete_all(&mut self) -> Result<(), Error> {
110 let mut failed = false;
111 let mut session = create_session()?;
112 for entry in session.list_files().map_err(|e| ss_err!(e, "Failed to list files"))? {
113 match entry {
114 Ok((filename, _state)) if filename.starts_with(PREFIX_V1) => {
115 let result = session
116 .remove(&filename)
117 .map_err(|e| ss_err!(e, "Failed to delete '{filename}'"));
118 if result.is_err() {
119 failed = true;
120 }
121 }
122 Ok((filename, _state)) => log::info!("Skipping unrelated file {filename}"),
123 Err(e) => log::error!("Failed to delete an entry: {e:?}"),
124 };
125 }
126 if failed {
127 Err(Error::UnexpectedError)
128 } else {
129 Ok(())
130 }
131 }
132 }
133
134 #[cfg(test)]
135 mod tests {
136 use super::*;
137 use test::expect_eq;
138
139 const KEY1: &[u8] = b"bogus key 1";
140 const KEY2: &[u8] = b"bogus key 2";
141 const SECRET1: &[u8] = b"bogus secret 1";
142 const SECRET2: &[u8] = b"bogus secret 2";
143
144 #[test]
test_secretkeeper_store()145 fn test_secretkeeper_store() {
146 let mut store = SecureStore::default();
147
148 // Ensure consistent state before.
149 store.delete(KEY1).unwrap();
150 store.delete(KEY2).unwrap();
151
152 store.store(KEY1, SECRET1).unwrap();
153 expect_eq!(store.get(KEY1), Ok(Some(SECRET1.to_vec())));
154 expect_eq!(store.get(KEY2), Ok(None));
155 store.store(KEY1, SECRET2).unwrap();
156 expect_eq!(store.get(KEY1), Ok(Some(SECRET2.to_vec())));
157 expect_eq!(store.get(KEY2), Ok(None));
158 store.store(KEY1, SECRET1).unwrap();
159 expect_eq!(store.get(KEY1), Ok(Some(SECRET1.to_vec())));
160 expect_eq!(store.get(KEY2), Ok(None));
161 store.store(KEY2, SECRET1).unwrap();
162 expect_eq!(store.get(KEY1), Ok(Some(SECRET1.to_vec())));
163 expect_eq!(store.get(KEY2), Ok(Some(SECRET1.to_vec())));
164 store.delete(KEY1).unwrap();
165 expect_eq!(store.get(KEY1), Ok(None));
166 expect_eq!(store.get(KEY2), Ok(Some(SECRET1.to_vec())));
167 store.delete(KEY2).unwrap();
168 expect_eq!(store.get(KEY1), Ok(None));
169 expect_eq!(store.get(KEY2), Ok(None));
170
171 // Ensure consistent state after.
172 store.delete(KEY1).unwrap();
173 store.delete(KEY2).unwrap();
174 }
175 }
176