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
16 #include "napi_x509_cert_chain.h"
17
18 #include "cf_api.h"
19 #include "cf_log.h"
20 #include "cf_memory.h"
21 #include "cf_param.h"
22 #include "cf_result.h"
23 #include "napi/native_api.h"
24 #include "napi/native_node_api.h"
25 #include "napi_cert_defines.h"
26 #include "napi_cert_utils.h"
27 #include "napi_common.h"
28 #include "napi_object.h"
29 #include "napi_x509_cert_chain_validate_params.h"
30 #include "napi_x509_cert_chain_validate_result.h"
31 #include "napi_x509_trust_anchor.h"
32 #include "napi_cert_crl_common.h"
33 #include "securec.h"
34 #include "x509_cert_chain_validate_params.h"
35 #include "x509_certificate.h"
36 #include "x509_cert_chain.h"
37 #include "cert_crl_common.h"
38
39 namespace OHOS {
40 namespace CertFramework {
41 thread_local napi_ref NapiX509CertChain::classRef_ = nullptr;
42
43 struct CfCtx {
44 AsyncCtx async;
45 NapiX509CertChain *certChainClass = nullptr;
46 HcfCertChain *certChain = nullptr;
47 CfEncodingBlob *encodingBlob = nullptr;
48 HcfX509CertChainValidateParams params;
49 HcfX509CertChainValidateResult result;
50 };
51
NapiX509CertChain(HcfCertChain * certChain)52 NapiX509CertChain::NapiX509CertChain(HcfCertChain *certChain)
53 {
54 this->certChain_ = certChain;
55 }
56
~NapiX509CertChain()57 NapiX509CertChain::~NapiX509CertChain()
58 {
59 CfObjDestroy(this->certChain_);
60 }
61
BuildCertChainContext()62 static CfCtx *BuildCertChainContext()
63 {
64 CfCtx *context = static_cast<CfCtx *>(HcfMalloc(sizeof(CfCtx), 0));
65 if (context == nullptr) {
66 LOGE("malloc context failed!");
67 return nullptr;
68 }
69 context->async = static_cast<AsyncCtx>(HcfMalloc(sizeof(AsyncContext), 0));
70 if (context->async == nullptr) {
71 LOGE("malloc context failed!");
72 CfFree(context);
73 return nullptr;
74 }
75 return context;
76 }
77
DeleteCertChainContext(napi_env env,CfCtx * & context,bool freeCertFlag=false)78 static void DeleteCertChainContext(napi_env env, CfCtx *&context, bool freeCertFlag = false)
79 {
80 if (context == nullptr) {
81 return;
82 }
83
84 FreeAsyncContext(env, context->async);
85
86 if (context->encodingBlob != nullptr) {
87 CfEncodingBlobDataFree(context->encodingBlob);
88 CF_FREE_PTR(context->encodingBlob);
89 }
90
91 FreeX509CertChainValidateParams(context->params);
92 FreeX509CertChainValidateResult(context->result, freeCertFlag);
93
94 CF_FREE_PTR(context);
95 }
96
CreateCertChainJSInstance(napi_env env)97 static napi_value CreateCertChainJSInstance(napi_env env)
98 {
99 napi_value constructor = nullptr;
100 napi_value instance = nullptr;
101 napi_get_reference_value(env, NapiX509CertChain::classRef_, &constructor);
102 napi_new_instance(env, constructor, 0, nullptr, &instance);
103 return instance;
104 }
105
CreateCallbackAndPromise(napi_env env,CfCtx * context,size_t argc,size_t maxCount,napi_value callbackValue)106 static bool CreateCallbackAndPromise(
107 napi_env env, CfCtx *context, size_t argc, size_t maxCount, napi_value callbackValue)
108 {
109 context->async->asyncType = GetAsyncType(env, argc, maxCount, callbackValue);
110 if (context->async->asyncType == ASYNC_TYPE_CALLBACK) {
111 if (!CertGetCallbackFromJSParams(env, callbackValue, &context->async->callback)) {
112 LOGE("x509 certificate: get callback failed!");
113 return false;
114 }
115 } else {
116 napi_create_promise(env, &context->async->deferred, &context->async->promise);
117 }
118 return true;
119 }
120
CreateCertChainExecute(napi_env env,void * data)121 static void CreateCertChainExecute(napi_env env, void *data)
122 {
123 CfCtx *context = static_cast<CfCtx *>(data);
124 context->async->errCode = HcfCertChainCreate(context->encodingBlob, nullptr, &context->certChain);
125 if (context->async->errCode != CF_SUCCESS) {
126 context->async->errMsg = "create cert chain failed";
127 }
128 }
129
BuildCreateInstance(napi_env env,HcfCertChain * certChain)130 static napi_value BuildCreateInstance(napi_env env, HcfCertChain *certChain)
131 {
132 napi_value instance = CreateCertChainJSInstance(env);
133 NapiX509CertChain *napiObject = new (std::nothrow) NapiX509CertChain(certChain);
134 if (napiObject == nullptr) {
135 LOGE("new napi object failed.");
136 return nullptr;
137 }
138 napi_wrap(
139 env, instance, napiObject,
140 [](napi_env env, void *data, void *hint) {
141 NapiX509CertChain *certchain = static_cast<NapiX509CertChain *>(data);
142 delete certchain;
143 return;
144 },
145 nullptr, nullptr);
146 return instance;
147 }
148
CreateCertChainComplete(napi_env env,napi_status status,void * data)149 static void CreateCertChainComplete(napi_env env, napi_status status, void *data)
150 {
151 CfCtx *context = static_cast<CfCtx *>(data);
152 if (context->async->errCode != CF_SUCCESS) {
153 ReturnJSResult(env, context->async, nullptr);
154 DeleteCertChainContext(env, context, false);
155 return;
156 }
157
158 napi_value instance = BuildCreateInstance(env, context->certChain);
159 if (instance == nullptr) {
160 context->async->errCode = CF_ERR_MALLOC;
161 context->async->errMsg = "Failed to create napi cert chain class";
162 LOGE("Failed to create napi cert chain class");
163 CfObjDestroy(context->certChain);
164 context->certChain = nullptr;
165 }
166 ReturnJSResult(env, context->async, instance);
167 DeleteCertChainContext(env, context);
168 }
169
CreateCertChainAsyncWork(napi_env env,CfCtx * context)170 static napi_value CreateCertChainAsyncWork(napi_env env, CfCtx *context)
171 {
172 napi_create_async_work(env, nullptr, GetResourceName(env, "createX509CertChain"), CreateCertChainExecute,
173 CreateCertChainComplete, static_cast<void *>(context), &context->async->asyncWork);
174
175 napi_queue_async_work(env, context->async->asyncWork);
176 if (context->async->asyncType == ASYNC_TYPE_PROMISE) {
177 return context->async->promise;
178 } else {
179 return NapiGetNull(env);
180 }
181 }
182
ValidateExecute(napi_env env,void * data)183 static void ValidateExecute(napi_env env, void *data)
184 {
185 LOGI("enter");
186 CfCtx *context = static_cast<CfCtx *>(data);
187 context->async->errCode = context->certChain->validate(context->certChain, &context->params, &context->result);
188 if (context->async->errCode != CF_SUCCESS) {
189 context->async->errMsg = "create cert chain failed";
190 }
191 }
192
ValidateComplete(napi_env env,napi_status status,void * data)193 static void ValidateComplete(napi_env env, napi_status status, void *data)
194 {
195 LOGI("enter");
196 CfCtx *context = static_cast<CfCtx *>(data);
197 if (context->async->errCode != CF_SUCCESS) {
198 ReturnJSResult(env, context->async, nullptr);
199 DeleteCertChainContext(env, context, false);
200 return;
201 }
202 napi_value instance = BuildX509CertChainValidateResultJS(env, &context->result);
203 if (instance == nullptr) {
204 LOGE("validate ret failed");
205 context->async->errCode = CF_ERR_MALLOC;
206 context->async->errMsg = "build return obj failed!";
207 ReturnJSResult(env, context->async, nullptr);
208 DeleteCertChainContext(env, context, true);
209 return;
210 }
211
212 ReturnJSResult(env, context->async, instance);
213 DeleteCertChainContext(env, context);
214 }
215
ValidateAsyncWork(napi_env env,CfCtx * context)216 static napi_value ValidateAsyncWork(napi_env env, CfCtx *context)
217 {
218 napi_create_async_work(env, nullptr, GetResourceName(env, "Validate"), ValidateExecute, ValidateComplete,
219 static_cast<void *>(context), &context->async->asyncWork);
220
221 napi_queue_async_work(env, context->async->asyncWork);
222 if (context->async->asyncType == ASYNC_TYPE_PROMISE) {
223 return context->async->promise;
224 } else {
225 return NapiGetNull(env);
226 }
227 }
228
Validate(napi_env env,napi_callback_info info)229 napi_value NapiX509CertChain::Validate(napi_env env, napi_callback_info info)
230 {
231 LOGI("enter");
232 size_t argc = ARGS_SIZE_TWO;
233 napi_value argv[ARGS_SIZE_TWO] = { nullptr };
234 napi_value thisVar = nullptr;
235 napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
236 if (!CertCheckArgsCount(env, argc, ARGS_SIZE_TWO, false)) {
237 napi_throw(env, CertGenerateBusinessError(env, CF_INVALID_PARAMS, "check args count failed!"));
238 LOGE("check args count failed.");
239 return nullptr;
240 }
241
242 CfCtx *context = BuildCertChainContext();
243 if (context == nullptr) {
244 napi_throw(env, CertGenerateBusinessError(env, CF_ERR_MALLOC, "malloc context failed!"));
245 LOGE("malloc context failed.");
246 return nullptr;
247 }
248
249 if (!CreateCallbackAndPromise(env, context, argc, ARGS_SIZE_TWO, argv[PARAM1])) {
250 napi_throw(env, CertGenerateBusinessError(env, CF_INVALID_PARAMS, "CreateCallbackAndPromise failed!"));
251 DeleteCertChainContext(env, context);
252 LOGE("CreateCallbackAndPromise failed!");
253 return nullptr;
254 }
255 context->certChainClass = this;
256 context->certChain = GetCertChain();
257 if (!BuildX509CertChainValidateParams(env, argv[PARAM0], context->params)) {
258 napi_throw(env, CertGenerateBusinessError(env, CF_INVALID_PARAMS, "BuildX509CertChainValidateParams failed!"));
259 LOGE("BuildX509CertChainValidateParams failed!");
260 DeleteCertChainContext(env, context);
261 return nullptr;
262 }
263
264 return ValidateAsyncWork(env, context);
265 }
266
CreateX509CertChainByArray(napi_env env,napi_value param)267 static napi_value CreateX509CertChainByArray(napi_env env, napi_value param)
268 {
269 HcfX509CertificateArray certs = { nullptr, 0 };
270 if (param != nullptr && !GetArrayCertFromNapiValue(env, param, &certs, false)) {
271 napi_throw(env, CertGenerateBusinessError(env, CF_INVALID_PARAMS, "get cert arr failed!"));
272 LOGE("get array cert from data failed!");
273 return nullptr;
274 }
275
276 HcfCertChain *certChain = nullptr;
277 CfResult res = HcfCertChainCreate(nullptr, &certs, &certChain);
278 if (res != CF_SUCCESS) {
279 LOGE("HcfCertChainCreate failed!");
280 napi_throw(env, CertGenerateBusinessError(env, res, "create cert chain by arr failed!"));
281 CF_FREE_PTR(certs.data);
282 return nullptr;
283 }
284 napi_value instance = BuildCreateInstance(env, certChain);
285 if (instance == nullptr) {
286 LOGE("HcfCertChainCreate failed!");
287 napi_throw(env, CertGenerateBusinessError(env, CF_ERR_MALLOC, "create instance failed!"));
288 CfObjDestroy(certChain);
289 CF_FREE_PTR(certs.data);
290 return nullptr;
291 }
292 return instance;
293 }
294
CreateX509CertChainByEncodingBlob(napi_env env,size_t argc,napi_value param1,napi_value param2)295 static napi_value CreateX509CertChainByEncodingBlob(napi_env env, size_t argc, napi_value param1, napi_value param2)
296 {
297 if (!CertCheckArgsCount(env, argc, ARGS_SIZE_TWO, false)) {
298 LOGE("CertCheckArgsCount failed");
299 napi_throw(env, CertGenerateBusinessError(env, CF_INVALID_PARAMS, "CertCheckArgsCount failed!"));
300 return nullptr;
301 }
302 CfCtx *context = BuildCertChainContext();
303 if (context == nullptr) {
304 LOGE("context is nullptr");
305 napi_throw(env, CertGenerateBusinessError(env, CF_ERR_MALLOC, "context is nullptr!"));
306 return nullptr;
307 }
308
309 if (!CreateCallbackAndPromise(env, context, argc, ARGS_SIZE_TWO, param2)) {
310 napi_throw(env, CertGenerateBusinessError(env, CF_INVALID_PARAMS, "Create Callback Promise failed!"));
311 LOGE("Create Callback Promise failed");
312 DeleteCertChainContext(env, context);
313 return nullptr;
314 }
315 if (!GetEncodingBlobFromValue(env, param1, &context->encodingBlob)) {
316 napi_throw(env, CertGenerateBusinessError(env, CF_INVALID_PARAMS, "Get Encoding Blob failed!"));
317 LOGE("Get Encoding Blob failed");
318 DeleteCertChainContext(env, context);
319 return nullptr;
320 }
321
322 return CreateCertChainAsyncWork(env, context);
323 }
324
NapiCreateX509CertChain(napi_env env,napi_callback_info info)325 napi_value NapiCreateX509CertChain(napi_env env, napi_callback_info info)
326 {
327 size_t argc = ARGS_SIZE_TWO;
328 napi_value argv[ARGS_SIZE_TWO] = { nullptr };
329 napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
330
331 bool flag = false;
332 napi_is_array(env, argv[PARAM0], &flag);
333 napi_value instance = nullptr;
334 if (flag) {
335 if (argc != ARGS_SIZE_ONE) {
336 LOGE("arg size is not correct");
337 napi_throw(env, CertGenerateBusinessError(env, CF_INVALID_PARAMS, "arg size is not correct!"));
338 return nullptr;
339 }
340 LOGI("NapiCreateX509CertChain : Array<X509Cert>!");
341 instance = CreateX509CertChainByArray(env, argv[PARAM0]);
342 } else {
343 LOGI("NapiCreateX509CertChain : inStream: EncodingBlob!");
344 instance = CreateX509CertChainByEncodingBlob(env, argc, argv[PARAM0], argv[PARAM1]);
345 }
346 return instance;
347 }
348
NapiGetCertList(napi_env env,napi_callback_info info)349 napi_value NapiGetCertList(napi_env env, napi_callback_info info)
350 {
351 napi_value thisVar = nullptr;
352 napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
353 NapiX509CertChain *napiCertChainObj = nullptr;
354 napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiCertChainObj));
355 if (napiCertChainObj == nullptr) {
356 LOGE("napi cert chain object is nullptr!");
357 napi_throw(env, CertGenerateBusinessError(env, CF_INVALID_PARAMS, "napi cert chain object is nullptr!"));
358 return nullptr;
359 }
360 HcfCertChain *certChain = napiCertChainObj->GetCertChain();
361 HcfX509CertificateArray certs = { nullptr, 0 };
362 CfResult res = certChain->getCertList(certChain, &certs);
363 if (res != CF_SUCCESS) {
364 LOGE("napi getCertList failed!");
365 napi_throw(env, CertGenerateBusinessError(env, res, "get cert list failed!"));
366 return nullptr;
367 }
368 napi_value instance = ConvertCertArrToNapiValue(env, &certs);
369 if (instance == nullptr) {
370 LOGE("convert arr to instance failed!");
371 napi_throw(env, CertGenerateBusinessError(env, res, "convert arr to instance failed!"));
372 FreeCertArrayData(&certs);
373 return nullptr;
374 }
375 CF_FREE_PTR(certs.data);
376 return instance;
377 }
378
NapiValidate(napi_env env,napi_callback_info info)379 napi_value NapiValidate(napi_env env, napi_callback_info info)
380 {
381 napi_value thisVar = nullptr;
382 napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
383 NapiX509CertChain *napiCertChainObj = nullptr;
384 napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiCertChainObj));
385 if (napiCertChainObj == nullptr) {
386 LOGE("napi cert chain object is nullptr!");
387 napi_throw(env, CertGenerateBusinessError(env, CF_INVALID_PARAMS, "napi cert chain object is nullptr!"));
388 return nullptr;
389 }
390 return napiCertChainObj->Validate(env, info);
391 }
392
CertChainConstructor(napi_env env,napi_callback_info info)393 static napi_value CertChainConstructor(napi_env env, napi_callback_info info)
394 {
395 napi_value thisVar = nullptr;
396 napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
397 return thisVar;
398 }
399
DefineX509CertChainJsClass(napi_env env,napi_value exports)400 void NapiX509CertChain::DefineX509CertChainJsClass(napi_env env, napi_value exports)
401 {
402 napi_property_descriptor desc[] = {
403 DECLARE_NAPI_FUNCTION("createX509CertChain", NapiCreateX509CertChain),
404 };
405 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
406
407 napi_property_descriptor CertChainDesc[] = {
408 DECLARE_NAPI_FUNCTION("getCertList", NapiGetCertList),
409 DECLARE_NAPI_FUNCTION("validate", NapiValidate),
410 };
411
412 napi_value constructor = nullptr;
413 napi_define_class(env, "X509CertChain", NAPI_AUTO_LENGTH, CertChainConstructor, nullptr,
414 sizeof(CertChainDesc) / sizeof(CertChainDesc[0]), CertChainDesc, &constructor);
415 napi_create_reference(env, constructor, 1, &classRef_);
416 }
417 } // namespace CertFramework
418 } // namespace OHOS
419