1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //! Rust types used for CBOR-encoded communication between HAL and TA,
18 //! corresponding to the schema in `comm/InternalHalTaMessages.cddl`.
19 
20 #![allow(missing_docs)] // needed for `enumn::N`, sadly
21 
22 use alloc::{string::String, vec, vec::Vec};
23 use ciborium::value::Value;
24 use coset::{AsCborValue, CborSerializable, CoseError};
25 use enumn::N;
26 
27 /// Wrapper type for communicating requests between the HAL service and the TA.
28 /// This is an internal implementation detail, and is not visible on the API.
29 #[derive(Debug, Clone, PartialEq, Eq)]
30 pub enum PerformOpReq {
31     /// A secret management request holds a CBOR-encoded `COSE_Encrypt0`.
32     SecretManagement(Vec<u8>),
33 
34     /// A (plaintext) request to delete some `SecretId`s.
35     DeleteIds(Vec<SecretId>),
36 
37     /// A (plaintext) request to delete all data.
38     DeleteAll,
39 }
40 
41 impl PerformOpReq {
code(&self) -> OpCode42     pub fn code(&self) -> OpCode {
43         match self {
44             Self::SecretManagement(_) => OpCode::SecretManagement,
45             Self::DeleteIds(_) => OpCode::DeleteIds,
46             Self::DeleteAll => OpCode::DeleteAll,
47         }
48     }
49 }
50 
51 impl AsCborValue for PerformOpReq {
to_cbor_value(self) -> Result<Value, CoseError>52     fn to_cbor_value(self) -> Result<Value, CoseError> {
53         Ok(Value::Array(match self {
54             Self::SecretManagement(encrypt0) => {
55                 vec![OpCode::SecretManagement.to_cbor_value()?, Value::Bytes(encrypt0)]
56             }
57             Self::DeleteIds(ids) => {
58                 vec![
59                     OpCode::DeleteIds.to_cbor_value()?,
60                     Value::Array(
61                         ids.into_iter().map(|id| Value::Bytes(id.to_vec())).collect::<Vec<Value>>(),
62                     ),
63                 ]
64             }
65             Self::DeleteAll => vec![OpCode::DeleteAll.to_cbor_value()?, Value::Null],
66         }))
67     }
68 
from_cbor_value(value: Value) -> Result<Self, CoseError>69     fn from_cbor_value(value: Value) -> Result<Self, CoseError> {
70         let mut a = match value {
71             Value::Array(a) if a.len() == 2 => a,
72             _ => return cbor_type_error(&value, "arr len 2"),
73         };
74         let val = a.remove(1);
75         let code = OpCode::from_cbor_value(a.remove(0))?;
76         Ok(match code {
77             OpCode::SecretManagement => Self::SecretManagement(match val {
78                 Value::Bytes(b) => b,
79                 _ => return cbor_type_error(&val, "bstr"),
80             }),
81             OpCode::DeleteIds => {
82                 let ids = match &val {
83                     Value::Array(a) => a,
84                     _ => return cbor_type_error(&val, "arr"),
85                 };
86                 let ids = ids
87                     .iter()
88                     .map(|id| match &id {
89                         Value::Bytes(b) => SecretId::try_from(b.as_slice())
90                             .map_err(|_e| CoseError::OutOfRangeIntegerValue),
91                         _ => cbor_type_error(&val, "bstr"),
92                     })
93                     .collect::<Result<Vec<_>, _>>()?;
94                 Self::DeleteIds(ids)
95             }
96             OpCode::DeleteAll => {
97                 if !val.is_null() {
98                     return cbor_type_error(&val, "nil");
99                 }
100                 Self::DeleteAll
101             }
102         })
103     }
104 }
105 
106 impl CborSerializable for PerformOpReq {}
107 
108 /// Op code value to distinguish requests.
109 #[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, N)]
110 pub enum OpCode {
111     SecretManagement = 0x10,
112     DeleteIds = 0x11,
113     DeleteAll = 0x12,
114 }
115 
116 impl AsCborValue for OpCode {
to_cbor_value(self) -> Result<Value, CoseError>117     fn to_cbor_value(self) -> Result<Value, CoseError> {
118         Ok(Value::Integer((self as i32).into()))
119     }
120 
from_cbor_value(value: Value) -> Result<Self, CoseError>121     fn from_cbor_value(value: Value) -> Result<Self, CoseError> {
122         let i = match value {
123             Value::Integer(i) => i,
124             _ => return cbor_type_error(&value, "int"),
125         };
126         let code: i32 = i.try_into().map_err(|_| CoseError::OutOfRangeIntegerValue)?;
127         OpCode::n(code).ok_or(CoseError::OutOfRangeIntegerValue)
128     }
129 }
130 
131 /// Wrapper type for communicating responses between the HAL service and the TA.
132 /// This is an internal implementation detail, and is not visible on the API.
133 #[derive(Debug, Clone, PartialEq, Eq)]
134 pub enum PerformOpResponse {
135     Success(PerformOpSuccessRsp),
136     Failure(ApiError),
137 }
138 
139 impl PerformOpResponse {
err_code(&self) -> AidlErrorCode140     pub fn err_code(&self) -> AidlErrorCode {
141         match self {
142             Self::Success(_) => AidlErrorCode::Ok,
143             Self::Failure(err) => err.err_code,
144         }
145     }
146 }
147 
148 impl AsCborValue for PerformOpResponse {
to_cbor_value(self) -> Result<Value, CoseError>149     fn to_cbor_value(self) -> Result<Value, CoseError> {
150         Ok(match self {
151             Self::Success(rsp) => {
152                 Value::Array(vec![Value::Integer(0.into()), rsp.to_cbor_value()?])
153             }
154             Self::Failure(err) => Value::Array(vec![
155                 Value::Integer((err.err_code as i32).into()),
156                 Value::Text(err.msg),
157             ]),
158         })
159     }
160 
from_cbor_value(value: Value) -> Result<Self, CoseError>161     fn from_cbor_value(value: Value) -> Result<Self, CoseError> {
162         let mut a = match value {
163             Value::Array(a) if a.len() == 2 => a,
164             _ => return cbor_type_error(&value, "arr len 2"),
165         };
166         let val = a.remove(1);
167         let err_code =
168             a.remove(0).as_integer().ok_or(CoseError::UnexpectedItem("non-int", "int"))?;
169         let err_code: i32 = err_code.try_into().map_err(|_| CoseError::OutOfRangeIntegerValue)?;
170         Ok(match err_code {
171             0 => Self::Success(PerformOpSuccessRsp::from_cbor_value(val)?),
172             err_code => {
173                 let msg = match val {
174                     Value::Text(t) => t,
175                     _ => return cbor_type_error(&val, "tstr"),
176                 };
177                 let err_code = AidlErrorCode::n(err_code).unwrap_or(AidlErrorCode::InternalError);
178                 Self::Failure(ApiError { err_code, msg })
179             }
180         })
181     }
182 }
183 
184 impl CborSerializable for PerformOpResponse {}
185 
186 /// Inner response type holding the result of a successful request.
187 #[derive(Debug, Clone, PartialEq, Eq)]
188 pub enum PerformOpSuccessRsp {
189     ProtectedResponse(Vec<u8>),
190     Empty,
191 }
192 
193 impl AsCborValue for PerformOpSuccessRsp {
to_cbor_value(self) -> Result<Value, CoseError>194     fn to_cbor_value(self) -> Result<Value, CoseError> {
195         Ok(match self {
196             Self::ProtectedResponse(data) => Value::Bytes(data),
197             Self::Empty => Value::Null,
198         })
199     }
200 
from_cbor_value(value: Value) -> Result<Self, CoseError>201     fn from_cbor_value(value: Value) -> Result<Self, CoseError> {
202         match value {
203             Value::Bytes(data) => Ok(Self::ProtectedResponse(data)),
204             Value::Null => Ok(Self::Empty),
205             _ => cbor_type_error(&value, "bstr/nil"),
206         }
207     }
208 }
209 
210 impl CborSerializable for PerformOpSuccessRsp {}
211 
212 /// Return an error indicating that an unexpected CBOR type was encountered.
cbor_type_error<T>(got: &Value, want: &'static str) -> Result<T, CoseError>213 pub fn cbor_type_error<T>(got: &Value, want: &'static str) -> Result<T, CoseError> {
214     let got = match got {
215         Value::Integer(_) => "int",
216         Value::Bytes(_) => "bstr",
217         Value::Text(_) => "tstr",
218         Value::Array(_) => "array",
219         Value::Map(_) => "map",
220         Value::Tag(_, _) => "tag",
221         Value::Float(_) => "float",
222         Value::Bool(_) => "bool",
223         Value::Null => "null",
224         _ => "unknown",
225     };
226     Err(CoseError::UnexpectedItem(got, want))
227 }
228 
229 /// Identifier for a secret
230 pub type SecretId = [u8; 64];
231 
232 /// Error information reported visibly on the external API.
233 #[derive(Debug, Clone, PartialEq, Eq)]
234 pub struct ApiError {
235     pub err_code: AidlErrorCode,
236     pub msg: String,
237 }
238 
239 /// Error codes emitted as service specific errors at the HAL.
240 /// Keep in sync with the ERROR_ codes in:
241 /// hardware/interfaces/security/secretkeeper/aidl/
242 ///   android/hardware/security/secretkeeper/ISecretkeeper.aidl
243 #[derive(Debug, Clone, Copy, PartialEq, Eq, N)]
244 pub enum AidlErrorCode {
245     Ok = 0,
246     UnknownKeyId = 1,
247     InternalError = 2,
248     RequestMalformed = 3,
249 }
250