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