Merge "Added tests to attest keys with attestation id."
This commit is contained in:
commit
152dd52ee6
8 changed files with 552 additions and 5 deletions
|
@ -161,6 +161,87 @@ impl AuthSetBuilder {
|
||||||
.push(KeyParameter { tag: Tag::MIN_MAC_LENGTH, value: KeyParameterValue::Integer(l) });
|
.push(KeyParameter { tag: Tag::MIN_MAC_LENGTH, value: KeyParameterValue::Integer(l) });
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add Attestation-Device-Brand.
|
||||||
|
pub fn attestation_device_brand(mut self, b: Vec<u8>) -> Self {
|
||||||
|
self.0.push(KeyParameter {
|
||||||
|
tag: Tag::ATTESTATION_ID_BRAND,
|
||||||
|
value: KeyParameterValue::Blob(b),
|
||||||
|
});
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add Attestation-Device-name.
|
||||||
|
pub fn attestation_device_name(mut self, b: Vec<u8>) -> Self {
|
||||||
|
self.0.push(KeyParameter {
|
||||||
|
tag: Tag::ATTESTATION_ID_DEVICE,
|
||||||
|
value: KeyParameterValue::Blob(b),
|
||||||
|
});
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add Attestation-Device-Product-Name.
|
||||||
|
pub fn attestation_device_product_name(mut self, b: Vec<u8>) -> Self {
|
||||||
|
self.0.push(KeyParameter {
|
||||||
|
tag: Tag::ATTESTATION_ID_PRODUCT,
|
||||||
|
value: KeyParameterValue::Blob(b),
|
||||||
|
});
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add Attestation-Device-Serial.
|
||||||
|
pub fn attestation_device_serial(mut self, b: Vec<u8>) -> Self {
|
||||||
|
self.0.push(KeyParameter {
|
||||||
|
tag: Tag::ATTESTATION_ID_SERIAL,
|
||||||
|
value: KeyParameterValue::Blob(b),
|
||||||
|
});
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add Attestation-Device-IMEI.
|
||||||
|
pub fn attestation_device_imei(mut self, b: Vec<u8>) -> Self {
|
||||||
|
self.0.push(KeyParameter {
|
||||||
|
tag: Tag::ATTESTATION_ID_IMEI,
|
||||||
|
value: KeyParameterValue::Blob(b),
|
||||||
|
});
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add Attestation-Device-IMEI.
|
||||||
|
pub fn attestation_device_second_imei(mut self, b: Vec<u8>) -> Self {
|
||||||
|
self.0.push(KeyParameter {
|
||||||
|
tag: Tag::ATTESTATION_ID_SECOND_IMEI,
|
||||||
|
value: KeyParameterValue::Blob(b),
|
||||||
|
});
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add Attestation-Device-MEID.
|
||||||
|
pub fn attestation_device_meid(mut self, b: Vec<u8>) -> Self {
|
||||||
|
self.0.push(KeyParameter {
|
||||||
|
tag: Tag::ATTESTATION_ID_MEID,
|
||||||
|
value: KeyParameterValue::Blob(b),
|
||||||
|
});
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add Attestation-Device-Manufacturer.
|
||||||
|
pub fn attestation_device_manufacturer(mut self, b: Vec<u8>) -> Self {
|
||||||
|
self.0.push(KeyParameter {
|
||||||
|
tag: Tag::ATTESTATION_ID_MANUFACTURER,
|
||||||
|
value: KeyParameterValue::Blob(b),
|
||||||
|
});
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add Attestation-Device-Model.
|
||||||
|
pub fn attestation_device_model(mut self, b: Vec<u8>) -> Self {
|
||||||
|
self.0.push(KeyParameter {
|
||||||
|
tag: Tag::ATTESTATION_ID_MODEL,
|
||||||
|
value: KeyParameterValue::Blob(b),
|
||||||
|
});
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for AuthSetBuilder {
|
impl Deref for AuthSetBuilder {
|
||||||
|
|
|
@ -306,6 +306,12 @@ pub enum Error {
|
||||||
/// Error code to indicate error while using keystore-engine API.
|
/// Error code to indicate error while using keystore-engine API.
|
||||||
#[error("Failed to perform crypto op using keystore-engine APIs.")]
|
#[error("Failed to perform crypto op using keystore-engine APIs.")]
|
||||||
Keystore2EngineOpFailed,
|
Keystore2EngineOpFailed,
|
||||||
|
/// Error code to indicate error in attestation-id validation.
|
||||||
|
#[error("Failed to validate attestation-id.")]
|
||||||
|
ValidateAttestIdFailed,
|
||||||
|
/// Error code to indicate error in getting value from attest record.
|
||||||
|
#[error("Failed to get value from attest record.")]
|
||||||
|
AttestRecordGetValueFailed,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Keystore2 error mapping.
|
/// Keystore2 error mapping.
|
||||||
|
@ -1109,3 +1115,77 @@ pub fn import_aes_keys(
|
||||||
|
|
||||||
Ok(imported_key_aliases)
|
Ok(imported_key_aliases)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generate attested EC-P_256 key with device id attestation.
|
||||||
|
pub fn generate_key_with_attest_id(
|
||||||
|
sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
|
||||||
|
algorithm: Algorithm,
|
||||||
|
alias: Option<String>,
|
||||||
|
att_challenge: &[u8],
|
||||||
|
attest_key: &KeyDescriptor,
|
||||||
|
attest_id: Tag,
|
||||||
|
value: Vec<u8>,
|
||||||
|
) -> binder::Result<KeyMetadata> {
|
||||||
|
assert!(algorithm == Algorithm::RSA || algorithm == Algorithm::EC);
|
||||||
|
|
||||||
|
let mut ec_gen_params;
|
||||||
|
if algorithm == Algorithm::EC {
|
||||||
|
ec_gen_params = AuthSetBuilder::new()
|
||||||
|
.no_auth_required()
|
||||||
|
.algorithm(Algorithm::EC)
|
||||||
|
.purpose(KeyPurpose::SIGN)
|
||||||
|
.purpose(KeyPurpose::VERIFY)
|
||||||
|
.digest(Digest::SHA_2_256)
|
||||||
|
.ec_curve(EcCurve::P_256)
|
||||||
|
.attestation_challenge(att_challenge.to_vec());
|
||||||
|
} else {
|
||||||
|
ec_gen_params = AuthSetBuilder::new()
|
||||||
|
.no_auth_required()
|
||||||
|
.algorithm(Algorithm::RSA)
|
||||||
|
.rsa_public_exponent(65537)
|
||||||
|
.key_size(2048)
|
||||||
|
.purpose(KeyPurpose::SIGN)
|
||||||
|
.purpose(KeyPurpose::VERIFY)
|
||||||
|
.digest(Digest::SHA_2_256)
|
||||||
|
.padding_mode(PaddingMode::RSA_PKCS1_1_5_SIGN)
|
||||||
|
.attestation_challenge(att_challenge.to_vec());
|
||||||
|
}
|
||||||
|
|
||||||
|
match attest_id {
|
||||||
|
Tag::ATTESTATION_ID_BRAND => {
|
||||||
|
ec_gen_params = ec_gen_params.attestation_device_brand(value);
|
||||||
|
}
|
||||||
|
Tag::ATTESTATION_ID_DEVICE => {
|
||||||
|
ec_gen_params = ec_gen_params.attestation_device_name(value);
|
||||||
|
}
|
||||||
|
Tag::ATTESTATION_ID_PRODUCT => {
|
||||||
|
ec_gen_params = ec_gen_params.attestation_device_product_name(value);
|
||||||
|
}
|
||||||
|
Tag::ATTESTATION_ID_SERIAL => {
|
||||||
|
ec_gen_params = ec_gen_params.attestation_device_serial(value);
|
||||||
|
}
|
||||||
|
Tag::ATTESTATION_ID_MANUFACTURER => {
|
||||||
|
ec_gen_params = ec_gen_params.attestation_device_manufacturer(value);
|
||||||
|
}
|
||||||
|
Tag::ATTESTATION_ID_MODEL => {
|
||||||
|
ec_gen_params = ec_gen_params.attestation_device_model(value);
|
||||||
|
}
|
||||||
|
Tag::ATTESTATION_ID_IMEI => {
|
||||||
|
ec_gen_params = ec_gen_params.attestation_device_imei(value);
|
||||||
|
}
|
||||||
|
Tag::ATTESTATION_ID_SECOND_IMEI => {
|
||||||
|
ec_gen_params = ec_gen_params.attestation_device_second_imei(value);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
panic!("Unknown attestation id");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sec_level.generateKey(
|
||||||
|
&KeyDescriptor { domain: Domain::APP, nspace: -1, alias, blob: None },
|
||||||
|
Some(attest_key),
|
||||||
|
&ec_gen_params,
|
||||||
|
0,
|
||||||
|
b"entropy",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -58,6 +58,7 @@ rust_test {
|
||||||
"libkeymaster_messages",
|
"libkeymaster_messages",
|
||||||
"libcppbor_external",
|
"libcppbor_external",
|
||||||
"libkeystore-engine",
|
"libkeystore-engine",
|
||||||
|
"libkeymint_support",
|
||||||
],
|
],
|
||||||
require_root: true,
|
require_root: true,
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
#include <keymaster/km_openssl/openssl_err.h>
|
#include <keymaster/km_openssl/openssl_err.h>
|
||||||
#include <keymaster/km_openssl/openssl_utils.h>
|
#include <keymaster/km_openssl/openssl_utils.h>
|
||||||
|
|
||||||
|
#include <android-base/logging.h>
|
||||||
|
|
||||||
using aidl::android::hardware::security::keymint::ErrorCode;
|
using aidl::android::hardware::security::keymint::ErrorCode;
|
||||||
|
|
||||||
#define TAG_SEQUENCE 0x30
|
#define TAG_SEQUENCE 0x30
|
||||||
|
@ -503,3 +505,94 @@ bool performCryptoOpUsingKeystoreEngine(int64_t grant_id) {
|
||||||
#endif
|
#endif
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CxxResult getValueFromAttestRecord(rust::Vec<rust::u8> cert_buf, int32_t tag) {
|
||||||
|
CxxResult cxx_result{};
|
||||||
|
cxx_result.error = KM_ERROR_OK;
|
||||||
|
|
||||||
|
uint8_t* cert_data = cert_buf.data();
|
||||||
|
int cert_data_size = cert_buf.size();
|
||||||
|
|
||||||
|
std::vector<uint8_t> cert_bytes;
|
||||||
|
cert_bytes.insert(cert_bytes.end(), cert_data, (cert_data + cert_data_size));
|
||||||
|
|
||||||
|
aidl::android::hardware::security::keymint::X509_Ptr cert(
|
||||||
|
aidl::android::hardware::security::keymint::test::parse_cert_blob(cert_bytes));
|
||||||
|
if (!cert.get()) {
|
||||||
|
cxx_result.error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
|
||||||
|
return cxx_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASN1_OCTET_STRING* attest_rec =
|
||||||
|
aidl::android::hardware::security::keymint::test::get_attestation_record(cert.get());
|
||||||
|
if (!attest_rec) {
|
||||||
|
cxx_result.error = keymaster::TranslateLastOpenSslError();
|
||||||
|
return cxx_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
aidl::android::hardware::security::keymint::AuthorizationSet att_sw_enforced;
|
||||||
|
aidl::android::hardware::security::keymint::AuthorizationSet att_hw_enforced;
|
||||||
|
uint32_t att_attestation_version;
|
||||||
|
uint32_t att_keymint_version;
|
||||||
|
aidl::android::hardware::security::keymint::SecurityLevel att_attestation_security_level;
|
||||||
|
aidl::android::hardware::security::keymint::SecurityLevel att_keymint_security_level;
|
||||||
|
std::vector<uint8_t> att_challenge;
|
||||||
|
std::vector<uint8_t> att_unique_id;
|
||||||
|
std::vector<uint8_t> att_app_id;
|
||||||
|
|
||||||
|
auto error = aidl::android::hardware::security::keymint::parse_attestation_record(
|
||||||
|
attest_rec->data, attest_rec->length, &att_attestation_version,
|
||||||
|
&att_attestation_security_level, &att_keymint_version, &att_keymint_security_level,
|
||||||
|
&att_challenge, &att_sw_enforced, &att_hw_enforced, &att_unique_id);
|
||||||
|
EXPECT_EQ(ErrorCode::OK, error);
|
||||||
|
if (error != ErrorCode::OK) {
|
||||||
|
cxx_result.error = static_cast<int32_t>(error);
|
||||||
|
return cxx_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
aidl::android::hardware::security::keymint::Tag auth_tag =
|
||||||
|
static_cast<aidl::android::hardware::security::keymint::Tag>(tag);
|
||||||
|
|
||||||
|
if (auth_tag == aidl::android::hardware::security::keymint::Tag::ATTESTATION_APPLICATION_ID) {
|
||||||
|
int pos = att_sw_enforced.find(
|
||||||
|
aidl::android::hardware::security::keymint::Tag::ATTESTATION_APPLICATION_ID);
|
||||||
|
if (pos == -1) {
|
||||||
|
cxx_result.error = KM_ERROR_ATTESTATION_APPLICATION_ID_MISSING;
|
||||||
|
return cxx_result;
|
||||||
|
}
|
||||||
|
aidl::android::hardware::security::keymint::KeyParameter param = att_sw_enforced[pos];
|
||||||
|
std::vector<uint8_t> val =
|
||||||
|
param.value.get<aidl::android::hardware::security::keymint::KeyParameterValue::blob>();
|
||||||
|
std::move(val.begin(), val.end(), std::back_inserter(cxx_result.data));
|
||||||
|
return cxx_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auth_tag == aidl::android::hardware::security::keymint::Tag::ATTESTATION_CHALLENGE) {
|
||||||
|
if (att_challenge.size() == 0) {
|
||||||
|
cxx_result.error = KM_ERROR_ATTESTATION_CHALLENGE_MISSING;
|
||||||
|
return cxx_result;
|
||||||
|
}
|
||||||
|
std::move(att_challenge.begin(), att_challenge.end(), std::back_inserter(cxx_result.data));
|
||||||
|
return cxx_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auth_tag == aidl::android::hardware::security::keymint::Tag::UNIQUE_ID) {
|
||||||
|
if (att_unique_id.size() == 0) {
|
||||||
|
cxx_result.error = KM_ERROR_UNSUPPORTED_TAG;
|
||||||
|
return cxx_result;
|
||||||
|
}
|
||||||
|
std::move(att_unique_id.begin(), att_unique_id.end(), std::back_inserter(cxx_result.data));
|
||||||
|
return cxx_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pos = att_hw_enforced.find(auth_tag);
|
||||||
|
if (pos == -1) {
|
||||||
|
cxx_result.error = KM_ERROR_UNSUPPORTED_TAG;
|
||||||
|
return cxx_result;
|
||||||
|
}
|
||||||
|
aidl::android::hardware::security::keymint::KeyParameter param = att_hw_enforced[pos];
|
||||||
|
std::vector<uint8_t> val =
|
||||||
|
param.value.get<aidl::android::hardware::security::keymint::KeyParameterValue::blob>();
|
||||||
|
std::move(val.begin(), val.end(), std::back_inserter(cxx_result.data));
|
||||||
|
return cxx_result;
|
||||||
|
}
|
||||||
|
|
|
@ -10,3 +10,4 @@ CxxResult createWrappedKey(rust::Vec<rust::u8> encrypted_secure_key,
|
||||||
rust::Vec<rust::u8> tag);
|
rust::Vec<rust::u8> tag);
|
||||||
CxxResult buildAsn1DerEncodedWrappedKeyDescription();
|
CxxResult buildAsn1DerEncodedWrappedKeyDescription();
|
||||||
bool performCryptoOpUsingKeystoreEngine(int64_t grant_id);
|
bool performCryptoOpUsingKeystoreEngine(int64_t grant_id);
|
||||||
|
CxxResult getValueFromAttestRecord(rust::Vec<rust::u8> cert_buf, int32_t tag);
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::Tag::Tag;
|
||||||
use keystore2_test_utils::key_generations::Error;
|
use keystore2_test_utils::key_generations::Error;
|
||||||
|
|
||||||
#[cxx::bridge]
|
#[cxx::bridge]
|
||||||
|
@ -32,6 +33,7 @@ mod ffi {
|
||||||
) -> CxxResult;
|
) -> CxxResult;
|
||||||
fn buildAsn1DerEncodedWrappedKeyDescription() -> CxxResult;
|
fn buildAsn1DerEncodedWrappedKeyDescription() -> CxxResult;
|
||||||
fn performCryptoOpUsingKeystoreEngine(grant_id: i64) -> bool;
|
fn performCryptoOpUsingKeystoreEngine(grant_id: i64) -> bool;
|
||||||
|
fn getValueFromAttestRecord(cert_buf: Vec<u8>, tag: i32) -> CxxResult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,3 +89,11 @@ pub fn perform_crypto_op_using_keystore_engine(grant_id: i64) -> Result<bool, Er
|
||||||
|
|
||||||
Err(Error::Keystore2EngineOpFailed)
|
Err(Error::Keystore2EngineOpFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_value_from_attest_record(cert_buf: &[u8], tag: Tag) -> Result<Vec<u8>, Error> {
|
||||||
|
let result = ffi::getValueFromAttestRecord(cert_buf.to_vec(), tag.0);
|
||||||
|
if result.error == 0 && !result.data.is_empty() {
|
||||||
|
return Ok(result.data);
|
||||||
|
}
|
||||||
|
Err(Error::AttestRecordGetValueFailed)
|
||||||
|
}
|
||||||
|
|
|
@ -17,21 +17,26 @@ use nix::unistd::getuid;
|
||||||
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
|
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
|
||||||
Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
|
Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
|
||||||
ErrorCode::ErrorCode, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
|
ErrorCode::ErrorCode, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
|
||||||
SecurityLevel::SecurityLevel,
|
SecurityLevel::SecurityLevel, Tag::Tag,
|
||||||
};
|
};
|
||||||
use android_system_keystore2::aidl::android::system::keystore2::{
|
use android_system_keystore2::aidl::android::system::keystore2::{
|
||||||
Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
|
Domain::Domain, IKeystoreService::IKeystoreService, KeyDescriptor::KeyDescriptor,
|
||||||
|
ResponseCode::ResponseCode,
|
||||||
};
|
};
|
||||||
|
|
||||||
use keystore2_test_utils::{
|
use keystore2_test_utils::{
|
||||||
authorizations, get_keystore_service, key_generations, key_generations::Error,
|
authorizations, get_keystore_service, key_generations, key_generations::Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::ffi_test_utils::validate_certchain;
|
use crate::ffi_test_utils::{get_value_from_attest_record, validate_certchain};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
keystore2_client_test_utils::app_attest_key_feature_exists,
|
skip_test_if_no_app_attest_key_feature, skip_test_if_no_device_id_attestation_feature,
|
||||||
skip_test_if_no_app_attest_key_feature,
|
};
|
||||||
|
|
||||||
|
use crate::keystore2_client_test_utils::{
|
||||||
|
app_attest_key_feature_exists, device_id_attestation_feature_exists, get_attest_id_value,
|
||||||
|
is_second_imei_id_attestation_required,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Generate RSA and EC attestation keys and use them for signing RSA-signing keys.
|
/// Generate RSA and EC attestation keys and use them for signing RSA-signing keys.
|
||||||
|
@ -480,3 +485,178 @@ fn keystore2_attest_symmetric_key_fail_sys_error() {
|
||||||
// Should not have an attestation record.
|
// Should not have an attestation record.
|
||||||
assert!(aes_key_metadata.certificateChain.is_none());
|
assert!(aes_key_metadata.certificateChain.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_attestation_ids(keystore2: &binder::Strong<dyn IKeystoreService>) -> Vec<(Tag, Vec<u8>)> {
|
||||||
|
let attest_ids = vec![
|
||||||
|
(Tag::ATTESTATION_ID_BRAND, "ro.product.brand_for_attestation"),
|
||||||
|
(Tag::ATTESTATION_ID_DEVICE, "ro.product.device"),
|
||||||
|
(Tag::ATTESTATION_ID_PRODUCT, "ro.product.name_for_attestation"),
|
||||||
|
(Tag::ATTESTATION_ID_SERIAL, "ro.serialno"),
|
||||||
|
(Tag::ATTESTATION_ID_MANUFACTURER, "ro.product.manufacturer"),
|
||||||
|
(Tag::ATTESTATION_ID_MODEL, "ro.product.model_for_attestation"),
|
||||||
|
(Tag::ATTESTATION_ID_IMEI, ""), //Get this value from Telephony service.
|
||||||
|
(Tag::ATTESTATION_ID_SECOND_IMEI, ""), //Get this value from Telephony service.
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut attest_id_params: Vec<(Tag, Vec<u8>)> = vec![];
|
||||||
|
for (attest_id, prop_name) in attest_ids {
|
||||||
|
if attest_id == Tag::ATTESTATION_ID_SECOND_IMEI
|
||||||
|
&& !is_second_imei_id_attestation_required(keystore2)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(value) = get_attest_id_value(attest_id, prop_name) {
|
||||||
|
if !value.is_empty() {
|
||||||
|
attest_id_params.push((attest_id, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attest_id_params
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate an attested key with attestation of the device's identifiers. Test should succeed in
|
||||||
|
/// generating a attested key with attestation of device identifiers. Test might fail on devices
|
||||||
|
/// which don't support device id attestation with error response code `CANNOT_ATTEST_IDS or
|
||||||
|
/// INVALID_TAG`
|
||||||
|
fn generate_attested_key_with_device_attest_ids(algorithm: Algorithm) {
|
||||||
|
skip_test_if_no_device_id_attestation_feature!();
|
||||||
|
let keystore2 = get_keystore_service();
|
||||||
|
let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
|
||||||
|
|
||||||
|
let att_challenge: &[u8] = b"foo";
|
||||||
|
|
||||||
|
let attest_key_metadata =
|
||||||
|
key_generations::generate_attestation_key(&sec_level, algorithm, att_challenge).unwrap();
|
||||||
|
|
||||||
|
let attest_id_params = get_attestation_ids(&keystore2);
|
||||||
|
|
||||||
|
for (attest_id, value) in attest_id_params {
|
||||||
|
// Create RSA/EC key and use attestation key to sign it.
|
||||||
|
let key_alias = format!("ks_attested_test_key_{}", getuid());
|
||||||
|
let key_metadata =
|
||||||
|
key_generations::map_ks_error(key_generations::generate_key_with_attest_id(
|
||||||
|
&sec_level,
|
||||||
|
algorithm,
|
||||||
|
Some(key_alias),
|
||||||
|
att_challenge,
|
||||||
|
&attest_key_metadata.key,
|
||||||
|
attest_id,
|
||||||
|
value.clone(),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(key_metadata.certificate.is_some());
|
||||||
|
assert!(key_metadata.certificateChain.is_none());
|
||||||
|
|
||||||
|
let mut cert_chain: Vec<u8> = Vec::new();
|
||||||
|
cert_chain.extend(key_metadata.certificate.as_ref().unwrap());
|
||||||
|
cert_chain.extend(attest_key_metadata.certificate.as_ref().unwrap());
|
||||||
|
cert_chain.extend(attest_key_metadata.certificateChain.as_ref().unwrap());
|
||||||
|
|
||||||
|
validate_certchain(&cert_chain).expect("Error while validating cert chain");
|
||||||
|
let attest_id_value =
|
||||||
|
get_value_from_attest_record(key_metadata.certificate.as_ref().unwrap(), attest_id)
|
||||||
|
.expect("Attest id verification failed.");
|
||||||
|
assert_eq!(attest_id_value, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn keystore2_attest_ecdsa_attestation_id() {
|
||||||
|
generate_attested_key_with_device_attest_ids(Algorithm::EC);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn keystore2_attest_rsa_attestation_id() {
|
||||||
|
generate_attested_key_with_device_attest_ids(Algorithm::RSA);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try to generate an attested key with attestation of invalid device's identifiers. Test should
|
||||||
|
/// fail with error response code `CANNOT_ATTEST_IDS`.
|
||||||
|
#[test]
|
||||||
|
fn keystore2_attest_key_fails_with_invalid_attestation_id() {
|
||||||
|
skip_test_if_no_device_id_attestation_feature!();
|
||||||
|
|
||||||
|
let keystore2 = get_keystore_service();
|
||||||
|
let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
|
||||||
|
|
||||||
|
let digest = Digest::SHA_2_256;
|
||||||
|
let att_challenge: &[u8] = b"foo";
|
||||||
|
|
||||||
|
// Create EC-Attestation key.
|
||||||
|
let attest_key_metadata = key_generations::generate_ec_attestation_key(
|
||||||
|
&sec_level,
|
||||||
|
att_challenge,
|
||||||
|
digest,
|
||||||
|
EcCurve::P_256,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let attest_id_params = vec![
|
||||||
|
(Tag::ATTESTATION_ID_BRAND, b"invalid-brand".to_vec()),
|
||||||
|
(Tag::ATTESTATION_ID_DEVICE, b"invalid-device-name".to_vec()),
|
||||||
|
(Tag::ATTESTATION_ID_PRODUCT, b"invalid-product-name".to_vec()),
|
||||||
|
(Tag::ATTESTATION_ID_SERIAL, b"invalid-ro-serial".to_vec()),
|
||||||
|
(Tag::ATTESTATION_ID_MANUFACTURER, b"invalid-ro-product-manufacturer".to_vec()),
|
||||||
|
(Tag::ATTESTATION_ID_MODEL, b"invalid-ro-product-model".to_vec()),
|
||||||
|
(Tag::ATTESTATION_ID_IMEI, b"invalid-imei".to_vec()),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (attest_id, value) in attest_id_params {
|
||||||
|
// Create EC key and use attestation key to sign it.
|
||||||
|
let ec_key_alias = format!("ks_ec_attested_test_key_fail_{}{}", getuid(), digest.0);
|
||||||
|
let result = key_generations::map_ks_error(key_generations::generate_key_with_attest_id(
|
||||||
|
&sec_level,
|
||||||
|
Algorithm::EC,
|
||||||
|
Some(ec_key_alias),
|
||||||
|
att_challenge,
|
||||||
|
&attest_key_metadata.key,
|
||||||
|
attest_id,
|
||||||
|
value,
|
||||||
|
));
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(result.unwrap_err(), Error::Km(ErrorCode::CANNOT_ATTEST_IDS));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If `DEVICE_ID_ATTESTATION_FEATURE` is not supported then test tries to generate an attested
|
||||||
|
/// key with attestation of valid device's identifiers. Test should fail to generate key with
|
||||||
|
/// error code `CANNOT_ATTEST_IDS`.
|
||||||
|
#[test]
|
||||||
|
fn keystore2_attest_key_without_attestation_id_support_fails_with_cannot_attest_id() {
|
||||||
|
if device_id_attestation_feature_exists() {
|
||||||
|
// Skip this test on device supporting `DEVICE_ID_ATTESTATION_FEATURE`.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let keystore2 = get_keystore_service();
|
||||||
|
let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
|
||||||
|
|
||||||
|
let att_challenge: &[u8] = b"foo";
|
||||||
|
let attest_key_metadata =
|
||||||
|
key_generations::generate_attestation_key(&sec_level, Algorithm::RSA, att_challenge)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let attest_id_params = get_attestation_ids(&keystore2);
|
||||||
|
for (attest_id, value) in attest_id_params {
|
||||||
|
// Create RSA/EC key and use attestation key to sign it.
|
||||||
|
let key_alias = format!("ks_attested_test_key_{}", getuid());
|
||||||
|
let result = key_generations::map_ks_error(key_generations::generate_key_with_attest_id(
|
||||||
|
&sec_level,
|
||||||
|
Algorithm::RSA,
|
||||||
|
Some(key_alias),
|
||||||
|
att_challenge,
|
||||||
|
&attest_key_metadata.key,
|
||||||
|
attest_id,
|
||||||
|
value.clone(),
|
||||||
|
));
|
||||||
|
assert!(
|
||||||
|
result.is_err(),
|
||||||
|
"Expected to fail as FEATURE_DEVICE_ID_ATTESTATION is not supported."
|
||||||
|
);
|
||||||
|
assert_eq!(result.unwrap_err(), Error::Km(ErrorCode::CANNOT_ATTEST_IDS));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
use nix::unistd::{Gid, Uid};
|
use nix::unistd::{Gid, Uid};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use std::process::{Command, Output};
|
||||||
|
|
||||||
use openssl::encrypt::Encrypter;
|
use openssl::encrypt::Encrypter;
|
||||||
use openssl::error::ErrorStack;
|
use openssl::error::ErrorStack;
|
||||||
use openssl::hash::MessageDigest;
|
use openssl::hash::MessageDigest;
|
||||||
|
@ -66,6 +68,7 @@ pub const SAMPLE_PLAIN_TEXT: &[u8] = b"my message 11111";
|
||||||
|
|
||||||
pub const PACKAGE_MANAGER_NATIVE_SERVICE: &str = "package_native";
|
pub const PACKAGE_MANAGER_NATIVE_SERVICE: &str = "package_native";
|
||||||
pub const APP_ATTEST_KEY_FEATURE: &str = "android.hardware.keystore.app_attest_key";
|
pub const APP_ATTEST_KEY_FEATURE: &str = "android.hardware.keystore.app_attest_key";
|
||||||
|
pub const DEVICE_ID_ATTESTATION_FEATURE: &str = "android.software.device_id_attestation";
|
||||||
|
|
||||||
/// Determines whether app_attest_key_feature is supported or not.
|
/// Determines whether app_attest_key_feature is supported or not.
|
||||||
pub fn app_attest_key_feature_exists() -> bool {
|
pub fn app_attest_key_feature_exists() -> bool {
|
||||||
|
@ -75,6 +78,14 @@ pub fn app_attest_key_feature_exists() -> bool {
|
||||||
pm.hasSystemFeature(APP_ATTEST_KEY_FEATURE, 0).expect("hasSystemFeature failed.")
|
pm.hasSystemFeature(APP_ATTEST_KEY_FEATURE, 0).expect("hasSystemFeature failed.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determines whether device_id_attestation is supported or not.
|
||||||
|
pub fn device_id_attestation_feature_exists() -> bool {
|
||||||
|
let pm = wait_for_interface::<dyn IPackageManagerNative>(PACKAGE_MANAGER_NATIVE_SERVICE)
|
||||||
|
.expect("Failed to get package manager native service.");
|
||||||
|
|
||||||
|
pm.hasSystemFeature(DEVICE_ID_ATTESTATION_FEATURE, 0).expect("hasSystemFeature failed.")
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! skip_test_if_no_app_attest_key_feature {
|
macro_rules! skip_test_if_no_app_attest_key_feature {
|
||||||
() => {
|
() => {
|
||||||
|
@ -84,6 +95,15 @@ macro_rules! skip_test_if_no_app_attest_key_feature {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! skip_test_if_no_device_id_attestation_feature {
|
||||||
|
() => {
|
||||||
|
if !device_id_attestation_feature_exists() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// Indicate whether the default device is KeyMint (rather than Keymaster).
|
/// Indicate whether the default device is KeyMint (rather than Keymaster).
|
||||||
pub fn has_default_keymint() -> bool {
|
pub fn has_default_keymint() -> bool {
|
||||||
binder::is_declared("android.hardware.security.keymint.IKeyMintDevice/default")
|
binder::is_declared("android.hardware.security.keymint.IKeyMintDevice/default")
|
||||||
|
@ -444,3 +464,84 @@ pub fn verify_aliases(
|
||||||
.iter()
|
.iter()
|
||||||
.all(|key| expected_aliases.contains(key.alias.as_ref().unwrap())));
|
.all(|key| expected_aliases.contains(key.alias.as_ref().unwrap())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the value of the given system property, if the given system property doesn't exist
|
||||||
|
// then returns an empty byte vector.
|
||||||
|
pub fn get_system_prop(name: &str) -> Vec<u8> {
|
||||||
|
match rustutils::system_properties::read(name) {
|
||||||
|
Ok(Some(value)) => {
|
||||||
|
return value.as_bytes().to_vec();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines whether the SECOND-IMEI can be used as device attest-id.
|
||||||
|
pub fn is_second_imei_id_attestation_required(
|
||||||
|
keystore2: &binder::Strong<dyn IKeystoreService>,
|
||||||
|
) -> bool {
|
||||||
|
let api_level = std::str::from_utf8(&get_system_prop("ro.vendor.api_level"))
|
||||||
|
.unwrap()
|
||||||
|
.parse::<i32>()
|
||||||
|
.unwrap();
|
||||||
|
keystore2.getInterfaceVersion().unwrap() >= 3 && api_level > 33
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run a service command and collect the output.
|
||||||
|
pub fn run_service_command(command: &[&str]) -> std::io::Result<Output> {
|
||||||
|
Command::new("cmd").args(command).output()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get IMEI from telephony service.
|
||||||
|
pub fn get_imei(slot: i32) -> Option<Vec<u8>> {
|
||||||
|
let mut cmd = vec!["phone", "get-imei"];
|
||||||
|
let slot_str = slot.to_string();
|
||||||
|
cmd.push(slot_str.as_str());
|
||||||
|
let output = run_service_command(&cmd).unwrap();
|
||||||
|
if output.status.success() {
|
||||||
|
let stdout = String::from_utf8(output.stdout).unwrap();
|
||||||
|
let mut split_out = stdout.split_whitespace();
|
||||||
|
let imei = split_out.next_back().unwrap();
|
||||||
|
if imei == "null" {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
return Some(imei.as_bytes().to_vec());
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get value of the given attestation id.
|
||||||
|
pub fn get_attest_id_value(attest_id: Tag, prop_name: &str) -> Option<Vec<u8>> {
|
||||||
|
match attest_id {
|
||||||
|
Tag::ATTESTATION_ID_IMEI => get_imei(0),
|
||||||
|
Tag::ATTESTATION_ID_SECOND_IMEI => get_imei(1),
|
||||||
|
Tag::ATTESTATION_ID_BRAND => {
|
||||||
|
let prop_val = get_system_prop(prop_name);
|
||||||
|
if prop_val.is_empty() {
|
||||||
|
Some(get_system_prop("ro.product.brand"))
|
||||||
|
} else {
|
||||||
|
Some(prop_val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Tag::ATTESTATION_ID_PRODUCT => {
|
||||||
|
let prop_val = get_system_prop(prop_name);
|
||||||
|
if prop_val.is_empty() {
|
||||||
|
Some(get_system_prop("ro.product.name"))
|
||||||
|
} else {
|
||||||
|
Some(prop_val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Tag::ATTESTATION_ID_MODEL => {
|
||||||
|
let prop_val = get_system_prop(prop_name);
|
||||||
|
if prop_val.is_empty() {
|
||||||
|
Some(get_system_prop("ro.product.model"))
|
||||||
|
} else {
|
||||||
|
Some(prop_val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Some(get_system_prop(prop_name)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue