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 = "__c_openssl",
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 with an error 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_pinning_error()222 fn sdv_client_public_key_pinning_error() {
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 
240     let runtime = init_test_work_runtime(1);
241 
242     let mut handles_vec = vec![];
243     start_server!(
244         HTTPS;
245         ServerNum: 1,
246         Runtime: runtime,
247         Handles: handles_vec,
248         ServeFnName: ylong_server_fn,
249     );
250     let handle = handles_vec.pop().expect("No more handles !");
251 
252     let pins = PubKeyPins::builder()
253         .add(
254             format!("https://127.0.0.1:{}", handle.port).as_str(),
255             "sha256//YhKJKSzoTt2b5FP18fvpHo7fJYqQCjAa3HWY3tvRMwE=",
256         )
257         .build()
258         .unwrap();
259 
260     let dir = env!("CARGO_MANIFEST_DIR");
261     let mut path = PathBuf::from(dir);
262     path.push("tests/file/root-ca.pem");
263 
264     let client = ylong_http_client::async_impl::Client::builder()
265         .tls_ca_file(path.to_str().unwrap())
266         .add_public_key_pins(pins)
267         .danger_accept_invalid_hostnames(true)
268         .build()
269         .unwrap();
270 
271     let shutdown_handle = runtime.spawn(async move {
272         let request = ylong_http_client::async_impl::Request::builder()
273             .method("GET")
274             .url(format!("{}:{}", "127.0.0.1", handle.port).as_str())
275             .header("Content-Length", "5")
276             .body(ylong_http_client::async_impl::Body::slice("hello"))
277             .expect("Request build failed");
278 
279         let response = client.request(request).await.err();
280 
281         assert_eq!(
282             format!("{:?}", response.expect("response is not an error")),
283             "HttpClientError { ErrorKind: Connect, Cause: Custom { kind: Other, error: SslError { \
284              code: SslErrorCode(1), internal: Some(User(VerifyError { ErrorKind: PubKeyPinning, \
285              Cause: Pinned public key verification failed. })) } } }"
286         );
287     });
288     runtime
289         .block_on(shutdown_handle)
290         .expect("Runtime block on server shutdown failed");
291 }
292