HAL service implementation
Bug: 239476214
Bug: 197891150
Test: VtsAidlKeyMintTargetTest (with rest of code)
Change-Id: I87265ddd9e5d4a6d063a4593d5b04aee13156846
diff --git a/hal/Android.bp b/hal/Android.bp
new file mode 100644
index 0000000..264754b
--- /dev/null
+++ b/hal/Android.bp
@@ -0,0 +1,60 @@
+// 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.
+
+package {
+ default_applicable_licenses: ["system_keymint_license"],
+}
+
+rust_defaults {
+ name: "kmr_hal_defaults",
+ edition: "2021",
+ lints: "vendor",
+ defaults: [
+ "keymint_use_latest_hal_aidl_rust",
+ ],
+ rustlibs: [
+ "android.hardware.security.secureclock-V1-rust",
+ "android.hardware.security.sharedsecret-V1-rust",
+ "libbinder_rs",
+ "libciborium",
+ "libciborium_io",
+ "libkmr_common",
+ "liblog_rust",
+ "libregex",
+ "librustutils",
+ ],
+ proc_macros: [
+ "libkmr_derive",
+ ],
+}
+
+rust_library {
+ name: "libkmr_hal",
+ crate_name: "kmr_hal",
+ srcs: ["src/lib.rs"],
+ vendor_available: true,
+ defaults: [
+ "kmr_hal_defaults",
+ ],
+}
+
+rust_test {
+ name: "libkmr_hal_test",
+ crate_name: "libkeymint_rust_test",
+ srcs: ["src/lib.rs"],
+ defaults: [
+ "kmr_hal_defaults",
+ ],
+ test_suites: ["general-tests"],
+}
diff --git a/hal/TEST_MAPPING b/hal/TEST_MAPPING
new file mode 100644
index 0000000..2b34c31
--- /dev/null
+++ b/hal/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libkmr_hal_test"
+ }
+ ]
+}
diff --git a/hal/src/env.rs b/hal/src/env.rs
new file mode 100644
index 0000000..060509c
--- /dev/null
+++ b/hal/src/env.rs
@@ -0,0 +1,188 @@
+//! Retrieve and populate information about userspace.
+
+use kmr_common::wire::SetHalInfoRequest;
+use regex::Regex;
+
+// The OS version property is of form "12" or "12.1" or "12.1.3".
+const OS_VERSION_PROPERTY: &str = "ro.build.version.release";
+const OS_VERSION_REGEX: &str = r"^(?P<major>\d{1,2})(\.(?P<minor>\d{1,2}))?(\.(?P<sub>\d{1,2}))?$";
+
+// The patchlevel properties are of form "YYYY-MM-DD".
+pub const OS_PATCHLEVEL_PROPERTY: &str = "ro.build.version.security_patch";
+const VENDOR_PATCHLEVEL_PROPERTY: &str = "ro.vendor.build.security_patch";
+const PATCHLEVEL_REGEX: &str = r"^(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})$";
+
+// Just use [`String`] for errors here.
+type Error = String;
+
+/// Retrieve a numeric value from a possible match.
+fn extract_u32(value: Option<regex::Match>) -> Result<u32, Error> {
+ match value {
+ Some(m) => {
+ let s = m.as_str();
+ match s.parse::<u32>() {
+ Ok(v) => Ok(v),
+ Err(e) => Err(format!("failed to parse integer: {:?}", e)),
+ }
+ }
+ None => Err("failed to find match".to_string()),
+ }
+}
+
+pub fn get_property(name: &str) -> Result<String, Error> {
+ match rustutils::system_properties::read(name) {
+ Ok(Some(value)) => Ok(value),
+ Ok(None) => Err(format!("no value for property {}", name)),
+ Err(e) => Err(format!("failed to get property {}: {:?}", name, e)),
+ }
+}
+
+/// Extract a patchlevel in form YYYYMM from a "YYYY-MM-DD" property value.
+pub fn extract_truncated_patchlevel(prop_value: &str) -> Result<u32, Error> {
+ let patchlevel_regex = Regex::new(PATCHLEVEL_REGEX)
+ .map_err(|e| format!("failed to compile patchlevel regexp: {:?}", e))?;
+
+ let captures = patchlevel_regex
+ .captures(prop_value)
+ .ok_or_else(|| "failed to match patchlevel regex".to_string())?;
+ let year = extract_u32(captures.name("year"))?;
+ let month = extract_u32(captures.name("month"))?;
+ if !(1..=12).contains(&month) {
+ return Err(format!("month out of range: {}", month));
+ }
+ // no day
+ Ok(year * 100 + month)
+}
+
+/// Extract a patchlevel in form YYYYMMDD from a "YYYY-MM-DD" property value.
+pub fn extract_patchlevel(prop_value: &str) -> Result<u32, Error> {
+ let patchlevel_regex = Regex::new(PATCHLEVEL_REGEX)
+ .map_err(|e| format!("failed to compile patchlevel regexp: {:?}", e))?;
+
+ let captures = patchlevel_regex
+ .captures(prop_value)
+ .ok_or_else(|| "failed to match patchlevel regex".to_string())?;
+ let year = extract_u32(captures.name("year"))?;
+ let month = extract_u32(captures.name("month"))?;
+ if !(1..=12).contains(&month) {
+ return Err(format!("month out of range: {}", month));
+ }
+ let day = extract_u32(captures.name("day"))?;
+ if !(1..=31).contains(&day) {
+ return Err(format!("day out of range: {}", day));
+ }
+ Ok(year * 10000 + month * 100 + day)
+}
+
+/// Generate HAL information from property values.
+fn populate_hal_info_from(
+ os_version_prop: &str,
+ os_patchlevel_prop: &str,
+ vendor_patchlevel_prop: &str,
+) -> Result<SetHalInfoRequest, Error> {
+ let os_version_regex = Regex::new(OS_VERSION_REGEX)
+ .map_err(|e| format!("failed to compile version regexp: {:?}", e))?;
+ let captures = os_version_regex
+ .captures(os_version_prop)
+ .ok_or_else(|| "failed to match OS version regex".to_string())?;
+ let major = extract_u32(captures.name("major"))?;
+ let minor = extract_u32(captures.name("minor")).unwrap_or(0u32);
+ let sub = extract_u32(captures.name("sub")).unwrap_or(0u32);
+ let os_version = (major * 10000) + (minor * 100) + sub;
+
+ Ok(SetHalInfoRequest {
+ os_version,
+ os_patchlevel: extract_truncated_patchlevel(os_patchlevel_prop)?,
+ vendor_patchlevel: extract_patchlevel(vendor_patchlevel_prop)?,
+ })
+}
+
+/// Populate a [`SetHalInfoRequest`] based on property values read from the environment.
+pub fn populate_hal_info() -> Result<SetHalInfoRequest, Error> {
+ let os_version_prop = get_property(OS_VERSION_PROPERTY)
+ .map_err(|e| format!("failed to retrieve property: {:?}", e))?;
+ let os_patchlevel_prop = get_property(OS_PATCHLEVEL_PROPERTY)
+ .map_err(|e| format!("failed to retrieve property: {:?}", e))?;
+ let vendor_patchlevel_prop = get_property(VENDOR_PATCHLEVEL_PROPERTY)
+ .map_err(|e| format!("failed to retrieve property: {:?}", e))?;
+
+ populate_hal_info_from(&os_version_prop, &os_patchlevel_prop, &vendor_patchlevel_prop)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use kmr_common::wire::SetHalInfoRequest;
+ #[test]
+ fn test_hal_info() {
+ let tests = vec![
+ (
+ "12",
+ "2021-02-02",
+ "2022-03-04",
+ SetHalInfoRequest {
+ os_version: 120000,
+ os_patchlevel: 202102,
+ vendor_patchlevel: 20220304,
+ },
+ ),
+ (
+ "12.5",
+ "2021-02-02",
+ "2022-03-04",
+ SetHalInfoRequest {
+ os_version: 120500,
+ os_patchlevel: 202102,
+ vendor_patchlevel: 20220304,
+ },
+ ),
+ (
+ "12.5.7",
+ "2021-02-02",
+ "2022-03-04",
+ SetHalInfoRequest {
+ os_version: 120507,
+ os_patchlevel: 202102,
+ vendor_patchlevel: 20220304,
+ },
+ ),
+ ];
+ for (os_version, os_patch, vendor_patch, want) in tests {
+ let got = populate_hal_info_from(os_version, os_patch, vendor_patch).unwrap();
+ assert_eq!(
+ got, want,
+ "Mismatch for input ({}, {}, {})",
+ os_version, os_patch, vendor_patch
+ );
+ }
+ }
+
+ #[test]
+ fn test_invalid_hal_info() {
+ let tests = vec![
+ ("xx", "2021-02-02", "2022-03-04", "failed to match OS version"),
+ ("12.xx", "2021-02-02", "2022-03-04", "failed to match OS version"),
+ ("12.5.xx", "2021-02-02", "2022-03-04", "failed to match OS version"),
+ ("12", "20212-02-02", "2022-03-04", "failed to match patchlevel regex"),
+ ("12", "2021-xx-02", "2022-03-04", "failed to match patchlevel"),
+ ("12", "2021-13-02", "2022-03-04", "month out of range"),
+ ("12", "2022-03-04", "2021-xx-02", "failed to match patchlevel"),
+ ("12", "2022-03-04", "2021-13-02", "month out of range"),
+ ("12", "2022-03-04", "2021-03-32", "day out of range"),
+ ];
+ for (os_version, os_patch, vendor_patch, want_err) in tests {
+ let result = populate_hal_info_from(os_version, os_patch, vendor_patch);
+ assert!(result.is_err());
+ let err = result.unwrap_err();
+ assert!(
+ err.contains(want_err),
+ "Mismatch for input ({}, {}, {}), got error '{}', want '{}'",
+ os_version,
+ os_patch,
+ vendor_patch,
+ err,
+ want_err
+ );
+ }
+ }
+}
diff --git a/hal/src/hal.rs b/hal/src/hal.rs
new file mode 100644
index 0000000..b419bc6
--- /dev/null
+++ b/hal/src/hal.rs
@@ -0,0 +1,710 @@
+//! Code for dealing with HAL-defined types, especially conversions to/from internal types.
+//!
+//! The internal code for KeyMint uses its own type definitions, not the HAL-defined autogenerated
+//! types, for two reasons:
+//!
+//! - The auto-generated types impose a dependency on Binder which is not appropriate for
+//! code being built for a secure environment.
+//! - The auto-generated types are not idiomatic Rust, and have reduced type safety.
+//!
+//! This module includes code to convert between HAL types (re-used under `kmr_hal::hal`) and
+//! internal types (under `kmr_common::wire`), via the [`Fromm`] / [`TryFromm`], [`Innto`] and
+//! [`TryInnto`] traits (which are deliberately misspelled to avoid a clash with standard
+//! traits -- see below).
+//!
+//! - Going from wire=>HAL is an infallible conversion, as the wire types are stricter.
+//! - Going from HAL=>wire is often a fallible conversion, as there may be "enum" values
+//! that are not in range.
+//!
+//! This module (and `kmr_common::wire`) must be kept in sync with the Android KeyMint HAL
+//! definition.
+
+#![allow(non_snake_case)]
+
+use crate::binder;
+use keymint::{KeyParameterValue::KeyParameterValue, Tag::Tag, TagType::TagType};
+use kmr_common::{
+ crypto::rsa, crypto::KeySizeInBits, wire, wire::keymint::DateTime, wire::keymint::KeyParam,
+};
+use log::{error, warn};
+use std::convert::TryFrom;
+use std::ffi::CString;
+
+pub use android_hardware_security_keymint::aidl::android::hardware::security::keymint;
+pub use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock;
+pub use android_hardware_security_sharedsecret::aidl::android::hardware::security::sharedsecret;
+
+#[cfg(test)]
+mod tests;
+
+/// Emit a failure for a failed type conversion.
+#[inline]
+pub fn failed_conversion(_err: wire::ValueNotRecognized) -> binder::Status {
+ binder::Status::new_service_specific_error(
+ keymint::ErrorCode::ErrorCode::INVALID_ARGUMENT.0,
+ Some(&CString::new("conversion from HAL type to internal type failed").unwrap()),
+ )
+}
+
+/// Determine the tag type for a tag, based on the top 4 bits of the tag number.
+pub fn tag_type(tag: Tag) -> TagType {
+ match ((tag.0 as u32) & 0xf0000000u32) as i32 {
+ x if x == TagType::ENUM.0 => TagType::ENUM,
+ x if x == TagType::ENUM_REP.0 => TagType::ENUM_REP,
+ x if x == TagType::UINT.0 => TagType::UINT,
+ x if x == TagType::UINT_REP.0 => TagType::UINT_REP,
+ x if x == TagType::ULONG.0 => TagType::ULONG,
+ x if x == TagType::DATE.0 => TagType::DATE,
+ x if x == TagType::BOOL.0 => TagType::BOOL,
+ x if x == TagType::BIGNUM.0 => TagType::BIGNUM,
+ x if x == TagType::BYTES.0 => TagType::BYTES,
+ x if x == TagType::ULONG_REP.0 => TagType::ULONG_REP,
+ _ => TagType::INVALID,
+ }
+}
+
+// Neither the `kmr_common::wire` types nor the `hal` types are local to this crate, which means
+// that Rust's orphan rule means we cannot implement the standard conversion traits. So instead
+// define our own equivalent conversion traits that are local, and for which we're allowed to
+// provide implementations. Give them an odd name to avoid confusion with the standard traits.
+
+/// Local equivalent of `From` trait, with a different name to avoid clashes.
+pub trait Fromm<T>: Sized {
+ fn fromm(val: T) -> Self;
+}
+/// Local equivalent of `TryFrom` trait, with a different name to avoid clashes.
+pub trait TryFromm<T>: Sized {
+ type Error;
+ fn try_fromm(val: T) -> Result<Self, Self::Error>;
+}
+/// Local equivalent of `Into` trait, with a different name to avoid clashes.
+pub trait Innto<T> {
+ fn innto(self) -> T;
+}
+/// Local equivalent of `TryInto` trait, with a different name to avoid clashes.
+pub trait TryInnto<T> {
+ type Error;
+ fn try_innto(self) -> Result<T, Self::Error>;
+}
+/// Blanket implementation of `Innto` from `Fromm`
+impl<T, U> Innto<U> for T
+where
+ U: Fromm<T>,
+{
+ fn innto(self) -> U {
+ U::fromm(self)
+ }
+}
+/// Blanket implementation of `TryInnto` from `TryFromm`
+impl<T, U> TryInnto<U> for T
+where
+ U: TryFromm<T>,
+{
+ type Error = U::Error;
+ fn try_innto(self) -> Result<U, Self::Error> {
+ U::try_fromm(self)
+ }
+}
+/// Blanket implementation of `Fromm<Vec<T>>` from `Fromm<T>`
+impl<T, U> Fromm<Vec<T>> for Vec<U>
+where
+ U: Fromm<T>,
+{
+ fn fromm(val: Vec<T>) -> Vec<U> {
+ val.into_iter().map(|t| <U>::fromm(t)).collect()
+ }
+}
+
+// Conversions from `kmr_common::wire` types into the equivalent types in the auto-generated HAL
+// code. These conversions are infallible, because the range of the `wire` types is strictly
+// contained within the HAL types.
+
+impl Fromm<wire::sharedsecret::SharedSecretParameters>
+ for sharedsecret::SharedSecretParameters::SharedSecretParameters
+{
+ fn fromm(val: wire::sharedsecret::SharedSecretParameters) -> Self {
+ Self { seed: val.seed, nonce: val.nonce }
+ }
+}
+impl Fromm<wire::secureclock::Timestamp> for secureclock::Timestamp::Timestamp {
+ fn fromm(val: wire::secureclock::Timestamp) -> Self {
+ Self { milliSeconds: val.milliseconds }
+ }
+}
+impl Fromm<wire::secureclock::TimeStampToken> for secureclock::TimeStampToken::TimeStampToken {
+ fn fromm(val: wire::secureclock::TimeStampToken) -> Self {
+ Self { challenge: val.challenge, timestamp: val.timestamp.innto(), mac: val.mac }
+ }
+}
+impl Fromm<wire::keymint::Certificate> for keymint::Certificate::Certificate {
+ fn fromm(val: wire::keymint::Certificate) -> Self {
+ Self { encodedCertificate: val.encoded_certificate }
+ }
+}
+impl Fromm<wire::keymint::DeviceInfo> for keymint::DeviceInfo::DeviceInfo {
+ fn fromm(val: wire::keymint::DeviceInfo) -> Self {
+ Self { deviceInfo: val.device_info }
+ }
+}
+impl Fromm<wire::keymint::HardwareAuthToken> for keymint::HardwareAuthToken::HardwareAuthToken {
+ fn fromm(val: wire::keymint::HardwareAuthToken) -> Self {
+ Self {
+ challenge: val.challenge,
+ userId: val.user_id,
+ authenticatorId: val.authenticator_id,
+ authenticatorType: val.authenticator_type.innto(),
+ timestamp: val.timestamp.innto(),
+ mac: val.mac,
+ }
+ }
+}
+impl Fromm<wire::keymint::KeyCharacteristics> for keymint::KeyCharacteristics::KeyCharacteristics {
+ fn fromm(val: wire::keymint::KeyCharacteristics) -> Self {
+ Self {
+ securityLevel: val.security_level.innto(),
+ authorizations: val.authorizations.innto(),
+ }
+ }
+}
+impl Fromm<wire::keymint::KeyCreationResult> for keymint::KeyCreationResult::KeyCreationResult {
+ fn fromm(val: wire::keymint::KeyCreationResult) -> Self {
+ Self {
+ keyBlob: val.key_blob,
+ keyCharacteristics: val.key_characteristics.innto(),
+ certificateChain: val.certificate_chain.innto(),
+ }
+ }
+}
+impl Fromm<wire::keymint::KeyMintHardwareInfo>
+ for keymint::KeyMintHardwareInfo::KeyMintHardwareInfo
+{
+ fn fromm(val: wire::keymint::KeyMintHardwareInfo) -> Self {
+ Self {
+ versionNumber: val.version_number,
+ securityLevel: val.security_level.innto(),
+ keyMintName: val.key_mint_name,
+ keyMintAuthorName: val.key_mint_author_name,
+ timestampTokenRequired: val.timestamp_token_required,
+ }
+ }
+}
+impl Fromm<wire::keymint::MacedPublicKey> for keymint::MacedPublicKey::MacedPublicKey {
+ fn fromm(val: wire::keymint::MacedPublicKey) -> Self {
+ Self { macedKey: val.maced_key }
+ }
+}
+impl Fromm<wire::keymint::ProtectedData> for keymint::ProtectedData::ProtectedData {
+ fn fromm(val: wire::keymint::ProtectedData) -> Self {
+ Self { protectedData: val.protected_data }
+ }
+}
+impl Fromm<wire::keymint::RpcHardwareInfo> for keymint::RpcHardwareInfo::RpcHardwareInfo {
+ fn fromm(val: wire::keymint::RpcHardwareInfo) -> Self {
+ Self {
+ versionNumber: val.version_number,
+ rpcAuthorName: val.rpc_author_name,
+ supportedEekCurve: val.supported_eek_curve as i32,
+ uniqueId: val.unique_id,
+ }
+ }
+}
+
+impl Fromm<wire::keymint::KeyParam> for keymint::KeyParameter::KeyParameter {
+ fn fromm(val: wire::keymint::KeyParam) -> Self {
+ let (tag, value) = match val {
+ // Enum-holding variants.
+ KeyParam::Purpose(v) => (Tag::PURPOSE, KeyParameterValue::KeyPurpose(v.innto())),
+ KeyParam::Algorithm(v) => (Tag::ALGORITHM, KeyParameterValue::Algorithm(v.innto())),
+ KeyParam::BlockMode(v) => (Tag::BLOCK_MODE, KeyParameterValue::BlockMode(v.innto())),
+ KeyParam::Digest(v) => (Tag::DIGEST, KeyParameterValue::Digest(v.innto())),
+ KeyParam::Padding(v) => (Tag::PADDING, KeyParameterValue::PaddingMode(v.innto())),
+ KeyParam::EcCurve(v) => (Tag::EC_CURVE, KeyParameterValue::EcCurve(v.innto())),
+ KeyParam::RsaOaepMgfDigest(v) => {
+ (Tag::RSA_OAEP_MGF_DIGEST, KeyParameterValue::Digest(v.innto()))
+ }
+ KeyParam::Origin(v) => (Tag::ORIGIN, KeyParameterValue::Origin(v.innto())),
+
+ // `u32`-holding variants.
+ KeyParam::KeySize(v) => (Tag::KEY_SIZE, KeyParameterValue::Integer(v.0 as i32)),
+ KeyParam::MinMacLength(v) => {
+ (Tag::MIN_MAC_LENGTH, KeyParameterValue::Integer(v as i32))
+ }
+ KeyParam::MaxUsesPerBoot(v) => {
+ (Tag::MAX_USES_PER_BOOT, KeyParameterValue::Integer(v as i32))
+ }
+ KeyParam::UsageCountLimit(v) => {
+ (Tag::USAGE_COUNT_LIMIT, KeyParameterValue::Integer(v as i32))
+ }
+ KeyParam::UserId(v) => (Tag::USER_ID, KeyParameterValue::Integer(v as i32)),
+ KeyParam::UserAuthType(v) => {
+ (Tag::USER_AUTH_TYPE, KeyParameterValue::Integer(v as i32))
+ }
+ KeyParam::AuthTimeout(v) => (Tag::AUTH_TIMEOUT, KeyParameterValue::Integer(v as i32)),
+ KeyParam::OsVersion(v) => (Tag::OS_VERSION, KeyParameterValue::Integer(v as i32)),
+ KeyParam::OsPatchlevel(v) => (Tag::OS_PATCHLEVEL, KeyParameterValue::Integer(v as i32)),
+ KeyParam::VendorPatchlevel(v) => {
+ (Tag::VENDOR_PATCHLEVEL, KeyParameterValue::Integer(v as i32))
+ }
+ KeyParam::BootPatchlevel(v) => {
+ (Tag::BOOT_PATCHLEVEL, KeyParameterValue::Integer(v as i32))
+ }
+ KeyParam::MacLength(v) => (Tag::MAC_LENGTH, KeyParameterValue::Integer(v as i32)),
+ KeyParam::MaxBootLevel(v) => {
+ (Tag::MAX_BOOT_LEVEL, KeyParameterValue::Integer(v as i32))
+ }
+
+ // `u64`-holding variants.
+ KeyParam::RsaPublicExponent(v) => {
+ (Tag::RSA_PUBLIC_EXPONENT, KeyParameterValue::LongInteger(v.0 as i64))
+ }
+ KeyParam::UserSecureId(v) => {
+ (Tag::USER_SECURE_ID, KeyParameterValue::LongInteger(v as i64))
+ }
+
+ // `true`-holding variants.
+ KeyParam::CallerNonce => (Tag::CALLER_NONCE, KeyParameterValue::BoolValue(true)),
+ KeyParam::IncludeUniqueId => {
+ (Tag::INCLUDE_UNIQUE_ID, KeyParameterValue::BoolValue(true))
+ }
+ KeyParam::BootloaderOnly => (Tag::BOOTLOADER_ONLY, KeyParameterValue::BoolValue(true)),
+ KeyParam::RollbackResistance => {
+ (Tag::ROLLBACK_RESISTANCE, KeyParameterValue::BoolValue(true))
+ }
+ KeyParam::EarlyBootOnly => (Tag::EARLY_BOOT_ONLY, KeyParameterValue::BoolValue(true)),
+ KeyParam::AllowWhileOnBody => {
+ (Tag::ALLOW_WHILE_ON_BODY, KeyParameterValue::BoolValue(true))
+ }
+ KeyParam::NoAuthRequired => (Tag::NO_AUTH_REQUIRED, KeyParameterValue::BoolValue(true)),
+ KeyParam::TrustedUserPresenceRequired => {
+ (Tag::TRUSTED_USER_PRESENCE_REQUIRED, KeyParameterValue::BoolValue(true))
+ }
+ KeyParam::TrustedConfirmationRequired => {
+ (Tag::TRUSTED_CONFIRMATION_REQUIRED, KeyParameterValue::BoolValue(true))
+ }
+ KeyParam::UnlockedDeviceRequired => {
+ (Tag::UNLOCKED_DEVICE_REQUIRED, KeyParameterValue::BoolValue(true))
+ }
+ KeyParam::DeviceUniqueAttestation => {
+ (Tag::DEVICE_UNIQUE_ATTESTATION, KeyParameterValue::BoolValue(true))
+ }
+ KeyParam::StorageKey => (Tag::STORAGE_KEY, KeyParameterValue::BoolValue(true)),
+ KeyParam::ResetSinceIdRotation => {
+ (Tag::RESET_SINCE_ID_ROTATION, KeyParameterValue::BoolValue(true))
+ }
+
+ // `DateTime`-holding variants.
+ KeyParam::ActiveDatetime(v) => {
+ (Tag::ACTIVE_DATETIME, KeyParameterValue::DateTime(v.ms_since_epoch))
+ }
+ KeyParam::OriginationExpireDatetime(v) => {
+ (Tag::ORIGINATION_EXPIRE_DATETIME, KeyParameterValue::DateTime(v.ms_since_epoch))
+ }
+ KeyParam::UsageExpireDatetime(v) => {
+ (Tag::USAGE_EXPIRE_DATETIME, KeyParameterValue::DateTime(v.ms_since_epoch))
+ }
+ KeyParam::CreationDatetime(v) => {
+ (Tag::CREATION_DATETIME, KeyParameterValue::DateTime(v.ms_since_epoch))
+ }
+ KeyParam::CertificateNotBefore(v) => {
+ (Tag::CERTIFICATE_NOT_BEFORE, KeyParameterValue::DateTime(v.ms_since_epoch))
+ }
+ KeyParam::CertificateNotAfter(v) => {
+ (Tag::CERTIFICATE_NOT_AFTER, KeyParameterValue::DateTime(v.ms_since_epoch))
+ }
+
+ // `Vec<u8>`-holding variants.
+ KeyParam::ApplicationId(v) => (Tag::APPLICATION_ID, KeyParameterValue::Blob(v)),
+ KeyParam::ApplicationData(v) => (Tag::APPLICATION_DATA, KeyParameterValue::Blob(v)),
+ KeyParam::AttestationChallenge(v) => {
+ (Tag::ATTESTATION_CHALLENGE, KeyParameterValue::Blob(v))
+ }
+ KeyParam::AttestationApplicationId(v) => {
+ (Tag::ATTESTATION_APPLICATION_ID, KeyParameterValue::Blob(v))
+ }
+ KeyParam::AttestationIdBrand(v) => {
+ (Tag::ATTESTATION_ID_BRAND, KeyParameterValue::Blob(v))
+ }
+ KeyParam::AttestationIdDevice(v) => {
+ (Tag::ATTESTATION_ID_DEVICE, KeyParameterValue::Blob(v))
+ }
+ KeyParam::AttestationIdProduct(v) => {
+ (Tag::ATTESTATION_ID_PRODUCT, KeyParameterValue::Blob(v))
+ }
+ KeyParam::AttestationIdSerial(v) => {
+ (Tag::ATTESTATION_ID_SERIAL, KeyParameterValue::Blob(v))
+ }
+ KeyParam::AttestationIdImei(v) => {
+ (Tag::ATTESTATION_ID_IMEI, KeyParameterValue::Blob(v))
+ }
+ KeyParam::AttestationIdMeid(v) => {
+ (Tag::ATTESTATION_ID_MEID, KeyParameterValue::Blob(v))
+ }
+ KeyParam::AttestationIdManufacturer(v) => {
+ (Tag::ATTESTATION_ID_MANUFACTURER, KeyParameterValue::Blob(v))
+ }
+ KeyParam::AttestationIdModel(v) => {
+ (Tag::ATTESTATION_ID_MODEL, KeyParameterValue::Blob(v))
+ }
+ KeyParam::Nonce(v) => (Tag::NONCE, KeyParameterValue::Blob(v)),
+ KeyParam::RootOfTrust(v) => (Tag::ROOT_OF_TRUST, KeyParameterValue::Blob(v)),
+ KeyParam::CertificateSerial(v) => (Tag::CERTIFICATE_SERIAL, KeyParameterValue::Blob(v)),
+ KeyParam::CertificateSubject(v) => {
+ (Tag::CERTIFICATE_SUBJECT, KeyParameterValue::Blob(v))
+ }
+ };
+ Self { tag, value }
+ }
+}
+
+// Conversions from auto-generated HAL types into the equivalent types from `kmr_common::wire`.
+// These conversions are generally fallible, because the "enum" types generated for the HAL are
+// actually `i32` values, which may contain invalid values.
+
+impl Fromm<secureclock::TimeStampToken::TimeStampToken> for wire::secureclock::TimeStampToken {
+ fn fromm(val: secureclock::TimeStampToken::TimeStampToken) -> Self {
+ Self { challenge: val.challenge, timestamp: val.timestamp.innto(), mac: val.mac }
+ }
+}
+impl Fromm<secureclock::Timestamp::Timestamp> for wire::secureclock::Timestamp {
+ fn fromm(val: secureclock::Timestamp::Timestamp) -> Self {
+ Self { milliseconds: val.milliSeconds }
+ }
+}
+impl Fromm<sharedsecret::SharedSecretParameters::SharedSecretParameters>
+ for wire::sharedsecret::SharedSecretParameters
+{
+ fn fromm(val: sharedsecret::SharedSecretParameters::SharedSecretParameters) -> Self {
+ Self { seed: val.seed, nonce: val.nonce }
+ }
+}
+impl TryFromm<keymint::AttestationKey::AttestationKey> for wire::keymint::AttestationKey {
+ type Error = wire::ValueNotRecognized;
+ fn try_fromm(val: keymint::AttestationKey::AttestationKey) -> Result<Self, Self::Error> {
+ Ok(Self {
+ key_blob: val.keyBlob,
+ attest_key_params: val
+ .attestKeyParams // Vec<KeyParameter>
+ .into_iter() // Iter<KeyParameter>
+ .filter_map(|p| (&p).try_innto().transpose())
+ .collect::<Result<Vec<KeyParam>, _>>()?,
+ issuer_subject_name: val.issuerSubjectName,
+ })
+ }
+}
+impl TryFromm<keymint::HardwareAuthToken::HardwareAuthToken> for wire::keymint::HardwareAuthToken {
+ type Error = wire::ValueNotRecognized;
+ fn try_fromm(val: keymint::HardwareAuthToken::HardwareAuthToken) -> Result<Self, Self::Error> {
+ Ok(Self {
+ challenge: val.challenge,
+ user_id: val.userId,
+ authenticator_id: val.authenticatorId,
+ authenticator_type: val.authenticatorType.try_innto()?,
+ timestamp: val.timestamp.innto(),
+ mac: val.mac,
+ })
+ }
+}
+impl Fromm<keymint::MacedPublicKey::MacedPublicKey> for wire::keymint::MacedPublicKey {
+ fn fromm(val: keymint::MacedPublicKey::MacedPublicKey) -> Self {
+ Self { maced_key: val.macedKey }
+ }
+}
+impl Fromm<&keymint::MacedPublicKey::MacedPublicKey> for wire::keymint::MacedPublicKey {
+ fn fromm(val: &keymint::MacedPublicKey::MacedPublicKey) -> Self {
+ Self { maced_key: val.macedKey.to_vec() }
+ }
+}
+
+macro_rules! value_of {
+ {
+ $val:expr, $variant:ident
+ } => {
+ if let keymint::KeyParameterValue::KeyParameterValue::$variant(v) = $val.value {
+ Ok(v)
+ } else {
+ Err(wire::ValueNotRecognized)
+ }
+ }
+}
+
+macro_rules! check_bool {
+ {
+ $val:expr
+ } => {
+ if let keymint::KeyParameterValue::KeyParameterValue::BoolValue(true) = $val.value {
+ Ok(())
+ } else {
+ Err(wire::ValueNotRecognized)
+ }
+ }
+}
+
+macro_rules! clone_blob {
+ {
+ $val:expr
+ } => {
+ if let keymint::KeyParameterValue::KeyParameterValue::Blob(b) = &$val.value {
+ Ok(b.clone())
+ } else {
+ Err(wire::ValueNotRecognized)
+ }
+ }
+}
+
+/// Converting a HAL `KeyParameter` to a wire `KeyParam` may fail (producing an `Err`) but may also
+/// silently drop unknown tags (producing `Ok(None)`)
+impl TryFromm<&keymint::KeyParameter::KeyParameter> for Option<KeyParam> {
+ type Error = wire::ValueNotRecognized;
+ fn try_fromm(val: &keymint::KeyParameter::KeyParameter) -> Result<Self, Self::Error> {
+ Ok(match val.tag {
+ // Enum-holding variants.
+ keymint::Tag::Tag::PURPOSE => {
+ Some(KeyParam::Purpose(value_of!(val, KeyPurpose)?.try_innto()?))
+ }
+ keymint::Tag::Tag::ALGORITHM => {
+ Some(KeyParam::Algorithm(value_of!(val, Algorithm)?.try_innto()?))
+ }
+ keymint::Tag::Tag::BLOCK_MODE => {
+ Some(KeyParam::BlockMode(value_of!(val, BlockMode)?.try_innto()?))
+ }
+ keymint::Tag::Tag::DIGEST => {
+ Some(KeyParam::Digest(value_of!(val, Digest)?.try_innto()?))
+ }
+ keymint::Tag::Tag::PADDING => {
+ Some(KeyParam::Padding(value_of!(val, PaddingMode)?.try_innto()?))
+ }
+ keymint::Tag::Tag::EC_CURVE => {
+ Some(KeyParam::EcCurve(value_of!(val, EcCurve)?.try_innto()?))
+ }
+ keymint::Tag::Tag::RSA_OAEP_MGF_DIGEST => {
+ Some(KeyParam::RsaOaepMgfDigest(value_of!(val, Digest)?.try_innto()?))
+ }
+ keymint::Tag::Tag::ORIGIN => {
+ Some(KeyParam::Origin(value_of!(val, Origin)?.try_innto()?))
+ }
+
+ // `u32`-holding variants.
+ keymint::Tag::Tag::KEY_SIZE => {
+ Some(KeyParam::KeySize(KeySizeInBits(value_of!(val, Integer)? as u32)))
+ }
+ keymint::Tag::Tag::MIN_MAC_LENGTH => {
+ Some(KeyParam::MinMacLength(value_of!(val, Integer)? as u32))
+ }
+ keymint::Tag::Tag::MAX_USES_PER_BOOT => {
+ Some(KeyParam::MaxUsesPerBoot(value_of!(val, Integer)? as u32))
+ }
+ keymint::Tag::Tag::USAGE_COUNT_LIMIT => {
+ Some(KeyParam::UsageCountLimit(value_of!(val, Integer)? as u32))
+ }
+ keymint::Tag::Tag::USER_ID => Some(KeyParam::UserId(value_of!(val, Integer)? as u32)),
+ keymint::Tag::Tag::USER_AUTH_TYPE => {
+ Some(KeyParam::UserAuthType(value_of!(val, Integer)? as u32))
+ }
+ keymint::Tag::Tag::AUTH_TIMEOUT => {
+ Some(KeyParam::AuthTimeout(value_of!(val, Integer)? as u32))
+ }
+ keymint::Tag::Tag::OS_VERSION => {
+ Some(KeyParam::OsVersion(value_of!(val, Integer)? as u32))
+ }
+ keymint::Tag::Tag::OS_PATCHLEVEL => {
+ Some(KeyParam::OsPatchlevel(value_of!(val, Integer)? as u32))
+ }
+ keymint::Tag::Tag::VENDOR_PATCHLEVEL => {
+ Some(KeyParam::VendorPatchlevel(value_of!(val, Integer)? as u32))
+ }
+ keymint::Tag::Tag::BOOT_PATCHLEVEL => {
+ Some(KeyParam::BootPatchlevel(value_of!(val, Integer)? as u32))
+ }
+ keymint::Tag::Tag::MAC_LENGTH => {
+ Some(KeyParam::MacLength(value_of!(val, Integer)? as u32))
+ }
+ keymint::Tag::Tag::MAX_BOOT_LEVEL => {
+ Some(KeyParam::MaxBootLevel(value_of!(val, Integer)? as u32))
+ }
+
+ // `u64`-holding variants.
+ keymint::Tag::Tag::RSA_PUBLIC_EXPONENT => Some(KeyParam::RsaPublicExponent(
+ rsa::Exponent(value_of!(val, LongInteger)? as u64),
+ )),
+ keymint::Tag::Tag::USER_SECURE_ID => {
+ Some(KeyParam::UserSecureId(value_of!(val, LongInteger)? as u64))
+ }
+
+ // `bool`-holding variants; only `true` is allowed.
+ keymint::Tag::Tag::CALLER_NONCE => {
+ check_bool!(val)?;
+ Some(KeyParam::CallerNonce)
+ }
+ keymint::Tag::Tag::INCLUDE_UNIQUE_ID => {
+ check_bool!(val)?;
+ Some(KeyParam::IncludeUniqueId)
+ }
+ keymint::Tag::Tag::BOOTLOADER_ONLY => {
+ check_bool!(val)?;
+ Some(KeyParam::BootloaderOnly)
+ }
+ keymint::Tag::Tag::ROLLBACK_RESISTANCE => {
+ check_bool!(val)?;
+ Some(KeyParam::RollbackResistance)
+ }
+ keymint::Tag::Tag::EARLY_BOOT_ONLY => {
+ check_bool!(val)?;
+ Some(KeyParam::EarlyBootOnly)
+ }
+ keymint::Tag::Tag::NO_AUTH_REQUIRED => {
+ check_bool!(val)?;
+ Some(KeyParam::NoAuthRequired)
+ }
+ keymint::Tag::Tag::ALLOW_WHILE_ON_BODY => {
+ check_bool!(val)?;
+ Some(KeyParam::AllowWhileOnBody)
+ }
+ keymint::Tag::Tag::TRUSTED_USER_PRESENCE_REQUIRED => {
+ check_bool!(val)?;
+ Some(KeyParam::TrustedUserPresenceRequired)
+ }
+ keymint::Tag::Tag::TRUSTED_CONFIRMATION_REQUIRED => {
+ check_bool!(val)?;
+ Some(KeyParam::TrustedConfirmationRequired)
+ }
+ keymint::Tag::Tag::UNLOCKED_DEVICE_REQUIRED => {
+ check_bool!(val)?;
+ Some(KeyParam::UnlockedDeviceRequired)
+ }
+ keymint::Tag::Tag::DEVICE_UNIQUE_ATTESTATION => {
+ check_bool!(val)?;
+ Some(KeyParam::DeviceUniqueAttestation)
+ }
+ keymint::Tag::Tag::STORAGE_KEY => {
+ check_bool!(val)?;
+ Some(KeyParam::StorageKey)
+ }
+ keymint::Tag::Tag::RESET_SINCE_ID_ROTATION => {
+ check_bool!(val)?;
+ Some(KeyParam::ResetSinceIdRotation)
+ }
+
+ // `DateTime`-holding variants.
+ keymint::Tag::Tag::ACTIVE_DATETIME => Some(KeyParam::ActiveDatetime(DateTime {
+ ms_since_epoch: value_of!(val, DateTime)?,
+ })),
+ keymint::Tag::Tag::ORIGINATION_EXPIRE_DATETIME => {
+ Some(KeyParam::OriginationExpireDatetime(DateTime {
+ ms_since_epoch: value_of!(val, DateTime)?,
+ }))
+ }
+ keymint::Tag::Tag::USAGE_EXPIRE_DATETIME => {
+ Some(KeyParam::UsageExpireDatetime(DateTime {
+ ms_since_epoch: value_of!(val, DateTime)?,
+ }))
+ }
+ keymint::Tag::Tag::CREATION_DATETIME => Some(KeyParam::CreationDatetime(DateTime {
+ ms_since_epoch: value_of!(val, DateTime)?,
+ })),
+ keymint::Tag::Tag::CERTIFICATE_NOT_BEFORE => {
+ Some(KeyParam::CertificateNotBefore(DateTime {
+ ms_since_epoch: value_of!(val, DateTime)?,
+ }))
+ }
+ keymint::Tag::Tag::CERTIFICATE_NOT_AFTER => {
+ Some(KeyParam::CertificateNotAfter(DateTime {
+ ms_since_epoch: value_of!(val, DateTime)?,
+ }))
+ }
+
+ // `Vec<u8>`-holding variants.
+ keymint::Tag::Tag::APPLICATION_ID => Some(KeyParam::ApplicationId(clone_blob!(val)?)),
+ keymint::Tag::Tag::APPLICATION_DATA => {
+ Some(KeyParam::ApplicationData(clone_blob!(val)?))
+ }
+ keymint::Tag::Tag::ROOT_OF_TRUST => Some(KeyParam::RootOfTrust(clone_blob!(val)?)),
+ keymint::Tag::Tag::ATTESTATION_CHALLENGE => {
+ Some(KeyParam::AttestationChallenge(clone_blob!(val)?))
+ }
+ keymint::Tag::Tag::ATTESTATION_APPLICATION_ID => {
+ Some(KeyParam::AttestationApplicationId(clone_blob!(val)?))
+ }
+ keymint::Tag::Tag::ATTESTATION_ID_BRAND => {
+ Some(KeyParam::AttestationIdBrand(clone_blob!(val)?))
+ }
+ keymint::Tag::Tag::ATTESTATION_ID_DEVICE => {
+ Some(KeyParam::AttestationIdDevice(clone_blob!(val)?))
+ }
+ keymint::Tag::Tag::ATTESTATION_ID_PRODUCT => {
+ Some(KeyParam::AttestationIdProduct(clone_blob!(val)?))
+ }
+ keymint::Tag::Tag::ATTESTATION_ID_SERIAL => {
+ Some(KeyParam::AttestationIdSerial(clone_blob!(val)?))
+ }
+ keymint::Tag::Tag::ATTESTATION_ID_IMEI => {
+ Some(KeyParam::AttestationIdImei(clone_blob!(val)?))
+ }
+ keymint::Tag::Tag::ATTESTATION_ID_MEID => {
+ Some(KeyParam::AttestationIdMeid(clone_blob!(val)?))
+ }
+ keymint::Tag::Tag::ATTESTATION_ID_MANUFACTURER => {
+ Some(KeyParam::AttestationIdManufacturer(clone_blob!(val)?))
+ }
+ keymint::Tag::Tag::ATTESTATION_ID_MODEL => {
+ Some(KeyParam::AttestationIdModel(clone_blob!(val)?))
+ }
+ keymint::Tag::Tag::NONCE => Some(KeyParam::Nonce(clone_blob!(val)?)),
+ keymint::Tag::Tag::CERTIFICATE_SERIAL => {
+ Some(KeyParam::CertificateSerial(clone_blob!(val)?))
+ }
+ keymint::Tag::Tag::CERTIFICATE_SUBJECT => {
+ Some(KeyParam::CertificateSubject(clone_blob!(val)?))
+ }
+
+ // Unsupported variants
+ keymint::Tag::Tag::UNIQUE_ID
+ | keymint::Tag::Tag::HARDWARE_TYPE
+ | keymint::Tag::Tag::MIN_SECONDS_BETWEEN_OPS
+ | keymint::Tag::Tag::IDENTITY_CREDENTIAL_KEY
+ | keymint::Tag::Tag::ASSOCIATED_DATA
+ | keymint::Tag::Tag::CONFIRMATION_TOKEN => {
+ error!("Unsupported tag {:?} encountered", val.tag);
+ return Err(wire::ValueNotRecognized);
+ }
+ _ => {
+ warn!("Unknown tag {:?} silently dropped", val.tag);
+ None
+ }
+ })
+ }
+}
+
+/// Macro that emits conversion implementations for `wire` and HAL enums.
+/// - The `hal::keymint` version of the enum is a newtype holding `i32`
+/// - The `wire::keymint` version of the enum is an exhaustive enum with `[repr(i32)]`
+macro_rules! enum_convert {
+ {
+ $wenum:ty => $henum:ty
+ } => {
+ impl Fromm<$wenum> for $henum {
+ fn fromm(val: $wenum) -> Self {
+ Self(val as i32)
+ }
+ }
+ impl TryFromm<$henum> for $wenum {
+ type Error = wire::ValueNotRecognized;
+ fn try_fromm(val: $henum) -> Result<Self, Self::Error> {
+ Self::try_from(val.0)
+ }
+ }
+ };
+}
+enum_convert! { wire::keymint::ErrorCode => keymint::ErrorCode::ErrorCode }
+enum_convert! { wire::keymint::Algorithm => keymint::Algorithm::Algorithm }
+enum_convert! { wire::keymint::BlockMode => keymint::BlockMode::BlockMode }
+enum_convert! { wire::keymint::Digest => keymint::Digest::Digest }
+enum_convert! { wire::keymint::EcCurve => keymint::EcCurve::EcCurve }
+enum_convert! { wire::keymint::HardwareAuthenticatorType =>
+keymint::HardwareAuthenticatorType::HardwareAuthenticatorType }
+enum_convert! { wire::keymint::KeyFormat => keymint::KeyFormat::KeyFormat }
+enum_convert! { wire::keymint::KeyOrigin => keymint::KeyOrigin::KeyOrigin }
+enum_convert! { wire::keymint::KeyPurpose => keymint::KeyPurpose::KeyPurpose }
+enum_convert! { wire::keymint::PaddingMode => keymint::PaddingMode::PaddingMode }
+enum_convert! { wire::keymint::SecurityLevel => keymint::SecurityLevel::SecurityLevel }
+enum_convert! { wire::keymint::Tag => keymint::Tag::Tag }
+enum_convert! { wire::keymint::TagType => keymint::TagType::TagType }
diff --git a/hal/src/hal/tests.rs b/hal/src/hal/tests.rs
new file mode 100644
index 0000000..f08f477
--- /dev/null
+++ b/hal/src/hal/tests.rs
@@ -0,0 +1,151 @@
+use crate::cbor::value::Value;
+use kmr_common::{cbor_type_error, AsCborValue, CborError};
+use kmr_derive::AsCborValue;
+
+#[derive(Debug, Clone, PartialEq, Eq, AsCborValue)]
+struct Timestamp {
+ milliseconds: i64,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, AsCborValue)]
+struct NamedFields {
+ challenge: i64,
+ timestamp: Timestamp,
+ mac: Vec<u8>,
+}
+
+#[test]
+fn test_cbor_value_cddl() {
+ assert_eq!(<NamedFields>::cddl_typename().unwrap(), "NamedFields");
+ assert_eq!(
+ <NamedFields>::cddl_schema().unwrap(),
+ r#"[
+ challenge: int,
+ timestamp: Timestamp,
+ mac: bstr,
+]"#
+ );
+}
+
+#[test]
+fn test_cbor_value_roundtrip() {
+ let obj = NamedFields {
+ challenge: 42,
+ timestamp: Timestamp { milliseconds: 10_000_000 },
+ mac: vec![1, 2, 3, 4],
+ };
+
+ let obj_val = obj.clone().to_cbor_value().unwrap();
+ let recovered_obj = <NamedFields>::from_cbor_value(obj_val).unwrap();
+ assert_eq!(obj, recovered_obj);
+}
+
+#[test]
+fn test_cbor_parse_fail() {
+ let tests = vec![
+ (Value::Map(vec![]), "expected arr"),
+ (Value::Integer(0.into()), "expected arr"),
+ (Value::Array(vec![]), "expected arr len 3"),
+ (
+ Value::Array(vec![
+ Value::Integer(0.into()),
+ Value::Integer(0.into()),
+ Value::Integer(0.into()),
+ Value::Integer(0.into()),
+ ]),
+ "expected arr len 3",
+ ),
+ (
+ Value::Array(vec![
+ Value::Integer(0.into()),
+ Value::Array(vec![Value::Integer(0.into())]),
+ Value::Integer(0.into()),
+ ]),
+ "expected bstr",
+ ),
+ (
+ Value::Array(vec![
+ Value::Integer(0.into()),
+ Value::Array(vec![Value::Integer(0.into()), Value::Integer(0.into())]),
+ Value::Bytes(vec![1, 2, 3]),
+ ]),
+ "expected arr len 1",
+ ),
+ ];
+ for (val, wanterr) in tests {
+ let result = <NamedFields>::from_cbor_value(val);
+ expect_err(result, wanterr);
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, AsCborValue)]
+struct UnnamedFields(i64, Timestamp);
+
+#[test]
+fn test_unnamed_cbor_value_cddl() {
+ assert_eq!(<UnnamedFields>::cddl_typename().unwrap(), "UnnamedFields");
+ assert_eq!(
+ <UnnamedFields>::cddl_schema().unwrap(),
+ r#"[
+ int,
+ Timestamp,
+]"#
+ );
+}
+
+#[test]
+fn test_unnamed_cbor_value_roundtrip() {
+ let obj = UnnamedFields(42, Timestamp { milliseconds: 10_000_000 });
+
+ let obj_val = obj.clone().to_cbor_value().unwrap();
+ let recovered_obj = <UnnamedFields>::from_cbor_value(obj_val).unwrap();
+ assert_eq!(obj, recovered_obj);
+}
+
+#[test]
+fn test_unnamed_cbor_parse_fail() {
+ let tests = vec![
+ (Value::Map(vec![]), "expected arr"),
+ (Value::Integer(0.into()), "expected arr"),
+ (Value::Array(vec![]), "expected arr len 2"),
+ (
+ Value::Array(vec![
+ Value::Integer(0.into()),
+ Value::Integer(0.into()),
+ Value::Integer(0.into()),
+ ]),
+ "expected arr len 2",
+ ),
+ (
+ Value::Array(vec![
+ Value::Bytes(vec![1, 2, 3]),
+ Value::Array(vec![Value::Integer(0.into())]),
+ ]),
+ "expected i64",
+ ),
+ (
+ Value::Array(vec![
+ Value::Integer(0.into()),
+ Value::Array(vec![Value::Integer(0.into()), Value::Integer(0.into())]),
+ ]),
+ "expected arr len 1",
+ ),
+ ];
+ for (val, wanterr) in tests {
+ let result = <UnnamedFields>::from_cbor_value(val);
+ expect_err(result, wanterr);
+ }
+}
+
+/// Check for an expected error.
+#[cfg(test)]
+pub fn expect_err<T, E: core::fmt::Debug>(result: Result<T, E>, err_msg: &str) {
+ assert!(result.is_err(), "unexpected success; wanted error containing '{}'", err_msg);
+ let err = result.err();
+ assert!(
+ format!("{:?}", err).contains(err_msg),
+ "unexpected error {:?}, doesn't contain '{}'",
+ err,
+ err_msg
+ );
+}
diff --git a/hal/src/keymint.rs b/hal/src/keymint.rs
new file mode 100644
index 0000000..5515513
--- /dev/null
+++ b/hal/src/keymint.rs
@@ -0,0 +1,465 @@
+//! KeyMint HAL device implementation.
+
+use crate::binder;
+use crate::hal::{
+ failed_conversion, keymint, keymint::IKeyMintOperation::IKeyMintOperation,
+ secureclock::TimeStampToken::TimeStampToken, Innto, TryInnto,
+};
+use crate::{ChannelHalService, SerializedChannel};
+use kmr_common::{wire::keymint::KeyParam, wire::*, AsCborValue};
+use std::ffi::CString;
+use std::{
+ ops::DerefMut,
+ sync::{Arc, Mutex, MutexGuard, RwLock},
+};
+
+/// Maximum size of input data in operation messages, allowing for overhead.
+///
+/// A serialized `FinishRequest` includes the following additional bytes over and
+/// above the size of the input (at most):
+/// - 1: array wrapper (0x86)
+/// - 9: int (0x1b + u64) [op_handle]
+/// - 1: array wrapper (0x81) [input]
+/// - 9: input data length
+/// - XX: input data
+/// - 1: array wrapper (0x81) [signature]
+/// - 5: signature data length
+/// - 132: signature data (P-521 point)
+/// - 1: array wrapper (0x81) [auth_token]
+/// - 9: int (0x1b + u64) [challenge]
+/// - 9: int (0x1b + u64) [user_id]
+/// - 9: int (0x1b + u64) [authenticator_id]
+/// - 9: int (0x1b + u64) [authenticator_type]
+/// - 1: array wrapper (0x81)[timestamp]
+/// - 9: int (0x1b + u64) [user_id]
+/// - 2: bstr header [mac]
+/// - 32: bstr [mac]
+/// - 1: array wrapper (0x81) [timestamp_token]
+/// - 1: array wrapper [TimeStampToken]
+/// - 9: int (0x1b + u64) [challenge]
+/// - 1: array wrapper (0x81)[timestamp]
+/// - 9: int (0x1b + u64) [user_id]
+/// - 2: bstr header [mac]
+/// - 32: bstr [mac]
+/// - 1: array wrapper (0x81) [confirmation_token]
+/// - 2: bstr header [confirmation token]
+/// - 32: bstr [confirmation token (HMAC-SHA256)]
+///
+/// Add some leeway in case encodings change.
+pub const MAX_DATA_SIZE: usize = MAX_SIZE - 350;
+
+/// IKeyMintDevice implementation which converts all method invocations to serialized
+/// requests that are sent down the associated channel.
+pub struct Device<T: SerializedChannel + 'static> {
+ channel: Arc<Mutex<T>>,
+}
+
+impl<T: SerializedChannel + 'static> Device<T> {
+ /// Construct a new instance that uses the provided channel.
+ pub fn new(channel: Arc<Mutex<T>>) -> Self {
+ Self { channel }
+ }
+
+ /// Create a new instance wrapped in a proxy object.
+ pub fn new_as_binder(
+ channel: Arc<Mutex<T>>,
+ ) -> binder::Strong<dyn keymint::IKeyMintDevice::IKeyMintDevice> {
+ keymint::IKeyMintDevice::BnKeyMintDevice::new_binder(
+ Self::new(channel),
+ binder::BinderFeatures::default(),
+ )
+ }
+}
+
+impl<T: SerializedChannel> ChannelHalService<T> for Device<T> {
+ fn channel(&self) -> MutexGuard<T> {
+ self.channel.lock().unwrap()
+ }
+}
+
+impl<T: SerializedChannel> binder::Interface for Device<T> {}
+
+impl<T: SerializedChannel> keymint::IKeyMintDevice::IKeyMintDevice for Device<T> {
+ fn getHardwareInfo(&self) -> binder::Result<keymint::KeyMintHardwareInfo::KeyMintHardwareInfo> {
+ let rsp: GetHardwareInfoResponse = self.execute(GetHardwareInfoRequest {})?;
+ Ok(rsp.ret.innto())
+ }
+ fn addRngEntropy(&self, data: &[u8]) -> binder::Result<()> {
+ let _rsp: AddRngEntropyResponse =
+ self.execute(AddRngEntropyRequest { data: data.to_vec() })?;
+ Ok(())
+ }
+ fn generateKey(
+ &self,
+ keyParams: &[keymint::KeyParameter::KeyParameter],
+ attestationKey: Option<&keymint::AttestationKey::AttestationKey>,
+ ) -> binder::Result<keymint::KeyCreationResult::KeyCreationResult> {
+ let rsp: GenerateKeyResponse = self.execute(GenerateKeyRequest {
+ key_params: keyParams
+ .iter()
+ .filter_map(|p| p.try_innto().transpose())
+ .collect::<Result<Vec<KeyParam>, _>>()
+ .map_err(failed_conversion)?,
+ attestation_key: match attestationKey {
+ None => None,
+ Some(k) => Some(k.clone().try_innto().map_err(failed_conversion)?),
+ },
+ })?;
+ Ok(rsp.ret.innto())
+ }
+ fn importKey(
+ &self,
+ keyParams: &[keymint::KeyParameter::KeyParameter],
+ keyFormat: keymint::KeyFormat::KeyFormat,
+ keyData: &[u8],
+ attestationKey: Option<&keymint::AttestationKey::AttestationKey>,
+ ) -> binder::Result<keymint::KeyCreationResult::KeyCreationResult> {
+ let rsp: ImportKeyResponse = self.execute(ImportKeyRequest {
+ key_params: keyParams
+ .iter()
+ .filter_map(|p| p.try_innto().transpose())
+ .collect::<Result<Vec<KeyParam>, _>>()
+ .map_err(failed_conversion)?,
+ key_format: keyFormat.try_innto().map_err(failed_conversion)?,
+ key_data: keyData.to_vec(),
+ attestation_key: match attestationKey {
+ None => None,
+ Some(k) => Some(k.clone().try_innto().map_err(failed_conversion)?),
+ },
+ })?;
+ Ok(rsp.ret.innto())
+ }
+ fn importWrappedKey(
+ &self,
+ wrappedKeyData: &[u8],
+ wrappingKeyBlob: &[u8],
+ maskingKey: &[u8],
+ unwrappingParams: &[keymint::KeyParameter::KeyParameter],
+ passwordSid: i64,
+ biometricSid: i64,
+ ) -> binder::Result<keymint::KeyCreationResult::KeyCreationResult> {
+ let rsp: ImportWrappedKeyResponse = self.execute(ImportWrappedKeyRequest {
+ wrapped_key_data: wrappedKeyData.to_vec(),
+ wrapping_key_blob: wrappingKeyBlob.to_vec(),
+ masking_key: maskingKey.to_vec(),
+ unwrapping_params: unwrappingParams
+ .iter()
+ .filter_map(|p| p.try_innto().transpose())
+ .collect::<Result<Vec<KeyParam>, _>>()
+ .map_err(failed_conversion)?,
+ password_sid: passwordSid,
+ biometric_sid: biometricSid,
+ })?;
+ Ok(rsp.ret.innto())
+ }
+ fn upgradeKey(
+ &self,
+ keyBlobToUpgrade: &[u8],
+ upgradeParams: &[keymint::KeyParameter::KeyParameter],
+ ) -> binder::Result<Vec<u8>> {
+ let rsp: UpgradeKeyResponse = self.execute(UpgradeKeyRequest {
+ key_blob_to_upgrade: keyBlobToUpgrade.to_vec(),
+ upgrade_params: upgradeParams
+ .iter()
+ .filter_map(|p| p.try_innto().transpose())
+ .collect::<Result<Vec<KeyParam>, _>>()
+ .map_err(failed_conversion)?,
+ })?;
+ Ok(rsp.ret)
+ }
+ fn deleteKey(&self, keyBlob: &[u8]) -> binder::Result<()> {
+ let _rsp: DeleteKeyResponse =
+ self.execute(DeleteKeyRequest { key_blob: keyBlob.to_vec() })?;
+ Ok(())
+ }
+ fn deleteAllKeys(&self) -> binder::Result<()> {
+ let _rsp: DeleteAllKeysResponse = self.execute(DeleteAllKeysRequest {})?;
+ Ok(())
+ }
+ fn destroyAttestationIds(&self) -> binder::Result<()> {
+ let _rsp: DestroyAttestationIdsResponse = self.execute(DestroyAttestationIdsRequest {})?;
+ Ok(())
+ }
+ fn begin(
+ &self,
+ purpose: keymint::KeyPurpose::KeyPurpose,
+ keyBlob: &[u8],
+ params: &[keymint::KeyParameter::KeyParameter],
+ authToken: Option<&keymint::HardwareAuthToken::HardwareAuthToken>,
+ ) -> binder::Result<keymint::BeginResult::BeginResult> {
+ let rsp: BeginResponse = self.execute(BeginRequest {
+ purpose: purpose.try_innto().map_err(failed_conversion)?,
+ key_blob: keyBlob.to_vec(),
+ params: params
+ .iter()
+ .filter_map(|p| p.try_innto().transpose())
+ .collect::<Result<Vec<KeyParam>, _>>()
+ .map_err(failed_conversion)?,
+ auth_token: match authToken {
+ None => None,
+ Some(t) => Some(t.clone().try_innto().map_err(failed_conversion)?),
+ },
+ })?;
+ // The `begin()` method is a special case.
+ // - Internally, the in-progress operation is identified by an opaque handle value.
+ // - Externally, the in-progress operation is represented as an `IKeyMintOperation` Binder
+ // object.
+ // The `WireOperation` struct contains the former, and acts as the latter.
+ let op = Operation::new_as_binder(self.channel.clone(), rsp.ret.op_handle);
+ Ok(keymint::BeginResult::BeginResult {
+ challenge: rsp.ret.challenge,
+ params: rsp.ret.params.innto(),
+ operation: Some(op),
+ })
+ }
+ fn deviceLocked(
+ &self,
+ passwordOnly: bool,
+ timestampToken: Option<&TimeStampToken>,
+ ) -> binder::Result<()> {
+ let _rsp: DeviceLockedResponse = self.execute(DeviceLockedRequest {
+ password_only: passwordOnly,
+ timestamp_token: timestampToken.map(|t| t.clone().innto()),
+ })?;
+ Ok(())
+ }
+ fn earlyBootEnded(&self) -> binder::Result<()> {
+ let _rsp: EarlyBootEndedResponse = self.execute(EarlyBootEndedRequest {})?;
+ Ok(())
+ }
+ fn convertStorageKeyToEphemeral(&self, storageKeyBlob: &[u8]) -> binder::Result<Vec<u8>> {
+ let rsp: ConvertStorageKeyToEphemeralResponse =
+ self.execute(ConvertStorageKeyToEphemeralRequest {
+ storage_key_blob: storageKeyBlob.to_vec(),
+ })?;
+ Ok(rsp.ret)
+ }
+ fn getKeyCharacteristics(
+ &self,
+ keyBlob: &[u8],
+ appId: &[u8],
+ appData: &[u8],
+ ) -> binder::Result<Vec<keymint::KeyCharacteristics::KeyCharacteristics>> {
+ let rsp: GetKeyCharacteristicsResponse = self.execute(GetKeyCharacteristicsRequest {
+ key_blob: keyBlob.to_vec(),
+ app_id: appId.to_vec(),
+ app_data: appData.to_vec(),
+ })?;
+ Ok(rsp.ret.innto())
+ }
+ fn getRootOfTrustChallenge(&self) -> binder::Result<[u8; 16]> {
+ let rsp: GetRootOfTrustChallengeResponse =
+ self.execute(GetRootOfTrustChallengeRequest {})?;
+ Ok(rsp.ret)
+ }
+ fn getRootOfTrust(&self, challenge: &[u8; 16]) -> binder::Result<Vec<u8>> {
+ let rsp: GetRootOfTrustResponse =
+ self.execute(GetRootOfTrustRequest { challenge: *challenge })?;
+ Ok(rsp.ret)
+ }
+ fn sendRootOfTrust(&self, root_of_trust: &[u8]) -> binder::Result<()> {
+ let _rsp: SendRootOfTrustResponse =
+ self.execute(SendRootOfTrustRequest { root_of_trust: root_of_trust.to_vec() })?;
+ Ok(())
+ }
+}
+
+/// Representation of an in-progress KeyMint operation on a `SerializedChannel`.
+#[derive(Debug)]
+struct Operation<T: SerializedChannel + 'static> {
+ channel: Arc<Mutex<T>>,
+ op_handle: RwLock<Option<i64>>,
+}
+
+impl<T: SerializedChannel + 'static> Drop for Operation<T> {
+ fn drop(&mut self) {
+ // Ensure that the TA is kept up-to-date by calling `abort()`, but ignore the result.
+ let _ = self.abort();
+ }
+}
+
+impl<T: SerializedChannel> ChannelHalService<T> for Operation<T> {
+ fn channel(&self) -> MutexGuard<T> {
+ self.channel.lock().unwrap()
+ }
+
+ /// Execute the given request as part of the operation. If the request fails, the operation is
+ /// invalidated (and any future requests for the operation will fail).
+ fn execute<R, S>(&self, req: R) -> binder::Result<S>
+ where
+ R: AsCborValue + Code<KeyMintOperation>,
+ S: AsCborValue + Code<KeyMintOperation>,
+ {
+ let result = super::channel_execute(self.channel().deref_mut(), req);
+ if result.is_err() {
+ // Any failed method on an operation terminates the operation.
+ self.invalidate();
+ }
+ result
+ }
+}
+
+impl<T: SerializedChannel> binder::Interface for Operation<T> {}
+
+impl<T: SerializedChannel + 'static> Operation<T> {
+ /// Create a new `Operation` wrapped in a proxy object.
+ fn new_as_binder(
+ channel: Arc<Mutex<T>>,
+ op_handle: i64,
+ ) -> binder::Strong<dyn keymint::IKeyMintOperation::IKeyMintOperation> {
+ let op = Self { channel, op_handle: RwLock::new(Some(op_handle)) };
+ keymint::IKeyMintOperation::BnKeyMintOperation::new_binder(
+ op,
+ binder::BinderFeatures::default(),
+ )
+ }
+}
+
+impl<T: SerializedChannel> Operation<T> {
+ /// Invalidate the operation.
+ fn invalidate(&self) {
+ *self.op_handle.write().unwrap() = None;
+ }
+
+ /// Retrieve the operation handle, if not already failed.
+ fn validate_handle(&self) -> binder::Result<i64> {
+ self.op_handle.read().unwrap().ok_or_else(|| {
+ binder::Status::new_service_specific_error(
+ keymint::ErrorCode::ErrorCode::INVALID_OPERATION_HANDLE.0,
+ Some(&CString::new("Operation handle not valid").unwrap()),
+ )
+ })
+ }
+}
+
+/// Implement the `IKeyMintOperation` interface for a [`Operation`]. Each method invocation is
+/// serialized into a request message that is sent over the `Operation`'s channel, and a
+/// corresponding response message is read. This response message is deserialized back into the
+/// method's output value(s).
+impl<T: SerializedChannel + 'static> keymint::IKeyMintOperation::IKeyMintOperation
+ for Operation<T>
+{
+ fn updateAad(
+ &self,
+ mut input: &[u8],
+ authToken: Option<&keymint::HardwareAuthToken::HardwareAuthToken>,
+ timeStampToken: Option<&TimeStampToken>,
+ ) -> binder::Result<()> {
+ let req_template = UpdateAadRequest {
+ op_handle: self.validate_handle()?,
+ input: vec![],
+ auth_token: match authToken {
+ None => None,
+ Some(t) => Some(t.clone().try_innto().map_err(failed_conversion)?),
+ },
+ timestamp_token: timeStampToken.map(|t| t.clone().innto()),
+ };
+ while !input.is_empty() {
+ let mut req = req_template.clone();
+ let batch_len = core::cmp::min(MAX_DATA_SIZE, input.len());
+ req.input = input[..batch_len].to_vec();
+ input = &input[batch_len..];
+ let _rsp: UpdateAadResponse = self.execute(req).map_err(|e| {
+ // Any failure invalidates the operation
+ self.invalidate();
+ e
+ })?;
+ }
+ Ok(())
+ }
+ fn update(
+ &self,
+ mut input: &[u8],
+ authToken: Option<&keymint::HardwareAuthToken::HardwareAuthToken>,
+ timeStampToken: Option<&TimeStampToken>,
+ ) -> binder::Result<Vec<u8>> {
+ let req_template = UpdateRequest {
+ op_handle: self.validate_handle()?,
+ input: input.to_vec(),
+ auth_token: match authToken {
+ None => None,
+ Some(t) => Some(t.clone().try_innto().map_err(failed_conversion)?),
+ },
+ timestamp_token: timeStampToken.map(|t| t.clone().innto()),
+ };
+ let mut output = vec![];
+ while !input.is_empty() {
+ let mut req = req_template.clone();
+ let batch_len = core::cmp::min(MAX_DATA_SIZE, input.len());
+ req.input = input[..batch_len].to_vec();
+ input = &input[batch_len..];
+ let rsp: UpdateResponse = self.execute(req).map_err(|e| {
+ self.invalidate();
+ e
+ })?;
+ output.extend_from_slice(&rsp.ret);
+ }
+ Ok(output)
+ }
+ fn finish(
+ &self,
+ input: Option<&[u8]>,
+ signature: Option<&[u8]>,
+ authToken: Option<&keymint::HardwareAuthToken::HardwareAuthToken>,
+ timestampToken: Option<&TimeStampToken>,
+ confirmationToken: Option<&[u8]>,
+ ) -> binder::Result<Vec<u8>> {
+ let op_handle = self.validate_handle()?;
+ let auth_token = match authToken {
+ None => None,
+ Some(t) => Some(t.clone().try_innto().map_err(failed_conversion)?),
+ };
+ let timestamp_token = timestampToken.map(|t| t.clone().innto());
+ let confirmation_token = confirmationToken.map(|v| v.to_vec());
+
+ let mut output = vec![];
+ let result: binder::Result<FinishResponse> = if let Some(mut input) = input {
+ while input.len() > MAX_DATA_SIZE {
+ let req = UpdateRequest {
+ op_handle,
+ input: input[..MAX_DATA_SIZE].to_vec(),
+ auth_token: auth_token.clone(),
+ timestamp_token: timestamp_token.clone(),
+ };
+ input = &input[MAX_DATA_SIZE..];
+ let rsp: UpdateResponse = self.execute(req).map_err(|e| {
+ self.invalidate();
+ e
+ })?;
+ output.extend_from_slice(&rsp.ret);
+ }
+
+ self.execute(FinishRequest {
+ op_handle,
+ input: Some(input.to_vec()),
+ signature: signature.map(|v| v.to_vec()),
+ auth_token,
+ timestamp_token,
+ confirmation_token,
+ })
+ } else {
+ self.execute(FinishRequest {
+ op_handle,
+ input: None,
+ signature: signature.map(|v| v.to_vec()),
+ auth_token,
+ timestamp_token,
+ confirmation_token,
+ })
+ };
+ // Finish always invalidates the operation.
+ self.invalidate();
+ result.map(|rsp| {
+ output.extend_from_slice(&rsp.ret);
+ output
+ })
+ }
+ fn abort(&self) -> binder::Result<()> {
+ let result: binder::Result<AbortResponse> =
+ self.execute(AbortRequest { op_handle: self.validate_handle()? });
+ // Abort always invalidates the operation.
+ self.invalidate();
+ let _ = result?;
+ Ok(())
+ }
+}
diff --git a/hal/src/lib.rs b/hal/src/lib.rs
new file mode 100644
index 0000000..cd9e216
--- /dev/null
+++ b/hal/src/lib.rs
@@ -0,0 +1,262 @@
+//! Implementation of a HAL service for KeyMint.
+//!
+//! This implementation relies on a `SerializedChannel` abstraction for a communication channel to
+//! the trusted application (TA). Incoming method invocations for the HAL service are converted
+//! into corresponding request structures, which are then serialized (using CBOR) and send down the
+//! channel. A serialized response is then read from the channel, which is deserialized into a
+//! response structure. The contents of this response structure are then used to populate the
+//! return values of the HAL service method.
+
+#![allow(non_snake_case)]
+
+use core::{convert::TryInto, fmt::Debug};
+use kmr_common::{
+ cbor,
+ wire::{keymint::ErrorCode, Code, KeyMintOperation},
+ AsCborValue, CborError,
+};
+use log::{error, info};
+use std::{
+ ffi::CString,
+ io::{Read, Write},
+ ops::DerefMut,
+ sync::MutexGuard,
+};
+
+pub use binder;
+
+pub mod env;
+pub mod hal;
+pub mod keymint;
+pub mod rpc;
+pub mod secureclock;
+pub mod sharedsecret;
+#[cfg(test)]
+mod tests;
+
+/// Emit a failure for a failed CBOR conversion.
+#[inline]
+pub fn failed_cbor(err: CborError) -> binder::Status {
+ binder::Status::new_service_specific_error(
+ ErrorCode::UnknownError as i32,
+ Some(&CString::new(format!("CBOR conversion failed: {:?}", err)).unwrap()),
+ )
+}
+
+/// Abstraction of a channel to a secure world TA implementation, which accepts serialized request
+/// messages and returns serialized return values (or an error if communication via the channel is
+/// lost).
+pub trait SerializedChannel: Debug + Send {
+ fn execute(&mut self, serialized_req: &[u8]) -> binder::Result<Vec<u8>>;
+}
+
+/// Write a message to a stream-oriented [`Write`] item, with length framing.
+pub fn write_msg<W: Write>(w: &mut W, data: &[u8]) -> binder::Result<()> {
+ // The underlying `Write` item does not guarantee delivery of complete messages.
+ // Make this possible by adding framing in the form of a big-endian `u32` holding
+ // the message length.
+ let data_len: u32 = data.len().try_into().map_err(|_e| {
+ binder::Status::new_exception(
+ binder::ExceptionCode::BAD_PARCELABLE,
+ Some(&CString::new("encoded request message too large").unwrap()),
+ )
+ })?;
+ let data_len_data = data_len.to_be_bytes();
+ w.write_all(&data_len_data[..]).map_err(|e| {
+ error!("Failed to write length to stream: {}", e);
+ binder::Status::new_exception(
+ binder::ExceptionCode::BAD_PARCELABLE,
+ Some(&CString::new("failed to write framing length").unwrap()),
+ )
+ })?;
+ w.write_all(data).map_err(|e| {
+ error!("Failed to write data to stream: {}", e);
+ binder::Status::new_exception(
+ binder::ExceptionCode::BAD_PARCELABLE,
+ Some(&CString::new("failed to write data").unwrap()),
+ )
+ })?;
+ Ok(())
+}
+
+/// Read a message from a stream-oriented [`Read`] item, with length framing.
+pub fn read_msg<R: Read>(r: &mut R) -> binder::Result<Vec<u8>> {
+ // The data read from the `Read` item has a 4-byte big-endian length prefix.
+ let mut len_data = [0u8; 4];
+ r.read_exact(&mut len_data).map_err(|e| {
+ error!("Failed to read length from stream: {}", e);
+ binder::Status::new_exception(binder::ExceptionCode::TRANSACTION_FAILED, None)
+ })?;
+ let len = u32::from_be_bytes(len_data);
+ let mut data = vec![0; len as usize];
+ r.read_exact(&mut data).map_err(|e| {
+ error!("Failed to read data from stream: {}", e);
+ binder::Status::new_exception(binder::ExceptionCode::TRANSACTION_FAILED, None)
+ })?;
+ Ok(data)
+}
+
+/// Message-oriented wrapper around a pair of stream-oriented channels. This allows a pair of
+/// uni-directional channels that don't necessarily preserve message boundaries to appear as a
+/// single bi-directional channel that does preserve message boundaries.
+#[derive(Debug)]
+pub struct MessageChannel<R: Read, W: Write> {
+ r: R,
+ w: W,
+}
+
+impl<R: Read + Debug + Send, W: Write + Debug + Send> SerializedChannel for MessageChannel<R, W> {
+ fn execute(&mut self, serialized_req: &[u8]) -> binder::Result<Vec<u8>> {
+ write_msg(&mut self.w, serialized_req)?;
+ read_msg(&mut self.r)
+ }
+}
+
+/// Execute an operation by serializing and sending a request structure down a channel, and
+/// deserializing and returning the response.
+///
+/// This implementation relies on the internal serialization format for `PerformOpReq` and
+/// `PerformOpRsp` to allow direct use of the specific request/response types.
+fn channel_execute<T, R, S>(channel: &mut T, req: R) -> binder::Result<S>
+where
+ T: SerializedChannel,
+ R: AsCborValue + Code<KeyMintOperation>,
+ S: AsCborValue + Code<KeyMintOperation>,
+{
+ // Manually build an array that includes the opcode and the encoded request and encode it.
+ // This is equivalent to `PerformOpReq::to_vec()`.
+ let req_arr = cbor::value::Value::Array(vec![
+ <R>::CODE.to_cbor_value().map_err(failed_cbor)?,
+ req.to_cbor_value().map_err(failed_cbor)?,
+ ]);
+ let mut req_data = Vec::new();
+ cbor::ser::into_writer(&req_arr, &mut req_data).map_err(|e| {
+ binder::Status::new_service_specific_error(
+ ErrorCode::UnknownError as i32,
+ Some(
+ &CString::new(format!("failed to write CBOR request to buffer: {:?}", e)).unwrap(),
+ ),
+ )
+ })?;
+
+ if req_data.len() > kmr_common::wire::MAX_SIZE {
+ error!(
+ "HAL operation {:?} encodes bigger {} than max size {}",
+ <R>::CODE,
+ req_data.len(),
+ kmr_common::wire::MAX_SIZE
+ );
+ return Err(binder::Status::new_service_specific_error(
+ ErrorCode::InvalidInputLength as i32,
+ Some(&CString::new("encoded request message too large").unwrap()),
+ ));
+ }
+
+ // Send in request bytes, get back response bytes.
+ let rsp_data = channel.execute(&req_data)?;
+
+ // Convert the raw response data to an array of [error code, opt_response].
+ let rsp_value = kmr_common::read_to_value(&rsp_data).map_err(failed_cbor)?;
+ let mut rsp_array = match rsp_value {
+ cbor::value::Value::Array(a) if a.len() == 2 => a,
+ _ => {
+ error!("HAL: failed to parse response data 2-array!");
+ return kmr_common::cbor_type_error(&rsp_value, "arr of len 2").map_err(failed_cbor);
+ }
+ };
+ let opt_response = rsp_array.remove(1);
+ let error_code = <ErrorCode>::from_cbor_value(rsp_array.remove(0)).map_err(failed_cbor)?;
+ if error_code != ErrorCode::Ok {
+ error!("HAL: command {:?} failed: {:?}", <R>::CODE, error_code);
+ return Err(binder::Status::new_service_specific_error(error_code as i32, None));
+ }
+
+ // The optional response should be an array of exactly 1 element (because the 0-element case
+ // corresponds to a non-OK error code, which has just been dealt with).
+ let rsp = match opt_response {
+ cbor::value::Value::Array(mut a) if a.len() == 1 => a.remove(0),
+ _ => {
+ error!("HAL: failed to parse response data structure!");
+ return kmr_common::cbor_type_error(&opt_response, "arr of len 1").map_err(failed_cbor);
+ }
+ };
+
+ // The response is expected to be an array of 2 elements: a op_type code and an encoded response
+ // structure. The op_type code indicates the type of response structure, which should be what
+ // we expect.
+ let mut inner_rsp_array = match rsp {
+ cbor::value::Value::Array(a) if a.len() == 2 => a,
+ _ => {
+ error!("HAL: failed to parse inner response data structure!");
+ return kmr_common::cbor_type_error(&rsp, "arr of len 2").map_err(failed_cbor);
+ }
+ };
+ let inner_rsp = inner_rsp_array.remove(1);
+ let op_type =
+ <KeyMintOperation>::from_cbor_value(inner_rsp_array.remove(0)).map_err(failed_cbor)?;
+ if op_type != <S>::CODE {
+ error!("HAL: inner response data for unexpected opcode {:?}!", op_type);
+ return Err(failed_cbor(CborError::UnexpectedItem("wrong ret code", "rsp ret code")));
+ }
+
+ <S>::from_cbor_value(inner_rsp).map_err(failed_cbor)
+}
+
+/// Abstraction of a HAL service that uses an underlying [`SerializedChannel`] to communicate with
+/// an associated TA.
+trait ChannelHalService<T: SerializedChannel> {
+ /// Return the underlying channel.
+ fn channel(&self) -> MutexGuard<T>;
+
+ /// Execute the given request, by serializing it and sending it down the internal channel. Then
+ /// read and deserialize the response.
+ fn execute<R, S>(&self, req: R) -> binder::Result<S>
+ where
+ R: AsCborValue + Code<KeyMintOperation>,
+ S: AsCborValue + Code<KeyMintOperation>,
+ {
+ channel_execute(self.channel().deref_mut(), req)
+ }
+}
+
+/// Let the TA know information about the userspace environment.
+pub fn send_hal_info<T: SerializedChannel>(channel: &mut T) -> binder::Result<()> {
+ let req = env::populate_hal_info().map_err(|e| {
+ binder::Status::new_exception(
+ binder::ExceptionCode::BAD_PARCELABLE,
+ Some(&CString::new(format!("failed to determine HAL environment: {}", e)).unwrap()),
+ )
+ })?;
+ info!("HAL->TA: environment info is {:?}", req);
+ let _rsp: kmr_common::wire::SetHalInfoResponse = channel_execute(channel, req)?;
+ Ok(())
+}
+
+/// Let the TA know information about the boot environment.
+pub fn send_boot_info<T: SerializedChannel>(
+ channel: &mut T,
+ req: kmr_common::wire::SetBootInfoRequest,
+) -> binder::Result<()> {
+ info!("boot->TA: boot info is {:?}", req);
+ let _rsp: kmr_common::wire::SetBootInfoResponse = channel_execute(channel, req)?;
+ Ok(())
+}
+
+/// Provision the TA with attestation ID information.
+pub fn send_attest_ids<T: SerializedChannel>(
+ channel: &mut T,
+ ids: kmr_common::wire::AttestationIdInfo,
+) -> binder::Result<()> {
+ let req = kmr_common::wire::SetAttestationIdsRequest { ids };
+ info!("provision->attestation IDs are {:?}", req);
+ let _rsp: kmr_common::wire::SetAttestationIdsResponse = channel_execute(channel, req)?;
+ Ok(())
+}
+
+/// Let the TA know that early boot has ended
+pub fn early_boot_ended<T: SerializedChannel>(channel: &mut T) -> binder::Result<()> {
+ info!("boot->TA: early boot ended");
+ let req = kmr_common::wire::EarlyBootEndedRequest {};
+ let _rsp: kmr_common::wire::EarlyBootEndedResponse = channel_execute(channel, req)?;
+ Ok(())
+}
diff --git a/hal/src/rpc.rs b/hal/src/rpc.rs
new file mode 100644
index 0000000..2af05e2
--- /dev/null
+++ b/hal/src/rpc.rs
@@ -0,0 +1,78 @@
+//! RemotelyProvisionedComponent HAL device implementation.
+
+use super::{ChannelHalService, SerializedChannel};
+use crate::binder;
+use crate::hal::{keymint, Innto};
+use kmr_common::wire::*;
+use std::sync::{Arc, Mutex, MutexGuard};
+
+/// `IRemotelyProvisionedComponent` implementation which converts all method invocations to
+/// serialized requests that are sent down the associated channel.
+pub struct Device<T: SerializedChannel + 'static> {
+ channel: Arc<Mutex<T>>,
+}
+
+impl<T: SerializedChannel + 'static> Device<T> {
+ /// Construct a new instance that uses the provided channel.
+ pub fn new(channel: Arc<Mutex<T>>) -> Self {
+ Self { channel }
+ }
+
+ /// Create a new instance wrapped in a proxy object.
+ pub fn new_as_binder(
+ channel: Arc<Mutex<T>>,
+ ) -> binder::Strong<dyn keymint::IRemotelyProvisionedComponent::IRemotelyProvisionedComponent>
+ {
+ keymint::IRemotelyProvisionedComponent::BnRemotelyProvisionedComponent::new_binder(
+ Self::new(channel),
+ binder::BinderFeatures::default(),
+ )
+ }
+}
+
+impl<T: SerializedChannel> ChannelHalService<T> for Device<T> {
+ fn channel(&self) -> MutexGuard<T> {
+ self.channel.lock().unwrap()
+ }
+}
+
+impl<T: SerializedChannel> binder::Interface for Device<T> {}
+
+impl<T: SerializedChannel> keymint::IRemotelyProvisionedComponent::IRemotelyProvisionedComponent
+ for Device<T>
+{
+ fn getHardwareInfo(&self) -> binder::Result<keymint::RpcHardwareInfo::RpcHardwareInfo> {
+ let rsp: GetRpcHardwareInfoResponse = self.execute(GetRpcHardwareInfoRequest {})?;
+ Ok(rsp.ret.innto())
+ }
+ fn generateEcdsaP256KeyPair(
+ &self,
+ testMode: bool,
+ macedPublicKey: &mut keymint::MacedPublicKey::MacedPublicKey,
+ ) -> binder::Result<Vec<u8>> {
+ let rsp: GenerateEcdsaP256KeyPairResponse =
+ self.execute(GenerateEcdsaP256KeyPairRequest { test_mode: testMode })?;
+ *macedPublicKey = rsp.maced_public_key.innto();
+ Ok(rsp.ret)
+ }
+ fn generateCertificateRequest(
+ &self,
+ testMode: bool,
+ keysToSign: &[keymint::MacedPublicKey::MacedPublicKey],
+ endpointEncryptionCertChain: &[u8],
+ challenge: &[u8],
+ deviceInfo: &mut keymint::DeviceInfo::DeviceInfo,
+ protectedData: &mut keymint::ProtectedData::ProtectedData,
+ ) -> binder::Result<Vec<u8>> {
+ let rsp: GenerateCertificateRequestResponse =
+ self.execute(GenerateCertificateRequestRequest {
+ test_mode: testMode,
+ keys_to_sign: keysToSign.iter().map(|k| k.innto()).collect(),
+ endpoint_encryption_cert_chain: endpointEncryptionCertChain.to_vec(),
+ challenge: challenge.to_vec(),
+ })?;
+ *deviceInfo = rsp.device_info.innto();
+ *protectedData = rsp.protected_data.innto();
+ Ok(rsp.ret)
+ }
+}
diff --git a/hal/src/secureclock.rs b/hal/src/secureclock.rs
new file mode 100644
index 0000000..cfa9e78
--- /dev/null
+++ b/hal/src/secureclock.rs
@@ -0,0 +1,44 @@
+//! SecureClock HAL device implementation.
+
+use super::{ChannelHalService, SerializedChannel};
+use crate::binder;
+use crate::hal::secureclock::{ISecureClock, TimeStampToken::TimeStampToken};
+use crate::hal::Innto;
+use kmr_common::wire::*;
+use std::sync::{Arc, Mutex, MutexGuard};
+
+/// `ISecureClock` implementation which converts all method invocations to serialized requests that
+/// are sent down the associated channel.
+pub struct Device<T: SerializedChannel + 'static> {
+ channel: Arc<Mutex<T>>,
+}
+
+impl<T: SerializedChannel + Send> binder::Interface for Device<T> {}
+
+impl<T: SerializedChannel + 'static> Device<T> {
+ /// Construct a new instance that uses the provided channel.
+ pub fn new(channel: Arc<Mutex<T>>) -> Self {
+ Self { channel }
+ }
+ /// Create a new instance wrapped in a proxy object.
+ pub fn new_as_binder(channel: Arc<Mutex<T>>) -> binder::Strong<dyn ISecureClock::ISecureClock> {
+ ISecureClock::BnSecureClock::new_binder(
+ Self::new(channel),
+ binder::BinderFeatures::default(),
+ )
+ }
+}
+
+impl<T: SerializedChannel> ChannelHalService<T> for Device<T> {
+ fn channel(&self) -> MutexGuard<T> {
+ self.channel.lock().unwrap()
+ }
+}
+
+impl<T: SerializedChannel> ISecureClock::ISecureClock for Device<T> {
+ fn generateTimeStamp(&self, challenge: i64) -> binder::Result<TimeStampToken> {
+ let rsp: GenerateTimeStampResponse =
+ self.execute(GenerateTimeStampRequest { challenge })?;
+ Ok(rsp.ret.innto())
+ }
+}
diff --git a/hal/src/sharedsecret.rs b/hal/src/sharedsecret.rs
new file mode 100644
index 0000000..2a66ae4
--- /dev/null
+++ b/hal/src/sharedsecret.rs
@@ -0,0 +1,53 @@
+//! SharedSecret HAL device implementation.
+
+use crate::binder;
+use crate::hal::{
+ sharedsecret::{ISharedSecret, SharedSecretParameters::SharedSecretParameters},
+ Innto,
+};
+use crate::{ChannelHalService, SerializedChannel};
+use kmr_common::wire::*;
+use std::sync::{Arc, Mutex, MutexGuard};
+
+/// `ISharedSecret` implementation which converts all method invocations to serialized requests that
+/// are sent down the associated channel.
+pub struct Device<T: SerializedChannel + 'static> {
+ channel: Arc<Mutex<T>>,
+}
+
+impl<T: SerializedChannel + Send> binder::Interface for Device<T> {}
+
+impl<T: SerializedChannel + 'static> Device<T> {
+ /// Construct a new instance that uses the provided channel.
+ pub fn new(channel: Arc<Mutex<T>>) -> Self {
+ Self { channel }
+ }
+ /// Create a new instance wrapped in a proxy object.
+ pub fn new_as_binder(
+ channel: Arc<Mutex<T>>,
+ ) -> binder::Strong<dyn ISharedSecret::ISharedSecret> {
+ ISharedSecret::BnSharedSecret::new_binder(
+ Self::new(channel),
+ binder::BinderFeatures::default(),
+ )
+ }
+}
+
+impl<T: SerializedChannel> ChannelHalService<T> for Device<T> {
+ fn channel(&self) -> MutexGuard<T> {
+ self.channel.lock().unwrap()
+ }
+}
+
+impl<T: SerializedChannel> ISharedSecret::ISharedSecret for Device<T> {
+ fn getSharedSecretParameters(&self) -> binder::Result<SharedSecretParameters> {
+ let rsp: GetSharedSecretParametersResponse =
+ self.execute(GetSharedSecretParametersRequest {})?;
+ Ok(rsp.ret.innto())
+ }
+ fn computeSharedSecret(&self, params: &[SharedSecretParameters]) -> binder::Result<Vec<u8>> {
+ let rsp: ComputeSharedSecretResponse =
+ self.execute(ComputeSharedSecretRequest { params: params.to_vec().innto() })?;
+ Ok(rsp.ret)
+ }
+}
diff --git a/hal/src/tests.rs b/hal/src/tests.rs
new file mode 100644
index 0000000..1a645b3
--- /dev/null
+++ b/hal/src/tests.rs
@@ -0,0 +1,87 @@
+use super::*;
+use crate::{
+ binder,
+ hal::keymint::{ErrorCode::ErrorCode, IKeyMintDevice::IKeyMintDevice},
+};
+use kmr_common::hex_decode;
+use std::sync::{Arc, Mutex};
+
+#[derive(Clone, Debug)]
+struct TestChannel {
+ req: Arc<Mutex<Vec<u8>>>,
+ rsp: Vec<u8>,
+}
+
+impl TestChannel {
+ fn new(rsp: &str) -> Self {
+ Self { req: Arc::new(Mutex::new(vec![])), rsp: hex_decode(rsp).unwrap() }
+ }
+ fn req_data(&self) -> Vec<u8> {
+ self.req.lock().unwrap().clone()
+ }
+}
+
+impl SerializedChannel for TestChannel {
+ fn execute(&mut self, serialized_req: &[u8]) -> binder::Result<Vec<u8>> {
+ *self.req.lock().unwrap() = serialized_req.to_vec();
+ Ok(self.rsp.clone())
+ }
+}
+
+#[test]
+fn test_method_roundtrip() {
+ let channel = TestChannel::new(concat!(
+ "82", // 2-arr (PerformOpResponse)
+ "00", // int (PerformOpResponse.error_code == ErrorCode::Ok)
+ "81", // 1-arr (PerformOpResponse.rsp)
+ "82", // 2-arr (PerformOpResponse.rsp.0 : PerformOpRsp)
+ "13", // 0x13 = KeyMintOperation::DEVICE_GENERATE_KEY
+ "81", // 1-arr (GenerateKeyResponse)
+ "83", // 3-arr (ret: KeyCreationResult)
+ "41", "01", // 1-bstr (KeyCreationResult.keyBlob)
+ "80", // 0-arr (KeyCreationResult.keyCharacteristics)
+ "80", // 0-arr (KeyCreationResult.certificateChain)
+ ));
+ let imp = keymint::Device::new(Arc::new(Mutex::new(channel.clone())));
+
+ let result = imp.generateKey(&[], None).unwrap();
+
+ let want_req = concat!(
+ "82", // 2-arr (PerformOpReq)
+ "13", // 0x13 = DEVICE_GENERATE_KEY
+ "82", // 1-arr (GenerateKeyRequest)
+ "80", // 0-arr (* KeyParameter)
+ "80", // 0-arr (? AttestationKey)
+ );
+ assert_eq!(channel.req_data(), hex_decode(want_req).unwrap());
+
+ assert_eq!(result.keyBlob, vec![0x01]);
+ assert!(result.keyCharacteristics.is_empty());
+ assert!(result.certificateChain.is_empty());
+}
+
+#[test]
+fn test_method_err_roundtrip() {
+ let channel = TestChannel::new(concat!(
+ "82", // 2-arr (PerformOpResponse)
+ "21", // (PerformOpResponse.error_code = ErrorCode::UNSUPPORTED_PURPOSE)
+ "80", // 0-arr (PerformOpResponse.rsp)
+ ));
+ let imp = keymint::Device::new(Arc::new(Mutex::new(channel.clone())));
+
+ let result = imp.generateKey(&[], None);
+
+ let want_req = concat!(
+ "82", // 2-arr (PerformOpReq)
+ "13", // 0x13 = DEVICE_GENERATE_KEY
+ "82", // 1-arr (GenerateKeyRequest)
+ "80", // 0-arr (* KeyParameter)
+ "80", // 0-arr (? AttestationKey)
+ );
+ assert_eq!(channel.req_data(), hex_decode(want_req).unwrap());
+
+ assert!(result.is_err());
+ let status = result.unwrap_err();
+ assert_eq!(status.exception_code(), binder::ExceptionCode::SERVICE_SPECIFIC);
+ assert_eq!(status.service_specific_error(), ErrorCode::UNSUPPORTED_PURPOSE.0);
+}