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