blob: a331eefed35e264244601daa83f917e1827c23d1 [file] [log] [blame]
David Drysdalefe418252023-11-07 09:27:56 +00001/*
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 sinks
18use super::*;
Hasini Gunasinghe5df6ed52023-11-13 09:18:25 +000019use authgraph_core::{key, keyexchange as ke};
David Drysdalefe418252023-11-07 09:27:56 +000020
21/// Run AuthGraph tests against the provided sink, using a local test source implementation.
Hasini Gunasinghe5df6ed52023-11-13 09:18:25 +000022pub fn test(
23 local_source: &mut ke::AuthGraphParticipant,
24 sink: binder::Strong<dyn IAuthGraphKeyExchange>,
25) {
26 test_mainline(local_source, sink.clone());
27 test_corrupt_sig(local_source, sink.clone());
28 test_corrupt_keys(local_source, sink);
David Drysdalefe418252023-11-07 09:27:56 +000029}
30
31/// Perform mainline AuthGraph key exchange with the provided sink and local implementation.
David Drysdalebef8d3a2023-11-27 14:08:42 +000032/// Return the agreed AES keys in plaintext, together with the session ID.
David Drysdalefe418252023-11-07 09:27:56 +000033pub fn test_mainline(
Hasini Gunasinghe5df6ed52023-11-13 09:18:25 +000034 local_source: &mut ke::AuthGraphParticipant,
David Drysdalefe418252023-11-07 09:27:56 +000035 sink: binder::Strong<dyn IAuthGraphKeyExchange>,
David Drysdalebef8d3a2023-11-27 14:08:42 +000036) -> ([key::AesKey; 2], Vec<u8>) {
David Drysdalefe418252023-11-07 09:27:56 +000037 // Step 1: create an ephemeral ECDH key at the (local) source.
Hasini Gunasinghe5df6ed52023-11-13 09:18:25 +000038 let source_init_info = local_source
39 .create()
40 .expect("failed to create() with local impl");
David Drysdalefe418252023-11-07 09:27:56 +000041
42 // Step 2: pass the source's ECDH public key and other session info to the (remote) sink.
43 let init_result = sink
44 .init(
45 &build_plain_pub_key(&source_init_info.ke_key.pub_key),
46 &vec_to_identity(&source_init_info.identity),
47 &source_init_info.nonce,
48 source_init_info.version,
49 )
50 .expect("failed to init() with remote impl");
51 let sink_init_info = init_result.sessionInitiationInfo;
52 let sink_pub_key = extract_plain_pub_key(&sink_init_info.key.pubKey);
53
54 let sink_info = init_result.sessionInfo;
55 assert!(!sink_info.sessionId.is_empty());
56
57 // The AuthGraph core library will verify the session ID signature, but do it here too.
Hasini Gunasinghe5df6ed52023-11-13 09:18:25 +000058 let sink_verification_key = local_source
59 .peer_verification_key_from_identity(&sink_init_info.identity.identity)
60 .expect("failed to get peer verification from identity");
61 local_source
62 .verify_signature_on_session_id(
63 &sink_verification_key,
64 &sink_info.sessionId,
65 &sink_info.signature.signature,
66 )
67 .expect("failed verification of signed session ID");
David Drysdalefe418252023-11-07 09:27:56 +000068
69 // Step 3: pass the sink's ECDH public key and other session info to the (local) source, so it
70 // can calculate the same pair of symmetric keys.
Hasini Gunasinghe5df6ed52023-11-13 09:18:25 +000071 let source_info = local_source
72 .finish(
73 &sink_pub_key.plainPubKey,
74 &sink_init_info.identity.identity,
75 &sink_info.signature.signature,
76 &sink_init_info.nonce,
77 sink_init_info.version,
78 source_init_info.ke_key,
79 )
80 .expect("failed to finish() with local impl");
David Drysdalefe418252023-11-07 09:27:56 +000081 assert!(!source_info.session_id.is_empty());
82
83 // The AuthGraph core library will verify the session ID signature, but do it here too.
Hasini Gunasinghe5df6ed52023-11-13 09:18:25 +000084 let source_verification_key = key::Identity::from_slice(&source_init_info.identity)
85 .expect("invalid identity CBOR")
86 .cert_chain
87 .root_key;
88 local_source
89 .verify_signature_on_session_id(
90 &source_verification_key,
91 &source_info.session_id,
92 &source_info.session_id_signature,
93 )
94 .expect("failed verification of signed session ID");
David Drysdalefe418252023-11-07 09:27:56 +000095
96 // Both ends should agree on the session ID.
97 assert_eq!(source_info.session_id, sink_info.sessionId);
98
99 // Step 4: pass the (local) source's session ID signature back to the sink, so it can check it
100 // and update the symmetric keys so they're marked as authentication complete.
101 let _sink_arcs = sink
102 .authenticationComplete(
103 &vec_to_signature(&source_info.session_id_signature),
104 &sink_info.sharedKeys,
105 )
106 .expect("failed to authenticationComplete() with remote sink");
David Drysdalefe418252023-11-07 09:27:56 +0000107 // Decrypt and return the session keys.
Hasini Gunasinghe5df6ed52023-11-13 09:18:25 +0000108 let decrypted_shared_keys = local_source
109 .decipher_shared_keys_from_arcs(&source_info.shared_keys)
110 .expect("failed to decrypt shared key arcs")
111 .try_into();
112 let decrypted_shared_keys_array = match decrypted_shared_keys {
113 Ok(array) => array,
114 Err(_) => panic!("wrong number of decrypted shared key arcs"),
115 };
David Drysdalebef8d3a2023-11-27 14:08:42 +0000116 (decrypted_shared_keys_array, sink_info.sessionId)
David Drysdalefe418252023-11-07 09:27:56 +0000117}
118
119/// Perform mainline AuthGraph key exchange with the provided sink, but provide an invalid
120/// session ID signature.
121pub fn test_corrupt_sig(
Hasini Gunasinghe5df6ed52023-11-13 09:18:25 +0000122 local_source: &mut ke::AuthGraphParticipant,
David Drysdalefe418252023-11-07 09:27:56 +0000123 sink: binder::Strong<dyn IAuthGraphKeyExchange>,
124) {
125 // Step 1: create an ephemeral ECDH key at the (local) source.
Hasini Gunasinghe5df6ed52023-11-13 09:18:25 +0000126 let source_init_info = local_source
127 .create()
128 .expect("failed to create() with local impl");
David Drysdalefe418252023-11-07 09:27:56 +0000129
130 // Step 2: pass the source's ECDH public key and other session info to the (remote) sink.
131 let init_result = sink
132 .init(
133 &build_plain_pub_key(&source_init_info.ke_key.pub_key),
134 &vec_to_identity(&source_init_info.identity),
135 &source_init_info.nonce,
136 source_init_info.version,
137 )
138 .expect("failed to init() with remote impl");
139 let sink_init_info = init_result.sessionInitiationInfo;
140 let sink_pub_key = extract_plain_pub_key(&sink_init_info.key.pubKey);
141
142 let sink_info = init_result.sessionInfo;
143 assert!(!sink_info.sessionId.is_empty());
144
145 // Step 3: pass the sink's ECDH public key and other session info to the (local) source, so it
146 // can calculate the same pair of symmetric keys.
Hasini Gunasinghe5df6ed52023-11-13 09:18:25 +0000147 let source_info = local_source
148 .finish(
149 &sink_pub_key.plainPubKey,
150 &sink_init_info.identity.identity,
151 &sink_info.signature.signature,
152 &sink_init_info.nonce,
153 sink_init_info.version,
154 source_init_info.ke_key,
155 )
156 .expect("failed to finish() with local impl");
David Drysdalefe418252023-11-07 09:27:56 +0000157 assert!(!source_info.session_id.is_empty());
158
159 // Build a corrupted version of the (local) source's session ID signature.
160 let mut corrupt_signature = source_info.session_id_signature.clone();
161 let sig_len = corrupt_signature.len();
162 corrupt_signature[sig_len - 1] ^= 0x01;
163
164 // Step 4: pass the (local) source's **invalid** session ID signature back to the sink,
165 // which should reject it.
166 let result =
167 sink.authenticationComplete(&vec_to_signature(&corrupt_signature), &sink_info.sharedKeys);
168 let err = result.expect_err("expect failure with corrupt signature");
169 assert_eq!(
170 err,
171 binder::Status::new_service_specific_error(Error::INVALID_SIGNATURE.0, None)
172 );
173}
174
175/// Perform mainline AuthGraph key exchange with the provided sink, but provide an invalid
176/// Arc for the sink's key.
177pub fn test_corrupt_keys(
Hasini Gunasinghe5df6ed52023-11-13 09:18:25 +0000178 local_source: &mut ke::AuthGraphParticipant,
David Drysdalefe418252023-11-07 09:27:56 +0000179 sink: binder::Strong<dyn IAuthGraphKeyExchange>,
180) {
181 // Step 1: create an ephemeral ECDH key at the (local) source.
Hasini Gunasinghe5df6ed52023-11-13 09:18:25 +0000182 let source_init_info = local_source
183 .create()
184 .expect("failed to create() with local impl");
David Drysdalefe418252023-11-07 09:27:56 +0000185
186 // Step 2: pass the source's ECDH public key and other session info to the (remote) sink.
187 let init_result = sink
188 .init(
189 &build_plain_pub_key(&source_init_info.ke_key.pub_key),
190 &vec_to_identity(&source_init_info.identity),
191 &source_init_info.nonce,
192 source_init_info.version,
193 )
194 .expect("failed to init() with remote impl");
195 let sink_init_info = init_result.sessionInitiationInfo;
196 let sink_pub_key = extract_plain_pub_key(&sink_init_info.key.pubKey);
197
198 let sink_info = init_result.sessionInfo;
199 assert!(!sink_info.sessionId.is_empty());
200
201 // Step 3: pass the sink's ECDH public key and other session info to the (local) source, so it
202 // can calculate the same pair of symmetric keys.
Hasini Gunasinghe5df6ed52023-11-13 09:18:25 +0000203 let source_info = local_source
204 .finish(
205 &sink_pub_key.plainPubKey,
206 &sink_init_info.identity.identity,
207 &sink_info.signature.signature,
208 &sink_init_info.nonce,
209 sink_init_info.version,
210 source_init_info.ke_key,
211 )
212 .expect("failed to finish() with local impl");
David Drysdalefe418252023-11-07 09:27:56 +0000213 assert!(!source_info.session_id.is_empty());
214
215 // Deliberately corrupt the sink's shared key Arcs before returning them
216 let mut corrupt_keys = sink_info.sharedKeys.clone();
217 let len0 = corrupt_keys[0].arc.len();
218 let len1 = corrupt_keys[1].arc.len();
219 corrupt_keys[0].arc[len0 - 1] ^= 0x01;
220 corrupt_keys[1].arc[len1 - 1] ^= 0x01;
221
222 // Step 4: pass the (local) source's session ID signature back to the sink, but with corrupted
223 // keys, which should be rejected.
224 let result = sink.authenticationComplete(
225 &vec_to_signature(&source_info.session_id_signature),
226 &corrupt_keys,
227 );
228 let err = result.expect_err("expect failure with corrupt keys");
229 assert_eq!(
230 err,
231 binder::Status::new_service_specific_error(Error::INVALID_SHARED_KEY_ARCS.0, None)
232 );
233}