Keystore 2.0: km_compat: Implement getKeyCharacteristics.
Ignore-AOSP-First: No automerge path from AOSP.
Bug: 188848331
Bug: 187862706
Test: keystore2_km_compat_test
Change-Id: Ib54916c4f5f4960a82d6e8695aa8b8caa085a2de
Merged-In: Ib54916c4f5f4960a82d6e8695aa8b8caa085a2de
diff --git a/keystore2/src/km_compat/km_compat.cpp b/keystore2/src/km_compat/km_compat.cpp
index bdc3f2a..19576aa 100644
--- a/keystore2/src/km_compat/km_compat.cpp
+++ b/keystore2/src/km_compat/km_compat.cpp
@@ -188,7 +188,7 @@
return std::make_pair(true, isSoftKeyMint);
}
-// Returns the prefix from a blob. If there's no prefix, returns the passed-in blob.
+// Removes the prefix from a blob. If there's no prefix, returns the passed-in blob.
//
std::vector<uint8_t> prefixedKeyBlobRemovePrefix(const std::vector<uint8_t>& prefixedBlob) {
auto parsed = prefixedKeyBlobParsePrefix(prefixedBlob);
@@ -208,6 +208,21 @@
return parsed.second;
}
+// Inspects the given blob for prefixes.
+// Returns the blob stripped of the prefix if present. The boolean argument is true if the blob was
+// a software blob.
+std::pair<std::vector<uint8_t>, bool>
+dissectPrefixedKeyBlob(const std::vector<uint8_t>& prefixedBlob) {
+ auto [hasPrefix, isSoftware] = prefixedKeyBlobParsePrefix(prefixedBlob);
+ if (!hasPrefix) {
+ // Not actually prefixed, blob was probably persisted to disk prior to the
+ // prefixing code being introduced.
+ return {prefixedBlob, false};
+ }
+ return {std::vector<uint8_t>(prefixedBlob.begin() + kKeyBlobPrefixSize, prefixedBlob.end()),
+ isSoftware};
+}
+
/*
* Returns true if the parameter is not understood by KM 4.1 and older but can be enforced by
* Keystore. These parameters need to be included in the returned KeyCharacteristics, but will not
@@ -289,7 +304,14 @@
static std::vector<KeyCharacteristics>
processLegacyCharacteristics(KeyMintSecurityLevel securityLevel,
const std::vector<KeyParameter>& genParams,
- const V4_0_KeyCharacteristics& legacyKc) {
+ const V4_0_KeyCharacteristics& legacyKc, bool hwEnforcedOnly = false) {
+
+ KeyCharacteristics hwEnforced{securityLevel,
+ convertKeyParametersFromLegacy(legacyKc.hardwareEnforced)};
+
+ if (hwEnforcedOnly) {
+ return {hwEnforced};
+ }
KeyCharacteristics keystoreEnforced{KeyMintSecurityLevel::KEYSTORE,
convertKeyParametersFromLegacy(legacyKc.softwareEnforced)};
@@ -308,8 +330,6 @@
return {keystoreEnforced};
}
- KeyCharacteristics hwEnforced{securityLevel,
- convertKeyParametersFromLegacy(legacyKc.hardwareEnforced)};
return {hwEnforced, keystoreEnforced};
}
@@ -687,12 +707,35 @@
return convertErrorCode(km_error);
}
-ScopedAStatus
-KeyMintDevice::getKeyCharacteristics(const std::vector<uint8_t>& /* storageKeyBlob */,
- const std::vector<uint8_t>& /* appId */,
- const std::vector<uint8_t>& /* appData */,
- std::vector<KeyCharacteristics>* /* keyCharacteristics */) {
- return convertErrorCode(KMV1::ErrorCode::UNIMPLEMENTED);
+ScopedAStatus KeyMintDevice::getKeyCharacteristics(
+ const std::vector<uint8_t>& prefixedKeyBlob, const std::vector<uint8_t>& appId,
+ const std::vector<uint8_t>& appData, std::vector<KeyCharacteristics>* keyCharacteristics) {
+ auto [strippedKeyBlob, isSoftware] = dissectPrefixedKeyBlob(prefixedKeyBlob);
+ if (isSoftware) {
+ return softKeyMintDevice_->getKeyCharacteristics(strippedKeyBlob, appId, appData,
+ keyCharacteristics);
+ } else {
+ KMV1::ErrorCode km_error;
+ auto ret = mDevice->getKeyCharacteristics(
+ strippedKeyBlob, appId, appData,
+ [&](V4_0_ErrorCode errorCode, const V4_0_KeyCharacteristics& v40KeyCharacteristics) {
+ km_error = convert(errorCode);
+ *keyCharacteristics =
+ processLegacyCharacteristics(securityLevel_, {} /* getParams */,
+ v40KeyCharacteristics, true /* hwEnforcedOnly */);
+ });
+
+ if (!ret.isOk()) {
+ LOG(ERROR) << __func__ << " getKeyCharacteristics failed: " << ret.description();
+ return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
+ }
+ if (km_error != KMV1::ErrorCode::OK) {
+ LOG(ERROR) << __func__
+ << " getKeyCharacteristics failed with code: " << toString(km_error);
+ }
+
+ return convertErrorCode(km_error);
+ }
}
ScopedAStatus KeyMintOperation::updateAad(const std::vector<uint8_t>& input,
diff --git a/keystore2/src/km_compat/lib.rs b/keystore2/src/km_compat/lib.rs
index eddd684..56c35bf 100644
--- a/keystore2/src/km_compat/lib.rs
+++ b/keystore2/src/km_compat/lib.rs
@@ -31,8 +31,9 @@
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, KeyParameter::KeyParameter, KeyParameterValue::KeyParameterValue,
- KeyPurpose::KeyPurpose, PaddingMode::PaddingMode, SecurityLevel::SecurityLevel, Tag::Tag,
+ 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;
@@ -375,4 +376,85 @@
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(_) }
+ )));
+ }
}