Merge "secretkeeper_comm library: for Sk & client impl" into main
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),
}
}