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 key hash
401 {
402 start_server!(
403 HTTPS;
404 ServerNum: 1,
405 Runtime: runtime,
406 Handles: handles_vec,
407 ServeFnName: ylong_server_fn,
408 ServeKeyPath: server_key_path,
409 ServeCrtPath: server_crt_chain_path,
410 );
411 let handle = handles_vec.pop().expect("No more handles !");
412
413 let pins = PubKeyPins::builder()
414 .add_with_root_strategy(
415 format!("https://127.0.0.1:{}", handle.port).as_str(),
416 "sha256//tldbIOQrcXdIACltObylTwTPzdxTm0E2VYDf3B1IQxU=",
417 )
418 .build()
419 .unwrap();
420
421 let client = ylong_http_client::async_impl::Client::builder()
422 .tls_ca_file(root_ca_path.to_str().unwrap())
423 .add_public_key_pins(pins)
424 .danger_accept_invalid_hostnames(true)
425 .build()
426 .unwrap();
427
428 let shutdown_handle = runtime.spawn(async move {
429 let request = ylong_http_client::async_impl::Request::builder()
430 .method("GET")
431 .url(format!("{}:{}", "127.0.0.1", handle.port).as_str())
432 .header("Content-Length", "5")
433 .body(ylong_http_client::async_impl::Body::slice("hello"))
434 .expect("Request build failed");
435
436 let response = client.request(request).await.err();
437
438 assert_eq!(
439 format!("{:?}", response.expect("response is not an error")),
440 "HttpClientError { ErrorKind: Connect, Cause: Custom { kind: Other, error: SslError { \
441 code: SslErrorCode(1), internal: Some(User(VerifyError { ErrorKind: PubKeyPinning, \
442 Cause: Pinned public key verification failed. })) } } }"
443 );
444 });
445 runtime
446 .block_on(shutdown_handle)
447 .expect("Runtime block on server shutdown failed");
448 }
449 }
450
451 /// SDV test cases for `async::Client`.
452 ///
453 /// # Brief
454 /// 1. Starts a hyper https server with the tokio coroutine.
455 /// 2. Creates an async::Client with an error public key pinning.
456 /// 3. The client sends a request message.
457 /// 4. Verifies the received request on the server.
458 /// 5. The server sends a response message.
459 /// 6. Verifies the received response on the client.
460 /// 7. Shuts down the server.
461 #[test]
sdv_client_public_key_pinning_error()462 fn sdv_client_public_key_pinning_error() {
463 define_service_handle!(HTTPS;);
464 set_server_fn!(
465 ASYNC;
466 ylong_server_fn,
467 Request: {
468 Method: "GET",
469 Header: "Content-Length", "5",
470 Body: "hello",
471 },
472 Response: {
473 Status: 200,
474 Version: "HTTP/1.1",
475 Header: "Content-Length", "3",
476 Body: "hi!",
477 },
478 );
479
480 let runtime = init_test_work_runtime(1);
481
482 let mut handles_vec = vec![];
483 start_server!(
484 HTTPS;
485 ServerNum: 1,
486 Runtime: runtime,
487 Handles: handles_vec,
488 ServeFnName: ylong_server_fn,
489 );
490 let handle = handles_vec.pop().expect("No more handles !");
491
492 let pins = PubKeyPins::builder()
493 .add(
494 format!("https://127.0.0.1:{}", handle.port).as_str(),
495 "sha256//YhKJKSzoTt2b5FP18fvpHo7fJYqQCjAa3HWY3tvRMwE=",
496 )
497 .build()
498 .unwrap();
499
500 let dir = env!("CARGO_MANIFEST_DIR");
501 let mut path = PathBuf::from(dir);
502 path.push("tests/file/root-ca.pem");
503
504 let client = ylong_http_client::async_impl::Client::builder()
505 .tls_ca_file(path.to_str().unwrap())
506 .add_public_key_pins(pins)
507 .danger_accept_invalid_hostnames(true)
508 .build()
509 .unwrap();
510
511 let shutdown_handle = runtime.spawn(async move {
512 let request = ylong_http_client::async_impl::Request::builder()
513 .method("GET")
514 .url(format!("{}:{}", "127.0.0.1", handle.port).as_str())
515 .header("Content-Length", "5")
516 .body(ylong_http_client::async_impl::Body::slice("hello"))
517 .expect("Request build failed");
518
519 let response = client.request(request).await.err();
520
521 assert_eq!(
522 format!("{:?}", response.expect("response is not an error")),
523 "HttpClientError { ErrorKind: Connect, Cause: Custom { kind: Other, error: SslError { \
524 code: SslErrorCode(1), internal: Some(User(VerifyError { ErrorKind: PubKeyPinning, \
525 Cause: Pinned public key verification failed. })) } } }"
526 );
527 });
528 runtime
529 .block_on(shutdown_handle)
530 .expect("Runtime block on server shutdown failed");
531 }
532