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