Implement a back-level KeyMint compatibility wrapper
- Implement a general back-level KeyMint wrapper, which forwards
requests to either a back-level real device, or an up-level
software device. Keyblobs from the latter are given a marker
prefix and an authentication suffix.
- Add an FFI wrapper function to allow calculation of HMAC-SHA256,
so this can be used to give an authenticated suffix to wrapped
keyblobs.
- Abstract out the decision process for whether emulation is required
to a EmulationDetector trait, and provide implementations for
KeyMint V1 and for a km_compat-wrapped Keymaster.
- Impose the KeyMint V1 wrapper whenever the real device is detected to
be a V1 implementation.
- Add support to the IKeystoreCompatService for returning a device for
SecurityLevel::SOFTWARE. This device will always be the most recent
KeyMint version.
- Clarify what level of IKeyMint implementation gets returned from
the IKeystoreCompatService for the other security levels.
- Add an inner function to the km_compat code to allow unit tests
to still work.
Co-authored-by: Janis Danisevskis <jdanis@google.com>
Bug: 194358913
Test: CtsKeystoreTestCases on oriole/bramble/cuttlefish
Change-Id: I297e8ad1cf00fd15cd5358b2760cd2ca88f53abb
diff --git a/keystore2/src/crypto/Android.bp b/keystore2/src/crypto/Android.bp
index 76c02c5..c3f6f3c 100644
--- a/keystore2/src/crypto/Android.bp
+++ b/keystore2/src/crypto/Android.bp
@@ -62,6 +62,7 @@
shared_libs: ["libcrypto"],
bindgen_flags: [
"--size_t-is-usize",
+ "--allowlist-function", "hmacSha256",
"--allowlist-function", "randomBytes",
"--allowlist-function", "AES_gcm_encrypt",
"--allowlist-function", "AES_gcm_decrypt",
diff --git a/keystore2/src/crypto/crypto.cpp b/keystore2/src/crypto/crypto.cpp
index 5d360a1..34a9a40 100644
--- a/keystore2/src/crypto/crypto.cpp
+++ b/keystore2/src/crypto/crypto.cpp
@@ -25,6 +25,7 @@
#include <openssl/ecdh.h>
#include <openssl/evp.h>
#include <openssl/hkdf.h>
+#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <openssl/x509.h>
@@ -66,6 +67,14 @@
return cipher;
}
+bool hmacSha256(const uint8_t* key, size_t key_size, const uint8_t* msg, size_t msg_size,
+ uint8_t* out, size_t out_size) {
+ const EVP_MD* digest = EVP_sha256();
+ unsigned int actual_out_size = out_size;
+ uint8_t* p = HMAC(digest, key, key_size, msg, msg_size, out, &actual_out_size);
+ return (p != nullptr);
+}
+
bool randomBytes(uint8_t* out, size_t len) {
return RAND_bytes(out, len);
}
diff --git a/keystore2/src/crypto/crypto.hpp b/keystore2/src/crypto/crypto.hpp
index f841eb3..d66532f 100644
--- a/keystore2/src/crypto/crypto.hpp
+++ b/keystore2/src/crypto/crypto.hpp
@@ -22,6 +22,8 @@
#include <stddef.h>
extern "C" {
+ bool hmacSha256(const uint8_t* key, size_t key_size, const uint8_t* msg, size_t msg_size,
+ uint8_t* out, size_t out_size);
bool randomBytes(uint8_t* out, size_t len);
bool AES_gcm_encrypt(const uint8_t* in, uint8_t* out, size_t len,
const uint8_t* key, size_t key_size, const uint8_t* iv, uint8_t* tag);
diff --git a/keystore2/src/crypto/error.rs b/keystore2/src/crypto/error.rs
index c6476f9..48a2d4c 100644
--- a/keystore2/src/crypto/error.rs
+++ b/keystore2/src/crypto/error.rs
@@ -95,6 +95,10 @@
#[error("Failed to extract certificate subject.")]
ExtractSubjectFailed,
+ /// This is returned if the C implementation of hmacSha256 failed.
+ #[error("Failed to calculate HMAC-SHA256.")]
+ HmacSha256Failed,
+
/// Zvec error.
#[error(transparent)]
ZVec(#[from] zvec::Error),
diff --git a/keystore2/src/crypto/lib.rs b/keystore2/src/crypto/lib.rs
index 92da965..14bdf04 100644
--- a/keystore2/src/crypto/lib.rs
+++ b/keystore2/src/crypto/lib.rs
@@ -19,8 +19,8 @@
pub mod zvec;
pub use error::Error;
use keystore2_crypto_bindgen::{
- extractSubjectFromCertificate, generateKeyFromPassword, randomBytes, AES_gcm_decrypt,
- AES_gcm_encrypt, ECDHComputeKey, ECKEYGenerateKey, ECKEYMarshalPrivateKey,
+ extractSubjectFromCertificate, generateKeyFromPassword, hmacSha256, randomBytes,
+ AES_gcm_decrypt, AES_gcm_encrypt, ECDHComputeKey, ECKEYGenerateKey, ECKEYMarshalPrivateKey,
ECKEYParsePrivateKey, ECPOINTOct2Point, ECPOINTPoint2Oct, EC_KEY_free, EC_KEY_get0_public_key,
EC_POINT_free, HKDFExpand, HKDFExtract, EC_KEY, EC_MAX_BYTES, EC_POINT, EVP_MAX_MD_SIZE,
};
@@ -39,6 +39,8 @@
pub const AES_128_KEY_LENGTH: usize = 16;
/// Length of the expected salt for key from password generation.
pub const SALT_LENGTH: usize = 16;
+/// Length of an HMAC-SHA256 tag in bytes.
+pub const HMAC_SHA256_LEN: usize = 32;
/// Older versions of keystore produced IVs with four extra
/// ignored zero bytes at the end; recognise and trim those.
@@ -72,6 +74,21 @@
}
}
+/// Perform HMAC-SHA256.
+pub fn hmac_sha256(key: &[u8], msg: &[u8]) -> Result<Vec<u8>, Error> {
+ let mut tag = vec![0; HMAC_SHA256_LEN];
+ // Safety: The first two pairs of arguments must point to const buffers with
+ // size given by the second arg of the pair. The final pair of arguments
+ // must point to an output buffer with size given by the second arg of the
+ // pair.
+ match unsafe {
+ hmacSha256(key.as_ptr(), key.len(), msg.as_ptr(), msg.len(), tag.as_mut_ptr(), tag.len())
+ } {
+ true => Ok(tag),
+ false => Err(Error::HmacSha256Failed),
+ }
+}
+
/// Uses AES GCM to decipher a message given an initialization vector, aead tag, and key.
/// This function accepts 128 and 256-bit keys and uses AES128 and AES256 respectively based
/// on the key length.
@@ -565,4 +582,18 @@
assert_eq!(left_key, right_key);
Ok(())
}
+
+ #[test]
+ fn test_hmac_sha256() {
+ let key = b"This is the key";
+ let msg1 = b"This is a message";
+ let msg2 = b"This is another message";
+ let tag1a = hmac_sha256(key, msg1).unwrap();
+ assert_eq!(tag1a.len(), HMAC_SHA256_LEN);
+ let tag1b = hmac_sha256(key, msg1).unwrap();
+ assert_eq!(tag1a, tag1b);
+ let tag2 = hmac_sha256(key, msg2).unwrap();
+ assert_eq!(tag2.len(), HMAC_SHA256_LEN);
+ assert_ne!(tag1a, tag2);
+ }
}