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/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);
+ }
}