blob: 9e6ef41046e2b49c90e148e52a92efb6d66d5e45 [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 },
22 error::{map_km_error, Error},
23 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::{
Paul Crowley618869e2021-04-08 20:30:54 -070028 BeginResult::BeginResult, ErrorCode::ErrorCode, HardwareAuthToken::HardwareAuthToken,
29 IKeyMintDevice::IKeyMintDevice, IKeyMintOperation::IKeyMintOperation,
30 KeyCreationResult::KeyCreationResult, KeyParameter::KeyParameter, KeyPurpose::KeyPurpose,
Paul Crowleyef611e52021-04-20 14:43:04 -070031 SecurityLevel::SecurityLevel,
32};
33use android_system_keystore2::aidl::android::system::keystore2::{
Paul Crowley618869e2021-04-08 20:30:54 -070034 Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
Paul Crowleyef611e52021-04-20 14:43:04 -070035};
36use anyhow::{Context, Result};
37use binder::Strong;
38
39/// Wrapper for operating directly on a KeyMint device.
40/// These methods often mirror methods in [`crate::security_level`]. However
41/// the functions in [`crate::security_level`] make assumptions that hold, and has side effects
42/// that make sense, only if called by an external client through binder.
43/// In addition we are trying to maintain a separation between interface services
44/// so that the architecture is compatible with a future move to multiple thread pools.
45/// So the simplest approach today is to write new implementations of them for internal use.
46/// Because these methods run very early, we don't even try to cooperate with
47/// the operation slot database; we assume there will be plenty of slots.
48pub struct KeyMintDevice {
49 asp: Asp,
50 km_uuid: Uuid,
51}
52
53impl KeyMintDevice {
54 /// Get a [`KeyMintDevice`] for the given [`SecurityLevel`]
55 pub fn get(security_level: SecurityLevel) -> Result<KeyMintDevice> {
56 let (asp, _hw_info, km_uuid) = get_keymint_device(&security_level)
57 .context("In KeyMintDevice::get: get_keymint_device failed")?;
58 Ok(KeyMintDevice { asp, km_uuid })
59 }
60
Paul Crowley618869e2021-04-08 20:30:54 -070061 /// Create a KM key and store in the database.
62 pub fn create_and_store_key<F>(
Paul Crowleyef611e52021-04-20 14:43:04 -070063 &self,
64 db: &mut KeystoreDB,
65 key_desc: &KeyDescriptor,
Paul Crowley618869e2021-04-08 20:30:54 -070066 creator: F,
67 ) -> Result<()>
68 where
69 F: FnOnce(Strong<dyn IKeyMintDevice>) -> Result<KeyCreationResult, binder::Status>,
70 {
Paul Crowleyef611e52021-04-20 14:43:04 -070071 let km_dev: Strong<dyn IKeyMintDevice> = self
72 .asp
73 .get_interface()
Paul Crowley618869e2021-04-08 20:30:54 -070074 .context("In create_and_store_key: Failed to get KeyMint device")?;
75 let creation_result =
76 map_km_error(creator(km_dev)).context("In create_and_store_key: creator failed")?;
Paul Crowleyef611e52021-04-20 14:43:04 -070077 let key_parameters = key_characteristics_to_internal(creation_result.keyCharacteristics);
78
79 let creation_date =
Paul Crowley618869e2021-04-08 20:30:54 -070080 DateTime::now().context("In create_and_store_key: DateTime::now() failed")?;
Paul Crowleyef611e52021-04-20 14:43:04 -070081
82 let mut key_metadata = KeyMetaData::new();
83 key_metadata.add(KeyMetaEntry::CreationDate(creation_date));
84 let mut blob_metadata = BlobMetaData::new();
85 blob_metadata.add(BlobMetaEntry::KmUuid(self.km_uuid));
86
87 db.store_new_key(
88 &key_desc,
89 &key_parameters,
90 &(&creation_result.keyBlob, &blob_metadata),
91 &CertificateInfo::new(None, None),
92 &key_metadata,
93 &self.km_uuid,
94 )
Paul Crowley618869e2021-04-08 20:30:54 -070095 .context("In create_and_store_key: store_new_key failed")?;
Paul Crowleyef611e52021-04-20 14:43:04 -070096 Ok(())
97 }
98
Paul Crowley618869e2021-04-08 20:30:54 -070099 /// Generate a KeyDescriptor for internal-use keys.
100 pub fn internal_descriptor(alias: String) -> KeyDescriptor {
101 KeyDescriptor {
102 domain: Domain::APP,
103 nspace: AID_KEYSTORE as i64,
104 alias: Some(alias),
105 blob: None,
106 }
107 }
108
109 /// Look up an internal-use key in the database given a key descriptor.
110 pub fn lookup_from_desc(
111 db: &mut KeystoreDB,
112 key_desc: &KeyDescriptor,
113 ) -> Result<(KeyIdGuard, KeyEntry)> {
114 db.load_key_entry(&key_desc, KeyType::Client, KeyEntryLoadBits::KM, AID_KEYSTORE, |_, _| {
115 Ok(())
116 })
117 .context("In lookup_from_desc: load_key_entry failed")
118 }
119
120 /// Look up the key in the database, and return None if it is absent.
121 pub fn not_found_is_none(
122 lookup: Result<(KeyIdGuard, KeyEntry)>,
123 ) -> Result<Option<(KeyIdGuard, KeyEntry)>> {
124 match lookup {
125 Ok(result) => Ok(Some(result)),
126 Err(e) => match e.root_cause().downcast_ref::<Error>() {
127 Some(&Error::Rc(ResponseCode::KEY_NOT_FOUND)) => Ok(None),
128 _ => Err(e),
129 },
130 }
131 }
132
Paul Crowleyef611e52021-04-20 14:43:04 -0700133 /// This does the lookup and store in separate transactions; caller must
134 /// hold a lock before calling.
135 pub fn lookup_or_generate_key(
136 &self,
137 db: &mut KeystoreDB,
138 key_desc: &KeyDescriptor,
139 params: &[KeyParameter],
140 ) -> Result<(KeyIdGuard, KeyEntry)> {
141 // We use a separate transaction for the lookup than for the store
142 // - to keep the code simple
143 // - because the caller needs to hold a lock in any case
144 // - because it avoids holding database locks during slow
145 // KeyMint operations
Paul Crowley618869e2021-04-08 20:30:54 -0700146 let lookup = Self::not_found_is_none(Self::lookup_from_desc(db, key_desc))
147 .context("In lookup_or_generate_key: first lookup failed")?;
148 if let Some(result) = lookup {
149 Ok(result)
150 } else {
151 self.create_and_store_key(db, &key_desc, |km_dev| km_dev.generateKey(&params, None))
152 .context("In lookup_or_generate_key: generate_and_store_key failed")?;
153 Self::lookup_from_desc(db, key_desc)
Janis Danisevskis2ee014b2021-05-05 14:29:08 -0700154 .context("In lookup_or_generate_key: second lookup failed")
Paul Crowleyef611e52021-04-20 14:43:04 -0700155 }
Paul Crowleyef611e52021-04-20 14:43:04 -0700156 }
157
158 /// Call the passed closure; if it returns `KEY_REQUIRES_UPGRADE`, call upgradeKey, and
159 /// write the upgraded key to the database.
160 fn upgrade_keyblob_if_required_with<T, F>(
161 &self,
162 db: &mut KeystoreDB,
163 km_dev: &Strong<dyn IKeyMintDevice>,
Paul Crowley618869e2021-04-08 20:30:54 -0700164 key_id_guard: &KeyIdGuard,
Paul Crowleyef611e52021-04-20 14:43:04 -0700165 key_blob: &KeyBlob,
166 f: F,
167 ) -> Result<T>
168 where
169 F: Fn(&[u8]) -> Result<T, Error>,
170 {
171 match f(key_blob) {
172 Err(Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
Janis Danisevskis2ee014b2021-05-05 14:29:08 -0700173 let upgraded_blob = map_km_error({
174 let _wp = wd::watch_millis(
175 "In KeyMintDevice::upgrade_keyblob_if_required_with: calling upgradeKey.",
176 500,
177 );
178 km_dev.upgradeKey(key_blob, &[])
179 })
180 .context("In upgrade_keyblob_if_required_with: Upgrade failed")?;
Paul Crowleyef611e52021-04-20 14:43:04 -0700181
182 let mut new_blob_metadata = BlobMetaData::new();
183 new_blob_metadata.add(BlobMetaEntry::KmUuid(self.km_uuid));
184
185 db.set_blob(
Paul Crowley618869e2021-04-08 20:30:54 -0700186 key_id_guard,
Paul Crowleyef611e52021-04-20 14:43:04 -0700187 SubComponentType::KEY_BLOB,
188 Some(&upgraded_blob),
189 Some(&new_blob_metadata),
190 )
191 .context(concat!(
192 "In upgrade_keyblob_if_required_with: ",
193 "Failed to insert upgraded blob into the database"
194 ))?;
195
196 Ok(f(&upgraded_blob).context(concat!(
197 "In upgrade_keyblob_if_required_with: ",
198 "Closure failed after upgrade"
199 ))?)
200 }
201 result => Ok(result.context("In upgrade_keyblob_if_required_with: Closure failed")?),
202 }
203 }
204
205 /// Use the created key in an operation that can be done with
206 /// a call to begin followed by a call to finish.
Paul Crowley618869e2021-04-08 20:30:54 -0700207 #[allow(clippy::too_many_arguments)]
Paul Crowleyef611e52021-04-20 14:43:04 -0700208 pub fn use_key_in_one_step(
209 &self,
210 db: &mut KeystoreDB,
Paul Crowley618869e2021-04-08 20:30:54 -0700211 key_id_guard: &KeyIdGuard,
Paul Crowleyef611e52021-04-20 14:43:04 -0700212 key_entry: &KeyEntry,
213 purpose: KeyPurpose,
214 operation_parameters: &[KeyParameter],
Paul Crowley618869e2021-04-08 20:30:54 -0700215 auth_token: Option<&HardwareAuthToken>,
Paul Crowleyef611e52021-04-20 14:43:04 -0700216 input: &[u8],
217 ) -> Result<Vec<u8>> {
218 let km_dev: Strong<dyn IKeyMintDevice> = self
219 .asp
220 .get_interface()
221 .context("In use_key_in_one_step: Failed to get KeyMint device")?;
222
223 let (key_blob, _blob_metadata) = key_entry
224 .key_blob_info()
225 .as_ref()
226 .ok_or_else(Error::sys)
227 .context("use_key_in_one_step: Keyblob missing")?;
228 let key_blob = KeyBlob::Ref(&key_blob);
229
230 let begin_result: BeginResult = self
231 .upgrade_keyblob_if_required_with(db, &km_dev, key_id_guard, &key_blob, |blob| {
Janis Danisevskis2ee014b2021-05-05 14:29:08 -0700232 map_km_error({
233 let _wp = wd::watch_millis("In use_key_in_one_step: calling: begin", 500);
234 km_dev.begin(purpose, blob, operation_parameters, auth_token)
235 })
Paul Crowleyef611e52021-04-20 14:43:04 -0700236 })
237 .context("In use_key_in_one_step: Failed to begin operation.")?;
238 let operation: Strong<dyn IKeyMintOperation> = begin_result
239 .operation
240 .ok_or_else(Error::sys)
241 .context("In use_key_in_one_step: Operation missing")?;
Janis Danisevskis2ee014b2021-05-05 14:29:08 -0700242 map_km_error({
243 let _wp = wd::watch_millis("In use_key_in_one_step: calling: finish", 500);
244 operation.finish(Some(input), None, None, None, None)
245 })
246 .context("In use_key_in_one_step: Failed to finish operation.")
Paul Crowleyef611e52021-04-20 14:43:04 -0700247 }
248}