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