• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //! VTS tests for sources
18 use super::*;
19 use authgraph_core::{key, keyexchange as ke};
20 
21 /// Run AuthGraph tests against the provided source, using a local test sink implementation.
test( local_sink: &mut ke::AuthGraphParticipant, source: binder::Strong<dyn IAuthGraphKeyExchange>, )22 pub fn test(
23     local_sink: &mut ke::AuthGraphParticipant,
24     source: binder::Strong<dyn IAuthGraphKeyExchange>,
25 ) {
26     test_mainline(local_sink, source.clone());
27     test_corrupt_sig(local_sink, source.clone());
28     test_corrupt_key(local_sink, source);
29 }
30 
31 /// Perform mainline AuthGraph key exchange with the provided source.
32 /// Return the agreed AES keys in plaintext, together with the session ID.
test_mainline( local_sink: &mut ke::AuthGraphParticipant, source: binder::Strong<dyn IAuthGraphKeyExchange>, ) -> ([key::AesKey; 2], Vec<u8>)33 pub fn test_mainline(
34     local_sink: &mut ke::AuthGraphParticipant,
35     source: binder::Strong<dyn IAuthGraphKeyExchange>,
36 ) -> ([key::AesKey; 2], Vec<u8>) {
37     // Step 1: create an ephemeral ECDH key at the (remote) source.
38     let source_init_info = source
39         .create()
40         .expect("failed to create() with remote impl");
41     assert!(source_init_info.key.pubKey.is_some());
42     assert!(source_init_info.key.arcFromPBK.is_some());
43     let source_pub_key = extract_plain_pub_key(&source_init_info.key.pubKey);
44 
45     // Step 2: pass the source's ECDH public key and other session info to the (local) sink.
46     let init_result = local_sink
47         .init(
48             &source_pub_key.plainPubKey,
49             &source_init_info.identity.identity,
50             &source_init_info.nonce,
51             source_init_info.version,
52         )
53         .expect("failed to init() with local impl");
54     let sink_init_info = init_result.session_init_info;
55     let sink_pub_key = sink_init_info
56         .ke_key
57         .pub_key
58         .expect("expect pub_key to be populated");
59 
60     let sink_info = init_result.session_info;
61     assert!(!sink_info.session_id.is_empty());
62 
63     // The AuthGraph core library will verify the session ID signature, but do it here too.
64     let sink_verification_key = key::Identity::from_slice(&sink_init_info.identity)
65         .expect("invalid identity CBOR")
66         .cert_chain
67         .root_key;
68     local_sink
69         .verify_signature_on_session_id(
70             &sink_verification_key,
71             &sink_info.session_id,
72             &sink_info.session_id_signature,
73         )
74         .expect("failed verification of signed session ID");
75 
76     // Step 3: pass the sink's ECDH public key and other session info to the (remote) source, so it
77     // can calculate the same pair of symmetric keys.
78     let source_info = source
79         .finish(
80             &PubKey::PlainKey(PlainPubKey {
81                 plainPubKey: sink_pub_key,
82             }),
83             &Identity {
84                 identity: sink_init_info.identity,
85             },
86             &vec_to_signature(&sink_info.session_id_signature),
87             &sink_init_info.nonce,
88             sink_init_info.version,
89             &source_init_info.key,
90         )
91         .expect("failed to finish() with remote impl");
92     assert!(!source_info.sessionId.is_empty());
93 
94     // The AuthGraph core library will verify the session ID signature, but do it here too.
95     let source_verification_key = local_sink
96         .peer_verification_key_from_identity(&source_init_info.identity.identity)
97         .expect("failed to get peer verification from identity");
98     local_sink
99         .verify_signature_on_session_id(
100             &source_verification_key,
101             &source_info.sessionId,
102             &source_info.signature.signature,
103         )
104         .expect("failed verification of signed session ID");
105 
106     // Both ends should agree on the session ID.
107     assert_eq!(source_info.sessionId, sink_info.session_id);
108 
109     // Step 4: pass the (remote) source's session ID signature back to the sink, so it can check it
110     // and update the symmetric keys so they're marked as authentication complete.
111     let sink_arcs = local_sink
112         .authentication_complete(&source_info.signature.signature, sink_info.shared_keys)
113         .expect("failed to authenticationComplete() with local sink");
114     // Decrypt and return the session keys.
115     let decrypted_shared_keys = local_sink
116         .decipher_shared_keys_from_arcs(&sink_arcs)
117         .expect("failed to decrypt shared key arcs")
118         .try_into();
119     let decrypted_shared_keys_array = match decrypted_shared_keys {
120         Ok(array) => array,
121         Err(_) => panic!("wrong number of decrypted shared key arcs"),
122     };
123     (decrypted_shared_keys_array, source_info.sessionId)
124 }
125 
126 /// Perform mainline AuthGraph key exchange with the provided source, but provide an invalid session
127 /// ID signature.
test_corrupt_sig( local_sink: &mut ke::AuthGraphParticipant, source: binder::Strong<dyn IAuthGraphKeyExchange>, )128 pub fn test_corrupt_sig(
129     local_sink: &mut ke::AuthGraphParticipant,
130     source: binder::Strong<dyn IAuthGraphKeyExchange>,
131 ) {
132     // Step 1: create an ephemeral ECDH key at the (remote) source.
133     let source_init_info = source
134         .create()
135         .expect("failed to create() with remote impl");
136     assert!(source_init_info.key.pubKey.is_some());
137     assert!(source_init_info.key.arcFromPBK.is_some());
138     let source_pub_key = extract_plain_pub_key(&source_init_info.key.pubKey);
139 
140     // Step 2: pass the source's ECDH public key and other session info to the (local) sink.
141     let init_result = local_sink
142         .init(
143             &source_pub_key.plainPubKey,
144             &source_init_info.identity.identity,
145             &source_init_info.nonce,
146             source_init_info.version,
147         )
148         .expect("failed to init() with local impl");
149     let sink_init_info = init_result.session_init_info;
150     let sink_pub_key = sink_init_info
151         .ke_key
152         .pub_key
153         .expect("expect pub_key to be populated");
154     let sink_info = init_result.session_info;
155     assert!(!sink_info.session_id.is_empty());
156 
157     // Deliberately corrupt the sink's session ID signature.
158     let mut corrupt_signature = sink_info.session_id_signature.clone();
159     let sig_len = corrupt_signature.len();
160     corrupt_signature[sig_len - 1] ^= 0x01;
161 
162     // Step 3: pass the sink's ECDH public key and other session info to the (remote) source, so it
163     // can calculate the same pair of symmetric keys.
164     let result = source.finish(
165         &PubKey::PlainKey(PlainPubKey {
166             plainPubKey: sink_pub_key,
167         }),
168         &Identity {
169             identity: sink_init_info.identity,
170         },
171         &vec_to_signature(&corrupt_signature),
172         &sink_init_info.nonce,
173         sink_init_info.version,
174         &source_init_info.key,
175     );
176     let err = result.expect_err("expect failure with corrupt signature");
177     assert_eq!(
178         err,
179         binder::Status::new_service_specific_error(Error::INVALID_SIGNATURE.0, None)
180     );
181 }
182 
183 /// Perform mainline AuthGraph key exchange with the provided source, but give it back
184 /// a corrupted key.
test_corrupt_key( local_sink: &mut ke::AuthGraphParticipant, source: binder::Strong<dyn IAuthGraphKeyExchange>, )185 pub fn test_corrupt_key(
186     local_sink: &mut ke::AuthGraphParticipant,
187     source: binder::Strong<dyn IAuthGraphKeyExchange>,
188 ) {
189     // Step 1: create an ephemeral ECDH key at the (remote) source.
190     let source_init_info = source
191         .create()
192         .expect("failed to create() with remote impl");
193     assert!(source_init_info.key.pubKey.is_some());
194     assert!(source_init_info.key.arcFromPBK.is_some());
195     let source_pub_key = extract_plain_pub_key(&source_init_info.key.pubKey);
196 
197     // Step 2: pass the source's ECDH public key and other session info to the (local) sink.
198     let init_result = local_sink
199         .init(
200             &source_pub_key.plainPubKey,
201             &source_init_info.identity.identity,
202             &source_init_info.nonce,
203             source_init_info.version,
204         )
205         .expect("failed to init() with local impl");
206     let sink_init_info = init_result.session_init_info;
207     let sink_pub_key = sink_init_info
208         .ke_key
209         .pub_key
210         .expect("expect pub_key to be populated");
211 
212     let sink_info = init_result.session_info;
213     assert!(!sink_info.session_id.is_empty());
214 
215     // The AuthGraph core library will verify the session ID signature, but do it here too.
216     let sink_verification_key = key::Identity::from_slice(&sink_init_info.identity)
217         .expect("invalid identity CBOR")
218         .cert_chain
219         .root_key;
220     local_sink
221         .verify_signature_on_session_id(
222             &sink_verification_key,
223             &sink_info.session_id,
224             &sink_info.session_id_signature,
225         )
226         .expect("failed verification of signed session ID");
227 
228     // Deliberately corrupt the source's encrypted key.
229     let mut corrupt_key = source_init_info.key.clone();
230     match &mut corrupt_key.arcFromPBK {
231         Some(a) => {
232             let len = a.arc.len();
233             a.arc[len - 1] ^= 0x01;
234         }
235         None => panic!("no arc data"),
236     }
237 
238     // Step 3: pass the sink's ECDH public key and other session info to the (remote) source, but
239     // give it back a corrupted version of its own key.
240     let result = source.finish(
241         &PubKey::PlainKey(PlainPubKey {
242             plainPubKey: sink_pub_key,
243         }),
244         &Identity {
245             identity: sink_init_info.identity,
246         },
247         &vec_to_signature(&sink_info.session_id_signature),
248         &sink_init_info.nonce,
249         sink_init_info.version,
250         &corrupt_key,
251     );
252 
253     let err = result.expect_err("expect failure with corrupt key");
254     assert!(
255         err == binder::Status::new_service_specific_error(Error::INVALID_KE_KEY.0, None)
256             || err
257                 == binder::Status::new_service_specific_error(
258                     Error::INVALID_PRIV_KEY_ARC_IN_KEY.0,
259                     None
260                 )
261     );
262 }
263