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