// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// //! An example implementation for the Authgraph `Device` trait for testing purposes. use authgraph_core::{ ag_err, error::Error, key::{ AesKey, CertChain, EcSignKey, EcVerifyKey, Identity, IdentityVerificationDecision, EXPLICIT_KEY_DICE_CERT_CHAIN_VERSION, IDENTITY_VERSION, }, traits, }; use authgraph_wire::{ErrorCode, SESSION_ID_LEN}; use core::cell::RefCell; use coset::{iana, CborOrdering}; /// The struct implementing the Authgraph `Device` trait. pub struct AgDevice { per_boot_key: RefCell>, identity: RefCell>, cose_sign_algorithm: RefCell>, // Make the (source/sink) version configurable for testing purposes version: RefCell, } impl Default for AgDevice { fn default() -> Self { AgDevice { per_boot_key: RefCell::new(None), identity: RefCell::new(None), cose_sign_algorithm: RefCell::new(None), version: RefCell::new(1), } } } impl AgDevice { /// Set the given identity pub fn set_identity( &self, identity: (EcSignKey, Identity), cose_sign_algorithm: iana::Algorithm, ) { *self.identity.borrow_mut() = Some(identity); *self.cose_sign_algorithm.borrow_mut() = Some(cose_sign_algorithm); } } impl traits::Device for AgDevice { fn get_or_create_per_boot_key( &self, aes: &dyn traits::AesGcm, rng: &mut dyn traits::Rng, ) -> Result { if self.per_boot_key.borrow().is_none() { let pbk = aes.generate_key(rng)?; *self.per_boot_key.borrow_mut() = Some(pbk); } self.per_boot_key .borrow() .as_ref() .cloned() .ok_or(ag_err!(InternalError, "per boot key cannot be none at this point")) } fn get_per_boot_key(&self) -> Result { self.per_boot_key .borrow() .as_ref() .cloned() .ok_or(ag_err!(InternalError, "per boot key is missing")) } /// If the `identity` field is not set (e.g. via `set_identity`), the default implementation /// creates identity with a ExplicitKeyDiceCertChain that only contains a /// DiceCertChainInitialPayload (i.e. no DiceChainEntry) and without a policy. /// DiceCertChainInitialPayload is an EC public key on P-256 curve in the default implementation fn get_identity(&self) -> Result<(Option, Identity), Error> { if self.identity.borrow().is_none() { let (priv_key, mut pub_key) = crate::ec::create_p256_key_pair(iana::Algorithm::ES256)?; pub_key.canonicalize(CborOrdering::Lexicographic); let identity = Identity { version: IDENTITY_VERSION, cert_chain: CertChain { version: EXPLICIT_KEY_DICE_CERT_CHAIN_VERSION, root_key: EcVerifyKey::P256(pub_key), dice_cert_chain: None, }, policy: None, }; self.set_identity((EcSignKey::P256(priv_key), identity), iana::Algorithm::ES256); } let (sign_key, identity) = self .identity .borrow() .as_ref() .cloned() .ok_or(ag_err!(InternalError, "identity is missing"))?; Ok((Some(sign_key), identity)) } fn get_cose_sign_algorithm(&self) -> Result { self.cose_sign_algorithm .borrow() .as_ref() .cloned() .ok_or(ag_err!(InternalError, "cose sign algorithm is missing")) } fn sign_data(&self, _ecdsa: &dyn traits::EcDsa, _data: &[u8]) -> Result, Error> { // Since the private signing key is returned in the `get_identity` method of this test // implementation of the `device` trait, and therefore we can use `EcDsa::sign` method, this // method is marked as `Unimplemented`. Err(ag_err!(Unimplemented, "unexpected signing request when the signing key available")) } fn evaluate_identity( &self, _latest_identity: &Identity, _previous_identity: &Identity, ) -> Result { // TODO (b/304623554): this trait method is not used in the key exchange protocol. This will // be implemented in the next phase of AuthGraph Err(ag_err!(Unimplemented, "")) } fn get_version(&self) -> i32 { *self.version.borrow() } fn get_negotiated_version(&self, peer_version: i32) -> i32 { let self_version = *self.version.borrow(); if peer_version < self_version { return peer_version; } self_version } fn record_shared_sessions( &mut self, _peer_identity: &Identity, _session_id: &[u8; SESSION_ID_LEN], _shared_keys: &[Vec; 2], _sha256: &dyn traits::Sha256, ) -> Result<(), Error> { // The test implementation does not need to store the shared keys because there is no // application protocol to run using the shared keys. Ok(()) } fn validate_shared_sessions( &self, _peer_identity: &Identity, _session_id: &[u8; SESSION_ID_LEN], _shared_keys: &[Vec], _sha256: &dyn traits::Sha256, ) -> Result<(), Error> { // The test implementation does not need to validate the shared keys because there is no // application protocol that depends on the shared keys. Ok(()) } } impl AgDevice { /// Make the version configurable for testing purposes pub fn set_version(&self, version: i32) { *self.version.borrow_mut() = version } }