1 // Copyright (c) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 #![cfg(all(
15 feature = "async",
16 feature = "http1_1",
17 feature = "__tls",
18 feature = "tokio_base"
19 ))]
20
21 #[macro_use]
22 mod common;
23
24 use std::path::PathBuf;
25
26 use ylong_http_client::PubKeyPins;
27
28 use crate::common::init_test_work_runtime;
29
30 /// SDV test cases for `async::Client`.
31 ///
32 /// # Brief
33 /// 1. Starts a hyper https server with the tokio coroutine.
34 /// 2. Creates an async::Client that with public key pinning.
35 /// 3. The client sends a request message.
36 /// 4. Verifies the received request on the server.
37 /// 5. The server sends a response message.
38 /// 6. Verifies the received response on the client.
39 /// 7. Shuts down the server.
40 #[test]
sdv_client_public_key_pinning()41 fn sdv_client_public_key_pinning() {
42 define_service_handle!(HTTPS;);
43 set_server_fn!(
44 ASYNC;
45 ylong_server_fn,
46 Request: {
47 Method: "GET",
48 Header: "Content-Length", "5",
49 Body: "hello",
50 },
51 Response: {
52 Status: 200,
53 Version: "HTTP/1.1",
54 Header: "Content-Length", "3",
55 Body: "hi!",
56 },
57 );
58 let runtime = init_test_work_runtime(1);
59 let mut handles_vec = vec![];
60 let dir = env!("CARGO_MANIFEST_DIR");
61 let mut path = PathBuf::from(dir);
62 path.push("tests/file/root-ca.pem");
63
64 {
65 start_server!(
66 HTTPS;
67 ServerNum: 1,
68 Runtime: runtime,
69 Handles: handles_vec,
70 ServeFnName: ylong_server_fn,
71 );
72 let handle = handles_vec.pop().expect("No more handles !");
73
74 let pins = PubKeyPins::builder()
75 .add(
76 format!("https://127.0.0.1:{}", handle.port).as_str(),
77 "sha256//VHQAbNl67nmkZJNESeTKvTxb5bQmd1maWnMKG/tjcAY=",
78 )
79 .build()
80 .unwrap();
81
82 let client = ylong_http_client::async_impl::Client::builder()
83 .tls_ca_file(path.to_str().unwrap())
84 .add_public_key_pins(pins)
85 .danger_accept_invalid_hostnames(true)
86 .build()
87 .unwrap();
88
89 let shutdown_handle = runtime.spawn(async move {
90 async_client_assertions!(
91 ServerHandle: handle,
92 ClientRef: client,
93 Request: {
94 Method: "GET",
95 Host: "127.0.0.1",
96 Header: "Content-Length", "5",
97 Body: "hello",
98 },
99 Response: {
100 Status: 200,
101 Version: "HTTP/1.1",
102 Header: "Content-Length", "3",
103 Body: "hi!",
104 },
105 );
106 });
107 runtime
108 .block_on(shutdown_handle)
109 .expect("Runtime block on server shutdown failed");
110 }
111
112 {
113 start_server!(
114 HTTPS;
115 ServerNum: 1,
116 Runtime: runtime,
117 Handles: handles_vec,
118 ServeFnName: ylong_server_fn,
119 );
120 let handle = handles_vec.pop().expect("No more handles !");
121
122 // Two wrong public keys and a correct public key in the middle.
123 let pins = PubKeyPins::builder()
124 .add(
125 format!("https://127.0.0.1:{}", handle.port).as_str(),
126 "sha256//YhKJKSzoTt2b5FP18fvpHo7fJYqQCjAa3HWY3tvRMwE=;sha256//VHQAbNl67nmkZJNESeTKvTxb5bQmd1maWnMKG/tjcAY=;sha256//t62CeU2tQiqkexU74Gxa2eg7fRbEgoChTociMee9wno=",
127 )
128 .build()
129 .unwrap();
130
131 let client = ylong_http_client::async_impl::Client::builder()
132 .tls_ca_file(path.to_str().unwrap())
133 .add_public_key_pins(pins)
134 .danger_accept_invalid_hostnames(true)
135 .build()
136 .unwrap();
137
138 let shutdown_handle = runtime.spawn(async move {
139 async_client_assertions!(
140 ServerHandle: handle,
141 ClientRef: client,
142 Request: {
143 Method: "GET",
144 Host: "127.0.0.1",
145 Header: "Content-Length", "5",
146 Body: "hello",
147 },
148 Response: {
149 Status: 200,
150 Version: "HTTP/1.1",
151 Header: "Content-Length", "3",
152 Body: "hi!",
153 },
154 );
155 });
156 runtime
157 .block_on(shutdown_handle)
158 .expect("Runtime block on server shutdown failed");
159 }
160
161 {
162 start_server!(
163 HTTPS;
164 ServerNum: 1,
165 Runtime: runtime,
166 Handles: handles_vec,
167 ServeFnName: ylong_server_fn,
168 );
169 let handle = handles_vec.pop().expect("No more handles !");
170
171 // The public key of an irrelevant domain.
172 let pins = PubKeyPins::builder()
173 .add(
174 "https://ylong_http.test:6789",
175 "sha256//t62CeU2tQiqkexU74Gxa2eg7fRbEgoChTociMee9wno=",
176 )
177 .build()
178 .unwrap();
179
180 let client = ylong_http_client::async_impl::Client::builder()
181 .tls_ca_file(path.to_str().unwrap())
182 .add_public_key_pins(pins)
183 .danger_accept_invalid_hostnames(true)
184 .build()
185 .unwrap();
186
187 let shutdown_handle = runtime.spawn(async move {
188 async_client_assertions!(
189 ServerHandle: handle,
190 ClientRef: client,
191 Request: {
192 Method: "GET",
193 Host: "127.0.0.1",
194 Header: "Content-Length", "5",
195 Body: "hello",
196 },
197 Response: {
198 Status: 200,
199 Version: "HTTP/1.1",
200 Header: "Content-Length", "3",
201 Body: "hi!",
202 },
203 );
204 });
205 runtime
206 .block_on(shutdown_handle)
207 .expect("Runtime block on server shutdown failed");
208 }
209 }
210
211 /// SDV test cases for `async::Client`.
212 ///
213 /// # Brief
214 /// 1. Starts a hyper https server with the tokio coroutine.
215 /// 2. Creates an async::Client that with Root public key pinning.
216 /// 3. The client sends a request message.
217 /// 4. Verifies the received request on the server.
218 /// 5. The server sends a response message.
219 /// 6. Verifies the received response on the client.
220 /// 7. Shuts down the server.
221 #[test]
sdv_client_public_key_root_pinning()222 fn sdv_client_public_key_root_pinning() {
223 define_service_handle!(HTTPS;);
224 set_server_fn!(
225 ASYNC;
226 ylong_server_fn,
227 Request: {
228 Method: "GET",
229 Header: "Content-Length", "5",
230 Body: "hello",
231 },
232 Response: {
233 Status: 200,
234 Version: "HTTP/1.1",
235 Header: "Content-Length", "3",
236 Body: "hi!",
237 },
238 );
239 let runtime = init_test_work_runtime(1);
240 let mut handles_vec = vec![];
241 let dir = env!("CARGO_MANIFEST_DIR");
242 let root_ca_path = PathBuf::from(dir).join("tests/file/cert_chain/rootCA.crt.pem");
243 let server_key_path = "tests/file/cert_chain/server.key.pem";
244 let server_crt_chain_path = "tests/file/cert_chain/chain.crt.pem";
245
246 // Root certificate pinning.
247 {
248 start_server!(
249 HTTPS;
250 ServerNum: 1,
251 Runtime: runtime,
252 Handles: handles_vec,
253 ServeFnName: ylong_server_fn,
254 ServeKeyPath: server_key_path,
255 ServeCrtPath: server_crt_chain_path,
256 );
257 let handle = handles_vec.pop().expect("No more handles !");
258
259 let pins = PubKeyPins::builder()
260 .add_with_root_strategy(
261 format!("https://127.0.0.1:{}", handle.port).as_str(),
262 "sha256//OTEKj2hCyGOWxN8Bdt2LPRMzJ4zs0e59cjgIPQgQe30=",
263 )
264 .build()
265 .unwrap();
266
267 let client = ylong_http_client::async_impl::Client::builder()
268 .tls_ca_file(root_ca_path.to_str().unwrap())
269 .add_public_key_pins(pins)
270 .danger_accept_invalid_hostnames(true)
271 .build()
272 .unwrap();
273
274 let shutdown_handle = runtime.spawn(async move {
275 async_client_assertions!(
276 ServerHandle: handle,
277 ClientRef: client,
278 Request: {
279 Method: "GET",
280 Host: "127.0.0.1",
281 Header: "Content-Length", "5",
282 Body: "hello",
283 },
284 Response: {
285 Status: 200,
286 Version: "HTTP/1.1",
287 Header: "Content-Length", "3",
288 Body: "hi!",
289 },
290 );
291 });
292 runtime
293 .block_on(shutdown_handle)
294 .expect("Runtime block on server shutdown failed");
295 }
296
297 // Server certificate pinning.
298 {
299 start_server!(
300 HTTPS;
301 ServerNum: 1,
302 Runtime: runtime,
303 Handles: handles_vec,
304 ServeFnName: ylong_server_fn,
305 ServeKeyPath: server_key_path,
306 ServeCrtPath: server_crt_chain_path,
307 );
308 let handle = handles_vec.pop().expect("No more handles !");
309
310 // Two wrong public keys and a correct public key in the middle.
311 let pins = PubKeyPins::builder()
312 .add(
313 format!("https://127.0.0.1:{}", handle.port).as_str(),
314 "sha256//tldbIOQrcXdIACltObylTwTPzdxTm0E2VYDf3B1IQxU=",
315 )
316 .build()
317 .unwrap();
318
319 let client = ylong_http_client::async_impl::Client::builder()
320 .tls_ca_file(root_ca_path.to_str().unwrap())
321 .add_public_key_pins(pins)
322 .danger_accept_invalid_hostnames(true)
323 .build()
324 .unwrap();
325
326 let shutdown_handle = runtime.spawn(async move {
327 async_client_assertions!(
328 ServerHandle: handle,
329 ClientRef: client,
330 Request: {
331 Method: "GET",
332 Host: "127.0.0.1",
333 Header: "Content-Length", "5",
334 Body: "hello",
335 },
336 Response: {
337 Status: 200,
338 Version: "HTTP/1.1",
339 Header: "Content-Length", "3",
340 Body: "hi!",
341 },
342 );
343 });
344 runtime
345 .block_on(shutdown_handle)
346 .expect("Runtime block on server shutdown failed");
347 }
348
349 // Public keys from unrelated domains will not verify public key pinning.
350 {
351 start_server!(
352 HTTPS;
353 ServerNum: 1,
354 Runtime: runtime,
355 Handles: handles_vec,
356 ServeFnName: ylong_server_fn,
357 ServeKeyPath: server_key_path,
358 ServeCrtPath: server_crt_chain_path,
359 );
360 let handle = handles_vec.pop().expect("No more handles !");
361
362 let pins = PubKeyPins::builder()
363 .add_with_root_strategy(
364 "https://ylong_http.test:6789",
365 "sha256//t62CeU2tQiqkexU74Gxa2eg7fRbEgoChTociMee9wno=",
366 )
367 .build()
368 .unwrap();
369
370 let client = ylong_http_client::async_impl::Client::builder()
371 .tls_ca_file(root_ca_path.to_str().unwrap())
372 .add_public_key_pins(pins)
373 .danger_accept_invalid_hostnames(true)
374 .build()
375 .unwrap();
376
377 let shutdown_handle = runtime.spawn(async move {
378 async_client_assertions!(
379 ServerHandle: handle,
380 ClientRef: client,
381 Request: {
382 Method: "GET",
383 Host: "127.0.0.1",
384 Header: "Content-Length", "5",
385 Body: "hello",
386 },
387 Response: {
388 Status: 200,
389 Version: "HTTP/1.1",
390 Header: "Content-Length", "3",
391 Body: "hi!",
392 },
393 );
394 });
395 runtime
396 .block_on(shutdown_handle)
397 .expect("Runtime block on server shutdown failed");
398 }
399
400 // Root certificate pinning strategy, but using the server certificate public
401 // key hash.
402 {
403 start_server!(
404 HTTPS;
405 ServerNum: 1,
406 Runtime: runtime,
407 Handles: handles_vec,
408 ServeFnName: ylong_server_fn,
409 ServeKeyPath: server_key_path,
410 ServeCrtPath: server_crt_chain_path,
411 );
412 let handle = handles_vec.pop().expect("No more handles !");
413
414 let pins = PubKeyPins::builder()
415 .add_with_root_strategy(
416 format!("https://127.0.0.1:{}", handle.port).as_str(),
417 "sha256//tldbIOQrcXdIACltObylTwTPzdxTm0E2VYDf3B1IQxU=",
418 )
419 .build()
420 .unwrap();
421
422 let client = ylong_http_client::async_impl::Client::builder()
423 .tls_ca_file(root_ca_path.to_str().unwrap())
424 .add_public_key_pins(pins)
425 .danger_accept_invalid_hostnames(true)
426 .build()
427 .unwrap();
428
429 let shutdown_handle = runtime.spawn(async move {
430 let request = ylong_http_client::async_impl::Request::builder()
431 .method("GET")
432 .url(format!("{}:{}", "127.0.0.1", handle.port).as_str())
433 .header("Content-Length", "5")
434 .body(ylong_http_client::async_impl::Body::slice("hello"))
435 .expect("Request build failed");
436
437 let response = client.request(request).await.err();
438
439 assert_eq!(
440 format!("{:?}", response.expect("response is not an error")),
441 "HttpClientError { ErrorKind: Connect, Cause: Custom { kind: Other, error: SslError { \
442 code: SslErrorCode(1), internal: Some(User(VerifyError { ErrorKind: PubKeyPinning, \
443 Cause: Pinned public key verification failed. })) } } }"
444 );
445 });
446 runtime
447 .block_on(shutdown_handle)
448 .expect("Runtime block on server shutdown failed");
449 }
450 }
451
452 /// SDV test cases for `async::Client`.
453 ///
454 /// # Brief
455 /// 1. Starts a hyper https server with the tokio coroutine.
456 /// 2. Creates an async::Client with an error public key pinning.
457 /// 3. The client sends a request message.
458 /// 4. Verifies the received request on the server.
459 /// 5. The server sends a response message.
460 /// 6. Verifies the received response on the client.
461 /// 7. Shuts down the server.
462 #[test]
sdv_client_public_key_pinning_error()463 fn sdv_client_public_key_pinning_error() {
464 define_service_handle!(HTTPS;);
465 set_server_fn!(
466 ASYNC;
467 ylong_server_fn,
468 Request: {
469 Method: "GET",
470 Header: "Content-Length", "5",
471 Body: "hello",
472 },
473 Response: {
474 Status: 200,
475 Version: "HTTP/1.1",
476 Header: "Content-Length", "3",
477 Body: "hi!",
478 },
479 );
480
481 let runtime = init_test_work_runtime(1);
482
483 let mut handles_vec = vec![];
484 start_server!(
485 HTTPS;
486 ServerNum: 1,
487 Runtime: runtime,
488 Handles: handles_vec,
489 ServeFnName: ylong_server_fn,
490 );
491 let handle = handles_vec.pop().expect("No more handles !");
492
493 let pins = PubKeyPins::builder()
494 .add(
495 format!("https://127.0.0.1:{}", handle.port).as_str(),
496 "sha256//YhKJKSzoTt2b5FP18fvpHo7fJYqQCjAa3HWY3tvRMwE=",
497 )
498 .build()
499 .unwrap();
500
501 let dir = env!("CARGO_MANIFEST_DIR");
502 let mut path = PathBuf::from(dir);
503 path.push("tests/file/root-ca.pem");
504
505 let client = ylong_http_client::async_impl::Client::builder()
506 .tls_ca_file(path.to_str().unwrap())
507 .add_public_key_pins(pins)
508 .danger_accept_invalid_hostnames(true)
509 .build()
510 .unwrap();
511
512 let shutdown_handle = runtime.spawn(async move {
513 let request = ylong_http_client::async_impl::Request::builder()
514 .method("GET")
515 .url(format!("{}:{}", "127.0.0.1", handle.port).as_str())
516 .header("Content-Length", "5")
517 .body(ylong_http_client::async_impl::Body::slice("hello"))
518 .expect("Request build failed");
519
520 let response = client.request(request).await.err();
521
522 assert_eq!(
523 format!("{:?}", response.expect("response is not an error")),
524 "HttpClientError { ErrorKind: Connect, Cause: Custom { kind: Other, error: SslError { \
525 code: SslErrorCode(1), internal: Some(User(VerifyError { ErrorKind: PubKeyPinning, \
526 Cause: Pinned public key verification failed. })) } } }"
527 );
528 });
529 runtime
530 .block_on(shutdown_handle)
531 .expect("Runtime block on server shutdown failed");
532 }
533