// 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. //! Export into Rust a function to create a KeyMintDevice and add it as a service. #[allow(missing_docs)] // TODO remove this extern "C" { fn addKeyMintDeviceService() -> i32; } #[allow(missing_docs)] // TODO remove this 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, Digest::Digest, ErrorCode::ErrorCode, IKeyMintDevice::IKeyMintDevice, KeyCreationResult::KeyCreationResult, KeyFormat::KeyFormat, KeyOrigin::KeyOrigin, KeyParameter::KeyParameter, KeyParameterValue::KeyParameterValue, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode, SecurityLevel::SecurityLevel, Tag::Tag, }; use android_hardware_security_keymint::binder::{self, Strong}; use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService; static COMPAT_NAME: &str = "android.security.compat"; fn get_device() -> Option> { add_keymint_device_service(); let compat_service: Strong = binder::get_interface(COMPAT_NAME).ok()?; compat_service.getKeyMintDevice(SecurityLevel::TRUSTED_ENVIRONMENT).ok() } macro_rules! get_device_or_skip_test { () => { match get_device() { Some(dev) => dev, None => return, } }; } #[test] fn test_get_hardware_info() { let legacy = get_device_or_skip_test!(); let hinfo = legacy.getHardwareInfo(); assert!(hinfo.is_ok()); } #[test] fn test_add_rng_entropy() { let legacy = get_device_or_skip_test!(); 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) -> KeyCreationResult { let creation_result = legacy.generateKey(&kps, None /* attest_key */).expect("Failed to generate key"); assert_ne!(creation_result.keyBlob.len(), 0); creation_result } // Per RFC 5280 4.1.2.5, an undefined expiration (not-after) field should be set to GeneralizedTime // 999912312359559, which is 253402300799000 ms from Jan 1, 1970. const UNDEFINED_NOT_AFTER: i64 = 253402300799000i64; fn generate_rsa_key(legacy: &dyn IKeyMintDevice, encrypt: bool, attest: bool) -> Vec { let mut kps = vec![ KeyParameter { tag: Tag::ALGORITHM, value: KeyParameterValue::Algorithm(Algorithm::RSA), }, KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(2048) }, KeyParameter { tag: Tag::RSA_PUBLIC_EXPONENT, value: KeyParameterValue::LongInteger(65537), }, KeyParameter { tag: Tag::DIGEST, value: KeyParameterValue::Digest(Digest::SHA_2_256) }, KeyParameter { tag: Tag::PADDING, value: KeyParameterValue::PaddingMode(PaddingMode::RSA_PSS), }, KeyParameter { tag: Tag::NO_AUTH_REQUIRED, value: KeyParameterValue::BoolValue(true) }, KeyParameter { tag: Tag::PURPOSE, value: KeyParameterValue::KeyPurpose(KeyPurpose::SIGN), }, KeyParameter { tag: Tag::CERTIFICATE_NOT_BEFORE, value: KeyParameterValue::DateTime(0), }, KeyParameter { tag: Tag::CERTIFICATE_NOT_AFTER, value: KeyParameterValue::DateTime(UNDEFINED_NOT_AFTER), }, ]; if encrypt { kps.push(KeyParameter { tag: Tag::PURPOSE, value: KeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT), }); } if attest { kps.push(KeyParameter { tag: Tag::ATTESTATION_CHALLENGE, value: KeyParameterValue::Blob(vec![42; 8]), }); kps.push(KeyParameter { tag: Tag::ATTESTATION_APPLICATION_ID, value: KeyParameterValue::Blob(vec![42; 8]), }); } let creation_result = generate_key(legacy, kps); if attest { // TODO: Will this always be greater than 1? assert!(creation_result.certificateChain.len() > 1); } else { assert_eq!(creation_result.certificateChain.len(), 1); } creation_result.keyBlob } #[test] fn test_generate_key_no_encrypt() { let legacy = get_device_or_skip_test!(); generate_rsa_key(legacy.as_ref(), false, false); } #[test] fn test_generate_key_encrypt() { let legacy = get_device_or_skip_test!(); generate_rsa_key(legacy.as_ref(), true, false); } #[test] fn test_generate_key_attested() { let legacy = get_device_or_skip_test!(); generate_rsa_key(legacy.as_ref(), false, true); } #[test] fn test_import_key() { let legacy = get_device_or_skip_test!(); let kps = [KeyParameter { tag: Tag::ALGORITHM, value: KeyParameterValue::Algorithm(Algorithm::AES), }]; let kf = KeyFormat::RAW; let kd = [0; 16]; let creation_result = legacy.importKey(&kps, kf, &kd, None /* attest_key */).expect("Failed to import key"); assert_ne!(creation_result.keyBlob.len(), 0); assert_eq!(creation_result.certificateChain.len(), 0); } #[test] fn test_import_wrapped_key() { let legacy = get_device_or_skip_test!(); let result = legacy.importWrappedKey(&[], &[], &[], &[], 0, 0); // For this test we only care that there was no crash. assert!(result.is_ok() || result.is_err()); } #[test] fn test_upgrade_key() { let legacy = get_device_or_skip_test!(); let blob = generate_rsa_key(legacy.as_ref(), false, false); let result = legacy.upgradeKey(&blob, &[]); // For this test we only care that there was no crash. assert!(result.is_ok() || result.is_err()); } #[test] fn test_delete_key() { let legacy = get_device_or_skip_test!(); 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_or_skip_test!(); let result = legacy.deleteAllKeys(); assert!(result.is_ok(), "{:?}", result); } #[test] fn test_destroy_attestation_ids() { let legacy = get_device_or_skip_test!(); 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 { let kps = vec![ KeyParameter { tag: Tag::ALGORITHM, value: KeyParameterValue::Algorithm(Algorithm::AES), }, KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(128) }, KeyParameter { tag: Tag::BLOCK_MODE, value: KeyParameterValue::BlockMode(BlockMode::CBC), }, KeyParameter { tag: Tag::PADDING, value: KeyParameterValue::PaddingMode(PaddingMode::NONE), }, KeyParameter { tag: Tag::NO_AUTH_REQUIRED, value: KeyParameterValue::BoolValue(true) }, KeyParameter { tag: Tag::PURPOSE, value: KeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT), }, KeyParameter { tag: Tag::PURPOSE, value: KeyParameterValue::KeyPurpose(KeyPurpose::DECRYPT), }, ]; let creation_result = generate_key(legacy, kps); assert_eq!(creation_result.certificateChain.len(), 0); creation_result.keyBlob } fn begin( legacy: &dyn IKeyMintDevice, blob: &[u8], purpose: KeyPurpose, extra_params: Option>, ) -> BeginResult { let mut kps = vec![ KeyParameter { tag: Tag::BLOCK_MODE, value: KeyParameterValue::BlockMode(BlockMode::CBC), }, KeyParameter { tag: Tag::PADDING, value: KeyParameterValue::PaddingMode(PaddingMode::NONE), }, ]; if let Some(mut extras) = extra_params { kps.append(&mut extras); } let result = legacy.begin(purpose, blob, &kps, None); assert!(result.is_ok(), "{:?}", result); result.unwrap() } #[test] fn test_begin_abort() { let legacy = get_device_or_skip_test!(); 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_or_skip_test!(); 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 update_aad_result = operation.updateAad( b"foobar".as_ref(), None, /* authToken */ None, /* timestampToken */ ); assert!(update_aad_result.is_ok(), "{:?}", update_aad_result); let message = [42; 128]; let result = operation.finish( Some(&message), None, /* signature */ None, /* authToken */ None, /* timestampToken */ None, /* confirmationToken */ ); assert!(result.is_ok(), "{:?}", result); let ciphertext = result.unwrap(); assert!(!ciphertext.is_empty()); let begin_result = begin(legacy.as_ref(), &blob, KeyPurpose::DECRYPT, Some(begin_result.params)); let operation = begin_result.operation.unwrap(); let update_aad_result = operation.updateAad( b"foobar".as_ref(), None, /* authToken */ None, /* timestampToken */ ); assert!(update_aad_result.is_ok(), "{:?}", update_aad_result); let result = operation.update( &ciphertext, None, /* authToken */ None, /* timestampToken */ ); assert!(result.is_ok(), "{:?}", result); assert_eq!(result.unwrap(), message); let result = operation.finish( None, /* input */ None, /* signature */ None, /* authToken */ None, /* timestampToken */ None, /* confirmationToken */ ); assert!(result.is_ok(), "{:?}", result); } #[test] fn test_secure_clock() { add_keymint_device_service(); let compat_service: binder::Strong = match binder::get_interface(COMPAT_NAME) { Ok(cs) => cs, _ => return, }; let secure_clock = match compat_service.getSecureClock() { Ok(sc) => sc, _ => return, }; let challenge = 42; let result = secure_clock.generateTimeStamp(challenge); assert!(result.is_ok(), "{:?}", result); let result = result.unwrap(); assert_eq!(result.challenge, challenge); assert_eq!(result.mac.len(), 32); } #[test] fn test_shared_secret() { add_keymint_device_service(); let compat_service: binder::Strong = match binder::get_interface(COMPAT_NAME) { Ok(cs) => cs, _ => return, }; let shared_secret = match compat_service.getSharedSecret(SecurityLevel::TRUSTED_ENVIRONMENT) { Ok(ss) => ss, _ => return, }; let result = shared_secret.getSharedSecretParameters(); assert!(result.is_ok(), "{:?}", result); let params = result.unwrap(); let result = shared_secret.computeSharedSecret(&[params]); assert!(result.is_ok(), "{:?}", result); assert_ne!(result.unwrap().len(), 0); } #[test] fn test_get_key_characteristics() { let legacy = get_device_or_skip_test!(); let hw_info = legacy.getHardwareInfo().expect("GetHardwareInfo"); let blob = generate_rsa_key(legacy.as_ref(), false, false); let characteristics = legacy.getKeyCharacteristics(&blob, &[], &[]).expect("GetKeyCharacteristics."); assert!(characteristics.iter().any(|kc| kc.securityLevel == hw_info.securityLevel)); let sec_level_enforced = &characteristics .iter() .find(|kc| kc.securityLevel == hw_info.securityLevel) .expect("There should be characteristics matching the device's security level.") .authorizations; assert!(sec_level_enforced.iter().any(|kp| matches!( kp, KeyParameter { tag: Tag::PURPOSE, value: KeyParameterValue::KeyPurpose(KeyPurpose::SIGN) } ))); assert!(sec_level_enforced.iter().any(|kp| matches!( kp, KeyParameter { tag: Tag::DIGEST, value: KeyParameterValue::Digest(Digest::SHA_2_256) } ))); assert!(sec_level_enforced.iter().any(|kp| matches!( kp, KeyParameter { tag: Tag::PADDING, value: KeyParameterValue::PaddingMode(PaddingMode::RSA_PSS) } ))); assert!(sec_level_enforced.iter().any(|kp| matches!( kp, KeyParameter { tag: Tag::ALGORITHM, value: KeyParameterValue::Algorithm(Algorithm::RSA) } ))); assert!(sec_level_enforced.iter().any(|kp| matches!( kp, KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(2048) } ))); assert!(sec_level_enforced.iter().any(|kp| matches!( kp, KeyParameter { tag: Tag::RSA_PUBLIC_EXPONENT, value: KeyParameterValue::LongInteger(65537) } ))); assert!(sec_level_enforced.iter().any(|kp| matches!( kp, KeyParameter { tag: Tag::NO_AUTH_REQUIRED, value: KeyParameterValue::BoolValue(true) } ))); assert!(sec_level_enforced.iter().any(|kp| matches!( kp, KeyParameter { tag: Tag::ORIGIN, value: KeyParameterValue::Origin(KeyOrigin::GENERATED) } ))); assert!(sec_level_enforced.iter().any(|kp| matches!( kp, KeyParameter { tag: Tag::OS_VERSION, value: KeyParameterValue::Integer(_) } ))); assert!(sec_level_enforced.iter().any(|kp| matches!( kp, KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KeyParameterValue::Integer(_) } ))); assert!(sec_level_enforced.iter().any(|kp| matches!( kp, KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KeyParameterValue::Integer(_) } ))); assert!(sec_level_enforced.iter().any(|kp| matches!( kp, KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KeyParameterValue::Integer(_) } ))); } }