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 //! Implementation of a Secretkeeper trusted application (TA).
18
19 use crate::alloc::string::ToString;
20 use crate::cipher;
21 use crate::store::{KeyValueStore, PolicyGatedStorage};
22 use alloc::boxed::Box;
23 use alloc::collections::VecDeque;
24 use alloc::vec::Vec;
25 use authgraph_core::ag_err;
26 use authgraph_core::error::Error;
27 use authgraph_core::key::{
28 AesKey, CertChain, EcSignKey, Identity, IdentityVerificationDecision,
29 EXPLICIT_KEY_DICE_CERT_CHAIN_VERSION, IDENTITY_VERSION,
30 };
31 use authgraph_core::traits::{AesGcm, CryptoTraitImpl, Device, EcDsa, Rng, Sha256};
32 use authgraph_wire::{ErrorCode, SESSION_ID_LEN};
33 use coset::{iana, CborSerializable, CoseEncrypt0};
34 use log::{error, trace, warn};
35 use secretkeeper_comm::data_types::{
36 error::Error as SkInternalError,
37 error::SecretkeeperError,
38 packet::{RequestPacket, ResponsePacket},
39 request::Request,
40 request_response_impl::{
41 GetSecretRequest, GetSecretResponse, GetVersionRequest, GetVersionResponse, Opcode,
42 StoreSecretRequest, StoreSecretResponse,
43 },
44 response::Response,
45 Id, SeqNum,
46 };
47 use secretkeeper_comm::wire::{
48 AidlErrorCode, ApiError, PerformOpReq, PerformOpResponse, PerformOpSuccessRsp, SecretId,
49 };
50
51 pub mod bootloader;
52
53 /// Current Secretkeeper version.
54 const CURRENT_VERSION: u64 = 1;
55
56 /// Default maximum number of live session keys.
57 const MAX_SESSIONS_DEFAULT: usize = 4;
58
59 /// Macro to build an [`ApiError`] instance.
60 /// E.g. use: `aidl_err!(InternalError, "some {} format", arg)`.
61 #[macro_export]
62 macro_rules! aidl_err {
63 { $error_code:ident, $($arg:tt)+ } => {
64 ApiError {
65 err_code: AidlErrorCode::$error_code,
66 msg: alloc::format!("{}:{}: {}", file!(), line!(), format_args!($($arg)+))
67 }
68 };
69 }
70
71 /// Secretkeeper trusted application instance.
72 pub struct SecretkeeperTa {
73 /// AES-GCM implementation.
74 aes_gcm: Box<dyn AesGcm>,
75
76 /// RNG implementation.
77 rng: Box<dyn Rng>,
78
79 /// AuthGraph per-boot-key.
80 per_boot_key: AesKey,
81
82 /// The signing key corresponding to the leaf of the Secretkeeper identity DICE chain.
83 identity_sign_key: EcSignKey,
84
85 /// Secretkeeper identity.
86 identity: Identity,
87
88 /// Current sessions.
89 session_artifacts: VecDeque<SessionArtifacts>,
90
91 /// Maximum number of current sessions.
92 max_sessions: usize,
93
94 /// Storage of secrets (& sealing policy)
95 store: PolicyGatedStorage,
96 }
97
98 impl SecretkeeperTa {
99 /// Create a TA instance using the provided trait implementations.
new( ag_impls: &mut CryptoTraitImpl, storage_impl: Box<dyn KeyValueStore>, identity_curve: iana::EllipticCurve, ) -> Result<Self, SkInternalError>100 pub fn new(
101 ag_impls: &mut CryptoTraitImpl,
102 storage_impl: Box<dyn KeyValueStore>,
103 identity_curve: iana::EllipticCurve,
104 ) -> Result<Self, SkInternalError> {
105 Self::new_with_session_limit(ag_impls, storage_impl, identity_curve, MAX_SESSIONS_DEFAULT)
106 }
107
108 /// Create a TA instance using the provided trait implementations.
new_with_session_limit( ag_impls: &mut CryptoTraitImpl, storage_impl: Box<dyn KeyValueStore>, identity_curve: iana::EllipticCurve, max_sessions: usize, ) -> Result<Self, SkInternalError>109 pub fn new_with_session_limit(
110 ag_impls: &mut CryptoTraitImpl,
111 storage_impl: Box<dyn KeyValueStore>,
112 identity_curve: iana::EllipticCurve,
113 max_sessions: usize,
114 ) -> Result<Self, SkInternalError> {
115 // Create a per-boot-key for AuthGraph to use.
116 let aes_gcm = ag_impls.aes_gcm.box_clone();
117 let rng = ag_impls.rng.box_clone();
118 let per_boot_key = aes_gcm.generate_key(&mut *ag_impls.rng).map_err(|e| {
119 error!("Failed to generate per-boot key: {e:?}");
120 SkInternalError::UnexpectedError
121 })?;
122
123 let (identity_sign_key, mut pub_key) =
124 ag_impls.ecdsa.generate_key(identity_curve).map_err(|e| {
125 error!("Failed to generate {identity_curve:?} keypair: {e:?}");
126 SkInternalError::UnexpectedError
127 })?;
128 pub_key.canonicalize_cose_key();
129 let identity = Identity {
130 version: IDENTITY_VERSION,
131 cert_chain: CertChain {
132 version: EXPLICIT_KEY_DICE_CERT_CHAIN_VERSION,
133 root_key: pub_key,
134 dice_cert_chain: None,
135 },
136 policy: None,
137 };
138 let store = PolicyGatedStorage::init(storage_impl);
139
140 Ok(Self {
141 aes_gcm,
142 rng,
143 per_boot_key,
144 identity_sign_key,
145 identity,
146 max_sessions,
147 session_artifacts: VecDeque::new(),
148 store,
149 })
150 }
151
152 /// Process a single serialized request from the bootloader, returning a serialized response
153 /// (even on failure).
process_bootloader(&self, req_data: &[u8]) -> Vec<u8>154 pub fn process_bootloader(&self, req_data: &[u8]) -> Vec<u8> {
155 use bootloader as bl;
156 let req = match bl::Request::from_slice(req_data) {
157 Ok(req) => req,
158 Err(e) => {
159 error!("Failed to deserialize bootloader request: {e:?}");
160 return bl::Response::Error(bl::OpCode::Unknown, bl::ErrorCode::CborFailure)
161 .into_vec();
162 }
163 };
164 let rsp = match req {
165 bl::Request::GetIdentityKey => {
166 let cose_key = self.identity.cert_chain.root_key.clone().get_key();
167 let cose_data = match cose_key.to_vec() {
168 Ok(data) => data,
169 Err(e) => {
170 error!("Failed to serialize COSE key: {e:?}");
171 return bl::Response::Error(
172 bl::OpCode::GetIdentityKey,
173 bl::ErrorCode::CborFailure,
174 )
175 .into_vec();
176 }
177 };
178 bl::Response::IdentityKey(cose_data)
179 }
180 };
181 rsp.into_vec()
182 }
183
184 /// Process a single serialized request, returning a serialized response (even on failure).
process(&mut self, req_data: &[u8]) -> Vec<u8>185 pub fn process(&mut self, req_data: &[u8]) -> Vec<u8> {
186 let (req_code, rsp) = match PerformOpReq::from_slice(req_data) {
187 Ok(req) => {
188 trace!("-> TA: received request {:?}", req.code());
189 (Some(req.code()), self.process_req(req))
190 }
191 Err(e) => {
192 error!("failed to decode CBOR request: {:?}", e);
193 (None, PerformOpResponse::Failure(aidl_err!(InternalError, "CBOR decode failure")))
194 }
195 };
196 trace!("<- TA: send response for {:?} rc {:?}", req_code, rsp.err_code());
197 rsp.to_vec().unwrap_or_else(|e| {
198 error!("failed to encode CBOR response: {:?}", e);
199 invalid_cbor_rsp_data().to_vec()
200 })
201 }
202
203 /// Process a single request, returning a [`PerformOpResponse`].
process_req(&mut self, req: PerformOpReq) -> PerformOpResponse204 fn process_req(&mut self, req: PerformOpReq) -> PerformOpResponse {
205 let code = req.code();
206 let result = match req {
207 PerformOpReq::SecretManagement(encrypt0) => {
208 self.secret_management(&encrypt0).map(PerformOpSuccessRsp::ProtectedResponse)
209 }
210 PerformOpReq::DeleteIds(ids) => {
211 self.delete_ids(ids).map(|_| PerformOpSuccessRsp::Empty)
212 }
213 PerformOpReq::DeleteAll => self.delete_all().map(|_| PerformOpSuccessRsp::Empty),
214 PerformOpReq::GetSecretkeeperIdentity => {
215 let Identity { cert_chain: CertChain { root_key, .. }, .. } = &self.identity;
216 root_key
217 .clone()
218 .get_key()
219 .to_vec()
220 .map_err(|e| ApiError {
221 err_code: AidlErrorCode::InternalError,
222 msg: e.to_string(),
223 })
224 .map(PerformOpSuccessRsp::ProtectedResponse)
225 }
226 };
227 match result {
228 Ok(rsp) => PerformOpResponse::Success(rsp),
229 Err(err) => {
230 warn!("failing {:?} request: {:?}", code, err);
231 PerformOpResponse::Failure(err)
232 }
233 }
234 }
235
delete_ids(&mut self, ids: Vec<SecretId>) -> Result<(), ApiError>236 fn delete_ids(&mut self, ids: Vec<SecretId>) -> Result<(), ApiError> {
237 for id in ids {
238 self.store
239 .delete(&Id(id))
240 .map_err(|e| aidl_err!(InternalError, "failed to delete id: {e:?}"))?;
241 }
242 Ok(())
243 }
244
delete_all(&mut self) -> Result<(), ApiError>245 fn delete_all(&mut self) -> Result<(), ApiError> {
246 warn!("deleting all Secretkeeper secrets");
247 self.store.delete_all().map_err(|e| aidl_err!(InternalError, "failed to delete all: {e:?}"))
248 }
249
250 // "SSL added and removed here :-)"
secret_management(&mut self, encrypt0: &[u8]) -> Result<Vec<u8>, ApiError>251 fn secret_management(&mut self, encrypt0: &[u8]) -> Result<Vec<u8>, ApiError> {
252 let encrypt0 = CoseEncrypt0::from_slice(encrypt0)
253 .map_err(|_e| aidl_err!(RequestMalformed, "malformed COSE_Encrypt0"))?;
254 let kid: &[u8; SESSION_ID_LEN] = key_id(&encrypt0)
255 .try_into()
256 .map_err(|e| aidl_err!(RequestMalformed, "session key of unexpected size: {e:?}"))?;
257 let peer_cert_chain = self.peer_cert_chain(kid)?;
258 let req = self.decrypt_request(&encrypt0, kid)?;
259 let result = self.process_inner(&req, &peer_cert_chain);
260
261 // An inner failure still converts to a response message that gets encrypted.
262 let rsp_data = match result {
263 Ok(data) => data,
264 Err(e) => e
265 .serialize_to_packet()
266 .to_vec()
267 .map_err(|_e| aidl_err!(InternalError, "failed to encode err rsp"))?,
268 };
269 self.encrypt_response(&rsp_data, kid)
270 }
271
peer_cert_chain(&self, kid: &[u8; SESSION_ID_LEN]) -> Result<Vec<u8>, ApiError>272 fn peer_cert_chain(&self, kid: &[u8; SESSION_ID_LEN]) -> Result<Vec<u8>, ApiError> {
273 let cert_chain = self
274 .session_artifacts
275 .iter()
276 .find_map(|info| {
277 if info.session_keys.session_id == *kid {
278 Some(info.session_keys.peer_cert_chain.clone())
279 } else {
280 None
281 }
282 })
283 .ok_or_else(|| aidl_err!(UnknownKeyId, "session info not found"))?;
284 Ok(cert_chain)
285 }
286
process_inner( &mut self, req: &[u8], peer_cert_chain: &[u8], ) -> Result<Vec<u8>, SecretkeeperError>287 fn process_inner(
288 &mut self,
289 req: &[u8],
290 peer_cert_chain: &[u8],
291 ) -> Result<Vec<u8>, SecretkeeperError> {
292 let req_packet = RequestPacket::from_slice(req).map_err(|e| {
293 error!("Failed to get Request packet from bytes: {e:?}");
294 SecretkeeperError::RequestMalformed
295 })?;
296 let rsp_packet =
297 match req_packet.opcode().map_err(|_| SecretkeeperError::RequestMalformed)? {
298 Opcode::GetVersion => Self::get_version(req_packet)?,
299 Opcode::StoreSecret => self.store_secret(req_packet, peer_cert_chain)?,
300 Opcode::GetSecret => self.get_secret(req_packet, peer_cert_chain)?,
301 _ => panic!("Unknown operation.."),
302 };
303 rsp_packet.to_vec().map_err(|_| SecretkeeperError::UnexpectedServerError)
304 }
305
get_version(req: RequestPacket) -> Result<ResponsePacket, SecretkeeperError>306 fn get_version(req: RequestPacket) -> Result<ResponsePacket, SecretkeeperError> {
307 // Deserialization really just verifies the structural integrity of the request such
308 // as args being empty.
309 let _req = GetVersionRequest::deserialize_from_packet(req)
310 .map_err(|_| SecretkeeperError::RequestMalformed)?;
311 let rsp = GetVersionResponse { version: CURRENT_VERSION };
312 Ok(rsp.serialize_to_packet())
313 }
314
store_secret( &mut self, request: RequestPacket, peer_cert_chain: &[u8], ) -> Result<ResponsePacket, SecretkeeperError>315 fn store_secret(
316 &mut self,
317 request: RequestPacket,
318 peer_cert_chain: &[u8],
319 ) -> Result<ResponsePacket, SecretkeeperError> {
320 let request = StoreSecretRequest::deserialize_from_packet(request)
321 .map_err(|_| SecretkeeperError::RequestMalformed)?;
322 self.store.store(request.id, request.secret, request.sealing_policy, peer_cert_chain)?;
323 let response = StoreSecretResponse {};
324 Ok(response.serialize_to_packet())
325 }
326
get_secret( &mut self, request: RequestPacket, peer_cert_chain: &[u8], ) -> Result<ResponsePacket, SecretkeeperError>327 fn get_secret(
328 &mut self,
329 request: RequestPacket,
330 peer_cert_chain: &[u8],
331 ) -> Result<ResponsePacket, SecretkeeperError> {
332 let request = GetSecretRequest::deserialize_from_packet(request)
333 .map_err(|_| SecretkeeperError::RequestMalformed)?;
334 let secret =
335 self.store.get(&request.id, peer_cert_chain, request.updated_sealing_policy)?;
336 let response = GetSecretResponse { secret };
337 Ok(response.serialize_to_packet())
338 }
339
keys_for_session( &mut self, session_id: &[u8; SESSION_ID_LEN], ) -> Option<&mut SessionArtifacts>340 fn keys_for_session(
341 &mut self,
342 session_id: &[u8; SESSION_ID_LEN],
343 ) -> Option<&mut SessionArtifacts> {
344 self.session_artifacts.iter_mut().find(|info| info.session_keys.session_id == *session_id)
345 }
346
decrypt_request( &mut self, encrypt0: &CoseEncrypt0, kid: &[u8; SESSION_ID_LEN], ) -> Result<Vec<u8>, ApiError>347 fn decrypt_request(
348 &mut self,
349 encrypt0: &CoseEncrypt0,
350 kid: &[u8; SESSION_ID_LEN],
351 ) -> Result<Vec<u8>, ApiError> {
352 let session_artifacts = self
353 .keys_for_session(kid)
354 .ok_or_else(|| aidl_err!(UnknownKeyId, "session info not found"))?;
355
356 let seq_num_incoming =
357 session_artifacts.seq_num_incoming.get_then_increment().map_err(|e| {
358 aidl_err!(InternalError, "Couldn't get the expected request seq_num: {e:?}")
359 })?;
360 let recv_key: &AesKey = &session_artifacts.session_keys.recv_key.clone();
361
362 cipher::decrypt_message(self.aes_gcm.as_ref(), recv_key, encrypt0, &seq_num_incoming)
363 }
364
encrypt_response( &mut self, rsp: &[u8], kid: &[u8; SESSION_ID_LEN], ) -> Result<Vec<u8>, ApiError>365 fn encrypt_response(
366 &mut self,
367 rsp: &[u8],
368 kid: &[u8; SESSION_ID_LEN],
369 ) -> Result<Vec<u8>, ApiError> {
370 let session_artifacts = self
371 .keys_for_session(kid)
372 .ok_or_else(|| aidl_err!(UnknownKeyId, "session info not found"))?;
373 let seq_num_outgoing = session_artifacts
374 .seq_num_outgoing
375 .get_then_increment()
376 .map_err(|e| aidl_err!(InternalError, "Couldn't get a seq number: {e:?}"))?;
377 let send_key: &AesKey = &session_artifacts.session_keys.send_key.clone();
378 cipher::encrypt_message(&*self.aes_gcm, &*self.rng, send_key, kid, rsp, &seq_num_outgoing)
379 }
380 }
381
382 impl Device for SecretkeeperTa {
get_or_create_per_boot_key(&self, _: &dyn AesGcm, _: &mut dyn Rng) -> Result<AesKey, Error>383 fn get_or_create_per_boot_key(&self, _: &dyn AesGcm, _: &mut dyn Rng) -> Result<AesKey, Error> {
384 self.get_per_boot_key()
385 }
get_per_boot_key(&self) -> Result<AesKey, Error>386 fn get_per_boot_key(&self) -> Result<AesKey, Error> {
387 Ok(self.per_boot_key.clone())
388 }
get_identity(&self) -> Result<(Option<EcSignKey>, Identity), Error>389 fn get_identity(&self) -> Result<(Option<EcSignKey>, Identity), Error> {
390 Ok((Some(self.identity_sign_key.clone()), self.identity.clone()))
391 }
get_cose_sign_algorithm(&self) -> Result<iana::Algorithm, Error>392 fn get_cose_sign_algorithm(&self) -> Result<iana::Algorithm, Error> {
393 match &self.identity_sign_key {
394 EcSignKey::Ed25519(_) => Ok(iana::Algorithm::EdDSA),
395 EcSignKey::P256(_) => Ok(iana::Algorithm::ES256),
396 EcSignKey::P384(_) => Ok(iana::Algorithm::ES384),
397 }
398 }
399
sign_data(&self, _ecdsa: &dyn EcDsa, _data: &[u8]) -> Result<Vec<u8>, Error>400 fn sign_data(&self, _ecdsa: &dyn EcDsa, _data: &[u8]) -> Result<Vec<u8>, Error> {
401 // `get_identity()` returns a key, so signing should be handled elsewhere.
402 Err(ag_err!(Unimplemented, "unexpected signing request"))
403 }
404
evaluate_identity( &self, _curr: &Identity, _prev: &Identity, ) -> Result<IdentityVerificationDecision, Error>405 fn evaluate_identity(
406 &self,
407 _curr: &Identity,
408 _prev: &Identity,
409 ) -> Result<IdentityVerificationDecision, Error> {
410 Err(ag_err!(Unimplemented, "not yet required"))
411 }
412
record_shared_sessions( &mut self, peer_identity: &Identity, session_id: &[u8; 32], shared_keys: &[Vec<u8>; 2], _sha256: &dyn Sha256, ) -> Result<(), Error>413 fn record_shared_sessions(
414 &mut self,
415 peer_identity: &Identity,
416 session_id: &[u8; 32],
417 shared_keys: &[Vec<u8>; 2],
418 _sha256: &dyn Sha256,
419 ) -> Result<(), Error> {
420 if self.session_artifacts.len() >= self.max_sessions {
421 warn!("Dropping oldest session key");
422 self.session_artifacts.pop_front();
423 }
424 let send_key =
425 authgraph_core::arc::decipher_arc(&self.per_boot_key, &shared_keys[0], &*self.aes_gcm)?;
426 let recv_key =
427 authgraph_core::arc::decipher_arc(&self.per_boot_key, &shared_keys[1], &*self.aes_gcm)?;
428 let peer_cert_chain = peer_identity
429 .cert_chain
430 .clone()
431 .to_vec()
432 .map_err(|e| ag_err!(InternalError, "Failed to encode peer's cert chain: {e:?}"))?;
433
434 // We assume that the session ID is unique and not already present in `session_keys`.
435 self.session_artifacts.push_back(SessionArtifacts {
436 session_keys: AgSessionKeys {
437 peer_cert_chain,
438 session_id: *session_id,
439 send_key: send_key.payload.try_into()?,
440 recv_key: recv_key.payload.try_into()?,
441 },
442 seq_num_outgoing: SeqNum::new(),
443 seq_num_incoming: SeqNum::new(),
444 });
445 Ok(())
446 }
447
validate_shared_sessions( &self, _peer_identity: &Identity, _session_id: &[u8; 32], _shared_keys: &[Vec<u8>], _sha256: &dyn Sha256, ) -> Result<(), Error>448 fn validate_shared_sessions(
449 &self,
450 _peer_identity: &Identity,
451 _session_id: &[u8; 32],
452 _shared_keys: &[Vec<u8>],
453 _sha256: &dyn Sha256,
454 ) -> Result<(), Error> {
455 // Secretkeeper does not need to validate shared session after successful Key Exchange.
456 Ok(())
457 }
458 }
459
key_id(encrypted_packet: &CoseEncrypt0) -> &[u8]460 fn key_id(encrypted_packet: &CoseEncrypt0) -> &[u8] {
461 &encrypted_packet.protected.header.key_id
462 }
463
464 // In addition to `session_keys` (received from authgraph key exchange protocol), Secretkeeper
465 // session maintains `sequence_numbers`. These change within session (for each message).
466 // Avoid cloning!
467 struct SessionArtifacts {
468 session_keys: AgSessionKeys,
469 // Sequence number to be used in next outgoing message encryption.
470 seq_num_outgoing: SeqNum,
471 // Sequence number to be used for decrypting the next incoming message.
472 seq_num_incoming: SeqNum,
473 }
474
475 struct AgSessionKeys {
476 peer_cert_chain: Vec<u8>,
477 session_id: [u8; 32],
478 send_key: AesKey,
479 recv_key: AesKey,
480 }
481
482 /// Hand-encoded [`PerformOpResponse`] data for [`AidlErrorCode::InternalError`].
483 /// Does not perform CBOR serialization (and so is suitable for error reporting if/when
484 /// CBOR serialization fails).
invalid_cbor_rsp_data() -> [u8; 3]485 fn invalid_cbor_rsp_data() -> [u8; 3] {
486 [
487 0x82, // 2-arr
488 0x02, // int, value 2
489 0x60, // 0-tstr
490 ]
491 }
492
493 #[cfg(test)]
494 mod tests {
495 use super::*;
496 use secretkeeper_comm::wire::{AidlErrorCode, ApiError, PerformOpResponse};
497
498 #[test]
test_invalid_data()499 fn test_invalid_data() {
500 // Cross-check that the hand-encoded invalid CBOR data matches an auto-encoded equivalent.
501 let rsp = PerformOpResponse::Failure(ApiError {
502 err_code: AidlErrorCode::InternalError,
503 msg: alloc::string::String::new(),
504 });
505 let rsp_data = rsp.to_vec().unwrap();
506 assert_eq!(rsp_data, invalid_cbor_rsp_data());
507 }
508 }
509