Alan Stokes | 337874a | 2021-06-16 16:49:32 +0100 | [diff] [blame] | 1 | // 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 | //! Provides a binder service for key generation & verification for CompOs. We assume we have |
| 16 | //! access to Keystore in the VM, but not persistent storage; instead the host stores the key |
| 17 | //! on our behalf via this service. |
| 18 | |
Alan Stokes | 7ec4e7f | 2021-07-21 11:29:10 +0100 | [diff] [blame] | 19 | use crate::compsvc; |
| 20 | use crate::signer::Signer; |
Alan Stokes | 337874a | 2021-06-16 16:49:32 +0100 | [diff] [blame] | 21 | use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{ |
| 22 | Algorithm::Algorithm, Digest::Digest, KeyParameter::KeyParameter, |
| 23 | KeyParameterValue::KeyParameterValue, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode, |
| 24 | SecurityLevel::SecurityLevel, Tag::Tag, |
| 25 | }; |
| 26 | use android_system_keystore2::aidl::android::system::keystore2::{ |
| 27 | Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel, |
| 28 | IKeystoreService::IKeystoreService, KeyDescriptor::KeyDescriptor, |
| 29 | }; |
| 30 | use anyhow::{anyhow, Context, Result}; |
| 31 | use compos_aidl_interface::aidl::com::android::compos::{ |
Alan Stokes | 7ec4e7f | 2021-07-21 11:29:10 +0100 | [diff] [blame] | 32 | CompOsKeyData::CompOsKeyData, |
| 33 | ICompOsKeyService::{BnCompOsKeyService, ICompOsKeyService}, |
| 34 | ICompService::ICompService, |
Alan Stokes | 337874a | 2021-06-16 16:49:32 +0100 | [diff] [blame] | 35 | }; |
| 36 | use compos_aidl_interface::binder::{ |
Alan Stokes | 7ec4e7f | 2021-07-21 11:29:10 +0100 | [diff] [blame] | 37 | self, wait_for_interface, BinderFeatures, ExceptionCode, Interface, Status, Strong, |
Alan Stokes | 337874a | 2021-06-16 16:49:32 +0100 | [diff] [blame] | 38 | }; |
Alan Stokes | 8ccebf1 | 2021-07-14 12:04:31 +0100 | [diff] [blame] | 39 | use log::warn; |
Alan Stokes | 337874a | 2021-06-16 16:49:32 +0100 | [diff] [blame] | 40 | use ring::rand::{SecureRandom, SystemRandom}; |
| 41 | use ring::signature; |
| 42 | use scopeguard::ScopeGuard; |
| 43 | use std::ffi::CString; |
Alan Stokes | 337874a | 2021-06-16 16:49:32 +0100 | [diff] [blame] | 44 | |
Alan Stokes | b15c93f | 2021-07-15 16:21:50 +0100 | [diff] [blame] | 45 | /// Keystore2 namespace IDs, used for access control to keys. |
| 46 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
| 47 | pub enum KeystoreNamespace { |
| 48 | /// In the host we re-use the ID assigned to odsign. See system/sepolicy/private/keystore2_key_contexts. |
| 49 | // TODO(alanstokes): Remove this. |
| 50 | Odsign = 101, |
| 51 | /// In a VM we can use the generic ID allocated for payloads. See microdroid's keystore2_key_contexts. |
| 52 | VmPayload = 140, |
| 53 | } |
| 54 | |
Alan Stokes | 7ec4e7f | 2021-07-21 11:29:10 +0100 | [diff] [blame] | 55 | /// Constructs a binder object that implements ICompOsKeyService. namespace is the Keystore2 namespace to |
| 56 | /// use for the keys. |
| 57 | pub fn new(namespace: KeystoreNamespace) -> Result<Strong<dyn ICompOsKeyService>> { |
| 58 | let keystore_service = wait_for_interface::<dyn IKeystoreService>(KEYSTORE_SERVICE_NAME) |
| 59 | .context("No Keystore service")?; |
| 60 | |
| 61 | let service = CompOsKeyService { |
| 62 | namespace, |
| 63 | random: SystemRandom::new(), |
| 64 | security_level: keystore_service |
| 65 | .getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT) |
| 66 | .context("Getting SecurityLevel failed")?, |
| 67 | }; |
| 68 | |
| 69 | Ok(BnCompOsKeyService::new_binder(service, BinderFeatures::default())) |
| 70 | } |
| 71 | |
Alan Stokes | 337874a | 2021-06-16 16:49:32 +0100 | [diff] [blame] | 72 | const KEYSTORE_SERVICE_NAME: &str = "android.system.keystore2.IKeystoreService/default"; |
Alan Stokes | 337874a | 2021-06-16 16:49:32 +0100 | [diff] [blame] | 73 | const PURPOSE_SIGN: KeyParameter = |
| 74 | KeyParameter { tag: Tag::PURPOSE, value: KeyParameterValue::KeyPurpose(KeyPurpose::SIGN) }; |
| 75 | const ALGORITHM: KeyParameter = |
| 76 | KeyParameter { tag: Tag::ALGORITHM, value: KeyParameterValue::Algorithm(Algorithm::RSA) }; |
| 77 | const PADDING: KeyParameter = KeyParameter { |
| 78 | tag: Tag::PADDING, |
| 79 | value: KeyParameterValue::PaddingMode(PaddingMode::RSA_PKCS1_1_5_SIGN), |
| 80 | }; |
| 81 | const DIGEST: KeyParameter = |
| 82 | KeyParameter { tag: Tag::DIGEST, value: KeyParameterValue::Digest(Digest::SHA_2_256) }; |
| 83 | const KEY_SIZE: KeyParameter = |
| 84 | KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(2048) }; |
| 85 | const EXPONENT: KeyParameter = |
| 86 | KeyParameter { tag: Tag::RSA_PUBLIC_EXPONENT, value: KeyParameterValue::LongInteger(65537) }; |
| 87 | const NO_AUTH_REQUIRED: KeyParameter = |
| 88 | KeyParameter { tag: Tag::NO_AUTH_REQUIRED, value: KeyParameterValue::BoolValue(true) }; |
| 89 | |
Alan Stokes | b15c93f | 2021-07-15 16:21:50 +0100 | [diff] [blame] | 90 | const BLOB_KEY_DESCRIPTOR: KeyDescriptor = |
| 91 | KeyDescriptor { domain: Domain::BLOB, nspace: 0, alias: None, blob: None }; |
Alan Stokes | 337874a | 2021-06-16 16:49:32 +0100 | [diff] [blame] | 92 | |
Alan Stokes | 7ec4e7f | 2021-07-21 11:29:10 +0100 | [diff] [blame] | 93 | #[derive(Clone)] |
| 94 | struct CompOsKeyService { |
Alan Stokes | b15c93f | 2021-07-15 16:21:50 +0100 | [diff] [blame] | 95 | namespace: KeystoreNamespace, |
Alan Stokes | 337874a | 2021-06-16 16:49:32 +0100 | [diff] [blame] | 96 | random: SystemRandom, |
Alan Stokes | 337874a | 2021-06-16 16:49:32 +0100 | [diff] [blame] | 97 | security_level: Strong<dyn IKeystoreSecurityLevel>, |
| 98 | } |
| 99 | |
| 100 | impl Interface for CompOsKeyService {} |
| 101 | |
| 102 | impl ICompOsKeyService for CompOsKeyService { |
| 103 | fn generateSigningKey(&self) -> binder::Result<CompOsKeyData> { |
| 104 | self.do_generate() |
| 105 | .map_err(|e| new_binder_exception(ExceptionCode::ILLEGAL_STATE, e.to_string())) |
| 106 | } |
| 107 | |
| 108 | fn verifySigningKey(&self, key_blob: &[u8], public_key: &[u8]) -> binder::Result<bool> { |
| 109 | Ok(if let Err(e) = self.do_verify(key_blob, public_key) { |
| 110 | warn!("Signing key verification failed: {}", e.to_string()); |
| 111 | false |
| 112 | } else { |
| 113 | true |
| 114 | }) |
| 115 | } |
Alan Stokes | c3bab54 | 2021-07-06 15:48:33 +0100 | [diff] [blame] | 116 | |
| 117 | fn sign(&self, key_blob: &[u8], data: &[u8]) -> binder::Result<Vec<u8>> { |
| 118 | self.do_sign(key_blob, data) |
| 119 | .map_err(|e| new_binder_exception(ExceptionCode::ILLEGAL_STATE, e.to_string())) |
| 120 | } |
Alan Stokes | 7ec4e7f | 2021-07-21 11:29:10 +0100 | [diff] [blame] | 121 | |
| 122 | fn getCompService(&self, key_blob: &[u8]) -> binder::Result<Strong<dyn ICompService>> { |
| 123 | let signer = |
| 124 | Box::new(CompOsSigner { key_blob: key_blob.to_owned(), key_service: self.clone() }); |
| 125 | let debuggable = true; |
| 126 | Ok(compsvc::new_binder( |
| 127 | "/apex/com.android.art/bin/dex2oat64".to_owned(), |
| 128 | debuggable, |
| 129 | Some(signer), |
| 130 | )) |
| 131 | } |
Alan Stokes | 337874a | 2021-06-16 16:49:32 +0100 | [diff] [blame] | 132 | } |
| 133 | |
| 134 | /// Constructs a new Binder error `Status` with the given `ExceptionCode` and message. |
| 135 | fn new_binder_exception<T: AsRef<str>>(exception: ExceptionCode, message: T) -> Status { |
| 136 | Status::new_exception(exception, CString::new(message.as_ref()).ok().as_deref()) |
| 137 | } |
| 138 | |
Alan Stokes | 7ec4e7f | 2021-07-21 11:29:10 +0100 | [diff] [blame] | 139 | struct CompOsSigner { |
| 140 | key_blob: Vec<u8>, |
| 141 | key_service: CompOsKeyService, |
| 142 | } |
Alan Stokes | 8ccebf1 | 2021-07-14 12:04:31 +0100 | [diff] [blame] | 143 | |
Alan Stokes | 7ec4e7f | 2021-07-21 11:29:10 +0100 | [diff] [blame] | 144 | impl Signer for CompOsSigner { |
| 145 | fn sign(&self, data: &[u8]) -> Result<Vec<u8>> { |
| 146 | self.key_service.do_sign(&self.key_blob, data) |
Alan Stokes | 337874a | 2021-06-16 16:49:32 +0100 | [diff] [blame] | 147 | } |
Alan Stokes | 7ec4e7f | 2021-07-21 11:29:10 +0100 | [diff] [blame] | 148 | } |
Alan Stokes | 337874a | 2021-06-16 16:49:32 +0100 | [diff] [blame] | 149 | |
Alan Stokes | 7ec4e7f | 2021-07-21 11:29:10 +0100 | [diff] [blame] | 150 | impl CompOsKeyService { |
Alan Stokes | 337874a | 2021-06-16 16:49:32 +0100 | [diff] [blame] | 151 | fn do_generate(&self) -> Result<CompOsKeyData> { |
Alan Stokes | b15c93f | 2021-07-15 16:21:50 +0100 | [diff] [blame] | 152 | let key_descriptor = KeyDescriptor { nspace: self.namespace as i64, ..BLOB_KEY_DESCRIPTOR }; |
Alan Stokes | 337874a | 2021-06-16 16:49:32 +0100 | [diff] [blame] | 153 | let key_parameters = |
| 154 | [PURPOSE_SIGN, ALGORITHM, PADDING, DIGEST, KEY_SIZE, EXPONENT, NO_AUTH_REQUIRED]; |
| 155 | let attestation_key = None; |
| 156 | let flags = 0; |
| 157 | let entropy = []; |
| 158 | |
| 159 | let key_metadata = self |
Alan Stokes | 4a489d4 | 2021-06-28 10:12:05 +0100 | [diff] [blame] | 160 | .security_level |
Alan Stokes | b15c93f | 2021-07-15 16:21:50 +0100 | [diff] [blame] | 161 | .generateKey(&key_descriptor, attestation_key, &key_parameters, flags, &entropy) |
Alan Stokes | 337874a | 2021-06-16 16:49:32 +0100 | [diff] [blame] | 162 | .context("Generating key failed")?; |
| 163 | |
| 164 | if let (Some(certificate), Some(blob)) = (key_metadata.certificate, key_metadata.key.blob) { |
| 165 | Ok(CompOsKeyData { certificate, keyBlob: blob }) |
| 166 | } else { |
| 167 | Err(anyhow!("Missing cert or blob")) |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | fn do_verify(&self, key_blob: &[u8], public_key: &[u8]) -> Result<()> { |
| 172 | let mut data = [0u8; 32]; |
| 173 | self.random.fill(&mut data).context("No random data")?; |
| 174 | |
Alan Stokes | c3bab54 | 2021-07-06 15:48:33 +0100 | [diff] [blame] | 175 | let signature = self.do_sign(key_blob, &data)?; |
Alan Stokes | 337874a | 2021-06-16 16:49:32 +0100 | [diff] [blame] | 176 | |
| 177 | let public_key = |
| 178 | signature::UnparsedPublicKey::new(&signature::RSA_PKCS1_2048_8192_SHA256, public_key); |
| 179 | public_key.verify(&data, &signature).context("Signature verification failed")?; |
| 180 | |
| 181 | Ok(()) |
| 182 | } |
| 183 | |
Alan Stokes | c3bab54 | 2021-07-06 15:48:33 +0100 | [diff] [blame] | 184 | fn do_sign(&self, key_blob: &[u8], data: &[u8]) -> Result<Vec<u8>> { |
Alan Stokes | b15c93f | 2021-07-15 16:21:50 +0100 | [diff] [blame] | 185 | let key_descriptor = KeyDescriptor { |
| 186 | nspace: self.namespace as i64, |
| 187 | blob: Some(key_blob.to_vec()), |
| 188 | ..BLOB_KEY_DESCRIPTOR |
| 189 | }; |
Alan Stokes | 337874a | 2021-06-16 16:49:32 +0100 | [diff] [blame] | 190 | let operation_parameters = [PURPOSE_SIGN, ALGORITHM, PADDING, DIGEST]; |
| 191 | let forced = false; |
| 192 | |
| 193 | let response = self |
Alan Stokes | 4a489d4 | 2021-06-28 10:12:05 +0100 | [diff] [blame] | 194 | .security_level |
Alan Stokes | 337874a | 2021-06-16 16:49:32 +0100 | [diff] [blame] | 195 | .createOperation(&key_descriptor, &operation_parameters, forced) |
| 196 | .context("Creating key failed")?; |
| 197 | let operation = scopeguard::guard( |
| 198 | response.iOperation.ok_or_else(|| anyhow!("No operation created"))?, |
| 199 | |op| op.abort().unwrap_or_default(), |
| 200 | ); |
| 201 | |
| 202 | if response.operationChallenge.is_some() { |
| 203 | return Err(anyhow!("Key requires user authorization")); |
| 204 | } |
| 205 | |
Chris Wailes | 68c39f8 | 2021-07-27 16:03:44 -0700 | [diff] [blame^] | 206 | let signature = operation.finish(Some(data), None).context("Signing failed")?; |
Alan Stokes | 337874a | 2021-06-16 16:49:32 +0100 | [diff] [blame] | 207 | // Operation has finished, we're no longer responsible for aborting it |
| 208 | ScopeGuard::into_inner(operation); |
| 209 | |
| 210 | signature.ok_or_else(|| anyhow!("No signature returned")) |
| 211 | } |
| 212 | } |