blob: 5c81593d69eee750f70f8fbdd0693ccdce7ca178 [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::*;
19use authgraph_core::traits;
20
21/// Run AuthGraph tests against the provided sink, using a local test source implementation.
22pub fn test(impls: &mut traits::TraitImpl, sink: binder::Strong<dyn IAuthGraphKeyExchange>) {
23 test_mainline(impls, sink.clone());
24 test_corrupt_sig(impls, sink.clone());
25 test_corrupt_keys(impls, sink);
26}
27
28/// Perform mainline AuthGraph key exchange with the provided sink and local implementation.
29/// Return the agreed AES keys in plaintext.
30pub fn test_mainline(
31 impls: &mut traits::TraitImpl,
32 sink: binder::Strong<dyn IAuthGraphKeyExchange>,
33) -> [key::AesKey; 2] {
34 // Step 1: create an ephemeral ECDH key at the (local) source.
35 let source_init_info = ke::create(impls).expect("failed to create() with local impl");
36
37 // Step 2: pass the source's ECDH public key and other session info to the (remote) sink.
38 let init_result = sink
39 .init(
40 &build_plain_pub_key(&source_init_info.ke_key.pub_key),
41 &vec_to_identity(&source_init_info.identity),
42 &source_init_info.nonce,
43 source_init_info.version,
44 )
45 .expect("failed to init() with remote impl");
46 let sink_init_info = init_result.sessionInitiationInfo;
47 let sink_pub_key = extract_plain_pub_key(&sink_init_info.key.pubKey);
48
49 let sink_info = init_result.sessionInfo;
50 assert!(!sink_info.sessionId.is_empty());
51
52 // The AuthGraph core library will verify the session ID signature, but do it here too.
53 let sink_verification_key =
54 verification_key_from_identity(&impls, &sink_init_info.identity.identity);
55 ke::verify_signature_on_session_id(
56 &sink_verification_key,
57 &sink_info.sessionId,
58 &sink_info.signature.signature,
59 &*impls.ecdsa,
60 )
61 .expect("failed verification of signed session ID");
62
63 // Step 3: pass the sink's ECDH public key and other session info to the (local) source, so it
64 // can calculate the same pair of symmetric keys.
65 let source_info = ke::finish(
66 impls,
67 &sink_pub_key.plainPubKey,
68 &sink_init_info.identity.identity,
69 &sink_info.signature.signature,
70 &sink_init_info.nonce,
71 sink_init_info.version,
72 source_init_info.ke_key,
73 )
74 .expect("failed to finish() with local impl");
75 assert!(!source_info.session_id.is_empty());
76
77 // The AuthGraph core library will verify the session ID signature, but do it here too.
78 let source_verification_key =
79 verification_key_from_identity(&impls, &source_init_info.identity);
80 ke::verify_signature_on_session_id(
81 &source_verification_key,
82 &source_info.session_id,
83 &source_info.session_id_signature,
84 &*impls.ecdsa,
85 )
86 .expect("failed verification of signed session ID");
87
88 // Both ends should agree on the session ID.
89 assert_eq!(source_info.session_id, sink_info.sessionId);
90
91 // Step 4: pass the (local) source's session ID signature back to the sink, so it can check it
92 // and update the symmetric keys so they're marked as authentication complete.
93 let _sink_arcs = sink
94 .authenticationComplete(
95 &vec_to_signature(&source_info.session_id_signature),
96 &sink_info.sharedKeys,
97 )
98 .expect("failed to authenticationComplete() with remote sink");
99
100 // Decrypt and return the session keys.
101 decipher_aes_keys(&impls, &source_info.shared_keys)
102}
103
104/// Perform mainline AuthGraph key exchange with the provided sink, but provide an invalid
105/// session ID signature.
106pub fn test_corrupt_sig(
107 impls: &mut traits::TraitImpl,
108 sink: binder::Strong<dyn IAuthGraphKeyExchange>,
109) {
110 // Step 1: create an ephemeral ECDH key at the (local) source.
111 let source_init_info = ke::create(impls).expect("failed to create() with local impl");
112
113 // Step 2: pass the source's ECDH public key and other session info to the (remote) sink.
114 let init_result = sink
115 .init(
116 &build_plain_pub_key(&source_init_info.ke_key.pub_key),
117 &vec_to_identity(&source_init_info.identity),
118 &source_init_info.nonce,
119 source_init_info.version,
120 )
121 .expect("failed to init() with remote impl");
122 let sink_init_info = init_result.sessionInitiationInfo;
123 let sink_pub_key = extract_plain_pub_key(&sink_init_info.key.pubKey);
124
125 let sink_info = init_result.sessionInfo;
126 assert!(!sink_info.sessionId.is_empty());
127
128 // Step 3: pass the sink's ECDH public key and other session info to the (local) source, so it
129 // can calculate the same pair of symmetric keys.
130 let source_info = ke::finish(
131 impls,
132 &sink_pub_key.plainPubKey,
133 &sink_init_info.identity.identity,
134 &sink_info.signature.signature,
135 &sink_init_info.nonce,
136 sink_init_info.version,
137 source_init_info.ke_key,
138 )
139 .expect("failed to finish() with local impl");
140 assert!(!source_info.session_id.is_empty());
141
142 // Build a corrupted version of the (local) source's session ID signature.
143 let mut corrupt_signature = source_info.session_id_signature.clone();
144 let sig_len = corrupt_signature.len();
145 corrupt_signature[sig_len - 1] ^= 0x01;
146
147 // Step 4: pass the (local) source's **invalid** session ID signature back to the sink,
148 // which should reject it.
149 let result =
150 sink.authenticationComplete(&vec_to_signature(&corrupt_signature), &sink_info.sharedKeys);
151 let err = result.expect_err("expect failure with corrupt signature");
152 assert_eq!(
153 err,
154 binder::Status::new_service_specific_error(Error::INVALID_SIGNATURE.0, None)
155 );
156}
157
158/// Perform mainline AuthGraph key exchange with the provided sink, but provide an invalid
159/// Arc for the sink's key.
160pub fn test_corrupt_keys(
161 impls: &mut traits::TraitImpl,
162 sink: binder::Strong<dyn IAuthGraphKeyExchange>,
163) {
164 // Step 1: create an ephemeral ECDH key at the (local) source.
165 let source_init_info = ke::create(impls).expect("failed to create() with local impl");
166
167 // Step 2: pass the source's ECDH public key and other session info to the (remote) sink.
168 let init_result = sink
169 .init(
170 &build_plain_pub_key(&source_init_info.ke_key.pub_key),
171 &vec_to_identity(&source_init_info.identity),
172 &source_init_info.nonce,
173 source_init_info.version,
174 )
175 .expect("failed to init() with remote impl");
176 let sink_init_info = init_result.sessionInitiationInfo;
177 let sink_pub_key = extract_plain_pub_key(&sink_init_info.key.pubKey);
178
179 let sink_info = init_result.sessionInfo;
180 assert!(!sink_info.sessionId.is_empty());
181
182 // Step 3: pass the sink's ECDH public key and other session info to the (local) source, so it
183 // can calculate the same pair of symmetric keys.
184 let source_info = ke::finish(
185 impls,
186 &sink_pub_key.plainPubKey,
187 &sink_init_info.identity.identity,
188 &sink_info.signature.signature,
189 &sink_init_info.nonce,
190 sink_init_info.version,
191 source_init_info.ke_key,
192 )
193 .expect("failed to finish() with local impl");
194 assert!(!source_info.session_id.is_empty());
195
196 // Deliberately corrupt the sink's shared key Arcs before returning them
197 let mut corrupt_keys = sink_info.sharedKeys.clone();
198 let len0 = corrupt_keys[0].arc.len();
199 let len1 = corrupt_keys[1].arc.len();
200 corrupt_keys[0].arc[len0 - 1] ^= 0x01;
201 corrupt_keys[1].arc[len1 - 1] ^= 0x01;
202
203 // Step 4: pass the (local) source's session ID signature back to the sink, but with corrupted
204 // keys, which should be rejected.
205 let result = sink.authenticationComplete(
206 &vec_to_signature(&source_info.session_id_signature),
207 &corrupt_keys,
208 );
209 let err = result.expect_err("expect failure with corrupt keys");
210 assert_eq!(
211 err,
212 binder::Status::new_service_specific_error(Error::INVALID_SHARED_KEY_ARCS.0, None)
213 );
214}