• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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