1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ////////////////////////////////////////////////////////////////////////////////
16
17 //! Authenticated Key Exchange (AKE) related operations. AKE is executed between two parties P1
18 //! (source) and P2 (sink).
19 use crate::{
20 ag_err, arc,
21 error::Error,
22 key::{
23 check_cose_key_params, AesKey, EcExchangeKey, EcExchangeKeyPriv, EcExchangeKeyPub,
24 EcSignKey, EcVerifyKey, EcdhSecret, HmacKey, Identity, Key, Nonce16, PseudoRandKey,
25 SaltInput, SessionIdInput, AES_256_KEY_LEN, SHA_256_LEN,
26 },
27 traits::{CryptoTraitImpl, Device, EcDh, Hkdf, Sha256},
28 try_to_vec, FallibleAllocExt,
29 };
30 use alloc::collections::VecDeque;
31 use alloc::rc::Rc;
32 use alloc::vec::Vec;
33 use authgraph_wire as wire;
34 use core::cell::RefCell;
35 use core::mem::take;
36 use coset::{
37 cbor::value::Value, iana, AsCborValue, CborSerializable, CoseKey, CoseSign1, CoseSign1Builder,
38 HeaderBuilder, Label,
39 };
40 use log::info;
41 use wire::{ErrorCode, SESSION_ID_LEN};
42
43 pub use wire::{KeInitResult, SessionInfo, SessionInitiationInfo};
44
45 /// Context for deriving the first shared encryption key
46 pub const CONTEXT_KE_ENCRYPTION_KEY_1: &[u8] = b"KE_ENCRYPTION_KEY_SOURCE_TO_SINK";
47 /// Context for deriving the second shared encryption key
48 pub const CONTEXT_KE_ENCRYPTION_KEY_2: &[u8] = b"KE_ENCRYPTION_KEY_SINK_TO_SOURCE";
49 /// Context for deriving the shared HMAC key
50 pub const CONTEXT_KE_HMAC_KEY: &[u8] = b"KE_HMAC_KEY";
51 /// Default limit for opened sessions for either source or sink operationss
52 pub const MAX_OPENED_SESSIONS: usize = 16;
53
54 /// Enum defining the roles played by the two AuthGraph participants
55 #[derive(Clone, Copy)]
56 pub enum Role {
57 /// AuthGraph source
58 Source = 1,
59 /// AuthGraph sink
60 Sink = 2,
61 }
62
63 /// Encapsulation of an AuthGraph Source
64 pub struct AuthGraphParticipant {
65 crypto: CryptoTraitImpl,
66 device: Rc<RefCell<dyn Device>>,
67 opened_ke_sessions: OpenedSessions,
68 opened_shared_sessions: OpenedSessions,
69 }
70
71 // Session identifier is the computation of HMAC over the concatenation of the nonces of source
72 // and sink with the HMAC key derived from the shared secret computed via the key exchange protocol.
73 type SessionIdentifier = [u8; SESSION_ID_LEN];
74
75 // Session digest is computed as the SHA-256 digest of the concatenation of the peer identity and
76 // the session identifier
77 type SessionDigest = [u8; SHA_256_LEN];
78
79 /// Data structure to hold the information about opened sessions during key exchange
80 struct OpenedSessions {
81 capacity: usize,
82 sessions: VecDeque<SessionDigest>,
83 }
84
85 impl OpenedSessions {
new(max_num_sessions: usize) -> Result<Self, Error>86 fn new(max_num_sessions: usize) -> Result<Self, Error> {
87 let mut sessions = VecDeque::new();
88 sessions.try_reserve(max_num_sessions)?;
89 Ok(OpenedSessions { capacity: max_num_sessions, sessions })
90 }
91
add(&mut self, opened_session: SessionDigest)92 fn add(&mut self, opened_session: SessionDigest) {
93 if self.sessions.len() == self.capacity {
94 info!("Max number of opened sessions reached. Removing the oldest session.");
95 self.sessions.pop_front();
96 }
97 self.sessions.push_back(opened_session);
98 }
99
100 // Remove a tracked session; return `InternalError`` if the provided session is not tracked.
remove(&mut self, opened_session: SessionDigest) -> Result<(), Error>101 fn remove(&mut self, opened_session: SessionDigest) -> Result<(), Error> {
102 if self.sessions.contains(&opened_session) {
103 self.sessions.retain(|&session| session != opened_session);
104 Ok(())
105 } else {
106 Err(ag_err!(InternalError, "session not found"))
107 }
108 }
109 }
110
111 impl AuthGraphParticipant {
112 /// Creates an instance of AuthGraphParticipant
new( crypto: CryptoTraitImpl, device: Rc<RefCell<dyn Device>>, max_num_sessions: usize, ) -> Result<Self, Error>113 pub fn new(
114 crypto: CryptoTraitImpl,
115 device: Rc<RefCell<dyn Device>>,
116 max_num_sessions: usize,
117 ) -> Result<Self, Error> {
118 Ok(AuthGraphParticipant {
119 crypto,
120 device,
121 opened_ke_sessions: OpenedSessions::new(max_num_sessions)?,
122 opened_shared_sessions: OpenedSessions::new(max_num_sessions)?,
123 })
124 }
125
126 /// First step in the key exchange protocol.
create(&mut self) -> Result<SessionInitiationInfo, Error>127 pub fn create(&mut self) -> Result<SessionInitiationInfo, Error> {
128 // Create an EC key for ECDH
129 let EcExchangeKey { pub_key: pub_key_for_ecdh, priv_key: mut priv_key_for_ecdh } =
130 self.crypto.ecdh.generate_key()?;
131 // Create a nonce for key agreement
132 let nonce_for_ke = Nonce16::new(&*self.crypto.rng);
133
134 let pbk = self
135 .device
136 .borrow()
137 .get_or_create_per_boot_key(&*self.crypto.aes_gcm, &mut *self.crypto.rng)?;
138 let mut protected_headers = Vec::<(Label, Value)>::new();
139 protected_headers
140 .try_push((arc::KE_NONCE, Value::Bytes(nonce_for_ke.0.clone().to_vec())))?;
141 let priv_key_arc = arc::create_arc(
142 &pbk,
143 arc::ArcContent {
144 payload: arc::ArcPayload(take(&mut priv_key_for_ecdh.0)),
145 protected_headers,
146 ..Default::default()
147 },
148 &*self.crypto.aes_gcm,
149 &mut *self.crypto.rng,
150 )?;
151 let (_sign_key, identity) = self.device.borrow().get_identity()?;
152 // Validate the identity returned from the trait implementation during the first time it is
153 // retrieved from the AuthGraphParticipant.
154 identity.validate(&*self.crypto.ecdsa)?;
155 let ke_key =
156 Key { pub_key: Some(pub_key_for_ecdh.0.to_vec()?), arc_from_pbk: Some(priv_key_arc) };
157 let ke_digest = compute_ke_key_digest(&ke_key, &*self.crypto.sha256)?;
158 self.opened_ke_sessions.add(ke_digest);
159 Ok(SessionInitiationInfo {
160 ke_key,
161 identity: identity.to_vec()?,
162 nonce: nonce_for_ke.0.to_vec(),
163 version: self.get_version(),
164 })
165 }
166
167 /// Second step in the key exchange protocol.
init( &mut self, peer_key: &[u8], peer_id: &[u8], peer_nonce: &[u8], peer_version: i32, ) -> Result<KeInitResult, Error>168 pub fn init(
169 &mut self,
170 peer_key: &[u8],
171 peer_id: &[u8],
172 peer_nonce: &[u8],
173 peer_version: i32,
174 ) -> Result<KeInitResult, Error> {
175 if self.device.borrow().get_negotiated_version(peer_version) > peer_version {
176 return Err(ag_err!(InternalError, "protocol version is greater than peer's version"));
177 }
178 let key_for_ecdh = self.crypto.ecdh.generate_key()?;
179 let own_nonce = Nonce16::new(&*self.crypto.rng);
180
181 let (peer_pub_key, peer_identity, peer_nonce) =
182 decode_peer_info(peer_key, peer_id, peer_nonce)?;
183
184 let ecdh_secret =
185 compute_shared_secret(&*self.crypto.ecdh, &key_for_ecdh.priv_key, &peer_pub_key)?;
186 let (sign_key, own_identity) = self.device.borrow().get_identity()?;
187 // Validate the identity returned from the trait implementation during the first time it is
188 // retrieved from the AuthGraphParticipant.
189 own_identity.validate(&*self.crypto.ecdsa)?;
190 let salt_input = SaltInput {
191 source_version: peer_version,
192 sink_ke_pub_key: key_for_ecdh.pub_key.clone(),
193 source_ke_pub_key: peer_pub_key,
194 sink_ke_nonce: own_nonce.clone(),
195 source_ke_nonce: peer_nonce.clone(),
196 sink_cert_chain: own_identity.cert_chain.clone(),
197 source_cert_chain: peer_identity.cert_chain.clone(),
198 };
199
200 let salt = compute_salt(salt_input, &*self.crypto.sha256)?;
201 let (in_encrypt_key, out_encrypt_key, hmac_key) =
202 derive_shared_keys(&ecdh_secret, &salt, Role::Sink, &*self.crypto.hkdf)?;
203
204 let (session_id, signature) = self.compute_sign_session_id(
205 SessionIdInput { sink_ke_nonce: own_nonce.clone(), source_ke_nonce: peer_nonce },
206 HmacKey(hmac_key.0),
207 sign_key,
208 )?;
209
210 // create two arcs encrypting the two shared encrypting keys with the per-boot-key
211 let shared_keys = self.create_shared_key_arcs(
212 own_identity.clone(),
213 peer_identity.clone(),
214 AesKey(in_encrypt_key.0),
215 AesKey(out_encrypt_key.0),
216 &session_id,
217 arc::AuthenticationCompleted(false),
218 )?;
219 let session_digest =
220 compute_session_digest(&peer_identity, &session_id, &*self.crypto.sha256)?;
221 self.opened_shared_sessions.add(session_digest);
222 Ok(KeInitResult {
223 session_init_info: SessionInitiationInfo {
224 ke_key: Key { pub_key: Some(key_for_ecdh.pub_key.0.to_vec()?), arc_from_pbk: None },
225 identity: own_identity.to_vec()?,
226 nonce: own_nonce.0.to_vec(),
227 version: self.device.borrow().get_negotiated_version(peer_version),
228 },
229 session_info: SessionInfo {
230 shared_keys,
231 session_id: session_id.to_vec(),
232 session_id_signature: signature,
233 },
234 })
235 }
236
237 /// Third step in the key exchange protocol.
finish( &mut self, peer_key: &[u8], peer_id: &[u8], peer_sig: &[u8], peer_nonce: &[u8], peer_version: i32, own_ke_key: Key, ) -> Result<SessionInfo, Error>238 pub fn finish(
239 &mut self,
240 peer_key: &[u8],
241 peer_id: &[u8],
242 peer_sig: &[u8],
243 peer_nonce: &[u8],
244 peer_version: i32,
245 own_ke_key: Key,
246 ) -> Result<SessionInfo, Error> {
247 let ke_digest = compute_ke_key_digest(&own_ke_key, &*self.crypto.sha256)?;
248 self.opened_ke_sessions
249 .remove(ke_digest)
250 .map_err(|_| ag_err!(InvalidKeKey, "finish is called on invalid session"))?;
251 if self.get_version() < peer_version {
252 return Err(ag_err!(
253 IncompatibleProtocolVersion,
254 "peer's protocol version is incompatible"
255 ));
256 }
257 let pbk = self.per_boot_key()?;
258 let mut own_ke_key_arc_content = arc::decipher_arc(
259 &pbk,
260 &own_ke_key
261 .arc_from_pbk
262 .ok_or(ag_err!(InvalidPrivKeyArcInKey, "missing priv key arc"))?,
263 &*self.crypto.aes_gcm,
264 )
265 .map_err(|e| ag_err!(InvalidPrivKeyArcInKey, "failed to decrypt arc: {e:?}"))?;
266
267 let own_ke_nonce = extract_nonce(&own_ke_key_arc_content.protected_headers)?;
268
269 let (peer_pub_key, peer_identity, peer_nonce) =
270 decode_peer_info(peer_key, peer_id, peer_nonce)?;
271
272 let ecdh_secret = compute_shared_secret(
273 &*self.crypto.ecdh,
274 &EcExchangeKeyPriv(take(&mut own_ke_key_arc_content.payload.0)),
275 &peer_pub_key,
276 )?;
277
278 let (sign_key, own_identity) = self.device.borrow().get_identity()?;
279 let own_ke_pub_key = own_ke_key
280 .pub_key
281 .ok_or(ag_err!(InvalidPubKeyInKey, "own public key is missing for key agreement"))?;
282 let own_ke_pub_key =
283 EcExchangeKeyPub(CoseKey::from_slice(&own_ke_pub_key).map_err(|e| {
284 ag_err!(InvalidPubKeyInKey, "invalid own key for key agreement: {:?}", e)
285 })?);
286
287 let salt_input = SaltInput {
288 source_version: self.get_version(),
289 sink_ke_pub_key: peer_pub_key,
290 source_ke_pub_key: own_ke_pub_key,
291 sink_ke_nonce: peer_nonce.clone(),
292 source_ke_nonce: own_ke_nonce.clone(),
293 sink_cert_chain: peer_identity.cert_chain.clone(),
294 source_cert_chain: own_identity.cert_chain.clone(),
295 };
296
297 let salt = compute_salt(salt_input, &*self.crypto.sha256)?;
298
299 let (in_encrypt_key, out_encrypt_key, hmac_key) =
300 derive_shared_keys(&ecdh_secret, &salt, Role::Source, &*self.crypto.hkdf)?;
301
302 let (session_id, signature) = self.compute_sign_session_id(
303 SessionIdInput { sink_ke_nonce: peer_nonce, source_ke_nonce: own_ke_nonce },
304 HmacKey(hmac_key.0),
305 sign_key,
306 )?;
307
308 // Validate peer's identity and the peer's signature on the session id
309 let verify_key =
310 self.device.borrow().validate_peer_identity(&peer_identity, &*self.crypto.ecdsa)?;
311 self.verify_signature_on_session_id(&verify_key, &session_id, peer_sig)?;
312
313 // create two arcs encrypting the two shared encrypting keys with the per-boot-key
314 let shared_keys = self.create_shared_key_arcs(
315 peer_identity.clone(),
316 own_identity,
317 AesKey(in_encrypt_key.0),
318 AesKey(out_encrypt_key.0),
319 &session_id,
320 arc::AuthenticationCompleted(true),
321 )?;
322
323 self.device.borrow_mut().record_shared_sessions(
324 &peer_identity,
325 &session_id,
326 &shared_keys,
327 &*self.crypto.sha256,
328 )?;
329 Ok(SessionInfo {
330 shared_keys,
331 session_id: session_id.to_vec(),
332 session_id_signature: signature,
333 })
334 }
335
336 /// Fourth step in the key exchange protocol.
authentication_complete( &mut self, peer_sig: &[u8], shared_keys: [Vec<u8>; 2], ) -> Result<[Vec<u8>; 2], Error>337 pub fn authentication_complete(
338 &mut self,
339 peer_sig: &[u8],
340 shared_keys: [Vec<u8>; 2],
341 ) -> Result<[Vec<u8>; 2], Error> {
342 let in_key_arc = &shared_keys[0];
343 let out_key_arc = &shared_keys[1];
344
345 let pbk = self.per_boot_key()?;
346
347 let in_key_arc_content = arc::decipher_arc(&pbk, in_key_arc, &*self.crypto.aes_gcm)
348 .map_err(|e| {
349 ag_err!(InvalidSharedKeyArcs, "failed to decrypt inbound shared key: {e:?}")
350 })?;
351 let (in_session_id, in_source_identity, _) = process_shared_key_arc_headers(
352 &in_key_arc_content.protected_headers,
353 &arc::AuthenticationCompleted(false),
354 )?;
355
356 let out_key_arc_content = arc::decipher_arc(&pbk, out_key_arc, &*self.crypto.aes_gcm)
357 .map_err(|e| {
358 ag_err!(InvalidSharedKeyArcs, "failed to decrypt inbound shared key: {e:?}")
359 })?;
360 let (out_session_id, out_source_identity, _) = process_shared_key_arc_headers(
361 &out_key_arc_content.protected_headers,
362 &arc::AuthenticationCompleted(false),
363 )?;
364
365 if in_session_id != out_session_id {
366 return Err(ag_err!(InvalidSharedKeyArcs, "session id mismatch"));
367 }
368
369 if in_source_identity != out_source_identity {
370 return Err(ag_err!(InvalidSharedKeyArcs, "peer identity mismatch"));
371 }
372 // Remove the corresponding entry from opened shared sessions which was inserted in `init`
373 let session_digest =
374 compute_session_digest(&in_source_identity, &in_session_id, &*self.crypto.sha256)?;
375 self.opened_shared_sessions.remove(session_digest).map_err(|_| {
376 ag_err!(InvalidSharedKeyArcs, "authentication_complete is called on invalid session")
377 })?;
378
379 // Validate peer's identity and the peer's signature on the session id
380 let verification_key = self
381 .device
382 .borrow()
383 .validate_peer_identity(&in_source_identity, &*self.crypto.ecdsa)?;
384 self.verify_signature_on_session_id(&verification_key, &in_session_id, peer_sig)?;
385
386 let updated_shared_key_arcs =
387 self.auth_complete_shared_key_arcs(in_key_arc_content, out_key_arc_content)?;
388
389 self.device.borrow_mut().record_shared_sessions(
390 &in_source_identity,
391 &in_session_id,
392 &updated_shared_key_arcs,
393 &*self.crypto.sha256,
394 )?;
395 Ok(updated_shared_key_arcs)
396 }
397
create_shared_key_arcs( &mut self, sink_id: Identity, source_id: Identity, in_encrypt_key: AesKey, out_encrypt_key: AesKey, session_id: &[u8], auth_complete: arc::AuthenticationCompleted, ) -> Result<[Vec<u8>; 2], Error>398 fn create_shared_key_arcs(
399 &mut self,
400 sink_id: Identity,
401 source_id: Identity,
402 in_encrypt_key: AesKey,
403 out_encrypt_key: AesKey,
404 session_id: &[u8],
405 auth_complete: arc::AuthenticationCompleted,
406 ) -> Result<[Vec<u8>; 2], Error> {
407 let pbk = match auth_complete.0 {
408 false => self
409 .device
410 .borrow()
411 .get_or_create_per_boot_key(&*self.crypto.aes_gcm, &mut *self.crypto.rng)?,
412 true => self.per_boot_key()?,
413 };
414
415 // auth complete protected header
416 let auth_complete_hdr = (arc::AUTHENTICATION_COMPLETE, Value::Bool(auth_complete.0));
417 // session id protected header
418 let session_id_hdr = (arc::SESSION_ID, Value::Bytes(try_to_vec(session_id)?));
419 // direction of encryption
420 let in_direction_hdr =
421 (arc::DIRECTION, Value::Integer((arc::DirectionOfEncryption::In as i32).into()));
422 // permissions header
423 let permissions_encoded = arc::Permissions {
424 source_id: Some(source_id),
425 sink_id: Some(sink_id),
426 ..Default::default()
427 }
428 .to_cbor_value()?;
429 let permission_hdr = (arc::PERMISSIONS, permissions_encoded);
430
431 let mut in_protected_headers = Vec::<(Label, Value)>::new();
432 in_protected_headers.try_reserve(4)?;
433 in_protected_headers.push(auth_complete_hdr.clone());
434 in_protected_headers.push(session_id_hdr.clone());
435 in_protected_headers.push(permission_hdr.clone());
436 in_protected_headers.push(in_direction_hdr);
437
438 let in_encrypt_key_arc = arc::create_arc(
439 &pbk,
440 arc::ArcContent {
441 payload: arc::ArcPayload(in_encrypt_key.0.to_vec()),
442 protected_headers: in_protected_headers,
443 ..Default::default()
444 },
445 &*self.crypto.aes_gcm,
446 &mut *self.crypto.rng,
447 )?;
448
449 // direction of encryption
450 let out_direction_hdr =
451 (arc::DIRECTION, Value::Integer((arc::DirectionOfEncryption::Out as i32).into()));
452 let mut out_protected_headers = Vec::<(Label, Value)>::new();
453 out_protected_headers.try_reserve(4)?;
454 out_protected_headers.push(auth_complete_hdr);
455 out_protected_headers.push(session_id_hdr);
456 out_protected_headers.push(permission_hdr);
457 out_protected_headers.push(out_direction_hdr);
458
459 let out_encrypt_key_arc = arc::create_arc(
460 &pbk,
461 arc::ArcContent {
462 payload: arc::ArcPayload(out_encrypt_key.0.to_vec()),
463 protected_headers: out_protected_headers,
464 ..Default::default()
465 },
466 &*self.crypto.aes_gcm,
467 &mut *self.crypto.rng,
468 )?;
469 Ok([in_encrypt_key_arc, out_encrypt_key_arc])
470 }
471
472 /// Update the `authentication_complete` protected header in both arcs to be true
auth_complete_shared_key_arcs( &mut self, mut in_arc_content: arc::ArcContent, mut out_arc_content: arc::ArcContent, ) -> Result<[Vec<u8>; 2], Error>473 pub fn auth_complete_shared_key_arcs(
474 &mut self,
475 mut in_arc_content: arc::ArcContent,
476 mut out_arc_content: arc::ArcContent,
477 ) -> Result<[Vec<u8>; 2], Error> {
478 in_arc_content.protected_headers.retain(|v| v.0 != arc::AUTHENTICATION_COMPLETE);
479 out_arc_content.protected_headers.retain(|v| v.0 != arc::AUTHENTICATION_COMPLETE);
480
481 let auth_complete_hdr = (arc::AUTHENTICATION_COMPLETE, Value::Bool(true));
482 in_arc_content.protected_headers.try_push(auth_complete_hdr.clone())?;
483 out_arc_content.protected_headers.try_push(auth_complete_hdr)?;
484
485 let pbk = self.per_boot_key()?;
486 let in_encrypt_key_arc =
487 arc::create_arc(&pbk, in_arc_content, &*self.crypto.aes_gcm, &mut *self.crypto.rng)?;
488 let out_encrypt_key_arc =
489 arc::create_arc(&pbk, out_arc_content, &*self.crypto.aes_gcm, &mut *self.crypto.rng)?;
490 Ok([in_encrypt_key_arc, out_encrypt_key_arc])
491 }
492
compute_sign_session_id( &self, session_id_input: SessionIdInput, hmac_key: HmacKey, sign_key: Option<EcSignKey>, ) -> Result<(SessionIdentifier, Vec<u8>), Error>493 fn compute_sign_session_id(
494 &self,
495 session_id_input: SessionIdInput,
496 hmac_key: HmacKey,
497 sign_key: Option<EcSignKey>,
498 ) -> Result<(SessionIdentifier, Vec<u8>), Error> {
499 let session_id = self.crypto.hmac.compute_hmac(&hmac_key, &session_id_input.to_vec()?)?;
500
501 // Construct `CoseSign1`
502 let protected =
503 HeaderBuilder::new().algorithm(self.device.borrow().get_cose_sign_algorithm()?).build();
504 let cose_sign1 = CoseSign1Builder::new()
505 .protected(protected)
506 .try_create_detached_signature(&session_id, &[], |input| match sign_key {
507 Some(key) => self.crypto.ecdsa.sign(&key, input),
508 None => self.device.borrow().sign_data(&*self.crypto.ecdsa, input),
509 })?
510 .build();
511
512 Ok((
513 session_id
514 .as_slice()
515 .try_into()
516 .map_err(|e| ag_err!(InternalError, "invalid session id: {:?}", e))?,
517 cose_sign1.to_vec()?,
518 ))
519 }
520
per_boot_key(&self) -> Result<AesKey, Error>521 fn per_boot_key(&self) -> Result<AesKey, Error> {
522 self.device.borrow().get_per_boot_key()
523 }
524
525 /// Returns the latest supported version
get_version(&self) -> i32526 pub fn get_version(&self) -> i32 {
527 self.device.borrow().get_version()
528 }
529
530 /// Validate and extract signature verification key from the peer's identity
peer_verification_key_from_identity( &self, identity: &[u8], ) -> Result<EcVerifyKey, Error>531 pub fn peer_verification_key_from_identity(
532 &self,
533 identity: &[u8],
534 ) -> Result<EcVerifyKey, Error> {
535 let identity = Identity::from_slice(identity)?;
536 self.device.borrow().validate_peer_identity(&identity, &*self.crypto.ecdsa)
537 }
538
539 /// Verify the signature in a CBOR-encoded `COSE_Sign1` with detached payload `session_id`.
verify_signature_on_session_id( &self, verify_key: &EcVerifyKey, session_id: &[u8], signature: &[u8], ) -> Result<(), Error>540 pub fn verify_signature_on_session_id(
541 &self,
542 verify_key: &EcVerifyKey,
543 session_id: &[u8],
544 signature: &[u8],
545 ) -> Result<(), Error> {
546 let cose_sign1 = CoseSign1::from_slice(signature)?;
547 verify_key.validate_cose_key_params()?;
548 cose_sign1.verify_detached_signature(session_id, &[], |signature, data| {
549 self.crypto.ecdsa.verify_signature(verify_key, data, signature)
550 })
551 }
552
553 /// An external entry point for retrieving the decrypted shared keys, given the arcs containing
554 /// the shared keys. The given arcs are validated against the records created at the end of each
555 /// key exchange. The number of arcs can be either one or two, depending on whether a particular
556 /// application protocol involves one-way (i.e. either outgoing or incoming messages) or
557 /// two-way communication (i.e. both outgoing and incoming messages).
decipher_shared_keys_from_arcs(&self, arcs: &[Vec<u8>]) -> Result<Vec<AesKey>, Error>558 pub fn decipher_shared_keys_from_arcs(&self, arcs: &[Vec<u8>]) -> Result<Vec<AesKey>, Error> {
559 if arcs.len() > 2 || arcs.is_empty() {
560 return Err(ag_err!(
561 InvalidSharedKeyArcs,
562 "expected one or two arcs, provided {} arcs",
563 arcs.len()
564 ));
565 }
566 let arc_contents = self.decrypt_and_validate_shared_key_arcs(arcs)?;
567 let mut shared_keys = Vec::<AesKey>::new();
568 for arc_content in arc_contents {
569 let key = AesKey::try_from(arc_content.payload)?;
570 if key.0 == [0; AES_256_KEY_LEN] {
571 return Err(ag_err!(InvalidSharedKeyArcs, "payload key is all zero",));
572 }
573 shared_keys.try_push(key)?;
574 }
575 Ok(shared_keys)
576 }
577 /// Decrypt the provided arcs, validate and return the arc contents.
decrypt_and_validate_shared_key_arcs( &self, arcs: &[Vec<u8>], ) -> Result<Vec<arc::ArcContent>, Error>578 fn decrypt_and_validate_shared_key_arcs(
579 &self,
580 arcs: &[Vec<u8>],
581 ) -> Result<Vec<arc::ArcContent>, Error> {
582 if arcs.is_empty() {
583 return Err(ag_err!(InvalidSharedKeyArcs, "array of arcs is empty"));
584 }
585 let mut session_identifier: Option<[u8; SESSION_ID_LEN]> = None;
586 let mut source_identity: Option<Identity> = None;
587 let mut sink_identity: Option<Identity> = None;
588 let mut arc_contents = Vec::<arc::ArcContent>::new();
589 for arc in arcs {
590 let pbk = self.per_boot_key()?;
591 let arc_content = arc::decipher_arc(&pbk, arc, &*self.crypto.aes_gcm).map_err(|e| {
592 ag_err!(InvalidSharedKeyArcs, "failed to decrypt shared key: {e:?}")
593 })?;
594 let (session_id, source_id, sink_id) = process_shared_key_arc_headers(
595 &arc_content.protected_headers,
596 &arc::AuthenticationCompleted(true),
597 )?;
598 arc_contents.try_push(arc_content)?;
599 match &session_identifier {
600 Some(id) if *id == session_id => {}
601 Some(_) => {
602 return Err(ag_err!(InvalidSharedKeyArcs, "session id mismatch"));
603 }
604 None => {
605 session_identifier = Some(session_id);
606 }
607 }
608 match &source_identity {
609 Some(identity) if *identity == source_id => {}
610 Some(_) => {
611 return Err(ag_err!(InvalidSharedKeyArcs, "source identity mismatch"));
612 }
613 None => {
614 source_identity = Some(source_id);
615 }
616 }
617 match &sink_identity {
618 Some(identity) if *identity == sink_id => {}
619 Some(_) => {
620 return Err(ag_err!(InvalidSharedKeyArcs, "sink identity mismatch"));
621 }
622 None => {
623 sink_identity = Some(sink_id);
624 }
625 }
626 }
627 // It is safe to unwrap because we ensure that there is at least one arc provided in the
628 // method call
629 let session_identifier = session_identifier.unwrap();
630 let source_identity = source_identity.unwrap();
631 let sink_identity = sink_identity.unwrap();
632 let (_, self_identity) = self.device.borrow().get_identity()?;
633 if self_identity == source_identity {
634 self.device.borrow().validate_shared_sessions(
635 &sink_identity,
636 &session_identifier,
637 arcs,
638 &*self.crypto.sha256,
639 )?;
640 } else if self_identity == sink_identity {
641 self.device.borrow().validate_shared_sessions(
642 &source_identity,
643 &session_identifier,
644 arcs,
645 &*self.crypto.sha256,
646 )?;
647 } else {
648 // Note: it is not possible to reach this branch because if the arc is decrypted by the
649 // per-boot key, the correct self identity should have been included in the arc.
650 return Err(ag_err!(InvalidSharedKeyArcs, "self identity is not included in the arc."));
651 }
652 Ok(arc_contents)
653 }
654 }
655
compute_ke_key_digest(ke_key: &Key, sha256: &dyn Sha256) -> Result<[u8; SHA_256_LEN], Error>656 fn compute_ke_key_digest(ke_key: &Key, sha256: &dyn Sha256) -> Result<[u8; SHA_256_LEN], Error> {
657 let mut key_input = Vec::new();
658 key_input.try_extend_from_slice(
659 ke_key.pub_key.as_ref().ok_or_else(|| ag_err!(InvalidPubKeyInKey, "missing public key"))?,
660 )?;
661 key_input.try_extend_from_slice(
662 ke_key
663 .arc_from_pbk
664 .as_ref()
665 .ok_or_else(|| ag_err!(InvalidPrivKeyArcInKey, "missing private key arc"))?,
666 )?;
667 sha256.compute_sha256(&key_input)
668 }
669
compute_session_digest( peer_identity: &Identity, session_id: &[u8; SESSION_ID_LEN], sha256: &dyn Sha256, ) -> Result<SessionDigest, Error>670 fn compute_session_digest(
671 peer_identity: &Identity,
672 session_id: &[u8; SESSION_ID_LEN],
673 sha256: &dyn Sha256,
674 ) -> Result<SessionDigest, Error> {
675 let mut session_info = Vec::new();
676 session_info.try_extend_from_slice(&peer_identity.clone().to_vec()?)?;
677 session_info.try_extend_from_slice(session_id)?;
678 sha256.compute_sha256(&session_info)
679 }
680
681 /// A helper function to process the headers of the shared key arcs, verify the given authentication
682 /// completed status, extract and return the session id, source identity and sink identity.
process_shared_key_arc_headers( protected_headers: &[(Label, Value)], expected_status: &arc::AuthenticationCompleted, ) -> Result<(SessionIdentifier, Identity, Identity), Error>683 pub fn process_shared_key_arc_headers(
684 protected_headers: &[(Label, Value)],
685 expected_status: &arc::AuthenticationCompleted,
686 ) -> Result<(SessionIdentifier, Identity, Identity), Error> {
687 let (_, val) = protected_headers
688 .iter()
689 .find(|(l, _v)| *l == arc::SESSION_ID)
690 .ok_or_else(|| ag_err!(InvalidSharedKeyArcs, "session id is missing"))?;
691 let session_id = match val {
692 Value::Bytes(b) => b.clone(),
693 _ => return Err(ag_err!(InvalidSharedKeyArcs, "invalid encoding of session id")),
694 };
695 let session_id: SessionIdentifier = session_id
696 .try_into()
697 .map_err(|e| ag_err!(InvalidSharedKeyArcs, "invalid session id: {:?}", e))?;
698
699 let (_, val) = protected_headers
700 .iter()
701 .find(|(l, _v)| *l == arc::PERMISSIONS)
702 .ok_or_else(|| ag_err!(InvalidSharedKeyArcs, "permissions are missing"))?;
703 let permissions = arc::Permissions::from_cbor_value(val.clone())?;
704 let source_identity = permissions.source_id.ok_or_else(|| {
705 ag_err!(InvalidSharedKeyArcs, "source identity is missing in the permissions")
706 })?;
707 let sink_identity = permissions.sink_id.ok_or_else(|| {
708 ag_err!(InvalidSharedKeyArcs, "sink identity is missing in the permissions")
709 })?;
710
711 let (_, val) = protected_headers
712 .iter()
713 .find(|(l, _v)| *l == arc::AUTHENTICATION_COMPLETE)
714 .ok_or_else(|| ag_err!(InvalidSharedKeyArcs, "authentication complete is missing"))?;
715 let status = match val {
716 Value::Bool(b) => *b,
717 _ => {
718 return Err(ag_err!(
719 InvalidSharedKeyArcs,
720 "invalid encoding of authentication complete"
721 ))
722 }
723 };
724 if status != expected_status.0 {
725 return Err(ag_err!(InvalidSharedKeyArcs, "invalid authentication complete status"));
726 }
727 Ok((session_id, source_identity, sink_identity))
728 }
729
decode_peer_info( pub_key: &[u8], identity: &[u8], nonce: &[u8], ) -> Result<(EcExchangeKeyPub, Identity, Nonce16), Error>730 fn decode_peer_info(
731 pub_key: &[u8],
732 identity: &[u8],
733 nonce: &[u8],
734 ) -> Result<(EcExchangeKeyPub, Identity, Nonce16), Error> {
735 let pub_key =
736 EcExchangeKeyPub(CoseKey::from_slice(pub_key).map_err(|e| {
737 ag_err!(InvalidPeerKeKey, "invalid peer key for key agreement: {:?}", e)
738 })?);
739 let identity = Identity::from_slice(identity)
740 .map_err(|e| ag_err!(InvalidIdentity, "invalid peer identity: {:?}", e))?;
741 let nonce = Nonce16(
742 nonce.try_into().map_err(|e| ag_err!(InvalidPeerNonce, "invalid nonce size: {:?}", e))?,
743 );
744 Ok((pub_key, identity, nonce))
745 }
746
compute_shared_secret( ecdh: &dyn EcDh, own_key: &EcExchangeKeyPriv, peer_key: &EcExchangeKeyPub, ) -> Result<EcdhSecret, Error>747 fn compute_shared_secret(
748 ecdh: &dyn EcDh,
749 own_key: &EcExchangeKeyPriv,
750 peer_key: &EcExchangeKeyPub,
751 ) -> Result<EcdhSecret, Error> {
752 check_cose_key_params(
753 &peer_key.0,
754 iana::KeyType::EC2,
755 iana::Algorithm::ECDH_ES_HKDF_256, // ECDH
756 iana::EllipticCurve::P_256,
757 ErrorCode::InvalidPeerKeKey,
758 )?;
759 ecdh.compute_shared_secret(own_key, peer_key)
760 }
761
compute_salt(salt_input: SaltInput, sha_256: &dyn Sha256) -> Result<[u8; SHA_256_LEN], Error>762 fn compute_salt(salt_input: SaltInput, sha_256: &dyn Sha256) -> Result<[u8; SHA_256_LEN], Error> {
763 sha_256.compute_sha256(&salt_input.to_vec()?)
764 }
765
derive_shared_keys( ecdh_secret: &EcdhSecret, salt: &[u8; SHA_256_LEN], role: Role, hkdf: &dyn Hkdf, ) -> Result<(PseudoRandKey, PseudoRandKey, PseudoRandKey), Error>766 fn derive_shared_keys(
767 ecdh_secret: &EcdhSecret,
768 salt: &[u8; SHA_256_LEN],
769 role: Role,
770 hkdf: &dyn Hkdf,
771 ) -> Result<(PseudoRandKey, PseudoRandKey, PseudoRandKey), Error> {
772 let pseudo_rand_key = hkdf.extract(salt, ecdh_secret)?;
773 let hmac_key = hkdf.expand(&pseudo_rand_key, CONTEXT_KE_HMAC_KEY)?;
774 match role {
775 Role::Sink => {
776 let in_encrypt_key = hkdf.expand(&pseudo_rand_key, CONTEXT_KE_ENCRYPTION_KEY_1)?;
777 let out_encrypt_key = hkdf.expand(&pseudo_rand_key, CONTEXT_KE_ENCRYPTION_KEY_2)?;
778 Ok((in_encrypt_key, out_encrypt_key, hmac_key))
779 }
780 Role::Source => {
781 let in_encrypt_key = hkdf.expand(&pseudo_rand_key, CONTEXT_KE_ENCRYPTION_KEY_2)?;
782 let out_encrypt_key = hkdf.expand(&pseudo_rand_key, CONTEXT_KE_ENCRYPTION_KEY_1)?;
783 Ok((in_encrypt_key, out_encrypt_key, hmac_key))
784 }
785 }
786 }
787
788 /// Process the given protected headers of an arc and return the nonce used for key agreement
extract_nonce(protected_headers: &[(Label, Value)]) -> Result<Nonce16, Error>789 pub fn extract_nonce(protected_headers: &[(Label, Value)]) -> Result<Nonce16, Error> {
790 for (label, val) in protected_headers {
791 if *label == arc::KE_NONCE {
792 match val {
793 Value::Bytes(ref b) => {
794 return Ok(Nonce16(b.clone().try_into().map_err(|e| {
795 ag_err!(
796 InvalidPrivKeyArcInKey,
797 "invalid length of own nonce for key agreement: {:?}",
798 e
799 )
800 })?))
801 }
802 _ => {
803 return Err(ag_err!(
804 InvalidPrivKeyArcInKey,
805 "invalid encoding of own nonce for key agreement"
806 ))
807 }
808 };
809 }
810 }
811
812 Err(ag_err!(InvalidPrivKeyArcInKey, "nonce for key agreement is missing in the headers"))
813 }
814