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