secretkeeper_comm library: for Sk & client impl
The purpose of the library is to serve as helper library for
Secretkeeper HAL implementation as well as the client stub.
In this patch we implement (subset of) data structures specified by
SecretManagement subset of the HAL. In particular, we implement
GetVersionRequest, GetVersionResponse and SecretkeeperError data types.
Also included are the units tests for serialization/deserialization.
Test: atest libsecretkeeper_comm.test
Bug: 291228655
Change-Id: I33229867817eb45611c4db9b2e3005bc75670be0
diff --git a/TEST_MAPPING b/TEST_MAPPING
index a9193d7..6983fde 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -34,6 +34,9 @@
},
{
"name": "libapkzip.test"
+ },
+ {
+ "name": "libsecretkeeper_comm.test"
}
],
"avf-postsubmit": [
diff --git a/secretkeeper/comm/Android.bp b/secretkeeper/comm/Android.bp
new file mode 100644
index 0000000..cb3e713
--- /dev/null
+++ b/secretkeeper/comm/Android.bp
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_defaults {
+ name: "libsecretkeeper_comm.defaults",
+ crate_name: "secretkeeper_comm",
+ defaults: ["avf_build_flags_rust"],
+ edition: "2021",
+ lints: "android",
+ rustlibs: [
+ "libciborium",
+ "libcoset",
+ ],
+ proc_macros: ["libenumn"],
+ vendor_available: true,
+}
+
+rust_library {
+ name: "libsecretkeeper_comm_nostd",
+ defaults: ["libsecretkeeper_comm.defaults"],
+ srcs: ["src/lib.rs"],
+}
+
+rust_test {
+ name: "libsecretkeeper_comm.test",
+ defaults: [
+ "libsecretkeeper_comm.defaults",
+ "rdroidtest.defaults",
+ ],
+ srcs: ["tests/*.rs"],
+ test_suites: ["general-tests"],
+ rustlibs: [
+ "libsecretkeeper_comm_nostd",
+ ],
+}
diff --git a/secretkeeper/comm/src/cbor_convert.rs b/secretkeeper/comm/src/cbor_convert.rs
new file mode 100644
index 0000000..ab6ca3f
--- /dev/null
+++ b/secretkeeper/comm/src/cbor_convert.rs
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+//! Implements various useful CBOR conversion method.
+
+use crate::data_types::error::Error;
+use alloc::vec::Vec;
+use ciborium::Value;
+
+/// Decodes the provided binary CBOR-encoded value and returns a
+/// [`ciborium::Value`] struct wrapped in Result.
+pub fn value_from_bytes(mut bytes: &[u8]) -> Result<Value, Error> {
+ let value = ciborium::de::from_reader(&mut bytes).map_err(|_| Error::ConversionError)?;
+ // Ciborium tries to read one Value, but doesn't care if there is trailing data after it. We do
+ if !bytes.is_empty() {
+ return Err(Error::ConversionError);
+ }
+ Ok(value)
+}
+
+/// Encodes a [`ciborium::Value`] into bytes.
+pub fn value_to_bytes(value: &Value) -> Result<Vec<u8>, Error> {
+ let mut bytes: Vec<u8> = Vec::new();
+ ciborium::ser::into_writer(&value, &mut bytes).map_err(|_| Error::UnexpectedError)?;
+ Ok(bytes)
+}
+
+// Useful to convert [`ciborium::Value`] to integer, we return largest integer range for
+// convenience, callers should downcast into appropriate type.
+pub fn value_to_integer(value: &Value) -> Result<i128, Error> {
+ let num = value.as_integer().ok_or(Error::ConversionError)?.into();
+ Ok(num)
+}
diff --git a/secretkeeper/comm/src/data_types/error.rs b/secretkeeper/comm/src/data_types/error.rs
new file mode 100644
index 0000000..6a5e24f
--- /dev/null
+++ b/secretkeeper/comm/src/data_types/error.rs
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+//! Error-like data structures. See `ResponsePacketError` in the CDDL
+
+// derive(N) generates a method that is missing a docstring.
+#![allow(missing_docs)]
+
+use crate::cbor_convert::value_to_integer;
+use crate::data_types::response::Response;
+use alloc::boxed::Box;
+use alloc::vec::Vec;
+use ciborium::Value;
+use enumn::N;
+
+/// 'Error code' corresponding to successful response.
+pub const ERROR_OK: u16 = 0; // All real errors must have non-zero error_codes
+
+/// Errors from Secretkeeper API. Keep in sync with `ErrorCode` defined for Secretkeeper HAL
+/// at SecretManagement.cddl
+#[derive(Clone, Copy, Debug, Eq, N, PartialEq)]
+pub enum SecretkeeperError {
+ // This is the Error code used if no other error codes explains the issue.
+ UnexpectedServerError = 1,
+ // Indicates the Request was malformed & hence couldn't be served.
+ RequestMalformed = 2,
+ // TODO(b/291228655): Add other errors such as DicePolicyError.
+}
+
+// [`SecretkeeperError`] is a valid [`Response`] type.
+// For more information see `ErrorCode` in SecretManagement.cddl alongside ISecretkeeper.aidl
+impl Response for SecretkeeperError {
+ fn new(response_cbor: Vec<Value>) -> Result<Box<Self>, Error> {
+ // TODO(b/291228655): This method currently discards the second value in response_cbor,
+ // which contains additional human-readable context in error. Include it!
+ if response_cbor.is_empty() || response_cbor.len() > 2 {
+ return Err(Error::ResponseMalformed);
+ }
+ let error_code: u16 = value_to_integer(&response_cbor[0])?.try_into()?;
+ SecretkeeperError::n(error_code)
+ .map_or_else(|| Err(Error::ResponseMalformed), |sk_err| Ok(Box::new(sk_err)))
+ }
+
+ fn error_code(&self) -> u16 {
+ *self as u16
+ }
+}
+
+/// Errors thrown internally by the library.
+#[derive(Debug, PartialEq)]
+pub enum Error {
+ /// Request was malformed.
+ RequestMalformed,
+ /// Response received from the server was malformed.
+ ResponseMalformed,
+ /// An error happened when serializing to/from a [`Value`].
+ CborValueError,
+ /// An error happened while casting a type to different type,
+ /// including one [`Value`] type to another.
+ ConversionError,
+ /// These are unexpected errors, which should never really happen.
+ UnexpectedError,
+}
+
+impl From<ciborium::value::Error> for Error {
+ fn from(_e: ciborium::value::Error) -> Self {
+ Self::CborValueError
+ }
+}
+
+impl From<ciborium::Value> for Error {
+ fn from(_e: ciborium::Value) -> Self {
+ Self::ConversionError
+ }
+}
+
+impl From<core::num::TryFromIntError> for Error {
+ fn from(_e: core::num::TryFromIntError) -> Self {
+ Self::ConversionError
+ }
+}
diff --git a/secretkeeper/comm/src/data_types/mod.rs b/secretkeeper/comm/src/data_types/mod.rs
new file mode 100644
index 0000000..096777f
--- /dev/null
+++ b/secretkeeper/comm/src/data_types/mod.rs
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+//! Implements the data structures specified by SecretManagement.cddl in Secretkeeper HAL.
+//! Data structures specified by SecretManagement.cddl in Secretkeeper HAL.
+//! Note this library must stay in sync with:
+//! platform/hardware/interfaces/security/\
+//! secretkeeper/aidl/android/hardware/security/secretkeeper/SecretManagement.cddl
+
+pub mod error;
+pub mod packet;
+pub mod request;
+pub mod request_response_impl;
+pub mod response;
diff --git a/secretkeeper/comm/src/data_types/packet.rs b/secretkeeper/comm/src/data_types/packet.rs
new file mode 100644
index 0000000..7a1e575
--- /dev/null
+++ b/secretkeeper/comm/src/data_types/packet.rs
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+//! Defines the packet structures passed between functional layer & the layer below.
+
+pub use ciborium::Value;
+
+use crate::cbor_convert::{value_from_bytes, value_to_bytes, value_to_integer};
+use crate::data_types::error::Error;
+use crate::data_types::error::ERROR_OK;
+use crate::data_types::request_response_impl::Opcode;
+use alloc::vec::Vec;
+
+/// Encapsulate Request-like data that functional layer operates on. All structures
+/// that implements `data_types::request::Request` can be serialized to [`ResponsePacket`].
+/// Similarly all [`RequestPacket`] can be deserialized to concrete Requests.
+/// Keep in sync with HAL spec (in particular RequestPacket):
+/// security/secretkeeper/aidl/android/hardware/security/secretkeeper/SecretManagement.cddl
+#[derive(Clone, Debug, PartialEq)]
+pub struct RequestPacket(Vec<Value>);
+
+impl RequestPacket {
+ /// Construct a [`RequestPacket`] from array of `ciborium::Value`
+ pub fn from(request_cbor: Vec<Value>) -> Self {
+ Self(request_cbor)
+ }
+
+ /// Get the containing CBOR. This can be used for getting concrete response objects.
+ /// Keep in sync with [`crate::data_types::request::Request::serialize_to_packet()`]
+ pub fn into_inner(self) -> Vec<Value> {
+ self.0
+ }
+
+ /// Extract [`Opcode`] corresponding to this packet. As defined in by the spec, this is
+ /// the first value in the CBOR array.
+ pub fn opcode(&self) -> Result<Opcode, Error> {
+ if self.0.is_empty() {
+ return Err(Error::RequestMalformed);
+ }
+ let num: u16 = value_to_integer(&self.0[0])?.try_into()?;
+
+ Opcode::n(num).ok_or(Error::RequestMalformed)
+ }
+
+ /// Serialize the [`ResponsePacket`] to bytes
+ pub fn into_bytes(self) -> Result<Vec<u8>, Error> {
+ value_to_bytes(&Value::Array(self.0))
+ }
+
+ /// Deserialize the bytes into [`ResponsePacket`]
+ pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
+ Ok(RequestPacket(value_from_bytes(bytes)?.into_array()?))
+ }
+}
+
+/// Encapsulate Response like data that the functional layer operates on. All structures
+/// that implements `data_types::response::Response` can be serialized to [`ResponsePacket`].
+/// Similarly all [`ResponsePacket`] can be deserialized to concrete Response.
+#[derive(Clone, Debug, PartialEq)]
+pub struct ResponsePacket(Vec<Value>);
+
+impl ResponsePacket {
+ /// Construct a [`ResponsePacket`] from array of `ciborium::Value`
+ pub fn from(response_cbor: Vec<Value>) -> Self {
+ Self(response_cbor)
+ }
+
+ /// Get raw content. This can be used for getting concrete response objects.
+ /// Keep in sync with `crate::data_types::response::Response::serialize_to_packet`
+ pub fn into_inner(self) -> Vec<Value> {
+ self.0
+ }
+
+ /// A [`ResponsePacket`] encapsulates different types of responses, find which one!
+ pub fn response_type(&self) -> Result<ResponseType, Error> {
+ if self.0.is_empty() {
+ return Err(Error::ResponseMalformed);
+ }
+ let error_code: u16 = value_to_integer(&self.0[0])?.try_into()?;
+ if error_code == ERROR_OK {
+ Ok(ResponseType::Success)
+ } else {
+ Ok(ResponseType::Error)
+ }
+ }
+
+ /// Serialize the [`ResponsePacket`] to bytes
+ pub fn into_bytes(self) -> Result<Vec<u8>, Error> {
+ value_to_bytes(&Value::Array(self.0))
+ }
+
+ /// Deserialize the bytes into [`ResponsePacket`]
+ pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
+ Ok(ResponsePacket(value_from_bytes(bytes)?.into_array()?))
+ }
+}
+
+/// Responses can be different type - `Success`-like or `Error`-like.
+#[derive(Debug, Eq, PartialEq)]
+pub enum ResponseType {
+ /// Indicates successful operation. See `ResponsePacketSuccess` in SecretManagement.cddl
+ Success,
+ /// Indicate failed operation. See `ResponsePacketError` in SecretManagement.cddl
+ Error,
+}
diff --git a/secretkeeper/comm/src/data_types/request.rs b/secretkeeper/comm/src/data_types/request.rs
new file mode 100644
index 0000000..0d54bcd
--- /dev/null
+++ b/secretkeeper/comm/src/data_types/request.rs
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+//! Defines the shared behaviour of all request like data structures.
+
+use crate::data_types::error::Error;
+use crate::data_types::packet::RequestPacket;
+use crate::data_types::request_response_impl::Opcode;
+use alloc::boxed::Box;
+use alloc::vec::Vec;
+use ciborium::Value;
+
+/// Collection of methods defined for Secretkeeper's request-like data structures,
+/// e.g. `GetVersionRequestPacket` in the HAL spec.
+///
+/// Keep in sync with SecretManagement.cddl, in particular `RequestPacket` type.
+pub trait Request {
+ /// [`Opcode`] of the request: Each Request type is associated with an opcode. See `Opcode` in
+ /// SecretManagement.cddl.
+ const OPCODE: Opcode;
+
+ /// Constructor of the [`Request`] object. Implementation of this constructor should check
+ /// the args' type adheres to the HAL spec.
+ ///
+ /// # Arguments
+ /// * `args` - The vector of arguments associated with this request. Each argument is a
+ /// `ciborium::Value` type. See `Params` in `RequestPacket` in SecretManagement.cddl
+ fn new(args: Vec<Value>) -> Result<Box<Self>, Error>;
+
+ /// Get the 'arguments' of this request.
+ fn args(&self) -> Vec<Value>;
+
+ /// Serialize the request to a [`RequestPacket`], which, as per SecretManagement.cddl is:
+ /// ```
+ /// RequestPacket<Opcode, Params> = [
+ /// Opcode,
+ /// Params
+ /// ]
+ /// ```
+ fn serialize_to_packet(&self) -> RequestPacket {
+ let mut res = self.args();
+ res.insert(0, Value::from(Self::OPCODE as u16));
+ RequestPacket::from(res)
+ }
+
+ /// Construct the [`Request`] struct from given [`RequestPacket`].
+ fn deserialize_from_packet(packet: RequestPacket) -> Result<Box<Self>, Error> {
+ let mut req = packet.into_inner();
+ if req.get(0) != Some(&Value::from(Self::OPCODE as u16)) {
+ return Err(Error::RequestMalformed);
+ }
+ req.remove(0);
+ Self::new(req)
+ }
+}
diff --git a/secretkeeper/comm/src/data_types/request_response_impl.rs b/secretkeeper/comm/src/data_types/request_response_impl.rs
new file mode 100644
index 0000000..a7d29cc
--- /dev/null
+++ b/secretkeeper/comm/src/data_types/request_response_impl.rs
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+//! Implementation of request & response like data structures.
+
+// derive(N) generates a method that is missing a docstring.
+#![allow(missing_docs)]
+
+use crate::cbor_convert::value_to_integer;
+use crate::data_types::error::Error;
+use crate::data_types::error::ERROR_OK;
+use crate::data_types::request::Request;
+use crate::data_types::response::Response;
+use alloc::boxed::Box;
+use alloc::vec;
+use alloc::vec::Vec;
+use ciborium::Value;
+use enumn::N;
+
+/// Set of all possible `Opcode` supported by SecretManagement API of the HAL.
+/// See `Opcode` in SecretManagement.cddl
+#[derive(Clone, Copy, Debug, N, PartialEq)]
+#[non_exhaustive]
+pub enum Opcode {
+ /// Get version of the SecretManagement API.
+ GetVersion = 1,
+ /// Store a secret
+ StoreSecret = 2,
+ /// Get the secret
+ GetSecret = 3,
+}
+
+/// Corresponds to `GetVersionRequestPacket` defined in SecretManagement.cddl
+#[derive(Debug, Eq, PartialEq)]
+pub struct GetVersionRequest;
+
+impl Request for GetVersionRequest {
+ const OPCODE: Opcode = Opcode::GetVersion;
+
+ fn new(args: Vec<Value>) -> Result<Box<Self>, Error> {
+ if !args.is_empty() {
+ return Err(Error::RequestMalformed);
+ }
+ Ok(Box::new(Self))
+ }
+
+ fn args(&self) -> Vec<Value> {
+ Vec::new()
+ }
+}
+
+/// Success response corresponding to `GetVersionResponsePacket`.
+#[derive(Debug, Eq, PartialEq)]
+pub struct GetVersionResponse {
+ /// Version of SecretManagement API
+ version: u64,
+}
+
+impl GetVersionResponse {
+ pub fn new(version: u64) -> Self {
+ Self { version }
+ }
+ pub fn version(&self) -> u64 {
+ self.version
+ }
+}
+
+impl Response for GetVersionResponse {
+ fn new(res: Vec<Value>) -> Result<Box<Self>, Error> {
+ if res.len() != 2 {
+ return Err(Error::ResponseMalformed);
+ }
+ let error_code: u16 = value_to_integer(&res[0])?.try_into()?;
+ if error_code != ERROR_OK {
+ return Err(Error::ResponseMalformed);
+ }
+ let version: u64 = value_to_integer(&res[1])?.try_into()?;
+ Ok(Box::new(Self::new(version)))
+ }
+
+ fn result(&self) -> Vec<Value> {
+ vec![self.version.into()]
+ }
+}
diff --git a/secretkeeper/comm/src/data_types/response.rs b/secretkeeper/comm/src/data_types/response.rs
new file mode 100644
index 0000000..e975ebc
--- /dev/null
+++ b/secretkeeper/comm/src/data_types/response.rs
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+//! Defines the shared behaviour of all response like data structures.
+
+use crate::data_types::error::{Error, ERROR_OK};
+use crate::data_types::packet::ResponsePacket;
+use alloc::boxed::Box;
+use alloc::vec::Vec;
+use ciborium::Value;
+
+/// Shared behaviour of all Secretkeeper's response-like data structures,
+/// e.g. `GetVersionResponsePacket`. Note - A valid [`Response`] can be error as well, like
+/// `SecretkeeperError::RequestMalformed`.
+///
+/// Keep in sync with SecretManagement.cddl, in particular `ResponsePacket` type.
+pub trait Response {
+ /// Constructor of the Response object.
+ /// # Arguments
+ /// * `response_cbor`: A vector of `[ciborium::Value]` such that:
+ /// ```
+ /// For success-like responses:
+ /// ResponsePacketSuccess = [
+ /// 0, ; Indicates successful Response
+ /// result : Result
+ /// ]
+ /// For error responses:
+ /// ResponsePacketError = [
+ /// error_code: ErrorCode, ; Indicate the error
+ /// error_message: tstr ; Additional human-readable context
+ /// ]
+ /// ```
+ /// See ResponsePacket<Result> in SecretManagement.cddl alongside ISecretkeeper.aidl
+ fn new(response_cbor: Vec<Value>) -> Result<Box<Self>, Error>;
+
+ /// The result in the `Response`. By default this is empty, but [`Response`] structures like
+ /// `GetVersionResponse` must overwrite these to return the expected non-empty result.
+ fn result(&self) -> Vec<Value> {
+ Vec::new()
+ }
+
+ /// Error code corresponding to the response. The default value is 0 but that will work only
+ /// for successful responses. Error-like response structures must overwrite this method.
+ fn error_code(&self) -> u16 {
+ ERROR_OK // Indicates success
+ }
+
+ /// Serialize the response to a [`ResponsePacket`].
+ fn serialize_to_packet(&self) -> ResponsePacket {
+ let mut res = self.result();
+ res.insert(0, Value::from(self.error_code()));
+ ResponsePacket::from(res)
+ }
+
+ /// Construct the response struct from given [`ResponsePacket`].
+ fn deserialize_from_packet(packet: ResponsePacket) -> Result<Box<Self>, Error> {
+ let res = packet.into_inner();
+ // Empty response packet is not allowed, all responses in Secretkeeper HAL at least
+ // have `error_code` or '0'; so throw an error!
+ if res.is_empty() {
+ return Err(Error::ResponseMalformed);
+ }
+ Self::new(res)
+ }
+}
diff --git a/secretkeeper/comm/src/lib.rs b/secretkeeper/comm/src/lib.rs
new file mode 100644
index 0000000..9a10ac0
--- /dev/null
+++ b/secretkeeper/comm/src/lib.rs
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+//! This library exposes data structures and methods that can be used by Secretkeeper HAL & client
+//! implementation. This is compatible with Secretkeeper HAL specification.
+
+#![no_std]
+extern crate alloc;
+
+mod cbor_convert;
+pub mod data_types;
diff --git a/secretkeeper/comm/tests/data_types.rs b/secretkeeper/comm/tests/data_types.rs
new file mode 100644
index 0000000..68964fd
--- /dev/null
+++ b/secretkeeper/comm/tests/data_types.rs
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+//! Unit tests for testing serialization & deserialization of exported data_types.
+
+use ciborium::Value;
+use secretkeeper_comm::data_types::error::{Error, SecretkeeperError, ERROR_OK};
+use secretkeeper_comm::data_types::packet::{RequestPacket, ResponsePacket, ResponseType};
+use secretkeeper_comm::data_types::request::Request;
+use secretkeeper_comm::data_types::request_response_impl::Opcode;
+use secretkeeper_comm::data_types::request_response_impl::{GetVersionRequest, GetVersionResponse};
+use secretkeeper_comm::data_types::response::Response;
+
+#[cfg(test)]
+rdroidtest::test_main!();
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use rdroidtest::test;
+
+ test!(request_serialization_deserialization);
+ fn request_serialization_deserialization() {
+ let req = GetVersionRequest {};
+ let packet = req.serialize_to_packet();
+ assert_eq!(packet.opcode().unwrap(), Opcode::GetVersion);
+ assert_eq!(
+ RequestPacket::from_bytes(&packet.clone().into_bytes().unwrap()).unwrap(),
+ packet
+ );
+ let req_deserialized = *GetVersionRequest::deserialize_from_packet(packet).unwrap();
+ assert_eq!(req, req_deserialized);
+ }
+
+ test!(success_response_serialization_deserialization);
+ fn success_response_serialization_deserialization() {
+ let response = GetVersionResponse::new(1);
+ let packet = response.serialize_to_packet();
+ assert_eq!(packet.response_type().unwrap(), ResponseType::Success);
+ assert_eq!(
+ ResponsePacket::from_bytes(&packet.clone().into_bytes().unwrap()).unwrap(),
+ packet
+ );
+ let response_deserialized = *GetVersionResponse::deserialize_from_packet(packet).unwrap();
+ assert_eq!(response, response_deserialized);
+ }
+
+ test!(error_response_serialization_deserialization);
+ fn error_response_serialization_deserialization() {
+ let response = SecretkeeperError::RequestMalformed;
+ let packet = response.serialize_to_packet();
+ assert_eq!(packet.response_type().unwrap(), ResponseType::Error);
+ assert_eq!(
+ ResponsePacket::from_bytes(&packet.clone().into_bytes().unwrap()).unwrap(),
+ packet
+ );
+ let response_deserialized = *SecretkeeperError::deserialize_from_packet(packet).unwrap();
+ assert_eq!(response, response_deserialized);
+ }
+
+ test!(request_creation);
+ fn request_creation() {
+ let req: GetVersionRequest = *Request::new(vec![]).unwrap();
+ assert_eq!(req, GetVersionRequest {});
+ }
+
+ test!(response_creation);
+ fn response_creation() {
+ let res: GetVersionResponse =
+ *Response::new(vec![Value::from(ERROR_OK), Value::from(5)]).unwrap();
+ assert_eq!(res.version(), 5);
+ }
+
+ test!(invalid_get_version_request_creation);
+ fn invalid_get_version_request_creation() {
+ // A request with non-zero arg is considered invalid.
+ assert_eq!(
+ <GetVersionRequest as Request>::new(vec![Value::Null]).unwrap_err(),
+ Error::RequestMalformed
+ );
+ }
+
+ test!(invalid_get_version_response_creation);
+ fn invalid_get_version_response_creation() {
+ // A response with non-zero error_code is an invalid success response.
+ assert_eq!(
+ <GetVersionResponse as Response>::new(vec![
+ Value::from(SecretkeeperError::RequestMalformed as u16),
+ Value::from(5)
+ ])
+ .unwrap_err(),
+ Error::ResponseMalformed
+ );
+
+ // A response with incorrect size of array is invalid.
+ assert_eq!(
+ <GetVersionResponse as Response>::new(vec![
+ Value::from(ERROR_OK),
+ Value::from(5),
+ Value::from(7)
+ ])
+ .unwrap_err(),
+ Error::ResponseMalformed
+ );
+
+ // A response with incorrect type is invalid.
+ <GetVersionResponse as Response>::new(vec![Value::from(ERROR_OK), Value::from("a tstr")])
+ .unwrap_err();
+ }
+
+ test!(invalid_error_response_creation);
+ fn invalid_error_response_creation() {
+ // A response with ERROR_OK(0) as the error_code is an invalid error response.
+ assert_eq!(
+ <SecretkeeperError as Response>::new(vec![Value::from(ERROR_OK)]).unwrap_err(),
+ Error::ResponseMalformed
+ );
+ }
+}
diff --git a/secretkeeper/dice_policy/src/lib.rs b/secretkeeper/dice_policy/src/lib.rs
index 2e91305..076ba3b 100644
--- a/secretkeeper/dice_policy/src/lib.rs
+++ b/secretkeeper/dice_policy/src/lib.rs
@@ -213,10 +213,10 @@
ConstraintType::GreaterOrEqual => {
let value_in_node = value_in_node
.as_integer()
- .ok_or(anyhow!("Mismatch type: expected a cbor integer"))?;
+ .ok_or(anyhow!("Mismatch type: expected a CBOR integer"))?;
let value_min = value_in_constraint
.as_integer()
- .ok_or(anyhow!("Mismatch type: expected a cbor integer"))?;
+ .ok_or(anyhow!("Mismatch type: expected a CBOR integer"))?;
ensure!(value_in_node >= value_min);
}
};
@@ -260,9 +260,9 @@
Value::Bytes(b) => value_from_bytes(b)?
.into_map()
.map(Cow::Owned)
- .map_err(|e| anyhow!("Expected a cbor map: {:?}", e)),
+ .map_err(|e| anyhow!("Expected a CBOR map: {:?}", e)),
Value::Map(map) => Ok(Cow::Borrowed(map)),
- _ => bail!("/Expected a cbor map {:?}", cbor_map),
+ _ => bail!("Expected a CBOR map {:?}", cbor_map),
}
}