Tests to verify importWrappedKey functionality.
- Build ASN.1 DER-encoded wrapped key material `SecureKeyWrapper`.
Import the wrapped key material. Test should create DER-encoded
wrapped key material and import the secure key successfully.
Imported key should be used perform crypto operation successfully.
- Build ASN.1 DER-encoded wrapped key material `SecureKeyWrapper` with
invalid AAD. Try to import the wrapped key material. Test should
create DER-encoded wrapped key material. Test should fail to import
the secure key with error code `VERIFICATION_FAILED`.
- Import wrapped AES key and use it for crypto operations. Test should
import wrapped key and user it for performing crypto operations
successfully.
- Import a key wihtout WRAP_KEY purpose and try to use it as wrapping
key while importing wrapped key. Test should fail to import wrapped
key with an error code `INCOMPATIBLE_PURPOSE`.
- Try to import wrapped key using non-existing wrapping key in Android
keystore. Test should fail to import wrapped key with an error code
`KEY_NOT_FOUND`.
Bug: 194359114
Test: atest keystore2_client_test
Change-Id: Ic9ed03b6f945bd41c4873f97a84c1658c284e918
diff --git a/keystore2/tests/Android.bp b/keystore2/tests/Android.bp
index dd5d782..78dd2d7 100644
--- a/keystore2/tests/Android.bp
+++ b/keystore2/tests/Android.bp
@@ -36,7 +36,7 @@
rustlibs: [
"librustutils",
"libkeystore2_test_utils",
- "packagemanager_aidl-rust",
+ "packagemanager_aidl-rust",
"libnix",
"libanyhow",
"libbinder_rs",
@@ -44,8 +44,8 @@
"liblibc",
"libserde",
"libthiserror",
- "libcxx",
- "libopenssl",
+ "libcxx",
+ "libopenssl",
],
static_libs: [
"libkeystore2_ffi_test_utils",
@@ -54,6 +54,9 @@
],
shared_libs: [
"libcrypto",
+ "libkeymaster_portable",
+ "libkeymaster_messages",
+ "libcppbor_external",
],
require_root: true,
}
@@ -67,17 +70,31 @@
],
generated_headers: [
"cxx-bridge-header",
+ "libkeystore2_ffi_test_utils_bridge_header",
],
generated_sources: ["libkeystore2_ffi_test_utils_bridge_code"],
static_libs: [
"libkeymint_vts_test_utils",
],
+ shared_libs: [
+ "libkeymaster_portable",
+ "libkeymaster_messages",
+ "libcppbor_external",
+ ],
}
genrule {
name: "libkeystore2_ffi_test_utils_bridge_code",
tools: ["cxxbridge"],
cmd: "$(location cxxbridge) $(in) >> $(out)",
- srcs: ["keystore2_client_attest_key_tests.rs"],
+ srcs: ["ffi_test_utils.rs"],
out: ["libkeystore2_test_utils_cxx_generated.cc"],
}
+
+genrule {
+ name: "libkeystore2_ffi_test_utils_bridge_header",
+ tools: ["cxxbridge"],
+ cmd: "$(location cxxbridge) $(in) --header >> $(out)",
+ srcs: ["ffi_test_utils.rs"],
+ out: ["ffi_test_utils.rs.h"],
+}
diff --git a/keystore2/tests/ffi_test_utils.cpp b/keystore2/tests/ffi_test_utils.cpp
index fb5a7d2..45ce02c 100644
--- a/keystore2/tests/ffi_test_utils.cpp
+++ b/keystore2/tests/ffi_test_utils.cpp
@@ -4,15 +4,86 @@
#include <KeyMintAidlTestBase.h>
#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <keymaster/UniquePtr.h>
+#include <memory>
#include <vector>
+#include <hardware/keymaster_defs.h>
+#include <keymaster/android_keymaster_utils.h>
+#include <keymaster/keymaster_tags.h>
+
+#include <keymaster/km_openssl/attestation_record.h>
+#include <keymaster/km_openssl/openssl_err.h>
+#include <keymaster/km_openssl/openssl_utils.h>
+#include <openssl/asn1t.h>
+
using aidl::android::hardware::security::keymint::ErrorCode;
#define TAG_SEQUENCE 0x30
#define LENGTH_MASK 0x80
#define LENGTH_VALUE_MASK 0x7F
+/**
+ * ASN.1 structure for `KeyDescription` Schema.
+ * See `IKeyMintDevice.aidl` for documentation of the `KeyDescription` schema.
+ * KeyDescription ::= SEQUENCE(
+ * keyFormat INTEGER, # Values from KeyFormat enum.
+ * keyParams AuthorizationList,
+ * )
+ */
+typedef struct key_description {
+ ASN1_INTEGER* key_format;
+ keymaster::KM_AUTH_LIST* key_params;
+} TEST_KEY_DESCRIPTION;
+
+ASN1_SEQUENCE(TEST_KEY_DESCRIPTION) = {
+ ASN1_SIMPLE(TEST_KEY_DESCRIPTION, key_format, ASN1_INTEGER),
+ ASN1_SIMPLE(TEST_KEY_DESCRIPTION, key_params, keymaster::KM_AUTH_LIST),
+} ASN1_SEQUENCE_END(TEST_KEY_DESCRIPTION);
+DECLARE_ASN1_FUNCTIONS(TEST_KEY_DESCRIPTION);
+
+/**
+ * ASN.1 structure for `SecureKeyWrapper` Schema.
+ * See `IKeyMintDevice.aidl` for documentation of the `SecureKeyWrapper` schema.
+ * SecureKeyWrapper ::= SEQUENCE(
+ * version INTEGER, # Contains value 0
+ * encryptedTransportKey OCTET_STRING,
+ * initializationVector OCTET_STRING,
+ * keyDescription KeyDescription,
+ * encryptedKey OCTET_STRING,
+ * tag OCTET_STRING
+ * )
+ */
+typedef struct secure_key_wrapper {
+ ASN1_INTEGER* version;
+ ASN1_OCTET_STRING* encrypted_transport_key;
+ ASN1_OCTET_STRING* initialization_vector;
+ TEST_KEY_DESCRIPTION* key_desc;
+ ASN1_OCTET_STRING* encrypted_key;
+ ASN1_OCTET_STRING* tag;
+} TEST_SECURE_KEY_WRAPPER;
+
+ASN1_SEQUENCE(TEST_SECURE_KEY_WRAPPER) = {
+ ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, version, ASN1_INTEGER),
+ ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, encrypted_transport_key, ASN1_OCTET_STRING),
+ ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, initialization_vector, ASN1_OCTET_STRING),
+ ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, key_desc, TEST_KEY_DESCRIPTION),
+ ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, encrypted_key, ASN1_OCTET_STRING),
+ ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, tag, ASN1_OCTET_STRING),
+} ASN1_SEQUENCE_END(TEST_SECURE_KEY_WRAPPER);
+DECLARE_ASN1_FUNCTIONS(TEST_SECURE_KEY_WRAPPER);
+
+IMPLEMENT_ASN1_FUNCTIONS(TEST_SECURE_KEY_WRAPPER);
+IMPLEMENT_ASN1_FUNCTIONS(TEST_KEY_DESCRIPTION);
+
+struct TEST_KEY_DESCRIPTION_Delete {
+ void operator()(TEST_KEY_DESCRIPTION* p) { TEST_KEY_DESCRIPTION_free(p); }
+};
+struct TEST_SECURE_KEY_WRAPPER_Delete {
+ void operator()(TEST_SECURE_KEY_WRAPPER* p) { TEST_SECURE_KEY_WRAPPER_free(p); }
+};
+
/* This function extracts a certificate from the certs_chain_buffer at the given
* offset. Each DER encoded certificate starts with TAG_SEQUENCE followed by the
* total length of the certificate. The length of the certificate is determined
@@ -118,3 +189,178 @@
return false;
}
+
+/**
+ * Below mentioned key parameters are used to create authorization list of
+ * secure key.
+ * Algorithm: AES-256
+ * Padding: PKCS7
+ * Blockmode: ECB
+ * Purpose: Encrypt, Decrypt
+ */
+keymaster::AuthorizationSet build_wrapped_key_auth_list() {
+ return keymaster::AuthorizationSet(keymaster::AuthorizationSetBuilder()
+ .AesEncryptionKey(256)
+ .Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_ECB)
+ .Authorization(keymaster::TAG_PADDING, KM_PAD_PKCS7)
+ .Authorization(keymaster::TAG_NO_AUTH_REQUIRED));
+}
+
+/**
+ * Creates ASN.1 DER-encoded data corresponding to `KeyDescription` schema as
+ * AAD. See `IKeyMintDevice.aidl` for documentation of the `KeyDescription` schema.
+ */
+CxxResult buildAsn1DerEncodedWrappedKeyDescription() {
+ CxxResult cxx_result{};
+ keymaster_error_t error;
+ cxx_result.error = KM_ERROR_OK;
+
+ keymaster::UniquePtr<TEST_KEY_DESCRIPTION, TEST_KEY_DESCRIPTION_Delete> key_description(
+ TEST_KEY_DESCRIPTION_new());
+ if (!key_description.get()) {
+ cxx_result.error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return cxx_result;
+ }
+
+ // Fill secure key authorizations.
+ keymaster::AuthorizationSet auth_list = build_wrapped_key_auth_list();
+ error = build_auth_list(auth_list, key_description->key_params);
+ if (error != KM_ERROR_OK) {
+ cxx_result.error = error;
+ return cxx_result;
+ }
+
+ // Fill secure key format.
+ if (!ASN1_INTEGER_set(key_description->key_format, KM_KEY_FORMAT_RAW)) {
+ cxx_result.error = keymaster::TranslateLastOpenSslError();
+ return cxx_result;
+ }
+
+ // Perform ASN.1 DER encoding of KeyDescription.
+ size_t asn1_data_len = i2d_TEST_KEY_DESCRIPTION(key_description.get(), nullptr);
+ if (asn1_data_len < 0) {
+ cxx_result.error = keymaster::TranslateLastOpenSslError();
+ return cxx_result;
+ }
+ std::vector<uint8_t> asn1_data(asn1_data_len, 0);
+
+ if (!asn1_data.data()) {
+ cxx_result.error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return cxx_result;
+ }
+
+ uint8_t* p = asn1_data.data();
+ asn1_data_len = i2d_TEST_KEY_DESCRIPTION(key_description.get(), &p);
+ if (asn1_data_len < 0) {
+ cxx_result.error = keymaster::TranslateLastOpenSslError();
+ return cxx_result;
+ }
+
+ std::move(asn1_data.begin(), asn1_data.end(), std::back_inserter(cxx_result.data));
+
+ return cxx_result;
+}
+
+/**
+ * Creates wrapped key material to import in ASN.1 DER-encoded data corresponding to
+ * `SecureKeyWrapper` schema. See `IKeyMintDevice.aidl` for documentation of the `SecureKeyWrapper`
+ * schema.
+ */
+CxxResult createWrappedKey(rust::Vec<rust::u8> encrypted_secure_key,
+ rust::Vec<rust::u8> encrypted_transport_key, rust::Vec<rust::u8> iv,
+ rust::Vec<rust::u8> tag) {
+ CxxResult cxx_result{};
+ keymaster_error_t error;
+ cxx_result.error = KM_ERROR_OK;
+
+ uint8_t* enc_secure_key_data = encrypted_secure_key.data();
+ int enc_secure_key_size = encrypted_secure_key.size();
+
+ uint8_t* iv_data = iv.data();
+ int iv_size = iv.size();
+
+ uint8_t* tag_data = tag.data();
+ int tag_size = tag.size();
+
+ uint8_t* enc_transport_key_data = encrypted_transport_key.data();
+ int enc_transport_key_size = encrypted_transport_key.size();
+
+ keymaster::UniquePtr<TEST_SECURE_KEY_WRAPPER, TEST_SECURE_KEY_WRAPPER_Delete> sec_key_wrapper(
+ TEST_SECURE_KEY_WRAPPER_new());
+ if (!sec_key_wrapper.get()) {
+ cxx_result.error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return cxx_result;
+ }
+
+ // Fill version = 0
+ if (!ASN1_INTEGER_set(sec_key_wrapper->version, 0)) {
+ cxx_result.error = keymaster::TranslateLastOpenSslError();
+ return cxx_result;
+ }
+
+ // Fill encrypted transport key.
+ if (enc_transport_key_size &&
+ !ASN1_OCTET_STRING_set(sec_key_wrapper->encrypted_transport_key, enc_transport_key_data,
+ enc_transport_key_size)) {
+ cxx_result.error = keymaster::TranslateLastOpenSslError();
+ return cxx_result;
+ }
+
+ // Fill encrypted secure key.
+ if (enc_secure_key_size && !ASN1_OCTET_STRING_set(sec_key_wrapper->encrypted_key,
+ enc_secure_key_data, enc_secure_key_size)) {
+ cxx_result.error = keymaster::TranslateLastOpenSslError();
+ return cxx_result;
+ }
+
+ // Fill secure key authorization list.
+ keymaster::AuthorizationSet auth_list = build_wrapped_key_auth_list();
+ error = build_auth_list(auth_list, sec_key_wrapper->key_desc->key_params);
+ if (error != KM_ERROR_OK) {
+ cxx_result.error = error;
+ return cxx_result;
+ }
+
+ // Fill secure key format.
+ if (!ASN1_INTEGER_set(sec_key_wrapper->key_desc->key_format, KM_KEY_FORMAT_RAW)) {
+ cxx_result.error = keymaster::TranslateLastOpenSslError();
+ return cxx_result;
+ }
+
+ // Fill initialization vector used for encrypting secure key.
+ if (iv_size &&
+ !ASN1_OCTET_STRING_set(sec_key_wrapper->initialization_vector, iv_data, iv_size)) {
+ cxx_result.error = keymaster::TranslateLastOpenSslError();
+ return cxx_result;
+ }
+
+ // Fill GCM-tag, extracted during secure key encryption.
+ if (tag_size && !ASN1_OCTET_STRING_set(sec_key_wrapper->tag, tag_data, tag_size)) {
+ cxx_result.error = keymaster::TranslateLastOpenSslError();
+ return cxx_result;
+ }
+
+ // ASN.1 DER-encoding of secure key wrapper.
+ size_t asn1_data_len = i2d_TEST_SECURE_KEY_WRAPPER(sec_key_wrapper.get(), nullptr);
+ if (asn1_data_len < 0) {
+ cxx_result.error = keymaster::TranslateLastOpenSslError();
+ return cxx_result;
+ }
+ std::vector<uint8_t> asn1_data(asn1_data_len, 0);
+
+ if (!asn1_data.data()) {
+ cxx_result.error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return cxx_result;
+ }
+
+ uint8_t* p = asn1_data.data();
+ asn1_data_len = i2d_TEST_SECURE_KEY_WRAPPER(sec_key_wrapper.get(), &p);
+ if (asn1_data_len < 0) {
+ cxx_result.error = keymaster::TranslateLastOpenSslError();
+ return cxx_result;
+ }
+
+ std::move(asn1_data.begin(), asn1_data.end(), std::back_inserter(cxx_result.data));
+
+ return cxx_result;
+}
diff --git a/keystore2/tests/ffi_test_utils.hpp b/keystore2/tests/ffi_test_utils.hpp
index 7f5c3b2..b8c7c48 100644
--- a/keystore2/tests/ffi_test_utils.hpp
+++ b/keystore2/tests/ffi_test_utils.hpp
@@ -1,5 +1,11 @@
#pragma once
#include "rust/cxx.h"
+#include "ffi_test_utils.rs.h"
bool validateCertChain(rust::Vec<rust::u8> cert_buf, uint32_t cert_len, bool strict_issuer_check);
+CxxResult createWrappedKey(rust::Vec<rust::u8> encrypted_secure_key,
+ rust::Vec<rust::u8> encrypted_transport_key,
+ rust::Vec<rust::u8> iv,
+ rust::Vec<rust::u8> tag);
+CxxResult buildAsn1DerEncodedWrappedKeyDescription();
diff --git a/keystore2/tests/ffi_test_utils.rs b/keystore2/tests/ffi_test_utils.rs
new file mode 100644
index 0000000..066d4a1
--- /dev/null
+++ b/keystore2/tests/ffi_test_utils.rs
@@ -0,0 +1,80 @@
+// Copyright 2022, 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.
+
+use keystore2_test_utils::key_generations::Error;
+
+#[cxx::bridge]
+mod ffi {
+ struct CxxResult {
+ data: Vec<u8>,
+ error: i32,
+ }
+
+ unsafe extern "C++" {
+ include!("ffi_test_utils.hpp");
+ fn validateCertChain(cert_buf: Vec<u8>, cert_len: u32, strict_issuer_check: bool) -> bool;
+ fn createWrappedKey(
+ encrypted_secure_key: Vec<u8>,
+ encrypted_transport_key: Vec<u8>,
+ iv: Vec<u8>,
+ tag: Vec<u8>,
+ ) -> CxxResult;
+ fn buildAsn1DerEncodedWrappedKeyDescription() -> CxxResult;
+ }
+}
+
+/// Validate given certificate chain.
+pub fn validate_certchain(cert_buf: &[u8]) -> Result<bool, Error> {
+ if ffi::validateCertChain(cert_buf.to_vec(), cert_buf.len().try_into().unwrap(), true) {
+ return Ok(true);
+ }
+
+ Err(Error::ValidateCertChainFailed)
+}
+
+fn get_result(result: ffi::CxxResult) -> Result<Vec<u8>, Error> {
+ if result.error == 0 && !result.data.is_empty() {
+ Ok(result.data)
+ } else {
+ Err(Error::DerEncodeFailed)
+ }
+}
+
+/// Creates wrapped key material to import in ASN.1 DER-encoded data corresponding to
+/// `SecureKeyWrapper`. See `IKeyMintDevice.aidl` for documentation of the `SecureKeyWrapper`
+/// schema.
+pub fn create_wrapped_key(
+ encrypted_secure_key: &[u8],
+ encrypted_transport_key: &[u8],
+ iv: &[u8],
+ tag: &[u8],
+) -> Result<Vec<u8>, Error> {
+ get_result(ffi::createWrappedKey(
+ encrypted_secure_key.to_vec(),
+ encrypted_transport_key.to_vec(),
+ iv.to_vec(),
+ tag.to_vec(),
+ ))
+}
+
+/// Creates ASN.1 DER-encoded data corresponding to `KeyDescription` schema.
+/// See `IKeyMintDevice.aidl` for documentation of the `KeyDescription` schema.
+/// Below mentioned key parameters are used -
+/// Algorithm: AES-256
+/// Padding: PKCS7
+/// Blockmode: ECB
+/// Purpose: Encrypt, Decrypt
+pub fn create_wrapped_key_additional_auth_data() -> Result<Vec<u8>, Error> {
+ get_result(ffi::buildAsn1DerEncodedWrappedKeyDescription())
+}
diff --git a/keystore2/tests/keystore2_client_attest_key_tests.rs b/keystore2/tests/keystore2_client_attest_key_tests.rs
index fc3148c..5644fbe 100644
--- a/keystore2/tests/keystore2_client_attest_key_tests.rs
+++ b/keystore2/tests/keystore2_client_attest_key_tests.rs
@@ -27,28 +27,13 @@
authorizations, get_keystore_service, key_generations, key_generations::Error,
};
+use crate::ffi_test_utils::validate_certchain;
+
use crate::{
keystore2_client_test_utils::app_attest_key_feature_exists,
skip_test_if_no_app_attest_key_feature,
};
-#[cxx::bridge]
-mod ffi {
- unsafe extern "C++" {
- include!("ffi_test_utils.hpp");
- fn validateCertChain(cert_buf: Vec<u8>, cert_len: u32, strict_issuer_check: bool) -> bool;
- }
-}
-
-/// Validate given certificate chain.
-pub fn validate_certchain(cert_buf: &[u8]) -> Result<bool, Error> {
- if ffi::validateCertChain(cert_buf.to_vec(), cert_buf.len().try_into().unwrap(), true) {
- return Ok(true);
- }
-
- Err(Error::ValidateCertChainFailed)
-}
-
/// Generate RSA and EC attestation keys and use them for signing RSA-signing keys.
/// Test should be able to generate attestation keys and use them successfully.
#[test]
diff --git a/keystore2/tests/keystore2_client_import_keys_tests.rs b/keystore2/tests/keystore2_client_import_keys_tests.rs
index abf35b5..c8f94b6 100644
--- a/keystore2/tests/keystore2_client_import_keys_tests.rs
+++ b/keystore2/tests/keystore2_client_import_keys_tests.rs
@@ -14,21 +14,29 @@
use nix::unistd::getuid;
+use openssl::rand::rand_bytes;
+use openssl::x509::X509;
+
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
- ErrorCode::ErrorCode, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
- SecurityLevel::SecurityLevel,
+ ErrorCode::ErrorCode, HardwareAuthenticatorType::HardwareAuthenticatorType,
+ KeyPurpose::KeyPurpose, PaddingMode::PaddingMode, SecurityLevel::SecurityLevel,
};
use android_system_keystore2::aidl::android::system::keystore2::{
- Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
+ AuthenticatorSpec::AuthenticatorSpec, Domain::Domain,
+ IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
+ KeyMetadata::KeyMetadata, ResponseCode::ResponseCode,
};
use keystore2_test_utils::{
authorizations, get_keystore_service, key_generations, key_generations::Error,
};
+use crate::ffi_test_utils::{create_wrapped_key, create_wrapped_key_additional_auth_data};
+
use crate::keystore2_client_test_utils::{
- has_trusty_keymint, perform_sample_asym_sign_verify_op, perform_sample_hmac_sign_verify_op,
+ encrypt_secure_key, encrypt_transport_key, has_trusty_keymint,
+ perform_sample_asym_sign_verify_op, perform_sample_hmac_sign_verify_op,
perform_sample_sym_key_decrypt_op, perform_sample_sym_key_encrypt_op, SAMPLE_PLAIN_TEXT,
};
@@ -51,6 +59,76 @@
);
}
+fn perform_sym_key_encrypt_decrypt_op(
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ key_metadata: &KeyMetadata,
+) {
+ let cipher_text = perform_sample_sym_key_encrypt_op(
+ sec_level,
+ PaddingMode::PKCS7,
+ BlockMode::ECB,
+ &mut None,
+ None,
+ &key_metadata.key,
+ )
+ .unwrap();
+
+ assert!(cipher_text.is_some());
+
+ let plain_text = perform_sample_sym_key_decrypt_op(
+ sec_level,
+ &cipher_text.unwrap(),
+ PaddingMode::PKCS7,
+ BlockMode::ECB,
+ &mut None,
+ None,
+ &key_metadata.key,
+ )
+ .unwrap();
+
+ assert!(plain_text.is_some());
+ assert_eq!(plain_text.unwrap(), SAMPLE_PLAIN_TEXT.to_vec());
+}
+
+fn build_secure_key_wrapper(
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ secure_key: &[u8],
+ transport_key: &[u8],
+ nonce: &[u8],
+ aad: &[u8],
+ wrapping_key_metadata: &KeyMetadata,
+) -> Result<Vec<u8>, Error> {
+ // Encrypt secure key with transport key.
+ let transport_key_alias = format!("ks_transport_key_aes_256_key_test_{}", getuid());
+ let transport_key_metadata =
+ key_generations::import_transport_key(sec_level, Some(transport_key_alias), transport_key)
+ .unwrap();
+ let encrypted_secure_key = encrypt_secure_key(
+ sec_level,
+ secure_key,
+ aad,
+ nonce.to_vec(),
+ 128,
+ &transport_key_metadata.key,
+ )
+ .unwrap();
+
+ // Extract GCM-tag and encrypted secure key data.
+ let encrypted_secure_key = encrypted_secure_key.unwrap();
+ let gcm_tag: Vec<u8> =
+ encrypted_secure_key[secure_key.len()..(encrypted_secure_key.len())].to_vec();
+ let encrypted_secure_key: Vec<u8> = encrypted_secure_key[0..secure_key.len()].to_vec();
+
+ // Get wrapping key puplic part and encrypt the transport key.
+ let cert_bytes = wrapping_key_metadata.certificate.as_ref().unwrap();
+ let cert = X509::from_der(cert_bytes.as_ref()).unwrap();
+ let public_key = cert.public_key().unwrap();
+ let encrypted_transport_key = encrypt_transport_key(transport_key, &public_key).unwrap();
+
+ // Create `SecureKeyWrapper` ASN.1 DER-encoded data.
+ create_wrapped_key(&encrypted_secure_key, &encrypted_transport_key, nonce, &gcm_tag)
+}
+
/// Import RSA key and verify imported key parameters. Try to create an operation using the
/// imported key. Test should be able to create an operation successfully.
#[test]
@@ -289,31 +367,7 @@
let key_metadata = key_generations::import_aes_key(&sec_level, Domain::APP, -1, Some(alias))
.expect("Failed to import AES key.");
- let cipher_text = perform_sample_sym_key_encrypt_op(
- &sec_level,
- PaddingMode::PKCS7,
- BlockMode::ECB,
- &mut None,
- None,
- &key_metadata.key,
- )
- .unwrap();
-
- assert!(cipher_text.is_some());
-
- let plain_text = perform_sample_sym_key_decrypt_op(
- &sec_level,
- &cipher_text.unwrap(),
- PaddingMode::PKCS7,
- BlockMode::ECB,
- &mut None,
- None,
- &key_metadata.key,
- )
- .unwrap();
-
- assert!(plain_text.is_some());
- assert_eq!(plain_text.unwrap(), SAMPLE_PLAIN_TEXT.to_vec());
+ perform_sym_key_encrypt_decrypt_op(&sec_level, &key_metadata);
}
/// Import 3DES key and verify key parameters. Try to create an operation using the imported key.
@@ -331,31 +385,7 @@
let key_metadata = key_generations::import_3des_key(&sec_level, Domain::APP, -1, Some(alias))
.expect("Failed to import 3DES key.");
- let cipher_text = perform_sample_sym_key_encrypt_op(
- &sec_level,
- PaddingMode::PKCS7,
- BlockMode::ECB,
- &mut None,
- None,
- &key_metadata.key,
- )
- .unwrap();
-
- assert!(cipher_text.is_some());
-
- let plain_text = perform_sample_sym_key_decrypt_op(
- &sec_level,
- &cipher_text.unwrap(),
- PaddingMode::PKCS7,
- BlockMode::ECB,
- &mut None,
- None,
- &key_metadata.key,
- )
- .unwrap();
-
- assert!(plain_text.is_some());
- assert_eq!(plain_text.unwrap(), SAMPLE_PLAIN_TEXT.to_vec());
+ perform_sym_key_encrypt_decrypt_op(&sec_level, &key_metadata);
}
/// Import HMAC key and verify key parameters. Try to create an operation using the imported key.
@@ -372,3 +402,234 @@
perform_sample_hmac_sign_verify_op(&sec_level, &key_metadata.key);
}
+
+/// This test creates a wrapped key data and imports it. Validates the imported wrapped key.
+/// 1. Create a wrapped key material to import, as ASN.1 DER-encoded data corresponding to the
+/// `SecureKeyWrapper` schema defined in IKeyMintDevice.aidl.
+/// 2. Import wrapped key and use it for crypto operations.
+/// Test should successfully import the wrapped key and perform crypto operations.
+#[test]
+fn keystore2_create_wrapped_key_and_import_wrapped_key_success() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let mut secure_key = [0; 32];
+ rand_bytes(&mut secure_key).unwrap();
+
+ let mut transport_key = [0; 32];
+ rand_bytes(&mut transport_key).unwrap();
+
+ let mut nonce = [0; 12];
+ rand_bytes(&mut nonce).unwrap();
+
+ // Import wrapping key.
+ let wrapping_key_alias = format!("ks_wrapping_key_test_import_2_{}_2048", getuid());
+ let wrapping_key_metadata = key_generations::import_wrapping_key(
+ &sec_level,
+ key_generations::RSA_2048_KEY,
+ Some(wrapping_key_alias),
+ )
+ .unwrap();
+
+ // Create the DER-encoded representation of `KeyDescription` schema defined in
+ // `IKeyMintDevice.aidl` and use it as additional authenticated data.
+ let aad = create_wrapped_key_additional_auth_data().unwrap();
+
+ // Build ASN.1 DER-encoded wrapped key material as described in `SecureKeyWrapper` schema.
+ let wrapped_key_data = build_secure_key_wrapper(
+ &sec_level,
+ &secure_key,
+ &transport_key,
+ &nonce,
+ &aad,
+ &wrapping_key_metadata,
+ )
+ .unwrap();
+
+ // Unwrap the key. Import wrapped key.
+ let secured_key_alias = format!("ks_wrapped_aes_key_{}", getuid());
+ let secured_key_metadata = key_generations::import_wrapped_key(
+ &sec_level,
+ Some(secured_key_alias),
+ &wrapping_key_metadata,
+ Some(wrapped_key_data.to_vec()),
+ )
+ .unwrap();
+
+ perform_sym_key_encrypt_decrypt_op(&sec_level, &secured_key_metadata);
+}
+
+/// Create a wrapped key data with invalid Additional Authenticated Data (AAD) and
+/// try to import wrapped key.
+/// 1. Create a wrapped key material with invalid AAD to import, as ASN.1 DER-encoded
+/// data corresponding to the `SecureKeyWrapper` schema defined in IKeyMintDevice.aidl.
+/// 2. Import wrapped key and use it for crypto operations.
+/// Test should fail to import the wrapped key with error code `VERIFICATION_FAILED`.
+#[test]
+fn keystore2_create_wrapped_key_with_invalid_aad_and_import_wrapped_key_fail() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let mut secure_key = [0; 32];
+ rand_bytes(&mut secure_key).unwrap();
+
+ let mut transport_key = [0; 32];
+ rand_bytes(&mut transport_key).unwrap();
+
+ let mut nonce = [0; 12];
+ rand_bytes(&mut nonce).unwrap();
+
+ // Import wrapping key.
+ let wrapping_key_alias = format!("ks_wrapping_key_test_import_2_{}_2048", getuid());
+ let wrapping_key_metadata = key_generations::import_wrapping_key(
+ &sec_level,
+ key_generations::RSA_2048_KEY,
+ Some(wrapping_key_alias),
+ )
+ .unwrap();
+
+ // Use invalid value as the additional authenticated data.
+ let aad = b"foo";
+
+ // Build ASN.1 DER-encoded wrapped key material as described in `SecureKeyWrapper` schema.
+ let wrapped_key_data = build_secure_key_wrapper(
+ &sec_level,
+ &secure_key,
+ &transport_key,
+ &nonce,
+ aad,
+ &wrapping_key_metadata,
+ )
+ .unwrap();
+
+ // Unwrap the key. Import wrapped key.
+ let secured_key_alias = format!("ks_wrapped_aes_key_{}", getuid());
+ let result = key_generations::map_ks_error(key_generations::import_wrapped_key(
+ &sec_level,
+ Some(secured_key_alias),
+ &wrapping_key_metadata,
+ Some(wrapped_key_data.to_vec()),
+ ));
+
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::VERIFICATION_FAILED), result.unwrap_err());
+}
+
+/// Import wrapped AES key and use it for crypto operations. Test should import wrapped key and
+/// perform crypto operations successfully.
+#[test]
+fn keystore2_import_wrapped_key_success() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let alias = format!("ks_wrapped_key_test_import_1_{}_256", getuid());
+ let wrapping_key_alias = format!("ks_wrapping_key_test_import_1_{}_2048", getuid());
+
+ let wrapping_key_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::RSA)
+ .digest(Digest::SHA_2_256)
+ .purpose(KeyPurpose::ENCRYPT)
+ .purpose(KeyPurpose::DECRYPT)
+ .purpose(KeyPurpose::WRAP_KEY)
+ .padding_mode(PaddingMode::RSA_OAEP)
+ .key_size(2048)
+ .rsa_public_exponent(65537)
+ .cert_not_before(0)
+ .cert_not_after(253402300799000);
+
+ let key_metadata = key_generations::import_wrapping_key_and_wrapped_key(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias),
+ Some(wrapping_key_alias),
+ wrapping_key_params,
+ )
+ .expect("Failed to import wrapped key.");
+
+ // Try to perform operations using wrapped key.
+ perform_sym_key_encrypt_decrypt_op(&sec_level, &key_metadata);
+}
+
+/// Import wrapping-key without specifying KeyPurpose::WRAP_KEY in import key parameters. Try to
+/// use this as wrapping-key for importing wrapped-key. Test should fail with an error code
+/// `INCOMPATIBLE_PURPOSE` to import wrapped-key using a wrapping-key which doesn't possess
+/// `WRAP_KEY` purpose.
+#[test]
+fn keystore2_import_wrapped_key_fails_with_wrong_purpose() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let wrapping_key_alias = format!("ks_wrapping_key_test_import_2_{}_2048", getuid());
+ let alias = format!("ks_wrapped_key_test_import_2_{}_256", getuid());
+
+ // In this KeyPurpose::WRAP_KEY is missing.
+ let wrapping_key_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::RSA)
+ .digest(Digest::SHA_2_256)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .padding_mode(PaddingMode::RSA_OAEP)
+ .key_size(2048)
+ .rsa_public_exponent(65537)
+ .cert_not_before(0)
+ .cert_not_after(253402300799000);
+
+ let result =
+ key_generations::map_ks_error(key_generations::import_wrapping_key_and_wrapped_key(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias),
+ Some(wrapping_key_alias),
+ wrapping_key_params,
+ ));
+
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_PURPOSE), result.unwrap_err());
+}
+
+/// Try to import wrapped key whose wrapping key is missing in Android Keystore.
+/// Test should fail to import wrapped key with `ResponseCode::KEY_NOT_FOUND`.
+#[test]
+fn keystore2_import_wrapped_key_fails_with_missing_wrapping_key() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let unwrap_params = authorizations::AuthSetBuilder::new()
+ .digest(Digest::SHA_2_256)
+ .padding_mode(PaddingMode::RSA_OAEP);
+
+ let authenticator_spec: &[AuthenticatorSpec] = &[AuthenticatorSpec {
+ authenticatorType: HardwareAuthenticatorType::NONE,
+ authenticatorId: 0,
+ }];
+
+ let alias = format!("ks_wrapped_key_test_import_3_{}_256", getuid());
+
+ // Wrapping key with this alias doesn't exist.
+ let wrapping_key_alias = format!("ks_wrapping_key_not_exist_{}_2048", getuid());
+
+ let result = key_generations::map_ks_error(sec_level.importWrappedKey(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(alias),
+ blob: Some(key_generations::WRAPPED_KEY.to_vec()),
+ },
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(wrapping_key_alias),
+ blob: None,
+ },
+ None,
+ &unwrap_params,
+ authenticator_spec,
+ ));
+
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+}
diff --git a/keystore2/tests/keystore2_client_test_utils.rs b/keystore2/tests/keystore2_client_test_utils.rs
index 758e88b..59819df 100644
--- a/keystore2/tests/keystore2_client_test_utils.rs
+++ b/keystore2/tests/keystore2_client_test_utils.rs
@@ -15,7 +15,11 @@
use nix::unistd::{Gid, Uid};
use serde::{Deserialize, Serialize};
+use openssl::encrypt::Encrypter;
+use openssl::error::ErrorStack;
use openssl::hash::MessageDigest;
+use openssl::pkey::PKey;
+use openssl::pkey::Public;
use openssl::rsa::Padding;
use openssl::sign::Verifier;
use openssl::x509::X509;
@@ -348,3 +352,45 @@
blob: None,
})
}
+
+/// Encrypt the secure key with given transport key.
+pub fn encrypt_secure_key(
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ secure_key: &[u8],
+ aad: &[u8],
+ nonce: Vec<u8>,
+ mac_len: i32,
+ key: &KeyDescriptor,
+) -> binder::Result<Option<Vec<u8>>> {
+ let op_params = authorizations::AuthSetBuilder::new()
+ .purpose(KeyPurpose::ENCRYPT)
+ .padding_mode(PaddingMode::NONE)
+ .block_mode(BlockMode::GCM)
+ .nonce(nonce)
+ .mac_length(mac_len);
+
+ let op_response = sec_level.createOperation(key, &op_params, false)?;
+
+ let op = op_response.iOperation.unwrap();
+ op.updateAad(aad)?;
+ op.finish(Some(secure_key), None)
+}
+
+/// Encrypt the transport key with given RSA wrapping key.
+pub fn encrypt_transport_key(
+ transport_key: &[u8],
+ pkey: &PKey<Public>,
+) -> Result<Vec<u8>, ErrorStack> {
+ let mut encrypter = Encrypter::new(pkey).unwrap();
+ encrypter.set_rsa_padding(Padding::PKCS1_OAEP).unwrap();
+ encrypter.set_rsa_oaep_md(MessageDigest::sha256()).unwrap();
+ encrypter.set_rsa_mgf1_md(MessageDigest::sha1()).unwrap();
+
+ let input = transport_key.to_vec();
+ let buffer_len = encrypter.encrypt_len(&input).unwrap();
+ let mut encoded = vec![0u8; buffer_len];
+ let encoded_len = encrypter.encrypt(&input, &mut encoded).unwrap();
+ let encoded = &encoded[..encoded_len];
+
+ Ok(encoded.to_vec())
+}
diff --git a/keystore2/tests/keystore2_client_tests.rs b/keystore2/tests/keystore2_client_tests.rs
index 41e3e36..d705aa4 100644
--- a/keystore2/tests/keystore2_client_tests.rs
+++ b/keystore2/tests/keystore2_client_tests.rs
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+pub mod ffi_test_utils;
pub mod keystore2_client_3des_key_tests;
pub mod keystore2_client_aes_key_tests;
pub mod keystore2_client_attest_key_tests;