• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 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 //! Tests for the core functionality of the AuthMgr BE.
18 //! This module defines structs that represent a pvm instance and a client in order to imitate an
19 //! AuthMgr FE in these tests. It also provides mock implementations for the *Connection traits that
20 //! should be tightly coupled with an actual AuthMgr BE TA running in TEE.
21 //! The generic tests provided by this module can be used to test the end to end AuthMgr protocol
22 //! with any implementation of the other three main traits of AuthMgr (Crypto, Device and *Storage),
23 //! that can be implemented fairly independent of an AuthMgr BE TA running in TEE.
24 use authgraph_core::key::{CertChain, DiceChainEntry, EcSignKey, Policy};
25 use authgraph_core::traits::{EcDsa, Rng};
26 use authmgr_be::authorization::AuthMgrBE;
27 use authmgr_be::data_structures::{AuthenticatedConnectionState, ClientId};
28 use authmgr_be::error::Error;
29 use authmgr_be::traits::{RawConnection, RpcConnection};
30 use authmgr_common::{
31     signed_connection_request::{
32         Challenge, ConnectionRequest, TransportID, TEMP_AUTHMGR_BE_TRANSPORT_ID,
33     },
34     Result as AuthMgrCommonResult, Token, TOKEN_LENGTH,
35 };
36 use coset::CborSerializable;
37 
38 /// Struct that represents a pvm instance for testing purposes
39 pub struct PVM {
40     /// The connection from pvm to AuthMgr
41     pub rpc_connection: RpcConnectionInfo,
42     /// Set of clients to be authorized
43     pub clients: Vec<Client>,
44     /// DICE certificate chain of the pvm
45     pub dice_chain: CertChain,
46     /// DICE policy of the pvm
47     pub dice_chain_policy: Policy,
48     /// Signing key that originates from the DICE CDI
49     pub sign_key: EcSignKey,
50     /// Implementation of the EcDsa trait
51     pub ecdsa: Box<dyn EcDsa>,
52     /// Implementation of the Rng trait
53     pub rng: Box<dyn Rng>,
54 }
55 
56 impl PVM {
57     /// Constructor
new( dice_chain: CertChain, dice_chain_policy: Policy, sign_key: EcSignKey, ecdsa: Box<dyn EcDsa>, rng: Box<dyn Rng>, transport_id: TransportID, ) -> Self58     pub fn new(
59         dice_chain: CertChain,
60         dice_chain_policy: Policy,
61         sign_key: EcSignKey,
62         ecdsa: Box<dyn EcDsa>,
63         rng: Box<dyn Rng>,
64         transport_id: TransportID,
65     ) -> Self {
66         Self {
67             rpc_connection: RpcConnectionInfo::new(transport_id),
68             clients: Vec::<Client>::new(),
69             dice_chain,
70             dice_chain_policy,
71             sign_key,
72             ecdsa,
73             rng,
74         }
75     }
76 
77     /// Add a client to the pvm instance
add_client(&mut self, client: Client)78     pub fn add_client(&mut self, client: Client) {
79         self.clients.push(client);
80     }
81 
82     /// Given the challenge returned by the AuthMgr-BE, sign the connection request.
sign_connection_request(&self, challenge: Challenge) -> AuthMgrCommonResult<Vec<u8>>83     pub fn sign_connection_request(&self, challenge: Challenge) -> AuthMgrCommonResult<Vec<u8>> {
84         let conn_req = ConnectionRequest::new_for_ffa_transport(
85             challenge,
86             self.rpc_connection.transport_id,
87             TEMP_AUTHMGR_BE_TRANSPORT_ID,
88         );
89         conn_req.sign(&self.sign_key, &*self.ecdsa, self.sign_key.get_cose_sign_algorithm())
90     }
91 
92     /// Create a new connection and a token for a client
create_connection_and_token_for_client(&self) -> (RawConnectionInfo, Token)93     pub fn create_connection_and_token_for_client(&self) -> (RawConnectionInfo, Token) {
94         let mut token = [0u8; TOKEN_LENGTH];
95         self.rng.fill_bytes(&mut token);
96         (
97             RawConnectionInfo::new(
98                 self.rpc_connection.transport_id,
99                 0, /*This is N/A for the mock implementation*/
100             ),
101             token,
102         )
103     }
104 }
105 
106 /// Struct that represents a client TA in a pvm instance for testing purposes
107 pub struct Client {
108     /// DICE leaf certificate of the client
109     pub dice_leaf: DiceChainEntry,
110     /// DICE policy of the client
111     pub dice_leaf_policy: Policy,
112     /// Client id
113     pub client_id: ClientId,
114     /// Name of the services that the client needs to connect to
115     pub services: Vec<String>,
116 }
117 
118 impl Client {
119     /// Constructor
new( dice_leaf: DiceChainEntry, dice_leaf_policy: Policy, services: Vec<String>, rng: Box<dyn Rng>, ) -> Self120     pub fn new(
121         dice_leaf: DiceChainEntry,
122         dice_leaf_policy: Policy,
123         services: Vec<String>,
124         rng: Box<dyn Rng>,
125     ) -> Self {
126         let mut client_id_bytes = [0u8; 32];
127         rng.fill_bytes(&mut client_id_bytes);
128         Self {
129             dice_leaf,
130             dice_leaf_policy,
131             client_id: ClientId(client_id_bytes.to_vec()),
132             services,
133         }
134     }
135 }
136 
137 /// Implementation of the `RpcConnection` trait
138 pub struct RpcConnectionInfo {
139     /// Transport ID (a.k.a VM ID)
140     pub transport_id: TransportID,
141     /// Conncection state to be stored by the AuthMgr protocol
142     pub connection_state: Option<AuthenticatedConnectionState>,
143 }
144 
145 impl RpcConnectionInfo {
146     /// Constructor
new(transport_id: TransportID) -> Self147     pub fn new(transport_id: TransportID) -> Self {
148         Self { transport_id, connection_state: None }
149     }
150 }
151 
152 impl RpcConnection for RpcConnectionInfo {
153     /// Return the transport ID of the instance that is on the other end of the connection
get_peer_transport_id(&self) -> Result<TransportID, Error>154     fn get_peer_transport_id(&self) -> Result<TransportID, Error> {
155         Ok(self.transport_id)
156     }
157 
158     /// Store the re-usable information in the connection state to be used while serving multiple
159     /// client authorization requests from the same pVM instance
store_authenticated_state( &mut self, connection_state: AuthenticatedConnectionState, ) -> Result<(), Error>160     fn store_authenticated_state(
161         &mut self,
162         connection_state: AuthenticatedConnectionState,
163     ) -> Result<(), Error> {
164         self.connection_state = Some(connection_state);
165         Ok(())
166     }
167 
168     /// Return the state if the state has been stored for this connecction. Since the state is
169     /// stored only after the peer on the other side of the connection is authenticated, the
170     /// existence of a connection state is also an indication that the connection is authenticated.
get_authenticated_state(&self) -> Result<Option<&AuthenticatedConnectionState>, Error>171     fn get_authenticated_state(&self) -> Result<Option<&AuthenticatedConnectionState>, Error> {
172         Ok(self.connection_state.as_ref())
173     }
174 
175     /// Return the mutable state if the state has been stored for this connecction. Since the state
176     /// is stored only after the peer on the other side of the connection is authenticated, the
177     /// existence of a connection state is also an indication that the connection is authenticated.
get_mutable_authenticated_state( &mut self, ) -> Result<Option<&mut AuthenticatedConnectionState>, Error>178     fn get_mutable_authenticated_state(
179         &mut self,
180     ) -> Result<Option<&mut AuthenticatedConnectionState>, Error> {
181         Ok(self.connection_state.as_mut())
182     }
183 
184     /// Delete the state stored associated with the connection. This is called when the connection
185     /// is closed.
remove_authenticated_state(&mut self) -> Result<(), Error>186     fn remove_authenticated_state(&mut self) -> Result<(), Error> {
187         self.connection_state = None;
188         Ok(())
189     }
190 }
191 
192 /// Implementation of the `RawConnection` trait
193 pub struct RawConnectionInfo {
194     /// Transport ID (a.k.a VM ID)
195     transport_id: TransportID,
196     /// Raw file descriptor corresponding to this connection.
197     raw_fd: i32,
198 }
199 
200 impl RawConnectionInfo {
201     /// Constructor
new(transport_id: TransportID, raw_fd: i32) -> Self202     pub fn new(transport_id: TransportID, raw_fd: i32) -> Self {
203         Self { transport_id, raw_fd }
204     }
205 }
206 
207 impl RawConnection for RawConnectionInfo {
208     /// Return the transport ID of the instance that is on the other end of the connection
get_peer_transport_id(&self) -> Result<TransportID, Error>209     fn get_peer_transport_id(&self) -> Result<TransportID, Error> {
210         Ok(self.transport_id)
211     }
212 
213     /// Return the raw file descriptor id of the underlying connection. This method is not called by
214     /// the authmgr backend core library. Rather, this is added for use of the vendor
215     /// implementations which pass in an implementation of `RawConnection` trait into this library
216     /// and then retrieve it back in their implementations.
into_raw_fd(self: Box<Self>) -> i32217     fn into_raw_fd(self: Box<Self>) -> i32 {
218         // Note: in the actual implementation, we need to make sure that the object is not destoyed
219         // at the end of this method
220         self.raw_fd
221     }
222 }
223 
224 /// Test the happy path for the full AuthMgr protocol with a single pvm instance.
test_auth_mgr_protocol_succeeds_single_pvm(authmgr_be: &mut AuthMgrBE, mut pvm: PVM)225 pub fn test_auth_mgr_protocol_succeeds_single_pvm(authmgr_be: &mut AuthMgrBE, mut pvm: PVM) {
226     let challenge: Challenge = authmgr_be
227         .init_authentication(
228             &pvm.rpc_connection,
229             &pvm.dice_chain.clone().to_vec().expect("error in encoding the DICE chain"),
230             None,
231         )
232         .expect("init_auth failed");
233     let signature =
234         pvm.sign_connection_request(challenge).expect("signing connection request failed");
235     authmgr_be
236         .complete_authentication(&mut pvm.rpc_connection, &signature, &pvm.dice_chain_policy.0)
237         .expect("complete_auth failed");
238     for client in &pvm.clients {
239         for service in &client.services {
240             let (conn, token) = pvm.create_connection_and_token_for_client();
241             authmgr_be
242                 .init_connection_for_client(Box::new(conn), token)
243                 .expect("init_connection_for_client failed");
244             authmgr_be
245                 .authorize_and_connect_client_to_trusted_service(
246                     &mut pvm.rpc_connection,
247                     &client.client_id.0,
248                     service,
249                     token,
250                     &client.dice_leaf.clone().to_vec().expect("error in encoding the DICE cert"),
251                     &client.dice_leaf_policy.0,
252                 )
253                 .expect("authorize_and_connect_client_to_trusted_service failed");
254         }
255     }
256 }
257