• 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 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