1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 extern crate key_enable;
16 extern crate ylong_json;
17 
18 use std::thread;
19 use ylong_json::JsonValue;
20 use key_enable::cert_chain_utils::PemCollection;
21 use key_enable::cert_path_utils::TrustCertPath;
22 use key_enable::profile_utils::{UDID, get_udid, validate_bundle_and_distribution_type};
23 
24 
25 // pem_cert_file
26 const VALID_PEM_CERT: &str = "/data/test/tmp/valid_pem_cert.json";
27 const NON_EXISTEND_PEM_CERT: &str = "/data/test/tmp/non_existent_cert_path.json";
28 const INVALID_STRUCTURE_PEM_CERT: &str = "/data/test/tmp/invalid_structure_cert_path.json";
29 const EMPTY_PEM_CERT: &str = "/data/test/tmp/empty_pem_cert.json";
30 // cert_path_file
31 const VALID_CERT_PATH: &str = "/data/test/tmp/valid_cert_path.json";
32 const NON_EXISTEND_CERT_PATH: &str = "/data/test/tmp/non_existent_cert_path.json";
33 const INVALID_STRUCTURE_CERT_PATH: &str = "/data/test/tmp/invalid_structure_cert_path.json";
34 const EMPTY_CERT_PATH: &str = "/data/test/tmp/empty_cert_path.json";
35 
36 const ALLOWED_ROOT_CERT_MEMBER_NAMES: &[&str] = &[
37     "C=CN, O=Huawei, OU=Huawei CBG, CN=Huawei CBG Root CA G2",
38     "C=CN, O=OpenHarmony, OU=OpenHarmony Team, CN=OpenHarmony Application Root CA",
39     "C=CN, O=Huawei, OU=Huawei CBG, CN=Huawei CBG Root CA G2 Test",
40 ];
41 
42 #[test]
test_load_pem_cert_from_valid_json_file()43 fn test_load_pem_cert_from_valid_json_file() {
44     // test is_debuggable true
45     let mut root_cert = PemCollection::new();
46     root_cert.load_pem_certs_from_json_file(VALID_PEM_CERT, ALLOWED_ROOT_CERT_MEMBER_NAMES);
47     assert_eq!(root_cert.pem_data.len(), 3);
48 }
49 
50 #[test]
test_invalid_pem_cert_file_path()51 fn test_invalid_pem_cert_file_path() {
52     let mut root_cert = PemCollection::new();
53     root_cert.load_pem_certs_from_json_file(NON_EXISTEND_PEM_CERT, ALLOWED_ROOT_CERT_MEMBER_NAMES);
54     assert!(root_cert.pem_data.is_empty());
55 }
56 
57 #[test]
test_invalid_pem_cert_json_structure()58 fn test_invalid_pem_cert_json_structure() {
59     let mut root_cert = PemCollection::new();
60     root_cert
61         .load_pem_certs_from_json_file(INVALID_STRUCTURE_PEM_CERT, ALLOWED_ROOT_CERT_MEMBER_NAMES);
62     assert!(root_cert.pem_data.is_empty());
63 }
64 
65 #[test]
test_empty_pem_cert_json_file()66 fn test_empty_pem_cert_json_file() {
67     let mut root_cert = PemCollection::new();
68     root_cert.load_pem_certs_from_json_file(EMPTY_PEM_CERT, ALLOWED_ROOT_CERT_MEMBER_NAMES);
69     assert!(root_cert.pem_data.is_empty());
70 }
71 
72 #[test]
test_successful_load_cert_path()73 fn test_successful_load_cert_path() {
74     let mut cert_paths = TrustCertPath::new();
75     cert_paths.load_cert_path_from_json_file(VALID_CERT_PATH);
76     assert_eq!(cert_paths.profile_signers.len(), 4);
77     assert_eq!(cert_paths.app_sources.len(), 6);
78 }
79 #[test]
test_invalid_cert_path_file_path()80 fn test_invalid_cert_path_file_path() {
81     let mut cert_paths = TrustCertPath::new();
82     cert_paths.load_cert_path_from_json_file(NON_EXISTEND_CERT_PATH);
83     assert!(
84         cert_paths.app_sources.is_empty(),
85         "Expected cert_paths.app_sources to be empty for an empty JSON file"
86     );
87 }
88 
89 #[test]
test_invalid_cert_path_json_structure()90 fn test_invalid_cert_path_json_structure() {
91     let mut cert_paths = TrustCertPath::new();
92     cert_paths.load_cert_path_from_json_file(INVALID_STRUCTURE_CERT_PATH);
93     assert!(
94         cert_paths.app_sources.is_empty(),
95         "Expected cert_paths.app_sources to be empty for an empty JSON file"
96     );
97 }
98 
99 #[test]
test_empty_cert_path_json_file()100 fn test_empty_cert_path_json_file() {
101     let mut cert_paths = TrustCertPath::new();
102     cert_paths.load_cert_path_from_json_file(EMPTY_CERT_PATH);
103     assert!(
104         cert_paths.app_sources.is_empty(),
105         "Expected cert_paths.app_sources to be empty for an empty JSON file"
106     );
107 }
108 
109 #[test]
test_parse_enterprise_profile()110 fn test_parse_enterprise_profile() {
111     let profile_str = r#"
112     {
113         "version-name": "2.0.0",
114         "version-code": 2,
115         "app-distribution-type": "enterprise",
116         "uuid": "",
117         "validity": {
118             "not-before": 1,
119             "not-after": 2
120         },
121         "type": "release",
122         "bundle-info": {
123             "developer-id": "",
124             "distribution-certificate": "",
125             "bundle-name": "com.test.enterprise",
126             "apl": "normal",
127             "app-feature": "test_app",
128             "app-identifier": "123123"
129         },
130         "acls": {
131             "allowed-acls": [
132                 ""
133             ]
134         },
135         "app-privilege-capabilities": [],
136         "permissions": {
137             "restricted-permissions": [
138                 ""
139             ]
140         }
141     }
142     "#;
143     let profile_json =JsonValue::from_text(profile_str).unwrap();
144     let result = validate_bundle_and_distribution_type(&profile_json, true);
145     assert!(result.is_ok());
146 }
147 
148 #[test]
test_parse_enterprise_normal_profile()149 fn test_parse_enterprise_normal_profile() {
150     let profile_str = r#"
151     {
152         "version-name": "2.0.0",
153         "version-code": 2,
154         "app-distribution-type": "enterprise_normal",
155         "uuid": "",
156         "validity": {
157             "not-before": 1,
158             "not-after": 2
159         },
160         "type": "release",
161         "bundle-info": {
162             "developer-id": "",
163             "distribution-certificate": "",
164             "bundle-name": "com.test.enterprise_normal",
165             "apl": "normal",
166             "app-feature": "test_app",
167             "app-identifier": "123123"
168         },
169         "acls": {
170             "allowed-acls": [
171                 ""
172             ]
173         },
174         "app-privilege-capabilities": [],
175         "permissions": {
176             "restricted-permissions": [
177                 ""
178             ]
179         }
180     }
181     "#;
182     let profile_json =JsonValue::from_text(profile_str).unwrap();
183     let result = validate_bundle_and_distribution_type(&profile_json, true);
184     assert!(result.is_ok());
185 }
186 
187 #[test]
test_parse_enterprise_mdm_profile()188 fn test_parse_enterprise_mdm_profile() {
189     let profile_str = r#"
190     {
191         "version-name": "2.0.0",
192         "version-code": 2,
193         "app-distribution-type": "enterprise_mdm",
194         "uuid": "",
195         "validity": {
196             "not-before": 1,
197             "not-after": 2
198         },
199         "type": "release",
200         "bundle-info": {
201             "developer-id": "",
202             "distribution-certificate": "",
203             "bundle-name": "com.test.enterprise_mdm",
204             "apl": "normal",
205             "app-feature": "test_app",
206             "app-identifier": "123123"
207         },
208         "acls": {
209             "allowed-acls": [
210                 ""
211             ]
212         },
213         "app-privilege-capabilities": [],
214         "permissions": {
215             "restricted-permissions": [
216                 ""
217             ]
218         }
219     }
220     "#;
221     let profile_json =JsonValue::from_text(profile_str).unwrap();
222     let result = validate_bundle_and_distribution_type(&profile_json, true);
223     assert!(result.is_ok());
224 }
225 
226 #[test]
test_parse_debug_profile()227 fn test_parse_debug_profile() {
228     let profile_str = r#"
229     {
230         "version-name": "2.0.0",
231         "version-code": 2,
232         "app-distribution-type": "developer",
233         "uuid": "",
234         "validity": {
235             "not-before": 1,
236             "not-after": 2
237         },
238         "type": "debug",
239         "bundle-info": {
240             "developer-id": "",
241             "development-certificate": "",
242             "bundle-name": "com.test.developer",
243             "apl": "normal",
244             "app-feature": "test_app",
245             "app-identifier": "123123"
246         },
247         "acls": {
248             "allowed-acls": [
249                 ""
250             ]
251         },
252         "app-privilege-capabilities": [],
253         "permissions": {
254             "restricted-permissions": [
255                 ""
256             ]
257         },
258         "debug-info": {
259             "device-ids": [],
260             "device-id-type": "udid"
261         }
262     }
263     "#;
264     let udid = get_udid().expect("Failed to get UDID");
265     let mut profile_json =JsonValue::from_text(profile_str).unwrap();
266     profile_json["debug-info"]["device-ids"][0] = JsonValue::String(udid);
267     let result = validate_bundle_and_distribution_type(&profile_json, true);
268     assert!(result.is_ok());
269 }
270 
271 #[test]
test_parse_iternaltesting_profile()272 fn test_parse_iternaltesting_profile() {
273     let profile_str = r#"
274     {
275         "version-name": "2.0.0",
276         "version-code": 2,
277         "app-distribution-type": "internaltesting",
278         "uuid": "",
279         "validity": {
280             "not-before": 1,
281             "not-after": 2
282         },
283         "type": "release",
284         "bundle-info": {
285             "developer-id": "",
286             "distribution-certificate": "",
287             "bundle-name": "com.test.internaltesting",
288             "apl": "normal",
289             "app-feature": "test_app",
290             "app-identifier": "123123"
291         },
292         "acls": {
293             "allowed-acls": [
294                 ""
295             ]
296         },
297         "app-privilege-capabilities": [],
298         "permissions": {
299             "restricted-permissions": [
300                 ""
301             ]
302         },
303         "debug-info": {
304             "device-ids": [],
305             "device-id-type": "udid"
306         }
307     }
308     "#;
309     let udid = get_udid().expect("Failed to get UDID");
310     let mut profile_json =JsonValue::from_text(profile_str).unwrap();
311     profile_json["debug-info"]["device-ids"][0] = JsonValue::String(udid);
312     let result = validate_bundle_and_distribution_type(&profile_json, true);
313     assert!(result.is_ok());
314 }
315 
316 #[test]
test_parse_invalid_profile()317 fn test_parse_invalid_profile() {
318     let no_type_profile = r#"
319     {
320         "version-name": "2.0.0",
321         "version-code": 2,
322         "app-distribution-type": "internaltesting",
323         "uuid": "",
324         "validity": {
325             "not-before": 1,
326             "not-after": 2
327         },
328         "bundle-info": {
329             "developer-id": "",
330             "distribution-certificate": "",
331             "bundle-name": "com.test.internaltesting",
332             "apl": "normal",
333             "app-feature": "test_app",
334             "app-identifier": "123123"
335         },
336         "acls": {
337             "allowed-acls": [
338                 ""
339             ]
340         },
341         "app-privilege-capabilities": [],
342         "permissions": {
343             "restricted-permissions": [
344                 ""
345             ]
346         },
347         "debug-info": {
348             "device-ids": [],
349             "device-id-type": "udid"
350         }
351     }
352     "#;
353     let no_distribution_profile = r#"
354     {
355         "version-name": "2.0.0",
356         "version-code": 2,
357         "uuid": "",
358         "validity": {
359             "not-before": 1,
360             "not-after": 2
361         },
362         "type": "release",
363         "bundle-info": {
364             "developer-id": "",
365             "distribution-certificate": "",
366             "bundle-name": "com.test.internaltesting",
367             "apl": "normal",
368             "app-feature": "test_app",
369             "app-identifier": "123123"
370         },
371         "acls": {
372             "allowed-acls": [
373                 ""
374             ]
375         },
376         "app-privilege-capabilities": [],
377         "permissions": {
378             "restricted-permissions": [
379                 ""
380             ]
381         },
382         "debug-info": {
383             "device-ids": [],
384             "device-id-type": "udid"
385         }
386     }
387     "#;
388     let no_debug_info_profile = r#"
389     {
390         "version-name": "2.0.0",
391         "version-code": 2,
392         "app-distribution-type": "internaltesting",
393         "uuid": "",
394         "validity": {
395             "not-before": 1,
396             "not-after": 2
397         },
398         "type": "release",
399         "bundle-info": {
400             "developer-id": "",
401             "distribution-certificate": "",
402             "bundle-name": "com.test.internaltesting",
403             "apl": "normal",
404             "app-feature": "test_app",
405             "app-identifier": "123123"
406         },
407         "acls": {
408             "allowed-acls": [
409                 ""
410             ]
411         },
412         "app-privilege-capabilities": [],
413         "permissions": {
414             "restricted-permissions": [
415                 ""
416             ]
417         }
418     }
419     "#;
420     let udid = get_udid().expect("Failed to get UDID");
421     let mut no_type_profile_json =JsonValue::from_text(no_type_profile).unwrap();
422     no_type_profile_json["debug-info"]["device-ids"][0] = JsonValue::String(udid.clone());
423     let result = validate_bundle_and_distribution_type(&no_type_profile_json, true);
424     assert!(result.is_err());
425 
426     let mut no_distribution_profile_json =JsonValue::from_text(no_distribution_profile).unwrap();
427     no_distribution_profile_json["debug-info"]["device-ids"][0] = JsonValue::String(udid.clone());
428     let result = validate_bundle_and_distribution_type(&no_distribution_profile_json, true);
429     assert!(result.is_err());
430 
431     let no_debug_info_profile_json =JsonValue::from_text(no_debug_info_profile).unwrap();
432     let result = validate_bundle_and_distribution_type(&no_debug_info_profile_json, true);
433     assert!(result.is_err());
434 }
435 
436 #[test]
test_get_udid_once()437 fn test_get_udid_once() {
438     let udid_from_get = get_udid().expect("Failed to get UDID");
439     let udid_from_global = UDID.clone().expect("UDID is None");
440 
441     assert_eq!(udid_from_get, udid_from_global);
442 }
443 
444 #[test]
test_get_udid_concurrent()445 fn test_get_udid_concurrent() {
446     let num_threads = 10;
447     let mut handles = vec![];
448 
449     for _ in 0..num_threads {
450         let handle = thread::spawn(|| {
451             let udid = get_udid().expect("Failed to get UDID");
452             assert_eq!(udid, UDID.clone().expect("UDID is None"));
453         });
454         handles.push(handle);
455     }
456 
457     for handle in handles {
458         handle.join().expect("Thread panicked");
459     }
460 }