xref: /aosp_15_r20/system/secretkeeper/core/src/ta/bootloader.rs (revision 3f8e9d82f4020c68ad19a99fc5fdc1fc90b79379)
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 //! Messages used for communication with the bootloader.
18 //!
19 //! The messages exchanged with the bootloader are encoded in a simple manual format rather than
20 //! CBOR, so that the corresponding code in the bootloader has an easy job (and doesn't need
21 //! additional dependencies on CBOR libraries).
22 
23 use alloc::format;
24 use alloc::string::{String, ToString};
25 use alloc::vec::Vec;
26 
27 const U32_LEN: usize = core::mem::size_of::<u32>();
28 
29 type Error = String;
30 
31 /// Op code values corresponding to Request variants.
32 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
33 #[repr(u32)]
34 pub enum OpCode {
35     /// Unknown op code, for example when an incoming request cannot be identified.
36     Unknown = 0,
37     /// Retrieve the Secretkeeper public key used as an identity.
38     GetIdentityKey = 1,
39 }
40 
41 /// Requests that Secretkeeper can receive from the bootloader.  Distinct requests
42 /// are identified by the corresponding [`OpCode`] value.
43 #[derive(Debug, Clone, PartialEq, Eq)]
44 pub enum Request {
45     /// Request to retrieve the Secretkeeper identity public key, as a COSE_Key.
46     GetIdentityKey, // `OpCode::GetIdentityKey`
47 }
48 
49 impl Request {
50     /// Build a request message structure from received serialized data, expected to be in the form:
51     /// - 4-byte big-endian opcode.
52     /// - N bytes of content, dependent on the opcode.
from_slice(data: &[u8]) -> Result<Self, Error>53     pub fn from_slice(data: &[u8]) -> Result<Self, Error> {
54         if data.len() < U32_LEN {
55             return Err(format!("data too short (len={})", data.len()));
56         }
57         let (opcode, remainder) = data.split_at(U32_LEN);
58         let opcode = u32::from_be_bytes(opcode.try_into().unwrap());
59         match opcode {
60             x if x == OpCode::GetIdentityKey as u32 => {
61                 if !remainder.is_empty() {
62                     Err("extra data after request".to_string())
63                 } else {
64                     Ok(Self::GetIdentityKey)
65                 }
66             }
67             _ => Err(format!("unrecognized opcode {opcode}")),
68         }
69     }
70 }
71 
72 /// Top bit is set in response message op codes.
73 const RESPONSE_MARKER: u32 = 0x8000_0000;
74 
75 /// Responses that Secretkeeper can return to the bootloader.
76 #[derive(Debug, Clone, PartialEq, Eq)]
77 pub enum Response {
78     /// Request failed
79     Error(OpCode, ErrorCode),
80     /// Identity key, as a CBOR-encoded COSE_Key.
81     IdentityKey(Vec<u8>),
82 }
83 
84 impl Response {
85     /// Convert a response message to a serialized message in the following form:
86     /// - 4-byte big-endian opcode, with top bit set to indicate a response.
87     /// - 4-byte error code, where 0 indicates success.
88     /// - N bytes of content, dependent on the opcode. Only present on success.
into_vec(self) -> Vec<u8>89     pub fn into_vec(self) -> Vec<u8> {
90         let content = self.content();
91         let size = 2 * U32_LEN + content.len();
92         let mut result = Vec::with_capacity(size);
93         let rsp_op_code = self.op_code() | RESPONSE_MARKER;
94         result.extend_from_slice(&rsp_op_code.to_be_bytes());
95         result.extend_from_slice(&self.error_code().to_be_bytes());
96         result.extend_from_slice(content);
97         result
98     }
99 
op_code(&self) -> u32100     fn op_code(&self) -> u32 {
101         match self {
102             Self::Error(op, _) => *op as u32,
103             Self::IdentityKey(_) => OpCode::GetIdentityKey as u32,
104         }
105     }
106 
error_code(&self) -> u32107     fn error_code(&self) -> u32 {
108         match self {
109             Self::Error(_, rc) => *rc as u32,
110             Self::IdentityKey(_) => ErrorCode::Ok as u32,
111         }
112     }
113 
content(&self) -> &[u8]114     fn content(&self) -> &[u8] {
115         match self {
116             Self::Error(_, _) => &[],
117             Self::IdentityKey(k) => k,
118         }
119     }
120 
121     /// Build a response message structure from serialized data.
122     /// (Only used for testing.)
from_slice(data: &[u8]) -> Result<Self, Error>123     pub fn from_slice(data: &[u8]) -> Result<Self, Error> {
124         if data.len() < 2 * U32_LEN {
125             return Err(format!("data too short (len={})", data.len()));
126         }
127         let (opcode, remainder) = data.split_at(U32_LEN);
128         let opcode = u32::from_be_bytes(opcode.try_into().unwrap());
129         if (opcode & RESPONSE_MARKER) != RESPONSE_MARKER {
130             return Err("response missing marker bit".to_string());
131         }
132         let opcode = match opcode & !RESPONSE_MARKER {
133             x if x == OpCode::GetIdentityKey as u32 => OpCode::GetIdentityKey,
134             op => return Err(format!("Unrecognized opcode {op}")),
135         };
136         let (errcode, remainder) = remainder.split_at(U32_LEN);
137         match u32::from_be_bytes(errcode.try_into().unwrap()) {
138             0 => match opcode {
139                 OpCode::GetIdentityKey => Ok(Self::IdentityKey(remainder.to_vec())),
140                 _ => Err(format!("unexpected opcode {opcode:?}")),
141             },
142             rc => match rc {
143                 x if x == ErrorCode::CborFailure as u32 => {
144                     Ok(Self::Error(opcode, ErrorCode::CborFailure))
145                 }
146                 _ => Err(format!("Unrecognized error code {rc}")),
147             },
148         }
149     }
150 }
151 
152 /// Error code values.
153 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
154 #[repr(u32)]
155 pub enum ErrorCode {
156     /// Success
157     Ok = 0,
158     /// Failed to CBOR-serialize data.
159     CborFailure = 1,
160 }
161 
162 #[cfg(test)]
163 mod tests {
164     use super::*;
165     use alloc::vec;
166 
167     #[test]
test_deserialize_req()168     fn test_deserialize_req() {
169         let tests = [
170             ("00000001", Ok(Request::GetIdentityKey)),
171             ("00000002", Err("unrecognized opcode 2".to_string())),
172             ("000000", Err("data too short (len=3)".to_string())),
173         ];
174         for (hexdata, want) in tests {
175             let data = hex::decode(hexdata).unwrap();
176             let got = Request::from_slice(&data);
177             assert_eq!(got, want, "Failed for input {hexdata}");
178         }
179     }
180 
181     #[test]
test_serialize_rsp()182     fn test_serialize_rsp() {
183         let tests = [
184             (
185                 Response::IdentityKey(vec![1, 2, 3]),
186                 concat!(
187                     "80000001", // opcode
188                     "00000000", // errcode
189                     "010203"
190                 ),
191             ),
192             (
193                 Response::Error(OpCode::GetIdentityKey, ErrorCode::CborFailure),
194                 concat!(
195                     "80000001", // opcode
196                     "00000001"  // errcode
197                 ),
198             ),
199         ];
200         for (rsp, want) in tests {
201             let got_data = rsp.clone().into_vec();
202             let got = hex::encode(&got_data);
203             assert_eq!(got, want, "Failed for input {rsp:?}");
204 
205             let recovered = Response::from_slice(&got_data)
206                 .unwrap_or_else(|e| panic!("failed to deserialize rsp from {got}: {e:?}"));
207             assert_eq!(rsp, recovered, "Failed to rebuild response from {got}");
208         }
209     }
210 }
211