Add compatibility wrapper
This implements the Keymint 1 spec by wrapping the legacy Keymaster
implementation.
Test: atest keystore2_km_compat_test
Test: atest keystore2_certificate_test
Test: Manually verify that keystore2 can find a legacy implementation.
Change-Id: Ia56c25eed0f97a7e6194954a655ceb62259b3273
diff --git a/keystore2/src/km_compat/lib.rs b/keystore2/src/km_compat/lib.rs
new file mode 100644
index 0000000..b6a6baf
--- /dev/null
+++ b/keystore2/src/km_compat/lib.rs
@@ -0,0 +1,310 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// TODO: Once this is stable, remove this and document everything public.
+#![allow(missing_docs)]
+
+extern "C" {
+ fn addKeyMintDeviceService() -> i32;
+}
+
+pub fn add_keymint_device_service() -> i32 {
+ unsafe { addKeyMintDeviceService() }
+}
+
+#[cfg(test)]
+mod tests {
+
+ use super::*;
+ use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Algorithm::Algorithm, BeginResult::BeginResult, BlockMode::BlockMode, ByteArray::ByteArray,
+ Certificate::Certificate, Digest::Digest, ErrorCode::ErrorCode,
+ HardwareAuthToken::HardwareAuthToken, IKeyMintDevice::IKeyMintDevice,
+ KeyCharacteristics::KeyCharacteristics, KeyFormat::KeyFormat, KeyParameter::KeyParameter,
+ KeyParameterArray::KeyParameterArray, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
+ SecurityLevel::SecurityLevel, Tag::Tag,
+ };
+ use android_hardware_security_keymint::binder;
+ use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
+
+ fn get_device() -> Box<dyn IKeyMintDevice> {
+ add_keymint_device_service();
+ let compat_service: Box<dyn IKeystoreCompatService> =
+ binder::get_interface("android.security.compat").unwrap();
+ compat_service.getKeyMintDevice(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap()
+ }
+
+ #[test]
+ fn test_get_hardware_info() {
+ let legacy = get_device();
+ let hinfo = legacy.getHardwareInfo().unwrap();
+ assert_eq!(hinfo.versionNumber, 0);
+ assert_ne!(hinfo.securityLevel, SecurityLevel::SOFTWARE);
+ assert_eq!(hinfo.keyMintName, "RemoteKeymaster");
+ assert_eq!(hinfo.keyMintAuthorName, "Google");
+ }
+
+ #[test]
+ fn test_verify_authorization() {
+ use android_hardware_security_keymint::aidl::android::hardware::security::keymint::HardwareAuthToken::HardwareAuthToken;
+ let legacy = get_device();
+ let result = legacy.verifyAuthorization(0, &HardwareAuthToken::default());
+ assert!(result.is_err());
+ assert_eq!(result.unwrap_err().service_specific_error(), ErrorCode::UNIMPLEMENTED.0,);
+ }
+
+ #[test]
+ fn test_add_rng_entropy() {
+ let legacy = get_device();
+ let result = legacy.addRngEntropy(&[42; 16]);
+ assert!(result.is_ok(), "{:?}", result);
+ }
+
+ // TODO: If I only need the key itself, don't return the other things.
+ fn generate_key(
+ legacy: &dyn IKeyMintDevice,
+ kps: Vec<KeyParameter>,
+ ) -> (ByteArray, KeyCharacteristics, Vec<Certificate>) {
+ let mut blob = ByteArray { data: vec![] };
+ let mut characteristics = KeyCharacteristics::default();
+ let mut cert_chain = vec![];
+ let result = legacy.generateKey(&kps, &mut blob, &mut characteristics, &mut cert_chain);
+ assert!(result.is_ok(), "{:?}", result);
+ assert_ne!(blob.data.len(), 0);
+ (blob, characteristics, cert_chain)
+ }
+
+ fn generate_rsa_key(legacy: &dyn IKeyMintDevice, encrypt: bool, attest: bool) -> Vec<u8> {
+ let mut kps = vec![
+ KeyParameter { tag: Tag::ALGORITHM, integer: Algorithm::RSA.0, ..Default::default() },
+ KeyParameter { tag: Tag::KEY_SIZE, integer: 2048, ..Default::default() },
+ KeyParameter {
+ tag: Tag::RSA_PUBLIC_EXPONENT,
+ longInteger: 65537,
+ ..Default::default()
+ },
+ KeyParameter { tag: Tag::DIGEST, integer: Digest::SHA_2_256.0, ..Default::default() },
+ KeyParameter {
+ tag: Tag::PADDING,
+ integer: PaddingMode::RSA_PSS.0,
+ ..Default::default()
+ },
+ KeyParameter { tag: Tag::NO_AUTH_REQUIRED, boolValue: true, ..Default::default() },
+ KeyParameter { tag: Tag::PURPOSE, integer: KeyPurpose::SIGN.0, ..Default::default() },
+ ];
+ if encrypt {
+ kps.push(KeyParameter {
+ tag: Tag::PURPOSE,
+ integer: KeyPurpose::ENCRYPT.0,
+ ..Default::default()
+ });
+ }
+ if attest {
+ kps.push(KeyParameter {
+ tag: Tag::ATTESTATION_CHALLENGE,
+ blob: vec![42; 8],
+ ..Default::default()
+ });
+ kps.push(KeyParameter {
+ tag: Tag::ATTESTATION_APPLICATION_ID,
+ blob: vec![42; 8],
+ ..Default::default()
+ });
+ }
+ let (blob, _, cert_chain) = generate_key(legacy, kps);
+ if attest {
+ // TODO: Will this always be greater than 1?
+ assert!(cert_chain.len() > 1);
+ } else {
+ assert_eq!(cert_chain.len(), 1);
+ }
+ blob.data
+ }
+
+ #[test]
+ fn test_generate_key_no_encrypt() {
+ let legacy = get_device();
+ generate_rsa_key(legacy.as_ref(), false, false);
+ }
+
+ #[test]
+ fn test_generate_key_encrypt() {
+ let legacy = get_device();
+ generate_rsa_key(legacy.as_ref(), true, false);
+ }
+
+ #[test]
+ fn test_generate_key_attested() {
+ let legacy = get_device();
+ generate_rsa_key(legacy.as_ref(), false, true);
+ }
+
+ #[test]
+ fn test_import_key() {
+ let legacy = get_device();
+ let kps =
+ [KeyParameter { tag: Tag::ALGORITHM, integer: Algorithm::AES.0, ..Default::default() }];
+ let kf = KeyFormat::RAW;
+ let kd = [0; 16];
+ let mut blob = ByteArray { data: vec![] };
+ let mut characteristics = KeyCharacteristics::default();
+ let mut cert_chain = vec![];
+ let result =
+ legacy.importKey(&kps, kf, &kd, &mut blob, &mut characteristics, &mut cert_chain);
+ assert!(result.is_ok(), "{:?}", result);
+ assert_ne!(blob.data.len(), 0);
+ assert_eq!(cert_chain.len(), 0);
+ }
+
+ #[test]
+ fn test_import_wrapped_key() {
+ let legacy = get_device();
+ let mut blob = ByteArray { data: vec![] };
+ let mut characteristics = KeyCharacteristics::default();
+ let result =
+ legacy.importWrappedKey(&[], &[], &[], &[], 0, 0, &mut blob, &mut characteristics);
+ // TODO: This test seems to fail on cuttlefish. How should I test it?
+ assert!(result.is_err());
+ }
+
+ #[test]
+ fn test_upgrade_key() {
+ let legacy = get_device();
+ let blob = generate_rsa_key(legacy.as_ref(), false, false);
+ let result = legacy.upgradeKey(&blob, &[]);
+ // TODO: This test seems to fail on cuttlefish. How should I test it?
+ assert!(result.is_err());
+ }
+
+ #[test]
+ fn test_delete_key() {
+ let legacy = get_device();
+ let blob = generate_rsa_key(legacy.as_ref(), false, false);
+ let result = legacy.deleteKey(&blob);
+ assert!(result.is_ok(), "{:?}", result);
+ }
+
+ #[test]
+ fn test_delete_all_keys() {
+ let legacy = get_device();
+ let result = legacy.deleteAllKeys();
+ assert!(result.is_ok(), "{:?}", result);
+ }
+
+ #[test]
+ fn test_destroy_attestation_ids() {
+ let legacy = get_device();
+ let result = legacy.destroyAttestationIds();
+ assert!(result.is_err());
+ assert_eq!(result.unwrap_err().service_specific_error(), ErrorCode::UNIMPLEMENTED.0,);
+ }
+
+ fn generate_aes_key(legacy: &dyn IKeyMintDevice) -> Vec<u8> {
+ let kps = vec![
+ KeyParameter { tag: Tag::ALGORITHM, integer: Algorithm::AES.0, ..Default::default() },
+ KeyParameter { tag: Tag::KEY_SIZE, integer: 128, ..Default::default() },
+ KeyParameter { tag: Tag::BLOCK_MODE, integer: BlockMode::CBC.0, ..Default::default() },
+ KeyParameter { tag: Tag::PADDING, integer: PaddingMode::NONE.0, ..Default::default() },
+ KeyParameter { tag: Tag::NO_AUTH_REQUIRED, boolValue: true, ..Default::default() },
+ KeyParameter {
+ tag: Tag::PURPOSE,
+ integer: KeyPurpose::ENCRYPT.0,
+ ..Default::default()
+ },
+ KeyParameter {
+ tag: Tag::PURPOSE,
+ integer: KeyPurpose::DECRYPT.0,
+ ..Default::default()
+ },
+ ];
+ let (blob, _, cert_chain) = generate_key(legacy, kps);
+ assert_eq!(cert_chain.len(), 0);
+ blob.data
+ }
+
+ fn begin(
+ legacy: &dyn IKeyMintDevice,
+ blob: &[u8],
+ purpose: KeyPurpose,
+ extra_params: Option<Vec<KeyParameter>>,
+ ) -> BeginResult {
+ let mut kps = vec![
+ KeyParameter { tag: Tag::BLOCK_MODE, integer: BlockMode::CBC.0, ..Default::default() },
+ KeyParameter { tag: Tag::PADDING, integer: PaddingMode::NONE.0, ..Default::default() },
+ ];
+ if let Some(mut extras) = extra_params {
+ kps.append(&mut extras);
+ }
+ let result = legacy.begin(purpose, &blob, &kps, &HardwareAuthToken::default());
+ assert!(result.is_ok(), "{:?}", result);
+ result.unwrap()
+ }
+
+ #[test]
+ fn test_begin_abort() {
+ let legacy = get_device();
+ let blob = generate_aes_key(legacy.as_ref());
+ let begin_result = begin(legacy.as_ref(), &blob, KeyPurpose::ENCRYPT, None);
+ let operation = begin_result.operation.unwrap();
+ let result = operation.abort();
+ assert!(result.is_ok(), "{:?}", result);
+ let result = operation.abort();
+ assert!(result.is_err());
+ }
+
+ #[test]
+ fn test_begin_update_finish() {
+ let legacy = get_device();
+ let blob = generate_aes_key(legacy.as_ref());
+
+ let begin_result = begin(legacy.as_ref(), &blob, KeyPurpose::ENCRYPT, None);
+ let operation = begin_result.operation.unwrap();
+ let params = KeyParameterArray {
+ params: vec![KeyParameter {
+ tag: Tag::ASSOCIATED_DATA,
+ blob: b"foobar".to_vec(),
+ ..Default::default()
+ }],
+ };
+ let message = [42; 128];
+ let mut out_params = None;
+ let result =
+ operation.finish(Some(¶ms), Some(&message), None, None, None, &mut out_params);
+ assert!(result.is_ok(), "{:?}", result);
+ let ciphertext = result.unwrap();
+ assert!(!ciphertext.is_empty());
+ assert!(out_params.is_some());
+
+ let begin_result =
+ begin(legacy.as_ref(), &blob, KeyPurpose::DECRYPT, Some(begin_result.params));
+ let operation = begin_result.operation.unwrap();
+ let mut out_params = None;
+ let mut output = None;
+ let result = operation.update(
+ Some(¶ms),
+ Some(&ciphertext),
+ None,
+ None,
+ &mut out_params,
+ &mut output,
+ );
+ assert!(result.is_ok(), "{:?}", result);
+ assert_eq!(result.unwrap(), message.len() as i32);
+ assert!(output.is_some());
+ assert_eq!(output.unwrap().data, message.to_vec());
+ let result = operation.finish(Some(¶ms), None, None, None, None, &mut out_params);
+ assert!(result.is_ok(), "{:?}", result);
+ assert!(out_params.is_some());
+ }
+}