xref: /aosp_15_r20/system/secretkeeper/client/src/authgraph_dev.rs (revision 3f8e9d82f4020c68ad19a99fc5fdc1fc90b79379)
1*3f8e9d82SAndroid Build Coastguard Worker /*
2*3f8e9d82SAndroid Build Coastguard Worker  * Copyright (C) 2023 The Android Open Source Project
3*3f8e9d82SAndroid Build Coastguard Worker  *
4*3f8e9d82SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*3f8e9d82SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*3f8e9d82SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*3f8e9d82SAndroid Build Coastguard Worker  *
8*3f8e9d82SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*3f8e9d82SAndroid Build Coastguard Worker  *
10*3f8e9d82SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*3f8e9d82SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*3f8e9d82SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*3f8e9d82SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*3f8e9d82SAndroid Build Coastguard Worker  * limitations under the License.
15*3f8e9d82SAndroid Build Coastguard Worker  */
16*3f8e9d82SAndroid Build Coastguard Worker 
17*3f8e9d82SAndroid Build Coastguard Worker //! Secretkeeper client acts as the "source" `AuthGraphParticipant` in context of AuthGraph
18*3f8e9d82SAndroid Build Coastguard Worker //! Key Exchange, while Secretkeeper itself acts as "sink" `AuthGraphParticipant`. This module
19*3f8e9d82SAndroid Build Coastguard Worker //! supports functionality to configure the local "source" `AuthGraphParticipant` for clients.
20*3f8e9d82SAndroid Build Coastguard Worker 
21*3f8e9d82SAndroid Build Coastguard Worker extern crate alloc;
22*3f8e9d82SAndroid Build Coastguard Worker 
23*3f8e9d82SAndroid Build Coastguard Worker use crate::dice::OwnedDiceArtifactsWithExplicitKey;
24*3f8e9d82SAndroid Build Coastguard Worker use authgraph_boringssl::{BoringAes, BoringRng};
25*3f8e9d82SAndroid Build Coastguard Worker use authgraph_core::ag_err;
26*3f8e9d82SAndroid Build Coastguard Worker use authgraph_core::error::Error as AgError;
27*3f8e9d82SAndroid Build Coastguard Worker use authgraph_core::error::Error;
28*3f8e9d82SAndroid Build Coastguard Worker use authgraph_core::key::{
29*3f8e9d82SAndroid Build Coastguard Worker     AesKey, CertChain, EcSignKey, EcVerifyKey, Identity, IdentityVerificationDecision,
30*3f8e9d82SAndroid Build Coastguard Worker     CURVE25519_PRIV_KEY_LEN, IDENTITY_VERSION,
31*3f8e9d82SAndroid Build Coastguard Worker };
32*3f8e9d82SAndroid Build Coastguard Worker use authgraph_core::traits;
33*3f8e9d82SAndroid Build Coastguard Worker use authgraph_core::traits::AG_KEY_EXCHANGE_PROTOCOL_VERSION_1;
34*3f8e9d82SAndroid Build Coastguard Worker use authgraph_core::traits::{AesGcm, Device, EcDsa};
35*3f8e9d82SAndroid Build Coastguard Worker use authgraph_wire::{ErrorCode, SESSION_ID_LEN};
36*3f8e9d82SAndroid Build Coastguard Worker use coset::CborSerializable;
37*3f8e9d82SAndroid Build Coastguard Worker use coset::{iana, CoseKey};
38*3f8e9d82SAndroid Build Coastguard Worker use diced_open_dice::derive_cdi_leaf_priv;
39*3f8e9d82SAndroid Build Coastguard Worker 
40*3f8e9d82SAndroid Build Coastguard Worker /// Implementation of `authgraph_core::traits::Device` required for configuring the local
41*3f8e9d82SAndroid Build Coastguard Worker /// `AuthGraphParticipant` for client.
42*3f8e9d82SAndroid Build Coastguard Worker pub struct AgDevice {
43*3f8e9d82SAndroid Build Coastguard Worker     per_boot_key: AesKey,
44*3f8e9d82SAndroid Build Coastguard Worker     identity: (EcSignKey, Identity),
45*3f8e9d82SAndroid Build Coastguard Worker     expected_peer_key: Option<CoseKey>,
46*3f8e9d82SAndroid Build Coastguard Worker }
47*3f8e9d82SAndroid Build Coastguard Worker 
48*3f8e9d82SAndroid Build Coastguard Worker impl AgDevice {
49*3f8e9d82SAndroid Build Coastguard Worker     const AG_KE_VERSION: i32 = AG_KEY_EXCHANGE_PROTOCOL_VERSION_1;
50*3f8e9d82SAndroid Build Coastguard Worker     /// Create a new `AgDevice`, using dice_artifacts for `Identity`.
new( dice_artifacts: &OwnedDiceArtifactsWithExplicitKey, expected_peer_key: Option<CoseKey>, ) -> Result<Self, AgError>51*3f8e9d82SAndroid Build Coastguard Worker     pub fn new(
52*3f8e9d82SAndroid Build Coastguard Worker         dice_artifacts: &OwnedDiceArtifactsWithExplicitKey,
53*3f8e9d82SAndroid Build Coastguard Worker         expected_peer_key: Option<CoseKey>,
54*3f8e9d82SAndroid Build Coastguard Worker     ) -> Result<Self, AgError> {
55*3f8e9d82SAndroid Build Coastguard Worker         let cdi_leaf_priv = derive_cdi_leaf_priv(dice_artifacts)
56*3f8e9d82SAndroid Build Coastguard Worker             .map_err(|_| ag_err!(InternalError, "Failed to get private key"))?;
57*3f8e9d82SAndroid Build Coastguard Worker         let identity = Identity {
58*3f8e9d82SAndroid Build Coastguard Worker             version: IDENTITY_VERSION,
59*3f8e9d82SAndroid Build Coastguard Worker             cert_chain: CertChain::from_slice(
60*3f8e9d82SAndroid Build Coastguard Worker                 dice_artifacts
61*3f8e9d82SAndroid Build Coastguard Worker                     .explicit_key_dice_chain()
62*3f8e9d82SAndroid Build Coastguard Worker                     .ok_or(ag_err!(InternalError, "Dice chain missing"))?,
63*3f8e9d82SAndroid Build Coastguard Worker             )?,
64*3f8e9d82SAndroid Build Coastguard Worker             policy: None,
65*3f8e9d82SAndroid Build Coastguard Worker         };
66*3f8e9d82SAndroid Build Coastguard Worker 
67*3f8e9d82SAndroid Build Coastguard Worker         let per_boot_key = BoringAes {}.generate_key(&mut BoringRng {})?;
68*3f8e9d82SAndroid Build Coastguard Worker 
69*3f8e9d82SAndroid Build Coastguard Worker         Ok(Self {
70*3f8e9d82SAndroid Build Coastguard Worker             per_boot_key,
71*3f8e9d82SAndroid Build Coastguard Worker             identity: (
72*3f8e9d82SAndroid Build Coastguard Worker                 EcSignKey::Ed25519(
73*3f8e9d82SAndroid Build Coastguard Worker                     // AuthGraph supports storing only the Ed25519 "seed", which is first 32 bytes
74*3f8e9d82SAndroid Build Coastguard Worker                     // of the private key, instead of the full private key.
75*3f8e9d82SAndroid Build Coastguard Worker                     cdi_leaf_priv.as_array()[0..CURVE25519_PRIV_KEY_LEN].try_into().map_err(
76*3f8e9d82SAndroid Build Coastguard Worker                         |e| {
77*3f8e9d82SAndroid Build Coastguard Worker                             ag_err!(
78*3f8e9d82SAndroid Build Coastguard Worker                                 InternalError,
79*3f8e9d82SAndroid Build Coastguard Worker                                 "Failed to construct private signing key {:?}",
80*3f8e9d82SAndroid Build Coastguard Worker                                 e
81*3f8e9d82SAndroid Build Coastguard Worker                             )
82*3f8e9d82SAndroid Build Coastguard Worker                         },
83*3f8e9d82SAndroid Build Coastguard Worker                     )?,
84*3f8e9d82SAndroid Build Coastguard Worker                 ),
85*3f8e9d82SAndroid Build Coastguard Worker                 identity,
86*3f8e9d82SAndroid Build Coastguard Worker             ),
87*3f8e9d82SAndroid Build Coastguard Worker             expected_peer_key,
88*3f8e9d82SAndroid Build Coastguard Worker         })
89*3f8e9d82SAndroid Build Coastguard Worker     }
90*3f8e9d82SAndroid Build Coastguard Worker }
91*3f8e9d82SAndroid Build Coastguard Worker 
92*3f8e9d82SAndroid Build Coastguard Worker impl Device for AgDevice {
get_or_create_per_boot_key( &self, _aes: &dyn traits::AesGcm, _rng: &mut dyn traits::Rng, ) -> Result<AesKey, AgError>93*3f8e9d82SAndroid Build Coastguard Worker     fn get_or_create_per_boot_key(
94*3f8e9d82SAndroid Build Coastguard Worker         &self,
95*3f8e9d82SAndroid Build Coastguard Worker         _aes: &dyn traits::AesGcm,
96*3f8e9d82SAndroid Build Coastguard Worker         _rng: &mut dyn traits::Rng,
97*3f8e9d82SAndroid Build Coastguard Worker     ) -> Result<AesKey, AgError> {
98*3f8e9d82SAndroid Build Coastguard Worker         Ok(self.per_boot_key.clone())
99*3f8e9d82SAndroid Build Coastguard Worker     }
100*3f8e9d82SAndroid Build Coastguard Worker 
get_per_boot_key(&self) -> Result<AesKey, AgError>101*3f8e9d82SAndroid Build Coastguard Worker     fn get_per_boot_key(&self) -> Result<AesKey, AgError> {
102*3f8e9d82SAndroid Build Coastguard Worker         Ok(self.per_boot_key.clone())
103*3f8e9d82SAndroid Build Coastguard Worker     }
104*3f8e9d82SAndroid Build Coastguard Worker 
get_identity(&self) -> Result<(Option<EcSignKey>, Identity), AgError>105*3f8e9d82SAndroid Build Coastguard Worker     fn get_identity(&self) -> Result<(Option<EcSignKey>, Identity), AgError> {
106*3f8e9d82SAndroid Build Coastguard Worker         let (sign_key, identity) = self.identity.clone();
107*3f8e9d82SAndroid Build Coastguard Worker         Ok((Some(sign_key), identity))
108*3f8e9d82SAndroid Build Coastguard Worker     }
109*3f8e9d82SAndroid Build Coastguard Worker 
get_cose_sign_algorithm(&self) -> Result<iana::Algorithm, AgError>110*3f8e9d82SAndroid Build Coastguard Worker     fn get_cose_sign_algorithm(&self) -> Result<iana::Algorithm, AgError> {
111*3f8e9d82SAndroid Build Coastguard Worker         // Microdroid Manager uses EdDSA as the signing algorithm while doing DICE derivation.
112*3f8e9d82SAndroid Build Coastguard Worker         Ok(iana::Algorithm::EdDSA)
113*3f8e9d82SAndroid Build Coastguard Worker     }
114*3f8e9d82SAndroid Build Coastguard Worker 
sign_data(&self, _ecdsa: &dyn traits::EcDsa, _data: &[u8]) -> Result<Vec<u8>, AgError>115*3f8e9d82SAndroid Build Coastguard Worker     fn sign_data(&self, _ecdsa: &dyn traits::EcDsa, _data: &[u8]) -> Result<Vec<u8>, AgError> {
116*3f8e9d82SAndroid Build Coastguard Worker         // Since the private signing key is already returned in the `get_identity` method of this
117*3f8e9d82SAndroid Build Coastguard Worker         // implementation, this method can be marked `Unimplemented`.
118*3f8e9d82SAndroid Build Coastguard Worker         Err(ag_err!(Unimplemented, "Unexpected signing request when the signing key available"))
119*3f8e9d82SAndroid Build Coastguard Worker     }
120*3f8e9d82SAndroid Build Coastguard Worker 
evaluate_identity( &self, _latest_identity: &Identity, _previous_identity: &Identity, ) -> Result<IdentityVerificationDecision, AgError>121*3f8e9d82SAndroid Build Coastguard Worker     fn evaluate_identity(
122*3f8e9d82SAndroid Build Coastguard Worker         &self,
123*3f8e9d82SAndroid Build Coastguard Worker         _latest_identity: &Identity,
124*3f8e9d82SAndroid Build Coastguard Worker         _previous_identity: &Identity,
125*3f8e9d82SAndroid Build Coastguard Worker     ) -> Result<IdentityVerificationDecision, AgError> {
126*3f8e9d82SAndroid Build Coastguard Worker         // AuthGraph Key Exchange protocol does not require this. Additionally, Secretkeeper
127*3f8e9d82SAndroid Build Coastguard Worker         // clients create fresh, short-lived sessions and their identity does not change within
128*3f8e9d82SAndroid Build Coastguard Worker         // a session.
129*3f8e9d82SAndroid Build Coastguard Worker         Err(ag_err!(Unimplemented, "Not required"))
130*3f8e9d82SAndroid Build Coastguard Worker     }
131*3f8e9d82SAndroid Build Coastguard Worker 
get_version(&self) -> i32132*3f8e9d82SAndroid Build Coastguard Worker     fn get_version(&self) -> i32 {
133*3f8e9d82SAndroid Build Coastguard Worker         Self::AG_KE_VERSION
134*3f8e9d82SAndroid Build Coastguard Worker     }
135*3f8e9d82SAndroid Build Coastguard Worker 
record_shared_sessions( &mut self, _peer_identity: &Identity, _session_id: &[u8; SESSION_ID_LEN], _shared_keys: &[Vec<u8>; 2], _sha256: &dyn traits::Sha256, ) -> Result<(), AgError>136*3f8e9d82SAndroid Build Coastguard Worker     fn record_shared_sessions(
137*3f8e9d82SAndroid Build Coastguard Worker         &mut self,
138*3f8e9d82SAndroid Build Coastguard Worker         _peer_identity: &Identity,
139*3f8e9d82SAndroid Build Coastguard Worker         _session_id: &[u8; SESSION_ID_LEN],
140*3f8e9d82SAndroid Build Coastguard Worker         _shared_keys: &[Vec<u8>; 2],
141*3f8e9d82SAndroid Build Coastguard Worker         _sha256: &dyn traits::Sha256,
142*3f8e9d82SAndroid Build Coastguard Worker     ) -> Result<(), AgError> {
143*3f8e9d82SAndroid Build Coastguard Worker         // There are alternative ways of recording the shared session such as inspecting the output
144*3f8e9d82SAndroid Build Coastguard Worker         // of KE methods.
145*3f8e9d82SAndroid Build Coastguard Worker         Ok(())
146*3f8e9d82SAndroid Build Coastguard Worker     }
147*3f8e9d82SAndroid Build Coastguard Worker 
validate_peer_identity( &self, identity: &Identity, ecdsa: &dyn EcDsa, ) -> Result<EcVerifyKey, Error>148*3f8e9d82SAndroid Build Coastguard Worker     fn validate_peer_identity(
149*3f8e9d82SAndroid Build Coastguard Worker         &self,
150*3f8e9d82SAndroid Build Coastguard Worker         identity: &Identity,
151*3f8e9d82SAndroid Build Coastguard Worker         ecdsa: &dyn EcDsa,
152*3f8e9d82SAndroid Build Coastguard Worker     ) -> Result<EcVerifyKey, Error> {
153*3f8e9d82SAndroid Build Coastguard Worker         if identity.cert_chain.dice_cert_chain.is_some() {
154*3f8e9d82SAndroid Build Coastguard Worker             return Err(ag_err!(InvalidPeerKeKey, "Expected peer's DICE chain to be None"));
155*3f8e9d82SAndroid Build Coastguard Worker         }
156*3f8e9d82SAndroid Build Coastguard Worker         let root_key = identity.validate(ecdsa)?;
157*3f8e9d82SAndroid Build Coastguard Worker 
158*3f8e9d82SAndroid Build Coastguard Worker         if let Some(expected_key) = &self.expected_peer_key {
159*3f8e9d82SAndroid Build Coastguard Worker             // Do bit by bit comparison of the keys. This assumes the 2 keys are equivalently
160*3f8e9d82SAndroid Build Coastguard Worker             // canonicalized
161*3f8e9d82SAndroid Build Coastguard Worker             if root_key.get_key_ref() != expected_key {
162*3f8e9d82SAndroid Build Coastguard Worker                 return Err(ag_err!(
163*3f8e9d82SAndroid Build Coastguard Worker                     InvalidPeerKeKey,
164*3f8e9d82SAndroid Build Coastguard Worker                     "Peer identity did not match the expected identity"
165*3f8e9d82SAndroid Build Coastguard Worker                 ));
166*3f8e9d82SAndroid Build Coastguard Worker             }
167*3f8e9d82SAndroid Build Coastguard Worker         }
168*3f8e9d82SAndroid Build Coastguard Worker         Ok(root_key)
169*3f8e9d82SAndroid Build Coastguard Worker     }
170*3f8e9d82SAndroid Build Coastguard Worker 
validate_shared_sessions( &self, _peer_identity: &Identity, _session_id: &[u8; SESSION_ID_LEN], _shared_keys: &[Vec<u8>], _sha256: &dyn traits::Sha256, ) -> Result<(), Error>171*3f8e9d82SAndroid Build Coastguard Worker     fn validate_shared_sessions(
172*3f8e9d82SAndroid Build Coastguard Worker         &self,
173*3f8e9d82SAndroid Build Coastguard Worker         _peer_identity: &Identity,
174*3f8e9d82SAndroid Build Coastguard Worker         _session_id: &[u8; SESSION_ID_LEN],
175*3f8e9d82SAndroid Build Coastguard Worker         _shared_keys: &[Vec<u8>],
176*3f8e9d82SAndroid Build Coastguard Worker         _sha256: &dyn traits::Sha256,
177*3f8e9d82SAndroid Build Coastguard Worker     ) -> Result<(), Error> {
178*3f8e9d82SAndroid Build Coastguard Worker         // Currently, there is no use of validation of shared session to application protocol.
179*3f8e9d82SAndroid Build Coastguard Worker         Ok(())
180*3f8e9d82SAndroid Build Coastguard Worker     }
181*3f8e9d82SAndroid Build Coastguard Worker }
182