1 /*
2  * Copyright (c) 2023-2024 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 
16 #include "napi_x509_cert_chain_validate_params.h"
17 
18 #include "cf_log.h"
19 #include "cf_memory.h"
20 #include "cf_type.h"
21 #include "napi/native_api.h"
22 #include "napi/native_common.h"
23 #include "napi_cert_crl_collection.h"
24 #include "napi_cert_defines.h"
25 #include "napi_cert_utils.h"
26 #include "napi_object.h"
27 #include "napi_x509_trust_anchor.h"
28 #include "napi_x509_certificate.h"
29 #include "utils.h"
30 #include "x509_cert_chain_validate_params.h"
31 
32 namespace OHOS {
33 namespace CertFramework {
34 
GetValidDate(napi_env env,napi_value arg,CfBlob * & out)35 static bool GetValidDate(napi_env env, napi_value arg, CfBlob *&out)
36 {
37     napi_value obj = GetProp(env, arg, CERT_CHAIN_VALIDATE_TAG_DATE.c_str());
38     if (obj == nullptr) {
39         LOGI("prop date do not exist!");
40         return true;
41     }
42     out = CertGetBlobFromStringJSParams(env, obj);
43     if (out == nullptr) {
44         LOGE("get blob failed!");
45         return false;
46     }
47     return true;
48 }
49 
GetArrayLength(napi_env env,napi_value arg,uint32_t & length)50 static bool GetArrayLength(napi_env env, napi_value arg, uint32_t &length)
51 {
52     length = 0;
53     bool flag = false;
54     napi_status status = napi_is_array(env, arg, &flag);
55     if (status != napi_ok || !flag) {
56         LOGE("param type not array!");
57         return false;
58     }
59     status = napi_get_array_length(env, arg, &length);
60     if (status != napi_ok || length == 0 || length > MAX_LEN_OF_ARRAY) {
61         LOGE("array length is invalid!");
62         return false;
63     }
64     return true;
65 }
66 
GetX509TrustAnchorArray(napi_env env,napi_value arg,HcfX509TrustAnchorArray * & out)67 static bool GetX509TrustAnchorArray(napi_env env, napi_value arg, HcfX509TrustAnchorArray *&out)
68 {
69     napi_value obj = GetProp(env, arg, CERT_CHAIN_VALIDATE_TAG_TRUSTANCHORS.c_str());
70     if (obj == nullptr) {
71         LOGE("param type not array!");
72         return false;
73     }
74 
75     uint32_t length;
76     if (!GetArrayLength(env, obj, length)) {
77         LOGE("get array length failed!");
78         return false;
79     }
80 
81     out = static_cast<HcfX509TrustAnchorArray *>(CfMalloc(sizeof(HcfX509TrustAnchorArray), 0));
82     if (out == nullptr) {
83         LOGE("Failed to allocate out memory!");
84         return false;
85     }
86 
87     out->count = length;
88     out->data = static_cast<HcfX509TrustAnchor **>(CfMalloc(length * sizeof(HcfX509TrustAnchor *), 0));
89     if (out->data == nullptr) {
90         LOGE("Failed to allocate data memory!");
91         CfFree(out);
92         out = nullptr;
93         return false;
94     }
95     for (uint32_t i = 0; i < length; ++i) {
96         napi_value element;
97         if (napi_get_element(env, obj, i, &element) != napi_ok) {
98             LOGE("get element failed!");
99             CfFree(out->data);
100             CfFree(out);
101             out = nullptr;
102             return false;
103         }
104 
105         if (!BuildX509TrustAnchorObj(env, element, out->data[i])) {
106             LOGE("get element failed!");
107             CfFree(out->data);
108             CfFree(out);
109             out = nullptr;
110             return false;
111         }
112     }
113     return true;
114 }
115 
GetCertCRLCollectionArray(napi_env env,napi_value arg,HcfCertCRLCollectionArray * & out)116 static bool GetCertCRLCollectionArray(napi_env env, napi_value arg, HcfCertCRLCollectionArray *&out)
117 {
118     napi_value obj = GetProp(env, arg, CERT_CHAIN_VALIDATE_TAG_CERTCRLS.c_str());
119     if (obj == nullptr) {
120         LOGI("prop certCRLs do not exist!");
121         return true;
122     }
123 
124     uint32_t length;
125     if (!GetArrayLength(env, obj, length)) {
126         LOGE("get array length failed!");
127         return false;
128     }
129 
130     out = static_cast<HcfCertCRLCollectionArray *>(CfMalloc(sizeof(HcfCertCRLCollectionArray), 0));
131     if (out == nullptr) {
132         LOGE("Failed to allocate out memory!");
133         return false;
134     }
135     out->count = length;
136     out->data = static_cast<HcfCertCrlCollection **>(CfMalloc(length * sizeof(HcfCertCrlCollection *), 0));
137     if (out->data == nullptr) {
138         LOGE("Failed to allocate data memory!");
139         CfFree(out);
140         out = nullptr;
141         return false;
142     }
143     for (uint32_t i = 0; i < length; i++) {
144         napi_value element;
145         napi_status status = napi_get_element(env, obj, i, &element);
146         if (status != napi_ok) {
147             LOGE("get element failed!");
148             CfFree(out->data);
149             CfFree(out);
150             out = nullptr;
151             return false;
152         }
153         NapiCertCRLCollection *napiCertCrlCollectionObj = nullptr;
154         napi_unwrap(env, element, reinterpret_cast<void **>(&napiCertCrlCollectionObj));
155         if (napiCertCrlCollectionObj == nullptr) {
156             LOGE("napi cert crl collection object is nullptr!");
157             CfFree(out->data);
158             CfFree(out);
159             out = nullptr;
160             return false;
161         }
162         out->data[i] = napiCertCrlCollectionObj->GetCertCrlCollection();
163     }
164     return true;
165 }
166 
GetRevocationOptions(napi_env env,napi_value rckObj,HcfRevocationCheckParam * & out)167 static bool GetRevocationOptions(napi_env env, napi_value rckObj, HcfRevocationCheckParam *&out)
168 {
169     napi_value obj = GetProp(env, rckObj, CERT_CHAIN_VALIDATE_TAG_OPTIONS.c_str());
170     if (obj == nullptr) {
171         return true;
172     }
173     bool flag = false;
174     napi_status status = napi_is_array(env, obj, &flag);
175     if (status != napi_ok || !flag) {
176         return false;
177     }
178 
179     uint32_t length = 0;
180     status = napi_get_array_length(env, obj, &length);
181     if (status != napi_ok || length == 0 || length > MAX_NAPI_ARRAY_OF_U8ARR) {
182         return false;
183     }
184     out->options = static_cast<HcfRevChkOpArray *>(CfMalloc(sizeof(HcfRevChkOpArray), 0));
185     if (out->options == nullptr) {
186         return false;
187     }
188     out->options->count = length;
189     out->options->data = static_cast<HcfRevChkOption *>(CfMalloc(length * sizeof(HcfRevChkOption), 0));
190     if (out->options->data == nullptr) {
191         CfFree(out->options);
192         out->options = nullptr;
193         return false;
194     }
195     for (uint32_t i = 0; i < length; i++) {
196         napi_value element;
197         if (napi_get_element(env, obj, i, &element) != napi_ok ||
198             napi_get_value_int32(env, element, (int32_t *)&(out->options->data[i])) != napi_ok) {
199             CfFree(out->options->data);
200             CfFree(out->options);
201             return false;
202         }
203         switch (out->options->data[i]) {
204             case REVOCATION_CHECK_OPTION_PREFER_OCSP:
205             case REVOCATION_CHECK_OPTION_ACCESS_NETWORK:
206             case REVOCATION_CHECK_OPTION_FALLBACK_NO_PREFER:
207             case REVOCATION_CHECK_OPTION_FALLBACK_LOCAL:
208                 break;
209             default:
210                 CfFree(out->options->data);
211                 out->options->data = nullptr;
212                 CfFree(out->options);
213                 out->options = nullptr;
214                 return false;
215         }
216     }
217     return true;
218 }
219 
GetRevocationocspDigest(napi_env env,napi_value rckObj,HcfRevocationCheckParam * & out)220 static bool GetRevocationocspDigest(napi_env env, napi_value rckObj, HcfRevocationCheckParam *&out)
221 {
222     napi_value obj = GetProp(env, rckObj, CERT_CHAIN_VALIDATE_TAG_OCSP_DIGEST.c_str());
223     if (obj == nullptr) {
224         return true;
225     }
226 
227     out->ocspDigest = CertGetBlobFromStringJSParams(env, obj);
228     if (out->ocspDigest == nullptr) {
229         return false;
230     }
231 
232     char *mdName = reinterpret_cast<char *>(out->ocspDigest->data);
233     if (strcmp(mdName, "SHA1") == 0) {
234         return true;
235     } else if (strcmp(mdName, "SHA224") == 0) {
236         return true;
237     } else if (strcmp(mdName, "SHA256") == 0) {
238         return true;
239     } else if (strcmp(mdName, "SHA384") == 0) {
240         return true;
241     } else if (strcmp(mdName, "SHA512") == 0) {
242         return true;
243     } else if (strcmp(mdName, "MD5") == 0) {
244         return true;
245     }
246 
247     CfFree(out->ocspDigest->data);
248     out->ocspDigest->data = nullptr;
249     CfFree(out->ocspDigest);
250     out->ocspDigest = nullptr;
251     return false;
252 }
253 
GetRevocationDetail(napi_env env,napi_value rckObj,HcfRevocationCheckParam * & out)254 static bool GetRevocationDetail(napi_env env, napi_value rckObj, HcfRevocationCheckParam *&out)
255 {
256     napi_value obj = GetProp(env, rckObj, CERT_CHAIN_VALIDATE_TAG_OCSP_REQ_EXTENSION.c_str());
257     if (obj != nullptr) {
258         out->ocspRequestExtension = CertGetBlobArrFromArrUarrJSParams(env, obj);
259         if (out->ocspRequestExtension == nullptr) {
260             return false;
261         }
262     }
263     obj = GetProp(env, rckObj, CERT_CHAIN_VALIDATE_TAG_OCSP_RESP_URI.c_str());
264     if (obj != nullptr) {
265         out->ocspResponderURI = CertGetBlobFromStringJSParams(env, obj);
266         if (out->ocspResponderURI == nullptr) {
267             return false;
268         }
269     }
270     obj = GetProp(env, rckObj, CERT_CHAIN_VALIDATE_TAG_OCSP_RESP_CERT.c_str());
271     if (obj != nullptr) {
272         NapiX509Certificate *napiX509Cert = nullptr;
273         napi_unwrap(env, obj, reinterpret_cast<void **>(&napiX509Cert));
274         if (napiX509Cert != nullptr) {
275             out->ocspResponderCert = napiX509Cert->GetX509Cert();
276             if (out->ocspResponderCert == nullptr) {
277                 return false;
278             }
279         } else {
280             return false;
281         }
282     }
283     obj = GetProp(env, rckObj, CERT_CHAIN_VALIDATE_TAG_OCSP_RESPS.c_str());
284     if (obj != nullptr) {
285         out->ocspResponses = CertGetBlobFromUint8ArrJSParams(env, obj);
286         if (out->ocspResponses == nullptr) {
287             return false;
288         }
289     }
290     obj = GetProp(env, rckObj, CERT_CHAIN_VALIDATE_TAG_CRL_DOWNLOAD_URI.c_str());
291     if (obj != nullptr) {
292         out->crlDownloadURI = CertGetBlobFromStringJSParams(env, obj);
293         if (out->crlDownloadURI == nullptr) {
294             return false;
295         }
296     }
297     if (!GetRevocationocspDigest(env, rckObj, out)) {
298         return false;
299     }
300     return GetRevocationOptions(env, rckObj, out);
301 }
302 
FreeHcfRevocationCheckParam(HcfRevocationCheckParam * param)303 static void FreeHcfRevocationCheckParam(HcfRevocationCheckParam *param)
304 {
305     if (param == nullptr) {
306         return;
307     }
308     if (param->ocspRequestExtension != nullptr) {
309         FreeCfBlobArray(param->ocspRequestExtension->data, param->ocspRequestExtension->count);
310         CfFree(param->ocspRequestExtension);
311     }
312     CfBlobFree(&param->ocspResponderURI);
313     CfBlobFree(&param->ocspResponses);
314     CfBlobFree(&param->crlDownloadURI);
315     if (param->options != nullptr) {
316         if (param->options->data != nullptr) {
317             CfFree(param->options->data);
318         }
319         CfFree(param->options);
320     }
321     CfBlobFree(&param->ocspDigest);
322     CfFree(param);
323 }
324 
GetRevocationCheckParam(napi_env env,napi_value arg,HcfRevocationCheckParam * & out)325 static bool GetRevocationCheckParam(napi_env env, napi_value arg, HcfRevocationCheckParam *&out)
326 {
327     napi_value rckObj = GetProp(env, arg, CERT_CHAIN_VALIDATE_TAG_REVOCATIONCHECKPARAM.c_str());
328     if (rckObj == nullptr) {
329         LOGI("RevocationCheckParam do not exist!");
330         return true;
331     }
332     napi_valuetype valueType;
333     napi_typeof(env, rckObj, &valueType);
334     if (valueType == napi_null || valueType != napi_object) {
335         LOGE("Failed to check input param!");
336         return false;
337     }
338 
339     out = static_cast<HcfRevocationCheckParam *>(CfMalloc(sizeof(HcfRevocationCheckParam), 0));
340     if (out == nullptr) {
341         LOGE("Failed to allocate out memory!");
342         return false;
343     }
344     if (!GetRevocationDetail(env, rckObj, out)) {
345         LOGE("Failed to get revocation detail!");
346         FreeHcfRevocationCheckParam(out);
347         out = nullptr;
348         return false;
349     }
350 
351     return true;
352 }
353 
GetValidationPolicyType(napi_env env,napi_value arg,HcfValPolicyType & out)354 static bool GetValidationPolicyType(napi_env env, napi_value arg, HcfValPolicyType &out)
355 {
356     napi_value obj = GetProp(env, arg, CERT_CHAIN_VALIDATE_TAG_POLICY.c_str());
357     if (obj != nullptr) {
358         napi_status status = napi_get_value_int32(env, obj, (int32_t *)&out);
359         if (status != napi_ok) {
360             return false;
361         }
362     }
363     return true;
364 }
365 
GetSSLHostname(napi_env env,napi_value arg,CfBlob * & out)366 static bool GetSSLHostname(napi_env env, napi_value arg, CfBlob *&out)
367 {
368     napi_value obj = GetProp(env, arg, CERT_CHAIN_VALIDATE_TAG_SSLHOSTNAME.c_str());
369     if (obj == nullptr) {
370         LOGI("Param type not SSLHostname!");
371         return true;
372     }
373     out = CertGetBlobFromStringJSParams(env, obj);
374     if (out == nullptr) {
375         LOGE("SSLHostname is nullptr");
376         return false;
377     }
378     return true;
379 }
380 
GetKeyUsage(napi_env env,napi_value arg,HcfKuArray * & out)381 static bool GetKeyUsage(napi_env env, napi_value arg, HcfKuArray *&out)
382 {
383     out = nullptr;
384     napi_value obj = GetProp(env, arg, CERT_CHAIN_VALIDATE_TAG_KEYUSAGE.c_str());
385     if (obj == nullptr) {
386         return true;
387     }
388     bool flag = false;
389     napi_status status = napi_is_array(env, obj, &flag);
390     if (status != napi_ok || !flag) {
391         return false;
392     }
393     uint32_t length = 0;
394     status = napi_get_array_length(env, obj, &length);
395     if (status != napi_ok || length == 0 || length > MAX_NAPI_ARRAY_OF_U8ARR) {
396         return false;
397     }
398     out = static_cast<HcfKuArray *>(CfMalloc(sizeof(HcfKuArray), 0));
399     if (out == nullptr) {
400         return false;
401     }
402     out->count = length;
403     out->data = static_cast<HcfKeyUsageType *>(CfMalloc(length * sizeof(HcfKeyUsageType), 0));
404     if (out->data == nullptr) {
405         CfFree(out);
406         out = nullptr;
407         return false;
408     }
409     for (uint32_t i = 0; i < length; i++) {
410         napi_value element;
411         if (napi_get_element(env, obj, i, &element) != napi_ok ||
412             napi_get_value_int32(env, element, (int32_t *)&(out->data[i])) != napi_ok) {
413             CfFree(out->data);
414             CfFree(out);
415             out = nullptr;
416             return false;
417         }
418     }
419     return true;
420 }
421 
FreeX509CertChainValidateParams(HcfX509CertChainValidateParams & param)422 void FreeX509CertChainValidateParams(HcfX509CertChainValidateParams &param)
423 {
424     CfBlobFree(&param.date);
425     if (param.trustAnchors != nullptr) {
426         for (uint32_t i = 0; i < param.trustAnchors->count; ++i) {
427             FreeX509TrustAnchorObj(param.trustAnchors->data[i]);
428         }
429         CfFree(param.trustAnchors);
430         param.trustAnchors = nullptr;
431     }
432 
433     if (param.certCRLCollections != nullptr) {
434         CfFree(param.certCRLCollections->data);
435         CfFree(param.certCRLCollections);
436         param.certCRLCollections = nullptr;
437     }
438 
439     CfBlobFree(&(param.sslHostname));
440     if (param.keyUsage != nullptr) {
441         CfFree(param.keyUsage->data);
442         CfFree(param.keyUsage);
443         param.keyUsage = nullptr;
444     }
445 
446     FreeHcfRevocationCheckParam(param.revocationCheckParam);
447     param.revocationCheckParam = nullptr;
448 }
449 
FreeTrustAnchorArray(HcfX509TrustAnchorArray * trustAnchorArray,bool freeCertFlag)450 void FreeTrustAnchorArray(HcfX509TrustAnchorArray *trustAnchorArray, bool freeCertFlag)
451 {
452     if (trustAnchorArray == NULL) {
453         return;
454     }
455     for (uint32_t i = 0; i < trustAnchorArray->count; i++) {
456         if (trustAnchorArray->data[i] != NULL) {
457             if (freeCertFlag) {
458                 CfObjDestroy(trustAnchorArray->data[i]->CACert);
459             }
460             trustAnchorArray->data[i]->CACert = NULL;
461             CfBlobFree(&trustAnchorArray->data[i]->CAPubKey);
462             CfBlobFree(&trustAnchorArray->data[i]->CASubject);
463             CfBlobFree(&trustAnchorArray->data[i]->nameConstraints);
464             CfFree(trustAnchorArray->data[i]);
465             trustAnchorArray->data[i] = NULL;
466         }
467     }
468 
469     CfFree(trustAnchorArray);
470 }
471 
BuildX509CertChainValidateParams(napi_env env,napi_value arg,HcfX509CertChainValidateParams & param)472 bool BuildX509CertChainValidateParams(napi_env env, napi_value arg, HcfX509CertChainValidateParams &param)
473 {
474     napi_valuetype type;
475     napi_typeof(env, arg, &type);
476     if (type != napi_object) {
477         LOGE("wrong argument type. expect string type. [Type]: %d", type);
478         return false;
479     }
480 
481     if (!GetValidDate(env, arg, param.date)) {
482         LOGE("Get valid date failed");
483         return false;
484     }
485     if (!GetX509TrustAnchorArray(env, arg, param.trustAnchors)) {
486         LOGE("Get X509 trust anchor array failed");
487         return false;
488     }
489     if (!GetCertCRLCollectionArray(env, arg, param.certCRLCollections)) {
490         LOGE("Get cert CRL collection array failed");
491         return false;
492     }
493     if (!GetRevocationCheckParam(env, arg, param.revocationCheckParam)) {
494         LOGE("Get revocation check param failed!");
495         return false;
496     }
497     if (!GetValidationPolicyType(env, arg, param.policy)) {
498         LOGE("Get validation policy type failed!");
499         return false;
500     }
501     if (!GetSSLHostname(env, arg, param.sslHostname)) {
502         LOGE("Get SSL hostname failed!");
503         return false;
504     }
505     if (!GetKeyUsage(env, arg, param.keyUsage)) {
506         LOGE("Get key usage failed!");
507         return false;
508     }
509 
510     return true;
511 }
512 
513 } // namespace CertFramework
514 } // namespace OHOS
515