Adding keymint SetWrappedAttestationKey command
Added code to handle legacy provisioning command
SetWrappedAttestationKey.
Bug: 258602662
Test: Build.py, run keymint TA unittests
Change-Id: I88ed715fff1132b01ed08851964a7cf6df3acc09
diff --git a/bindings.h b/bindings.h
new file mode 100644
index 0000000..5e7c9bf
--- /dev/null
+++ b/bindings.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+// Definitions on this file will be processed during the Rust build to generate
+// a version of it that can be directly accessed from Rust code
+#include <interface/keybox/keybox.h>
\ No newline at end of file
diff --git a/ffi_bindings.rs b/ffi_bindings.rs
new file mode 100644
index 0000000..14c422e
--- /dev/null
+++ b/ffi_bindings.rs
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+//! Wrapper around autogenerated ffi bindings.
+
+// Get the bindgen definitions
+#[allow(non_upper_case_globals)]
+#[allow(non_camel_case_types)]
+#[allow(unused)]
+#[allow(deref_nullptr)] // https://github.com/rust-lang/rust-bindgen/issues/1651
+#[allow(unaligned_references)] // https://github.com/rust-lang/rust/issues/82523
+mod sys {
+ include!(env!("BINDGEN_INC_FILE"));
+}
+
+use core::mem;
+use kmr_common::try_to_vec;
+use kmr_wire::legacy::InnerSerialize;
+use log::error;
+use tipc::{Deserialize, Handle, Serialize, Serializer, TipcError};
+
+pub(crate) const KEYBOX_PORT: &'static [u8; 28] = sys::KEYBOX_PORT;
+
+type KeyboxReqHdr = sys::keybox_req;
+type KeyboxUnwrapReqPayloadHdr = sys::keybox_unwrap_req;
+
+pub(crate) const KEYBOX_RESP_HDR_SIZE: usize =
+ mem::size_of::<sys::keybox_resp>() + mem::size_of::<sys::keybox_unwrap_resp>();
+
+const KEYBOX_RESP_MAX_SIZE: usize = KEYBOX_RESP_HDR_SIZE + (sys::KEYBOX_MAX_SIZE as usize);
+
+// Because KeyboxUnwrapResp deserialization doesn't use the ffi types, we add this check in case the
+// c definitions change.
+const _: () = assert!(
+ KEYBOX_RESP_HDR_SIZE == (mem::size_of::<u32>() + mem::size_of::<u32>() + mem::size_of::<u64>()),
+ "KEYBOX_RESP_HDR_SIZE do not match KeyboxUnwrapResp deserialization assumptions"
+);
+
+// KeyboxUnwrapReq is a type created to be processed by Trusty IPC Rust serialization. This means
+// that any data that needs to be serialized when implementing the `Serialize` trait needs to
+// outlive its `serialize` function. A consequence of that is that the serialized values might need
+// some memory backup space until they have been transmitted.
+// We do not have this restriction on types we are deserializing (like `KeyboxUnwrapResp`); on which
+// this implementation prefers to use a type that can be directly used by the Rust code. This ends
+// up creating an asymmetry between requests and responses.
+pub(crate) struct KeyboxUnwrapReq<'a> {
+ req_header: KeyboxReqHdr,
+ unwrap_req_header: KeyboxUnwrapReqPayloadHdr,
+ wrapped_keybox: &'a [u8],
+}
+
+impl<'a> KeyboxUnwrapReq<'a> {
+ pub(crate) fn new(wrapped_keybox: &'a [u8]) -> Self {
+ let req_header = KeyboxReqHdr { cmd: sys::keybox_cmd_KEYBOX_CMD_UNWRAP, reserved: 0 };
+ let unwrap_req_header =
+ KeyboxUnwrapReqPayloadHdr { wrapped_keybox_len: wrapped_keybox.len() as u64 };
+ KeyboxUnwrapReq { req_header, unwrap_req_header, wrapped_keybox }
+ }
+}
+
+impl<'s> Serialize<'s> for KeyboxUnwrapReq<'s> {
+ fn serialize<'a: 's, S: Serializer<'s>>(
+ &'a self,
+ serializer: &mut S,
+ ) -> Result<S::Ok, S::Error> {
+ // SAFETY:
+ // - self.req_header.cmd and self.req_header.reserved are u32 and live long enough
+ // - self.unwrap_req_header.wrapped_keybox_len is a u64 and live long enough
+ unsafe {
+ serializer.serialize_as_bytes(&self.req_header.cmd)?;
+ serializer.serialize_as_bytes(&self.req_header.reserved)?;
+ serializer.serialize_as_bytes(&self.unwrap_req_header.wrapped_keybox_len)?;
+ }
+ serializer.serialize_bytes(self.wrapped_keybox)
+ }
+}
+
+pub(crate) struct KeyboxUnwrapResp {
+ unwrapped_keybox: Vec<u8>,
+}
+
+impl KeyboxUnwrapResp {
+ pub(crate) fn get_unwrapped_keybox(self) -> Vec<u8> {
+ self.unwrapped_keybox
+ }
+}
+
+impl Deserialize for KeyboxUnwrapResp {
+ type Error = TipcError;
+ const MAX_SERIALIZED_SIZE: usize = KEYBOX_RESP_MAX_SIZE;
+ fn deserialize(bytes: &[u8], _handles: &mut [Option<Handle>]) -> Result<Self, Self::Error> {
+ let (cmd, bytes) = <u32>::deserialize(bytes).map_err(|e| {
+ error!("received error when deserializing cmd: {:?}", e);
+ TipcError::UnknownError
+ })?;
+ if cmd != (sys::keybox_cmd_KEYBOX_CMD_UNWRAP | sys::keybox_cmd_KEYBOX_CMD_RSP_BIT) {
+ error!("Keybox unwrap deserialization received wrong cmd: {:?}", cmd);
+ return Err(TipcError::UnknownError);
+ }
+
+ let (status, bytes) = <u32>::deserialize(bytes).map_err(|e| {
+ error!("received error when deserializing status: {:?}", e);
+ TipcError::UnknownError
+ })?;
+ if status != sys::keybox_status_KEYBOX_STATUS_SUCCESS {
+ error!(" Keybox unwrap cmd failed: {:?}", status);
+ return Err(TipcError::UnknownError);
+ }
+
+ let (unwrapped_keybox_len, bytes) = <u64>::deserialize(bytes).map_err(|e| {
+ error!("received error when deserializing status: {:?}", e);
+ TipcError::UnknownError
+ })?;
+ if unwrapped_keybox_len as usize != bytes.len() {
+ error!(
+ "unwrapped keybox had wrong size. Expected: {:?}, Received: {:?}",
+ unwrapped_keybox_len,
+ bytes.len()
+ );
+ return Err(TipcError::UnknownError);
+ }
+
+ let unwrapped_keybox = try_to_vec(bytes).map_err(|e| {
+ error!("received error when trying to copy unwrapped keybox into vector: {:?}", e);
+ TipcError::AllocError
+ })?;
+
+ Ok(Self { unwrapped_keybox })
+ }
+}
diff --git a/ipc_manager.rs b/ipc_manager.rs
index b83899c..0d4a062 100644
--- a/ipc_manager.rs
+++ b/ipc_manager.rs
@@ -27,8 +27,8 @@
ConfigureBootPatchlevelResponse, GetAuthTokenKeyResponse, GetDeviceInfoResponse,
GetVersion2Response, GetVersionResponse, SetAttestationIdsKM3Response,
SetAttestationIdsResponse, SetAttestationKeyResponse, SetBootParamsResponse,
- TrustyMessageId, TrustyPerformOpReq, TrustyPerformOpRsp, TrustyPerformSecureOpReq,
- TrustyPerformSecureOpRsp,
+ SetWrappedAttestationKeyResponse, TrustyMessageId, TrustyPerformOpReq, TrustyPerformOpRsp,
+ TrustyPerformSecureOpReq, TrustyPerformSecureOpRsp,
},
Error,
};
@@ -341,6 +341,13 @@
ClearAttestationCertChainResponse {},
))
}
+ TrustyPerformOpReq::SetWrappedAttestationKey(req) => {
+ let algorithm = keymaster_algorithm_to_signing_algorithm(req.algorithm)?;
+ secure_storage_manager::set_wrapped_attestation_key(algorithm, &req.key_data)?;
+ Ok(TrustyPerformOpRsp::SetWrappedAttestationKey(
+ SetWrappedAttestationKeyResponse {},
+ ))
+ }
TrustyPerformOpReq::SetAttestationIds(req) => {
secure_storage_manager::provision_attestation_id_file(
&req.brand,
@@ -369,9 +376,6 @@
)?;
Ok(TrustyPerformOpRsp::SetAttestationIdsKM3(SetAttestationIdsKM3Response {}))
}
- // TODO: Check if we need to support other provisioning messages:
- // (SetWrappedAttestationKey)
- _ => Err(km_err!(Unimplemented, "received command {:?} not supported", cmd_code)),
}
}
}
diff --git a/keybox.rs b/keybox.rs
new file mode 100644
index 0000000..afcaa4d
--- /dev/null
+++ b/keybox.rs
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+//! Client used to connect to the keybox service.
+use crate::ffi_bindings::{KeyboxUnwrapReq, KeyboxUnwrapResp, KEYBOX_PORT, KEYBOX_RESP_HDR_SIZE};
+use core::ffi::CStr;
+use kmr_common::{km_err, vec_try, Error};
+use tipc::{Handle, TipcError};
+
+/// A `KeyboxSession` is a `Handle`.
+type KeyboxSession = Handle;
+/// Connection to the keybox service.
+#[derive(Debug, Eq, PartialEq)]
+struct Keybox(KeyboxSession);
+
+impl Keybox {
+ /// Attempt to open a keybox session.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// let keybox = keybox::open().expect("could not open hwkey session");
+ /// ```
+ ///
+ fn new() -> Result<Self, TipcError> {
+ let port =
+ CStr::from_bytes_with_nul(KEYBOX_PORT).expect("KEYBOX_PORT was not null terminated");
+ KeyboxSession::connect(port).map(Self)
+ }
+
+ pub(crate) fn keybox_unwrap(&self, wrapped_keybox: &[u8]) -> Result<Vec<u8>, Error> {
+ let req = KeyboxUnwrapReq::new(wrapped_keybox);
+ self.0
+ .send(&req)
+ .map_err(|e| km_err!(SecureHwCommunicationFailed, "send unwrap cmd failed: {:?}", e))?;
+ // This uses the same assumption as SetWrappedAttestationKey on the c++ code; which is that
+ // the size of the unwrapped key won't be bigger than the size of the wrapped one.
+ let mut buffer = vec_try![0; wrapped_keybox.len() + KEYBOX_RESP_HDR_SIZE]?;
+ let response: KeyboxUnwrapResp = self
+ .0
+ .recv(&mut buffer)
+ .map_err(|e| km_err!(SecureHwCommunicationFailed, "unwrap response error: {:?}", e))?;
+ Ok(response.get_unwrapped_keybox())
+ }
+}
+
+pub(crate) fn keybox_unwrap(wrapped_keybox: &[u8]) -> Result<Vec<u8>, Error> {
+ let keybox = Keybox::new().map_err(|e| {
+ km_err!(SecureHwCommunicationFailed, "error opening keybox service: {:?}", e)
+ })?;
+ keybox.keybox_unwrap(wrapped_keybox)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use test::expect_eq;
+
+ // AOSP keybox test server just XORs data with a constant and checksum it, it is not intended to
+ // be secure; just to be used to check the IPC commands.
+ fn create_fake_wrapped_data(data: &[u8]) -> Vec<u8> {
+ let mut wrapped_data = Vec::<u8>::new();
+ let mut checksum: u8 = 0;
+ for &element in data {
+ let wrapped_element = element ^ 0x42;
+ wrapped_data.push(wrapped_element);
+ checksum ^= wrapped_element;
+ }
+ wrapped_data.push(checksum);
+ wrapped_data
+ }
+
+ #[test]
+ fn unwrap_fake_keybox_data() {
+ let original_data = b"test_data_to_wrap".as_slice();
+ let wrapped_data = create_fake_wrapped_data(original_data);
+ let unwrapped_data = keybox_unwrap(&wrapped_data).expect("Couldn't unwrap data");
+ expect_eq!(original_data, unwrapped_data, "Unwrapped data do not match original one");
+ }
+}
diff --git a/lib.rs b/lib.rs
index c527b85..8e140e7 100644
--- a/lib.rs
+++ b/lib.rs
@@ -17,8 +17,10 @@
//! This library implements the functionality used by the KeyMint Trusty
//! application.
+mod ffi_bindings;
mod ipc_manager;
mod key_wrapper;
+mod keybox;
mod keymaster_attributes;
mod keys;
mod monotonic_clock;
diff --git a/rules.mk b/rules.mk
index ef05620..f4a0f08 100644
--- a/rules.mk
+++ b/rules.mk
@@ -25,6 +25,7 @@
MODULE_CRATE_NAME := keymint
MODULE_LIBRARY_DEPS += \
+ trusty/user/base/interface/keybox \
trusty/user/base/lib/hwbcc/rust \
trusty/user/base/lib/hwkey/rust \
trusty/user/base/lib/hwwsk/rust \
@@ -51,4 +52,12 @@
MODULE_RUST_TESTS := true
+MODULE_BINDGEN_ALLOW_TYPES := \
+ keybox.* \
+
+MODULE_BINDGEN_ALLOW_VARS := \
+ KEYBOX.* \
+
+MODULE_BINDGEN_SRC_HEADER := $(LOCAL_DIR)/bindings.h
+
include make/library.mk
diff --git a/secure_storage_manager.rs b/secure_storage_manager.rs
index aeebb50..0c969b5 100644
--- a/secure_storage_manager.rs
+++ b/secure_storage_manager.rs
@@ -14,6 +14,7 @@
* limitations under the License.
*/
//! Module used to interact with keymint secure storage data.
+use crate::keybox;
use crate::keymaster_attributes;
use alloc::{format, string::String, vec::Vec};
use kmr_common::{
@@ -137,6 +138,15 @@
Ok(())
}
+/// Unwraps a keybox wrapped key and uses it to provision the key on the device.
+pub(crate) fn set_wrapped_attestation_key(
+ algorithm: SigningAlgorithm,
+ key_data: &[u8],
+) -> Result<(), Error> {
+ let unwrapped_key = keybox::keybox_unwrap(key_data)?;
+ provision_attestation_key_file(algorithm, &unwrapped_key)
+}
+
/// Tries to read the file containing the attestation key and certificates and only replaces the key
/// section. If the file doesn't exist it will create it and save the provided key.
pub(crate) fn provision_attestation_key_file(