Snap for 11216811 from 45d1b2ecfc3708f47b2ef8bfd41317f3cf551ed2 to 24Q1-release
Change-Id: I807817bfc16bf4d87b65dfe2c9cd7fbc036903bb
diff --git a/comm/InternalHalTaMessages.cddl b/comm/InternalHalTaMessages.cddl
new file mode 100644
index 0000000..7225382
--- /dev/null
+++ b/comm/InternalHalTaMessages.cddl
@@ -0,0 +1,25 @@
+; Clients of the HAL service call various methods on the HAL service; these
+; method invocations are internally translated into request messages
+; encoded as a CBOR 2-array holding:
+; - An integer indicating which request is present.
+; - An inner object holding the content for that specific request.
+PerformOpReq =
+ [SecretManagementOpCode, ProtectedRequestPacket] /
+ [DeleteIdsOpCode, [*SecretId]] /
+ [DeleteAllOpCode, nil]
+
+SecretManagementOpCode = 0x10
+DeleteIdsOpCode = 0x11
+DeleteAllOpCode = 0x12
+
+; Internally, a HAL request corresponds to a response message encoded as a CBOR 2-array holding:
+; - An integer return code value.
+; - One of:
+; - If the return code is zero: a result value.
+; - If the return code is non-zero: an error message.
+PerformOpResponse =
+ [0, PerformOpSuccessRsp] /
+ [error_code: int .ne 0, error_message: tstr]
+
+; The content of an OK response depends on the corresponding `HalRequest` message
+PerformOpSuccessRsp = ProtectedResponsePacket / nil
diff --git a/comm/src/lib.rs b/comm/src/lib.rs
index 9a10ac0..b1d0893 100644
--- a/comm/src/lib.rs
+++ b/comm/src/lib.rs
@@ -22,3 +22,4 @@
mod cbor_convert;
pub mod data_types;
+pub mod wire;
diff --git a/comm/src/wire.rs b/comm/src/wire.rs
new file mode 100644
index 0000000..3559f6e
--- /dev/null
+++ b/comm/src/wire.rs
@@ -0,0 +1,249 @@
+/*
+ * 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.
+ */
+
+//! Rust types used for CBOR-encoded communication between HAL and TA,
+//! corresponding to the schema in `comm/InternalHalTaMessages.cddl`.
+
+#![allow(missing_docs)] // needed for `enumn::N`, sadly
+
+use alloc::{string::String, vec, vec::Vec};
+use ciborium::value::Value;
+use coset::{AsCborValue, CborSerializable, CoseError};
+use enumn::N;
+
+/// Wrapper type for communicating requests between the HAL service and the TA.
+/// This is an internal implementation detail, and is not visible on the API.
+#[derive(Debug, Clone)]
+pub enum PerformOpReq {
+ /// A secret management request holds a CBOR-encoded `COSE_Encrypt0`.
+ SecretManagement(Vec<u8>),
+
+ /// A (plaintext) request to delete some `SecretId`s.
+ DeleteIds(Vec<SecretId>),
+
+ /// A (plaintext) request to delete all data.
+ DeleteAll,
+}
+
+impl PerformOpReq {
+ pub fn code(&self) -> OpCode {
+ match self {
+ Self::SecretManagement(_) => OpCode::SecretManagement,
+ Self::DeleteIds(_) => OpCode::DeleteIds,
+ Self::DeleteAll => OpCode::DeleteAll,
+ }
+ }
+}
+
+impl AsCborValue for PerformOpReq {
+ fn to_cbor_value(self) -> Result<Value, CoseError> {
+ Ok(Value::Array(match self {
+ Self::SecretManagement(encrypt0) => {
+ vec![OpCode::SecretManagement.to_cbor_value()?, Value::Bytes(encrypt0)]
+ }
+ Self::DeleteIds(ids) => {
+ vec![
+ OpCode::DeleteIds.to_cbor_value()?,
+ Value::Array(
+ ids.into_iter().map(|id| Value::Bytes(id.to_vec())).collect::<Vec<Value>>(),
+ ),
+ ]
+ }
+ Self::DeleteAll => vec![OpCode::DeleteAll.to_cbor_value()?, Value::Null],
+ }))
+ }
+
+ fn from_cbor_value(value: Value) -> Result<Self, CoseError> {
+ let mut a = match value {
+ Value::Array(a) if a.len() == 2 => a,
+ _ => return cbor_type_error(&value, "arr len 2"),
+ };
+ let val = a.remove(1);
+ let code = OpCode::from_cbor_value(a.remove(0))?;
+ Ok(match code {
+ OpCode::SecretManagement => Self::SecretManagement(match val {
+ Value::Bytes(b) => b,
+ _ => return cbor_type_error(&val, "bstr"),
+ }),
+ OpCode::DeleteIds => {
+ let ids = match &val {
+ Value::Array(a) => a,
+ _ => return cbor_type_error(&val, "arr"),
+ };
+ let ids = ids
+ .iter()
+ .map(|id| match &id {
+ Value::Bytes(b) => SecretId::try_from(b.as_slice())
+ .map_err(|_e| CoseError::OutOfRangeIntegerValue),
+ _ => cbor_type_error(&val, "bstr"),
+ })
+ .collect::<Result<Vec<_>, _>>()?;
+ Self::DeleteIds(ids)
+ }
+ OpCode::DeleteAll => {
+ if !val.is_null() {
+ return cbor_type_error(&val, "nil");
+ }
+ Self::DeleteAll
+ }
+ })
+ }
+}
+
+impl CborSerializable for PerformOpReq {}
+
+/// Op code value to distinguish requests.
+#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, N)]
+pub enum OpCode {
+ SecretManagement = 0x10,
+ DeleteIds = 0x11,
+ DeleteAll = 0x12,
+}
+
+impl AsCborValue for OpCode {
+ fn to_cbor_value(self) -> Result<Value, CoseError> {
+ Ok(Value::Integer((self as i32).into()))
+ }
+
+ fn from_cbor_value(value: Value) -> Result<Self, CoseError> {
+ let i = match value {
+ Value::Integer(i) => i,
+ _ => return cbor_type_error(&value, "int"),
+ };
+ let code: i32 = i.try_into().map_err(|_| CoseError::OutOfRangeIntegerValue)?;
+ OpCode::n(code).ok_or(CoseError::OutOfRangeIntegerValue)
+ }
+}
+
+/// Wrapper type for communicating responses between the HAL service and the TA.
+/// This is an internal implementation detail, and is not visible on the API.
+#[derive(Debug, Clone)]
+pub enum PerformOpResponse {
+ Success(PerformOpSuccessRsp),
+ Failure(ApiError),
+}
+
+impl PerformOpResponse {
+ pub fn err_code(&self) -> AidlErrorCode {
+ match self {
+ Self::Success(_) => AidlErrorCode::Ok,
+ Self::Failure(err) => err.err_code,
+ }
+ }
+}
+
+impl AsCborValue for PerformOpResponse {
+ fn to_cbor_value(self) -> Result<Value, CoseError> {
+ Ok(match self {
+ Self::Success(rsp) => {
+ Value::Array(vec![Value::Integer(0.into()), rsp.to_cbor_value()?])
+ }
+ Self::Failure(err) => Value::Array(vec![
+ Value::Integer((err.err_code as i32).into()),
+ Value::Text(err.msg),
+ ]),
+ })
+ }
+
+ fn from_cbor_value(value: Value) -> Result<Self, CoseError> {
+ let mut a = match value {
+ Value::Array(a) if a.len() == 2 => a,
+ _ => return cbor_type_error(&value, "arr len 2"),
+ };
+ let val = a.remove(1);
+ let err_code =
+ a.remove(0).as_integer().ok_or(CoseError::UnexpectedItem("non-int", "int"))?;
+ let err_code: i32 = err_code.try_into().map_err(|_| CoseError::OutOfRangeIntegerValue)?;
+ Ok(match err_code {
+ 0 => Self::Success(PerformOpSuccessRsp::from_cbor_value(val)?),
+ err_code => {
+ let msg = match val {
+ Value::Text(t) => t,
+ _ => return cbor_type_error(&val, "tstr"),
+ };
+ let err_code = AidlErrorCode::n(err_code).unwrap_or(AidlErrorCode::InternalError);
+ Self::Failure(ApiError { err_code, msg })
+ }
+ })
+ }
+}
+
+impl CborSerializable for PerformOpResponse {}
+
+/// Inner response type holding the result of a successful request.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum PerformOpSuccessRsp {
+ ProtectedResponse(Vec<u8>),
+ Empty,
+}
+
+impl AsCborValue for PerformOpSuccessRsp {
+ fn to_cbor_value(self) -> Result<Value, CoseError> {
+ Ok(match self {
+ Self::ProtectedResponse(data) => Value::Bytes(data),
+ Self::Empty => Value::Null,
+ })
+ }
+
+ fn from_cbor_value(value: Value) -> Result<Self, CoseError> {
+ match value {
+ Value::Bytes(data) => Ok(Self::ProtectedResponse(data)),
+ Value::Null => Ok(Self::Empty),
+ _ => cbor_type_error(&value, "bstr/nil"),
+ }
+ }
+}
+
+impl CborSerializable for PerformOpSuccessRsp {}
+
+/// Return an error indicating that an unexpected CBOR type was encountered.
+pub fn cbor_type_error<T>(got: &Value, want: &'static str) -> Result<T, CoseError> {
+ let got = match got {
+ Value::Integer(_) => "int",
+ Value::Bytes(_) => "bstr",
+ Value::Text(_) => "tstr",
+ Value::Array(_) => "array",
+ Value::Map(_) => "map",
+ Value::Tag(_, _) => "tag",
+ Value::Float(_) => "float",
+ Value::Bool(_) => "bool",
+ Value::Null => "null",
+ _ => "unknown",
+ };
+ Err(CoseError::UnexpectedItem(got, want))
+}
+
+/// Identifier for a secret
+pub type SecretId = [u8; 64];
+
+/// Error information reported visibly on the external API.
+#[derive(Debug, Clone)]
+pub struct ApiError {
+ pub err_code: AidlErrorCode,
+ pub msg: String,
+}
+
+/// Error codes emitted as service specific errors at the HAL.
+/// Keep in sync with:
+/// hardware/interfaces/security/secretkeeper/aidl/
+/// android/hardware/security/secretkeeper/AidlErrorCode.aidl
+#[derive(Debug, Clone, Copy, N)]
+pub enum AidlErrorCode {
+ Ok = 0,
+ UnknownKeyId = 1,
+ InternalError = 2,
+ RequestMalformed = 3,
+}
diff --git a/core/src/cipher.rs b/core/src/cipher.rs
new file mode 100644
index 0000000..fb7eaab
--- /dev/null
+++ b/core/src/cipher.rs
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+//! Utilities for encryption and decryption.
+
+use crate::aidl_err;
+use alloc::vec::Vec;
+use authgraph_core::key::{AesKey, Nonce12};
+use authgraph_core::traits::{AesGcm, Rng};
+use coset::{iana, CborSerializable, CoseEncrypt0, CoseEncrypt0Builder, HeaderBuilder};
+use secretkeeper_comm::wire::{AidlErrorCode, ApiError};
+
+/// Decrypt a message.
+pub fn decrypt_message(
+ aes_gcm: &dyn AesGcm,
+ key: &AesKey,
+ encrypt0: &CoseEncrypt0,
+) -> Result<Vec<u8>, ApiError> {
+ let nonce12 = Nonce12::try_from(&encrypt0.unprotected.iv[..])
+ .map_err(|e| aidl_err!(InternalError, "nonce of unexpected size: {e:?}"))?;
+ encrypt0
+ .decrypt(&[], |ct, aad| aes_gcm.decrypt(key, ct, aad, &nonce12))
+ .map_err(|e| aidl_err!(InternalError, "failed to decrypt message: {e:?}"))
+}
+
+/// Encrypt a message.
+pub fn encrypt_message(
+ aes_gcm: &dyn AesGcm,
+ rng: &dyn Rng,
+ key: &AesKey,
+ session_id: &[u8],
+ msg: &[u8],
+) -> Result<Vec<u8>, ApiError> {
+ let nonce12 = Nonce12::new(rng);
+ let encrypt0 = CoseEncrypt0Builder::new()
+ .protected(
+ HeaderBuilder::new()
+ .algorithm(iana::Algorithm::A256GCM)
+ .key_id(session_id.to_vec())
+ .build(),
+ )
+ .unprotected(HeaderBuilder::new().iv(nonce12.0.to_vec()).build())
+ .try_create_ciphertext(msg, &[], |plaintext, aad| {
+ aes_gcm.encrypt(key, plaintext, aad, &nonce12)
+ })
+ .map_err(|e| aidl_err!(InternalError, "failed to encrypt message: {e:?}"))?
+ .build();
+ encrypt0
+ .to_vec()
+ .map_err(|e| aidl_err!(InternalError, "failed to serialize CoseEncrypt0: {e:?}"))
+}
diff --git a/core/src/lib.rs b/core/src/lib.rs
index 8868eed..99f7da1 100644
--- a/core/src/lib.rs
+++ b/core/src/lib.rs
@@ -20,5 +20,6 @@
#![no_std]
extern crate alloc;
+pub mod cipher;
pub mod store;
pub mod ta;
diff --git a/core/src/ta.rs b/core/src/ta.rs
index 2e30aa4..01161d9 100644
--- a/core/src/ta.rs
+++ b/core/src/ta.rs
@@ -16,9 +16,11 @@
//! Implementation of a SecretKeeper trusted application (TA).
+use crate::cipher;
use crate::store::{KeyValueStore, PolicyGatedStorage};
use alloc::boxed::Box;
use alloc::collections::VecDeque;
+use alloc::string::ToString;
use alloc::vec::Vec;
use authgraph_core::ag_err;
use authgraph_core::error::Error;
@@ -28,8 +30,8 @@
};
use authgraph_core::traits::{AesGcm, CryptoTraitImpl, Device, EcDsa, Rng, Sha256};
use authgraph_wire::{ErrorCode, SESSION_ID_LEN};
-use coset::{iana, CborSerializable};
-use log::{error, warn};
+use coset::{iana, CborSerializable, CoseEncrypt0};
+use log::{error, trace, warn};
use secretkeeper_comm::data_types::{
error::Error as SkInternalError,
error::SecretkeeperError,
@@ -41,6 +43,9 @@
},
response::Response,
};
+use secretkeeper_comm::wire::{
+ AidlErrorCode, ApiError, PerformOpReq, PerformOpResponse, PerformOpSuccessRsp, SecretId,
+};
/// Current Secretkeeper version.
const CURRENT_VERSION: u64 = 1;
@@ -61,13 +66,24 @@
0x65, 0x66,
];
+/// Macro to build an [`ApiError`] instance.
+/// E.g. use: `aidl_err!(InternalError, "some {} format", arg)`.
+#[macro_export]
+macro_rules! aidl_err {
+ { $error_code:ident, $($arg:tt)+ } => {
+ ApiError {
+ err_code: AidlErrorCode::$error_code,
+ msg: alloc::format!("{}:{}: {}", file!(), line!(), format_args!($($arg)+))
+ }
+ };
+}
+
/// Secretkeeper trusted application instance.
pub struct SecretkeeperTa {
/// AES-GCM implementation.
aes_gcm: Box<dyn AesGcm>,
/// RNG implementation.
- #[allow(dead_code)] // TODO: remove this
rng: Box<dyn Rng>,
/// AuthGraph per-boot-key.
@@ -129,19 +145,72 @@
})
}
- /// Process a single serialized request, returning a serialized response.
- pub fn process(&mut self, req: &[u8]) -> Vec<u8> {
- // TODO(b/291224769) The request will need to be decrypted & response need to be encrypted
- // with key & related artifacts pre-shared via Authgraph Key Exchange HAL.
- self.process_inner(req).unwrap_or_else(
- // SecretkeeperError is also a valid 'Response', serialize to a response packet.
- |sk_err| {
- Response::serialize_to_packet(&sk_err)
- .to_vec()
- .expect("Panicking due to serialization failing")
- },
- )
+ /// Process a single serialized request, returning a serialized response (even on failure).
+ pub fn process(&mut self, req_data: &[u8]) -> Vec<u8> {
+ let (req_code, rsp) = match PerformOpReq::from_slice(req_data) {
+ Ok(req) => {
+ trace!("-> TA: received request {:?}", req.code());
+ (Some(req.code()), self.process_req(req))
+ }
+ Err(e) => {
+ error!("failed to decode CBOR request: {:?}", e);
+ (None, PerformOpResponse::Failure(aidl_err!(InternalError, "CBOR decode failure")))
+ }
+ };
+ trace!("<- TA: send response for {:?} rc {:?}", req_code, rsp.err_code());
+ rsp.to_vec().unwrap_or_else(|e| {
+ error!("failed to encode CBOR response: {:?}", e);
+ invalid_cbor_rsp_data().to_vec()
+ })
}
+
+ /// Process a single request, returning a [`PerformOpResponse`].
+ fn process_req(&mut self, req: PerformOpReq) -> PerformOpResponse {
+ let code = req.code();
+ let result = match req {
+ PerformOpReq::SecretManagement(encrypt0) => {
+ self.secret_management(&encrypt0).map(PerformOpSuccessRsp::ProtectedResponse)
+ }
+ PerformOpReq::DeleteIds(ids) => {
+ self.delete_ids(ids).map(|_| PerformOpSuccessRsp::Empty)
+ }
+ PerformOpReq::DeleteAll => self.delete_all().map(|_| PerformOpSuccessRsp::Empty),
+ };
+ match result {
+ Ok(rsp) => PerformOpResponse::Success(rsp),
+ Err(err) => {
+ warn!("failing {:?} request: {:?}", code, err);
+ PerformOpResponse::Failure(err)
+ }
+ }
+ }
+
+ fn delete_ids(&mut self, ids: Vec<SecretId>) -> Result<(), ApiError> {
+ Err(ApiError {
+ err_code: AidlErrorCode::InternalError,
+ msg: alloc::format!("TODO with {ids:?}"),
+ })
+ }
+
+ fn delete_all(&mut self) -> Result<(), ApiError> {
+ Err(ApiError { err_code: AidlErrorCode::InternalError, msg: "TODO".to_string() })
+ }
+
+ fn secret_management(&mut self, encrypt0: &[u8]) -> Result<Vec<u8>, ApiError> {
+ let (req, session_keys) = self.decrypt_request(encrypt0)?;
+ let result = self.process_inner(&req);
+
+ // An inner failure still converts to a response message that gets encrypted.
+ let rsp_data = match result {
+ Ok(data) => data,
+ Err(e) => e
+ .serialize_to_packet()
+ .to_vec()
+ .map_err(|_e| aidl_err!(InternalError, "failed to encode err rsp"))?,
+ };
+ self.encrypt_response(&session_keys, &rsp_data)
+ }
+
fn process_inner(&mut self, req: &[u8]) -> Result<Vec<u8>, SecretkeeperError> {
let req_packet = RequestPacket::from_slice(req).map_err(|e| {
error!("Failed to get Request packet from bytes: {e:?}");
@@ -154,7 +223,6 @@
Opcode::GetSecret => self.get_secret(req_packet)?,
_ => panic!("Unknown operation.."),
};
-
rsp_packet.to_vec().map_err(|_| SecretkeeperError::UnexpectedServerError)
}
@@ -199,10 +267,34 @@
Ok(response.serialize_to_packet())
}
- #[allow(dead_code)] // TODO: remove this
+ // "SSL added and removed here :-)"
fn keys_for_session(&self, session_id: &[u8; SESSION_ID_LEN]) -> Option<&SessionKeyInfo> {
self.session_keys.iter().find(|info| info.session_id == *session_id)
}
+ fn decrypt_request(&self, req: &[u8]) -> Result<(Vec<u8>, SessionKeyInfo), ApiError> {
+ let encrypt0 = CoseEncrypt0::from_slice(req)
+ .map_err(|_e| aidl_err!(RequestMalformed, "malformed COSE_Encrypt0"))?;
+ let session_keys = self
+ .keys_for_session(&encrypt0.protected.header.key_id[..].try_into().map_err(|e| {
+ aidl_err!(RequestMalformed, "session key of unexpected size: {e:?}")
+ })?)
+ .ok_or_else(|| aidl_err!(UnknownKeyId, "session key not found"))?;
+ let payload = cipher::decrypt_message(&*self.aes_gcm, &session_keys.recv_key, &encrypt0)?;
+ Ok((payload, session_keys.clone()))
+ }
+ fn encrypt_response(
+ &self,
+ session_keys: &SessionKeyInfo,
+ rsp: &[u8],
+ ) -> Result<Vec<u8>, ApiError> {
+ cipher::encrypt_message(
+ &*self.aes_gcm,
+ &*self.rng,
+ &session_keys.send_key,
+ &session_keys.session_id,
+ rsp,
+ )
+ }
}
impl Device for SecretkeeperTa {
@@ -283,10 +375,22 @@
}
}
-#[allow(dead_code)] // TODO: remove this
+#[derive(Clone)]
struct SessionKeyInfo {
_peer_identity: Identity,
session_id: [u8; 32],
send_key: AesKey,
recv_key: AesKey,
}
+
+/// Hand-encoded [`PerformOpResponse`] data for [`AidlErrorCode::InternalError`].
+/// Does not perform CBOR serialization (and so is suitable for error reporting if/when
+/// CBOR serialization fails).
+// TODO: add a unit test that confirms that this hand-encoded data is correct
+fn invalid_cbor_rsp_data() -> [u8; 3] {
+ [
+ 0x82, // 2-arr
+ 0x02, // int, value 2
+ 0x60, // 0-tstr
+ ]
+}
diff --git a/hal/Android.bp b/hal/Android.bp
index 74b1d8a..f72db6b 100644
--- a/hal/Android.bp
+++ b/hal/Android.bp
@@ -28,6 +28,8 @@
rustlibs: [
"libauthgraph_hal",
"libbinder_rs",
+ "libcoset",
"liblog_rust",
+ "libsecretkeeper_comm_nostd",
],
}
diff --git a/hal/src/lib.rs b/hal/src/lib.rs
index 9ea4eab..ee12e20 100644
--- a/hal/src/lib.rs
+++ b/hal/src/lib.rs
@@ -24,6 +24,9 @@
};
use authgraph_hal::channel::SerializedChannel;
use authgraph_hal::service::AuthGraphService;
+use coset::CborSerializable;
+use secretkeeper_comm::wire::{PerformOpSuccessRsp, PerformOpResponse, ApiError, PerformOpReq, AidlErrorCode, SecretId};
+use std::ffi::CString;
/// Implementation of the Secretkeeper HAL service, communicating with a TA
/// in a secure environment via a communication channel.
@@ -55,10 +58,80 @@
/// Implement the `ISecretkeeper` interface.
impl<T: SerializedChannel> ISecretkeeper for SecretkeeperService<T> {
fn processSecretManagementRequest(&self, req: &[u8]) -> binder::Result<Vec<u8>> {
- // Pass the request to the channel, and read the response.
- self.channel.execute(req)
+ let wrapper = PerformOpReq::SecretManagement(req.to_vec());
+ let wrapper_data = wrapper.to_vec().map_err(failed_cbor)?;
+ let rsp_data = self.channel.execute(&wrapper_data)?;
+ let rsp = PerformOpResponse::from_slice(&rsp_data).map_err(failed_cbor)?;
+ match rsp {
+ PerformOpResponse::Success(PerformOpSuccessRsp::ProtectedResponse(data)) => Ok(data),
+ PerformOpResponse::Success(_) => Err(unexpected_response_type()),
+ PerformOpResponse::Failure(err) => Err(service_specific_error(err)),
+ }
}
fn getAuthGraphKe(&self) -> binder::Result<binder::Strong<dyn IAuthGraphKeyExchange>> {
Ok(self.authgraph.clone())
}
}
+
+// TODO: fold these into `impl ISecretkeeper` above.
+#[allow(non_snake_case)]
+impl<T: SerializedChannel> SecretkeeperService<T> {
+ /// Delete some secrets.
+ pub fn deleteById(&self, ids: &[Vec<u8>]) -> binder::Result<()> {
+ let ids: Vec<SecretId> = ids
+ .iter()
+ .map(|id| SecretId::try_from(id.as_slice()))
+ .collect::<Result<Vec<_>, _>>()
+ .map_err(|_e| {
+ binder::Status::new_exception(
+ binder::ExceptionCode::ILLEGAL_ARGUMENT,
+ Some(&std::ffi::CString::new("secret ID of wrong length".to_string()).unwrap()),
+ )
+ })?;
+ let wrapper = PerformOpReq::DeleteIds(ids);
+ let wrapper_data = wrapper.to_vec().map_err(failed_cbor)?;
+ let rsp_data = self.channel.execute(&wrapper_data)?;
+ let rsp = PerformOpResponse::from_slice(&rsp_data).map_err(failed_cbor)?;
+ match rsp {
+ PerformOpResponse::Success(PerformOpSuccessRsp::Empty) => Ok(()),
+ PerformOpResponse::Success(_) => Err(unexpected_response_type()),
+ PerformOpResponse::Failure(err) => Err(service_specific_error(err)),
+ }
+ }
+ /// No more secrets.
+ pub fn deleteAll(&self) -> binder::Result<()> {
+ let wrapper = PerformOpReq::DeleteAll;
+ let wrapper_data = wrapper.to_vec().map_err(failed_cbor)?;
+ let rsp_data = self.channel.execute(&wrapper_data)?;
+ let rsp = PerformOpResponse::from_slice(&rsp_data).map_err(failed_cbor)?;
+ match rsp {
+ PerformOpResponse::Success(PerformOpSuccessRsp::Empty) => Ok(()),
+ PerformOpResponse::Success(_) => Err(unexpected_response_type()),
+ PerformOpResponse::Failure(err) => Err(service_specific_error(err)),
+ }
+ }
+}
+
+/// Emit a failure for a failed CBOR conversion.
+fn failed_cbor(err: coset::CoseError) -> binder::Status {
+ binder::Status::new_exception(
+ binder::ExceptionCode::BAD_PARCELABLE,
+ Some(&std::ffi::CString::new(format!("CBOR conversion failed: {err:?}")).unwrap()),
+ )
+}
+
+/// Emit a service specific error.
+fn service_specific_error(err: ApiError) -> binder::Status {
+ binder::Status::new_service_specific_error(
+ err.err_code as i32,
+ Some(&CString::new(err.msg).unwrap()),
+ )
+}
+
+/// Emit an error indicating an unexpected type of response received from the TA.
+fn unexpected_response_type() -> binder::Status {
+ service_specific_error(ApiError {
+ err_code: AidlErrorCode::InternalError,
+ msg: "unexpected response type".to_string(),
+ })
+}