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(