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