blob: 9cc69ef6c1e1bb03903ae6a30ef097be46917438 [file] [log] [blame]
Paul Crowleyef611e52021-04-20 14:43:04 -07001// Copyright 2021, The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Provide the [`KeyMintDevice`] wrapper for operating directly on a KeyMint device.
16
17use crate::{
18 database::{
19 BlobMetaData, BlobMetaEntry, CertificateInfo, DateTime, KeyEntry, KeyEntryLoadBits,
20 KeyIdGuard, KeyMetaData, KeyMetaEntry, KeyType, KeystoreDB, SubComponentType, Uuid,
21 },
Janis Danisevskis5c748212021-05-17 17:13:56 -070022 error::{map_km_error, Error, ErrorCode},
Paul Crowleyef611e52021-04-20 14:43:04 -070023 globals::get_keymint_device,
24 super_key::KeyBlob,
Janis Danisevskis2ee014b2021-05-05 14:29:08 -070025 utils::{key_characteristics_to_internal, watchdog as wd, Asp, AID_KEYSTORE},
Paul Crowleyef611e52021-04-20 14:43:04 -070026};
27use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Janis Danisevskis5c748212021-05-17 17:13:56 -070028 BeginResult::BeginResult, HardwareAuthToken::HardwareAuthToken, IKeyMintDevice::IKeyMintDevice,
29 IKeyMintOperation::IKeyMintOperation, KeyCreationResult::KeyCreationResult,
30 KeyParameter::KeyParameter, KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
Paul Crowleyef611e52021-04-20 14:43:04 -070031};
32use android_system_keystore2::aidl::android::system::keystore2::{
Paul Crowley618869e2021-04-08 20:30:54 -070033 Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
Paul Crowleyef611e52021-04-20 14:43:04 -070034};
35use anyhow::{Context, Result};
36use binder::Strong;
37
38/// Wrapper for operating directly on a KeyMint device.
39/// These methods often mirror methods in [`crate::security_level`]. However
40/// the functions in [`crate::security_level`] make assumptions that hold, and has side effects
41/// that make sense, only if called by an external client through binder.
42/// In addition we are trying to maintain a separation between interface services
43/// so that the architecture is compatible with a future move to multiple thread pools.
44/// So the simplest approach today is to write new implementations of them for internal use.
45/// Because these methods run very early, we don't even try to cooperate with
46/// the operation slot database; we assume there will be plenty of slots.
47pub struct KeyMintDevice {
48 asp: Asp,
49 km_uuid: Uuid,
Janis Danisevskis5c748212021-05-17 17:13:56 -070050 version: i32,
Paul Crowleyef611e52021-04-20 14:43:04 -070051}
52
53impl KeyMintDevice {
Janis Danisevskis5c748212021-05-17 17:13:56 -070054 /// Version number of KeyMasterDevice@V4_0
55 pub const KEY_MASTER_V4_0: i32 = 40;
56 /// Version number of KeyMasterDevice@V4_1
57 pub const KEY_MASTER_V4_1: i32 = 41;
58 /// Version number of KeyMintDevice@V1
59 pub const KEY_MINT_V1: i32 = 100;
60
Paul Crowleyef611e52021-04-20 14:43:04 -070061 /// Get a [`KeyMintDevice`] for the given [`SecurityLevel`]
62 pub fn get(security_level: SecurityLevel) -> Result<KeyMintDevice> {
Janis Danisevskis5c748212021-05-17 17:13:56 -070063 let (asp, hw_info, km_uuid) = get_keymint_device(&security_level)
Paul Crowleyef611e52021-04-20 14:43:04 -070064 .context("In KeyMintDevice::get: get_keymint_device failed")?;
Janis Danisevskis5c748212021-05-17 17:13:56 -070065
66 Ok(KeyMintDevice { asp, km_uuid, version: hw_info.versionNumber })
67 }
68
69 /// Get a [`KeyMintDevice`] for the given [`SecurityLevel`], return
70 /// [`None`] if the error `HARDWARE_TYPE_UNAVAILABLE` is returned
71 pub fn get_or_none(security_level: SecurityLevel) -> Result<Option<KeyMintDevice>> {
72 KeyMintDevice::get(security_level).map(Some).or_else(|e| {
73 match e.root_cause().downcast_ref::<Error>() {
74 Some(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)) => Ok(None),
75 _ => Err(e),
76 }
77 })
78 }
79
80 /// Returns the version of the underlying KeyMint/KeyMaster device.
81 pub fn version(&self) -> i32 {
82 self.version
Paul Crowleyef611e52021-04-20 14:43:04 -070083 }
84
Paul Crowley618869e2021-04-08 20:30:54 -070085 /// Create a KM key and store in the database.
86 pub fn create_and_store_key<F>(
Paul Crowleyef611e52021-04-20 14:43:04 -070087 &self,
88 db: &mut KeystoreDB,
89 key_desc: &KeyDescriptor,
Paul Crowley618869e2021-04-08 20:30:54 -070090 creator: F,
91 ) -> Result<()>
92 where
93 F: FnOnce(Strong<dyn IKeyMintDevice>) -> Result<KeyCreationResult, binder::Status>,
94 {
Paul Crowleyef611e52021-04-20 14:43:04 -070095 let km_dev: Strong<dyn IKeyMintDevice> = self
96 .asp
97 .get_interface()
Paul Crowley618869e2021-04-08 20:30:54 -070098 .context("In create_and_store_key: Failed to get KeyMint device")?;
99 let creation_result =
100 map_km_error(creator(km_dev)).context("In create_and_store_key: creator failed")?;
Paul Crowleyef611e52021-04-20 14:43:04 -0700101 let key_parameters = key_characteristics_to_internal(creation_result.keyCharacteristics);
102
103 let creation_date =
Paul Crowley618869e2021-04-08 20:30:54 -0700104 DateTime::now().context("In create_and_store_key: DateTime::now() failed")?;
Paul Crowleyef611e52021-04-20 14:43:04 -0700105
106 let mut key_metadata = KeyMetaData::new();
107 key_metadata.add(KeyMetaEntry::CreationDate(creation_date));
108 let mut blob_metadata = BlobMetaData::new();
109 blob_metadata.add(BlobMetaEntry::KmUuid(self.km_uuid));
110
111 db.store_new_key(
112 &key_desc,
113 &key_parameters,
114 &(&creation_result.keyBlob, &blob_metadata),
115 &CertificateInfo::new(None, None),
116 &key_metadata,
117 &self.km_uuid,
118 )
Paul Crowley618869e2021-04-08 20:30:54 -0700119 .context("In create_and_store_key: store_new_key failed")?;
Paul Crowleyef611e52021-04-20 14:43:04 -0700120 Ok(())
121 }
122
Paul Crowley618869e2021-04-08 20:30:54 -0700123 /// Generate a KeyDescriptor for internal-use keys.
124 pub fn internal_descriptor(alias: String) -> KeyDescriptor {
125 KeyDescriptor {
126 domain: Domain::APP,
127 nspace: AID_KEYSTORE as i64,
128 alias: Some(alias),
129 blob: None,
130 }
131 }
132
133 /// Look up an internal-use key in the database given a key descriptor.
134 pub fn lookup_from_desc(
135 db: &mut KeystoreDB,
136 key_desc: &KeyDescriptor,
137 ) -> Result<(KeyIdGuard, KeyEntry)> {
138 db.load_key_entry(&key_desc, KeyType::Client, KeyEntryLoadBits::KM, AID_KEYSTORE, |_, _| {
139 Ok(())
140 })
141 .context("In lookup_from_desc: load_key_entry failed")
142 }
143
144 /// Look up the key in the database, and return None if it is absent.
145 pub fn not_found_is_none(
146 lookup: Result<(KeyIdGuard, KeyEntry)>,
147 ) -> Result<Option<(KeyIdGuard, KeyEntry)>> {
148 match lookup {
149 Ok(result) => Ok(Some(result)),
150 Err(e) => match e.root_cause().downcast_ref::<Error>() {
151 Some(&Error::Rc(ResponseCode::KEY_NOT_FOUND)) => Ok(None),
152 _ => Err(e),
153 },
154 }
155 }
156
Paul Crowleyef611e52021-04-20 14:43:04 -0700157 /// This does the lookup and store in separate transactions; caller must
158 /// hold a lock before calling.
159 pub fn lookup_or_generate_key(
160 &self,
161 db: &mut KeystoreDB,
162 key_desc: &KeyDescriptor,
163 params: &[KeyParameter],
164 ) -> Result<(KeyIdGuard, KeyEntry)> {
165 // We use a separate transaction for the lookup than for the store
166 // - to keep the code simple
167 // - because the caller needs to hold a lock in any case
168 // - because it avoids holding database locks during slow
169 // KeyMint operations
Paul Crowley618869e2021-04-08 20:30:54 -0700170 let lookup = Self::not_found_is_none(Self::lookup_from_desc(db, key_desc))
171 .context("In lookup_or_generate_key: first lookup failed")?;
Janis Danisevskis5c748212021-05-17 17:13:56 -0700172
173 if let Some((key_id_guard, key_entry)) = lookup {
174 // If the key is associated with a different km instance
175 // or if there is no blob metadata for some reason the key entry
176 // is considered corrupted and needs to be replaced with a new one.
177 if key_entry
178 .key_blob_info()
179 .as_ref()
180 .map(|(_, blob_metadata)| Some(&self.km_uuid) == blob_metadata.km_uuid())
181 .unwrap_or(false)
182 {
183 return Ok((key_id_guard, key_entry));
184 }
Paul Crowleyef611e52021-04-20 14:43:04 -0700185 }
Janis Danisevskis5c748212021-05-17 17:13:56 -0700186 self.create_and_store_key(db, &key_desc, |km_dev| km_dev.generateKey(&params, None))
187 .context("In lookup_or_generate_key: generate_and_store_key failed")?;
188 Self::lookup_from_desc(db, key_desc)
189 .context("In lookup_or_generate_key: second lookup failed")
Paul Crowleyef611e52021-04-20 14:43:04 -0700190 }
191
192 /// Call the passed closure; if it returns `KEY_REQUIRES_UPGRADE`, call upgradeKey, and
193 /// write the upgraded key to the database.
194 fn upgrade_keyblob_if_required_with<T, F>(
195 &self,
196 db: &mut KeystoreDB,
197 km_dev: &Strong<dyn IKeyMintDevice>,
Paul Crowley618869e2021-04-08 20:30:54 -0700198 key_id_guard: &KeyIdGuard,
Paul Crowleyef611e52021-04-20 14:43:04 -0700199 key_blob: &KeyBlob,
200 f: F,
201 ) -> Result<T>
202 where
203 F: Fn(&[u8]) -> Result<T, Error>,
204 {
205 match f(key_blob) {
206 Err(Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
Janis Danisevskis2ee014b2021-05-05 14:29:08 -0700207 let upgraded_blob = map_km_error({
208 let _wp = wd::watch_millis(
209 "In KeyMintDevice::upgrade_keyblob_if_required_with: calling upgradeKey.",
210 500,
211 );
212 km_dev.upgradeKey(key_blob, &[])
213 })
214 .context("In upgrade_keyblob_if_required_with: Upgrade failed")?;
Paul Crowleyef611e52021-04-20 14:43:04 -0700215
216 let mut new_blob_metadata = BlobMetaData::new();
217 new_blob_metadata.add(BlobMetaEntry::KmUuid(self.km_uuid));
218
219 db.set_blob(
Paul Crowley618869e2021-04-08 20:30:54 -0700220 key_id_guard,
Paul Crowleyef611e52021-04-20 14:43:04 -0700221 SubComponentType::KEY_BLOB,
222 Some(&upgraded_blob),
223 Some(&new_blob_metadata),
224 )
225 .context(concat!(
226 "In upgrade_keyblob_if_required_with: ",
227 "Failed to insert upgraded blob into the database"
228 ))?;
229
230 Ok(f(&upgraded_blob).context(concat!(
231 "In upgrade_keyblob_if_required_with: ",
232 "Closure failed after upgrade"
233 ))?)
234 }
235 result => Ok(result.context("In upgrade_keyblob_if_required_with: Closure failed")?),
236 }
237 }
238
239 /// Use the created key in an operation that can be done with
240 /// a call to begin followed by a call to finish.
Paul Crowley618869e2021-04-08 20:30:54 -0700241 #[allow(clippy::too_many_arguments)]
Paul Crowleyef611e52021-04-20 14:43:04 -0700242 pub fn use_key_in_one_step(
243 &self,
244 db: &mut KeystoreDB,
Paul Crowley618869e2021-04-08 20:30:54 -0700245 key_id_guard: &KeyIdGuard,
Paul Crowleyef611e52021-04-20 14:43:04 -0700246 key_entry: &KeyEntry,
247 purpose: KeyPurpose,
248 operation_parameters: &[KeyParameter],
Paul Crowley618869e2021-04-08 20:30:54 -0700249 auth_token: Option<&HardwareAuthToken>,
Paul Crowleyef611e52021-04-20 14:43:04 -0700250 input: &[u8],
251 ) -> Result<Vec<u8>> {
252 let km_dev: Strong<dyn IKeyMintDevice> = self
253 .asp
254 .get_interface()
255 .context("In use_key_in_one_step: Failed to get KeyMint device")?;
256
257 let (key_blob, _blob_metadata) = key_entry
258 .key_blob_info()
259 .as_ref()
260 .ok_or_else(Error::sys)
261 .context("use_key_in_one_step: Keyblob missing")?;
262 let key_blob = KeyBlob::Ref(&key_blob);
263
264 let begin_result: BeginResult = self
265 .upgrade_keyblob_if_required_with(db, &km_dev, key_id_guard, &key_blob, |blob| {
Janis Danisevskis2ee014b2021-05-05 14:29:08 -0700266 map_km_error({
267 let _wp = wd::watch_millis("In use_key_in_one_step: calling: begin", 500);
268 km_dev.begin(purpose, blob, operation_parameters, auth_token)
269 })
Paul Crowleyef611e52021-04-20 14:43:04 -0700270 })
271 .context("In use_key_in_one_step: Failed to begin operation.")?;
272 let operation: Strong<dyn IKeyMintOperation> = begin_result
273 .operation
274 .ok_or_else(Error::sys)
275 .context("In use_key_in_one_step: Operation missing")?;
Janis Danisevskis2ee014b2021-05-05 14:29:08 -0700276 map_km_error({
277 let _wp = wd::watch_millis("In use_key_in_one_step: calling: finish", 500);
278 operation.finish(Some(input), None, None, None, None)
279 })
280 .context("In use_key_in_one_step: Failed to finish operation.")
Paul Crowleyef611e52021-04-20 14:43:04 -0700281 }
282}