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 //! An example implementation for the Authgraph `Device` trait for testing purposes.
18 
19 use authgraph_core::{
20     ag_err,
21     error::Error,
22     key::{
23         AesKey, CertChain, EcSignKey, EcVerifyKey, Identity, IdentityVerificationDecision,
24         EXPLICIT_KEY_DICE_CERT_CHAIN_VERSION, IDENTITY_VERSION,
25     },
26     traits,
27 };
28 use authgraph_wire::{ErrorCode, SESSION_ID_LEN};
29 use core::cell::RefCell;
30 use coset::{iana, CborOrdering};
31 
32 /// The struct implementing the Authgraph `Device` trait.
33 pub struct AgDevice {
34     per_boot_key: RefCell<Option<AesKey>>,
35     identity: RefCell<Option<(EcSignKey, Identity)>>,
36     cose_sign_algorithm: RefCell<Option<iana::Algorithm>>,
37     // Make the (source/sink) version configurable for testing purposes
38     version: RefCell<i32>,
39 }
40 
41 impl Default for AgDevice {
default() -> Self42     fn default() -> Self {
43         AgDevice {
44             per_boot_key: RefCell::new(None),
45             identity: RefCell::new(None),
46             cose_sign_algorithm: RefCell::new(None),
47             version: RefCell::new(1),
48         }
49     }
50 }
51 
52 impl AgDevice {
53     /// Set the given identity
set_identity( &self, identity: (EcSignKey, Identity), cose_sign_algorithm: iana::Algorithm, )54     pub fn set_identity(
55         &self,
56         identity: (EcSignKey, Identity),
57         cose_sign_algorithm: iana::Algorithm,
58     ) {
59         *self.identity.borrow_mut() = Some(identity);
60         *self.cose_sign_algorithm.borrow_mut() = Some(cose_sign_algorithm);
61     }
62 }
63 
64 impl traits::Device for AgDevice {
get_or_create_per_boot_key( &self, aes: &dyn traits::AesGcm, rng: &mut dyn traits::Rng, ) -> Result<AesKey, Error>65     fn get_or_create_per_boot_key(
66         &self,
67         aes: &dyn traits::AesGcm,
68         rng: &mut dyn traits::Rng,
69     ) -> Result<AesKey, Error> {
70         if self.per_boot_key.borrow().is_none() {
71             let pbk = aes.generate_key(rng)?;
72             *self.per_boot_key.borrow_mut() = Some(pbk);
73         }
74         self.per_boot_key
75             .borrow()
76             .as_ref()
77             .cloned()
78             .ok_or(ag_err!(InternalError, "per boot key cannot be none at this point"))
79     }
80 
get_per_boot_key(&self) -> Result<AesKey, Error>81     fn get_per_boot_key(&self) -> Result<AesKey, Error> {
82         self.per_boot_key
83             .borrow()
84             .as_ref()
85             .cloned()
86             .ok_or(ag_err!(InternalError, "per boot key is missing"))
87     }
88 
89     /// If the `identity` field is not set (e.g. via `set_identity`), the default implementation
90     /// creates identity with a ExplicitKeyDiceCertChain that only contains a
91     /// DiceCertChainInitialPayload (i.e. no DiceChainEntry) and without a policy.
92     /// DiceCertChainInitialPayload is an EC public key on P-256 curve in the default implementation
get_identity(&self) -> Result<(Option<EcSignKey>, Identity), Error>93     fn get_identity(&self) -> Result<(Option<EcSignKey>, Identity), Error> {
94         if self.identity.borrow().is_none() {
95             let (priv_key, mut pub_key) = crate::ec::create_p256_key_pair(iana::Algorithm::ES256)?;
96             pub_key.canonicalize(CborOrdering::Lexicographic);
97             let identity = Identity {
98                 version: IDENTITY_VERSION,
99                 cert_chain: CertChain {
100                     version: EXPLICIT_KEY_DICE_CERT_CHAIN_VERSION,
101                     root_key: EcVerifyKey::P256(pub_key),
102                     dice_cert_chain: None,
103                 },
104                 policy: None,
105             };
106             self.set_identity((EcSignKey::P256(priv_key), identity), iana::Algorithm::ES256);
107         }
108         let (sign_key, identity) = self
109             .identity
110             .borrow()
111             .as_ref()
112             .cloned()
113             .ok_or(ag_err!(InternalError, "identity is missing"))?;
114         Ok((Some(sign_key), identity))
115     }
116 
get_cose_sign_algorithm(&self) -> Result<iana::Algorithm, Error>117     fn get_cose_sign_algorithm(&self) -> Result<iana::Algorithm, Error> {
118         self.cose_sign_algorithm
119             .borrow()
120             .as_ref()
121             .cloned()
122             .ok_or(ag_err!(InternalError, "cose sign algorithm is missing"))
123     }
124 
sign_data(&self, _ecdsa: &dyn traits::EcDsa, _data: &[u8]) -> Result<Vec<u8>, Error>125     fn sign_data(&self, _ecdsa: &dyn traits::EcDsa, _data: &[u8]) -> Result<Vec<u8>, Error> {
126         // Since the private signing key is returned in the `get_identity` method of this test
127         // implementation of the `device` trait, and therefore we can use `EcDsa::sign` method, this
128         // method is marked as `Unimplemented`.
129         Err(ag_err!(Unimplemented, "unexpected signing request when the signing key available"))
130     }
131 
evaluate_identity( &self, _latest_identity: &Identity, _previous_identity: &Identity, ) -> Result<IdentityVerificationDecision, Error>132     fn evaluate_identity(
133         &self,
134         _latest_identity: &Identity,
135         _previous_identity: &Identity,
136     ) -> Result<IdentityVerificationDecision, Error> {
137         // TODO (b/304623554): this trait method is not used in the key exchange protocol. This will
138         // be implemented in the next phase of AuthGraph
139         Err(ag_err!(Unimplemented, ""))
140     }
141 
get_version(&self) -> i32142     fn get_version(&self) -> i32 {
143         *self.version.borrow()
144     }
145 
get_negotiated_version(&self, peer_version: i32) -> i32146     fn get_negotiated_version(&self, peer_version: i32) -> i32 {
147         let self_version = *self.version.borrow();
148         if peer_version < self_version {
149             return peer_version;
150         }
151         self_version
152     }
153 
record_shared_sessions( &mut self, _peer_identity: &Identity, _session_id: &[u8; SESSION_ID_LEN], _shared_keys: &[Vec<u8>; 2], _sha256: &dyn traits::Sha256, ) -> Result<(), Error>154     fn record_shared_sessions(
155         &mut self,
156         _peer_identity: &Identity,
157         _session_id: &[u8; SESSION_ID_LEN],
158         _shared_keys: &[Vec<u8>; 2],
159         _sha256: &dyn traits::Sha256,
160     ) -> Result<(), Error> {
161         // The test implementation does not need to store the shared keys because there is no
162         // application protocol to run using the shared keys.
163         Ok(())
164     }
165 
validate_shared_sessions( &self, _peer_identity: &Identity, _session_id: &[u8; SESSION_ID_LEN], _shared_keys: &[Vec<u8>], _sha256: &dyn traits::Sha256, ) -> Result<(), Error>166     fn validate_shared_sessions(
167         &self,
168         _peer_identity: &Identity,
169         _session_id: &[u8; SESSION_ID_LEN],
170         _shared_keys: &[Vec<u8>],
171         _sha256: &dyn traits::Sha256,
172     ) -> Result<(), Error> {
173         // The test implementation does not need to validate the shared keys because there is no
174         // application protocol that depends on the shared keys.
175         Ok(())
176     }
177 }
178 
179 impl AgDevice {
180     /// Make the version configurable for testing purposes
set_version(&self, version: i32)181     pub fn set_version(&self, version: i32) {
182         *self.version.borrow_mut() = version
183     }
184 }
185