1 /*
2 * Copyright (C) 2022-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_md.h"
17
18 #include "securec.h"
19 #include "log.h"
20 #include "memory.h"
21
22 #include "napi_utils.h"
23 #include "napi_crypto_framework_defines.h"
24
25 namespace OHOS {
26 namespace CryptoFramework {
27 thread_local napi_ref NapiMd::classRef_ = nullptr;
28
29 struct MdCtx {
30 napi_env env = nullptr;
31
32 AsyncType asyncType = ASYNC_CALLBACK;
33 napi_ref callback = nullptr;
34 napi_deferred deferred = nullptr;
35 napi_value promise = nullptr;
36 napi_ref mdRef = nullptr;
37
38 napi_async_work asyncWork = nullptr;
39
40 std::string algoName = "";
41 HcfBlob *inBlob = nullptr;
42
43 HcfResult errCode = HCF_SUCCESS;
44 const char *errMsg = nullptr;
45 HcfBlob *outBlob = nullptr;
46 HcfMd *md = nullptr;
47 };
48
FreeCryptoFwkCtx(napi_env env,MdCtx * context)49 static void FreeCryptoFwkCtx(napi_env env, MdCtx *context)
50 {
51 if (context == nullptr) {
52 return;
53 }
54 if (context->asyncWork != nullptr) {
55 napi_delete_async_work(env, context->asyncWork);
56 context->asyncWork = nullptr;
57 }
58 if (context->callback != nullptr) {
59 napi_delete_reference(env, context->callback);
60 context->callback = nullptr;
61 }
62 if (context->mdRef != nullptr) {
63 napi_delete_reference(env, context->mdRef);
64 context->mdRef = nullptr;
65 }
66 if (context->inBlob != nullptr) {
67 HcfFree(context->inBlob->data);
68 context->inBlob->data = nullptr;
69 context->inBlob->len = 0;
70 HcfFree(context->inBlob);
71 context->inBlob = nullptr;
72 }
73 if (context->outBlob != nullptr) {
74 HcfFree(context->outBlob->data);
75 context->outBlob->data = nullptr;
76 context->outBlob->len = 0;
77 HcfFree(context->outBlob);
78 context->outBlob = nullptr;
79 }
80 context->errMsg = nullptr;
81 context->md = nullptr;
82 HcfFree(context);
83 }
84
ReturnCallbackResult(napi_env env,MdCtx * context,napi_value result)85 static void ReturnCallbackResult(napi_env env, MdCtx *context, napi_value result)
86 {
87 napi_value businessError = nullptr;
88 if (context->errCode != HCF_SUCCESS) {
89 businessError = GenerateBusinessError(env, context->errCode, context->errMsg);
90 }
91 napi_value params[ARGS_SIZE_TWO] = { businessError, result };
92 napi_value func = nullptr;
93 napi_get_reference_value(env, context->callback, &func);
94
95 napi_value recv = nullptr;
96 napi_value callFuncRet = nullptr;
97 napi_get_undefined(env, &recv);
98 napi_call_function(env, recv, func, ARGS_SIZE_TWO, params, &callFuncRet);
99 }
100
ReturnPromiseResult(napi_env env,MdCtx * context,napi_value result)101 static void ReturnPromiseResult(napi_env env, MdCtx *context, napi_value result)
102 {
103 if (context->errCode == HCF_SUCCESS) {
104 napi_resolve_deferred(env, context->deferred, result);
105 } else {
106 napi_reject_deferred(env, context->deferred,
107 GenerateBusinessError(env, context->errCode, context->errMsg));
108 }
109 }
110
MdUpdateExecute(napi_env env,void * data)111 static void MdUpdateExecute(napi_env env, void *data)
112 {
113 MdCtx *context = static_cast<MdCtx *>(data);
114 HcfMd *mdObj = context->md;
115 context->errCode = mdObj->update(mdObj, context->inBlob);
116 if (context->errCode != HCF_SUCCESS) {
117 LOGD("[error] update failed!");
118 context->errMsg = "update failed";
119 }
120 }
121
MdDoFinalExecute(napi_env env,void * data)122 static void MdDoFinalExecute(napi_env env, void *data)
123 {
124 MdCtx *context = static_cast<MdCtx *>(data);
125 HcfMd *mdObj = context->md;
126 HcfBlob *outBlob = reinterpret_cast<HcfBlob *>(HcfMalloc(sizeof(HcfBlob), 0));
127 if (outBlob == nullptr) {
128 LOGE("outBlob is null!");
129 context->errCode = HCF_ERR_MALLOC;
130 context->errMsg = "malloc data blob failed";
131 return;
132 }
133 context->errCode = mdObj->doFinal(mdObj, outBlob);
134 if (context->errCode != HCF_SUCCESS) {
135 HcfFree(outBlob);
136 outBlob = nullptr;
137 LOGD("[error] doFinal failed!");
138 context->errMsg = "doFinal failed";
139 return;
140 }
141 context->outBlob = outBlob;
142 }
143
MdUpdateComplete(napi_env env,napi_status status,void * data)144 static void MdUpdateComplete(napi_env env, napi_status status, void *data)
145 {
146 MdCtx *context = static_cast<MdCtx *>(data);
147 napi_value nullInstance = nullptr;
148 napi_get_null(env, &nullInstance);
149 if (context->asyncType == ASYNC_CALLBACK) {
150 ReturnCallbackResult(env, context, nullInstance);
151 } else {
152 ReturnPromiseResult(env, context, nullInstance);
153 }
154 FreeCryptoFwkCtx(env, context);
155 }
156
MdDoFinalComplete(napi_env env,napi_status status,void * data)157 static void MdDoFinalComplete(napi_env env, napi_status status, void *data)
158 {
159 MdCtx *context = static_cast<MdCtx *>(data);
160 napi_value returnOutBlob = ConvertBlobToNapiValue(env, context->outBlob);
161 if (returnOutBlob == nullptr) {
162 LOGE("returnOutBlob is nullptr!");
163 returnOutBlob = NapiGetNull(env);
164 }
165 if (context->asyncType == ASYNC_CALLBACK) {
166 ReturnCallbackResult(env, context, returnOutBlob);
167 } else {
168 ReturnPromiseResult(env, context, returnOutBlob);
169 }
170 FreeCryptoFwkCtx(env, context);
171 }
172
BuildMdJsUpdateCtx(napi_env env,napi_callback_info info,MdCtx * context)173 static bool BuildMdJsUpdateCtx(napi_env env, napi_callback_info info, MdCtx *context)
174 {
175 napi_value thisVar = nullptr;
176 NapiMd *napiMd = nullptr;
177 size_t expectedArgsCount = ARGS_SIZE_TWO;
178 size_t argc = expectedArgsCount;
179 napi_value argv[ARGS_SIZE_TWO] = { nullptr };
180 napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
181 if (!CheckArgsCount(env, argc, ARGS_SIZE_TWO, false)) {
182 return false;
183 }
184
185 context->asyncType = isCallback(env, argv[expectedArgsCount - 1], argc, expectedArgsCount) ?
186 ASYNC_CALLBACK : ASYNC_PROMISE;
187 context->inBlob = GetBlobFromNapiDataBlob(env, argv[PARAM0]);
188 if (context->inBlob == nullptr) {
189 LOGE("inBlob is null!");
190 return false;
191 }
192 napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiMd));
193 if (status != napi_ok || napiMd == nullptr) {
194 LOGE("failed to unwrap NapiMd obj!");
195 return false;
196 }
197
198 context->md = napiMd->GetMd();
199
200 if (napi_create_reference(env, thisVar, 1, &context->mdRef) != napi_ok) {
201 LOGE("create md ref failed when do md update!");
202 return false;
203 }
204
205 if (context->asyncType == ASYNC_PROMISE) {
206 napi_create_promise(env, &context->deferred, &context->promise);
207 return true;
208 } else {
209 return GetCallbackFromJSParams(env, argv[PARAM1], &context->callback);
210 }
211 }
212
BuildMdJsDoFinalCtx(napi_env env,napi_callback_info info,MdCtx * context)213 static bool BuildMdJsDoFinalCtx(napi_env env, napi_callback_info info, MdCtx *context)
214 {
215 napi_value thisVar = nullptr;
216 NapiMd *napiMd = nullptr;
217 size_t expectedArgsCount = ARGS_SIZE_ONE;
218 size_t argc = expectedArgsCount;
219 napi_value argv[ARGS_SIZE_ONE] = { nullptr };
220 napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
221 if (!CheckArgsCount(env, argc, ARGS_SIZE_ONE, false)) {
222 return false;
223 }
224
225 context->asyncType = isCallback(env, argv[expectedArgsCount - 1], argc, expectedArgsCount) ?
226 ASYNC_CALLBACK : ASYNC_PROMISE;
227
228 napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiMd));
229 if (status != napi_ok || napiMd == nullptr) {
230 LOGE("failed to unwrap NapiMd obj!");
231 return false;
232 }
233
234 context->md = napiMd->GetMd();
235
236 if (napi_create_reference(env, thisVar, 1, &context->mdRef) != napi_ok) {
237 LOGE("create md ref failed when do md final!");
238 return false;
239 }
240
241 if (context->asyncType == ASYNC_PROMISE) {
242 napi_create_promise(env, &context->deferred, &context->promise);
243 return true;
244 } else {
245 return GetCallbackFromJSParams(env, argv[PARAM0], &context->callback);
246 }
247 }
248
NewMdJsUpdateAsyncWork(napi_env env,MdCtx * context)249 static napi_value NewMdJsUpdateAsyncWork(napi_env env, MdCtx *context)
250 {
251 napi_create_async_work(
252 env, nullptr, GetResourceName(env, "MdUpdate"),
253 [](napi_env env, void *data) {
254 MdUpdateExecute(env, data);
255 return;
256 },
257 [](napi_env env, napi_status status, void *data) {
258 MdUpdateComplete(env, status, data);
259 return;
260 },
261 static_cast<void *>(context),
262 &context->asyncWork);
263
264 napi_queue_async_work(env, context->asyncWork);
265 if (context->asyncType == ASYNC_PROMISE) {
266 return context->promise;
267 } else {
268 return NapiGetNull(env);
269 }
270 }
271
NewMdJsDoFinalAsyncWork(napi_env env,MdCtx * context)272 static napi_value NewMdJsDoFinalAsyncWork(napi_env env, MdCtx *context)
273 {
274 napi_create_async_work(
275 env, nullptr, GetResourceName(env, "MdDoFinal"),
276 [](napi_env env, void *data) {
277 MdDoFinalExecute(env, data);
278 return;
279 },
280 [](napi_env env, napi_status status, void *data) {
281 MdDoFinalComplete(env, status, data);
282 return;
283 },
284 static_cast<void *>(context),
285 &context->asyncWork);
286
287 napi_queue_async_work(env, context->asyncWork);
288 if (context->asyncType == ASYNC_PROMISE) {
289 return context->promise;
290 } else {
291 return NapiGetNull(env);
292 }
293 }
294
NapiMd(HcfMd * mdObj)295 NapiMd::NapiMd(HcfMd *mdObj)
296 {
297 this->mdObj_ = mdObj;
298 }
299
~NapiMd()300 NapiMd::~NapiMd()
301 {
302 HcfObjDestroy(this->mdObj_);
303 this->mdObj_ = nullptr;
304 }
305
GetMd()306 HcfMd *NapiMd::GetMd()
307 {
308 return this->mdObj_;
309 }
310
JsMdUpdate(napi_env env,napi_callback_info info)311 napi_value NapiMd::JsMdUpdate(napi_env env, napi_callback_info info)
312 {
313 MdCtx *context = static_cast<MdCtx *>(HcfMalloc(sizeof(MdCtx), 0));
314 if (context == nullptr) {
315 napi_throw(env, GenerateBusinessError(env, HCF_ERR_MALLOC, "malloc context failed"));
316 LOGE("malloc context failed!");
317 return nullptr;
318 }
319
320 if (!BuildMdJsUpdateCtx(env, info, context)) {
321 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "build context fail."));
322 LOGE("build context fail.");
323 FreeCryptoFwkCtx(env, context);
324 return nullptr;
325 }
326
327 return NewMdJsUpdateAsyncWork(env, context);
328 }
329
JsMdUpdateSync(napi_env env,napi_callback_info info)330 napi_value NapiMd::JsMdUpdateSync(napi_env env, napi_callback_info info)
331 {
332 napi_value thisVar = nullptr;
333 NapiMd *napiMd = nullptr;
334 size_t expectedArgsCount = ARGS_SIZE_ONE;
335 size_t argc = expectedArgsCount;
336 napi_value argv[ARGS_SIZE_ONE] = { nullptr };
337 napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
338 if (argc != expectedArgsCount) {
339 LOGE("The input args num is invalid.");
340 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "invalid parameters."));
341 return nullptr;
342 }
343 HcfBlob *inBlob = GetBlobFromNapiDataBlob(env, argv[PARAM0]);
344 if (inBlob == nullptr) {
345 LOGE("inBlob is null!");
346 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "invalid parameters."));
347 return nullptr;
348 }
349 napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiMd));
350 if (status != napi_ok || napiMd == nullptr) {
351 LOGE("failed to unwrap NapiMd obj!");
352 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "invalid parameters."));
353 HcfBlobDataClearAndFree(inBlob);
354 HCF_FREE_PTR(inBlob);
355 return nullptr;
356 }
357 HcfMd *md = napiMd->GetMd();
358 if (md == nullptr) {
359 LOGE("md is nullptr!");
360 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "md is nullptr!"));
361 HcfBlobDataClearAndFree(inBlob);
362 HCF_FREE_PTR(inBlob);
363 return nullptr;
364 }
365 HcfResult errCode = md->update(md, inBlob);
366 if (errCode != HCF_SUCCESS) {
367 LOGE("update failed!");
368 napi_throw(env, GenerateBusinessError(env, HCF_ERR_CRYPTO_OPERATION, "crypto operation error."));
369 HcfBlobDataClearAndFree(inBlob);
370 HCF_FREE_PTR(inBlob);
371 return nullptr;
372 }
373 napi_value nullInstance = nullptr;
374 napi_get_null(env, &nullInstance);
375 HcfBlobDataClearAndFree(inBlob);
376 HCF_FREE_PTR(inBlob);
377 return nullInstance;
378 }
379
JsMdDoFinal(napi_env env,napi_callback_info info)380 napi_value NapiMd::JsMdDoFinal(napi_env env, napi_callback_info info)
381 {
382 MdCtx *context = static_cast<MdCtx *>(HcfMalloc(sizeof(MdCtx), 0));
383 if (context == nullptr) {
384 napi_throw(env, GenerateBusinessError(env, HCF_ERR_MALLOC, "malloc context failed"));
385 LOGE("malloc context failed!");
386 return nullptr;
387 }
388
389 if (!BuildMdJsDoFinalCtx(env, info, context)) {
390 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "build context fail."));
391 LOGE("build context fail.");
392 FreeCryptoFwkCtx(env, context);
393 return nullptr;
394 }
395
396 return NewMdJsDoFinalAsyncWork(env, context);
397 }
398
JsMdDoFinalSync(napi_env env,napi_callback_info info)399 napi_value NapiMd::JsMdDoFinalSync(napi_env env, napi_callback_info info)
400 {
401 NapiMd *napiMd = nullptr;
402 napi_value thisVar = nullptr;
403 napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
404
405 napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiMd));
406 if (status != napi_ok || napiMd == nullptr) {
407 LOGE("failed to unwrap NapiMd obj!");
408 napi_throw(env, GenerateBusinessError(env, HCF_ERR_NAPI, "failed to unwrap NapiMd obj!"));
409 return nullptr;
410 }
411
412 HcfMd *md = napiMd->GetMd();
413 if (md == nullptr) {
414 LOGE("md is nullptr!");
415 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "md is nullptr!"));
416 return nullptr;
417 }
418
419 HcfBlob outBlob = { .data = nullptr, .len = 0 };
420 HcfResult errCode = md->doFinal(md, &outBlob);
421 if (errCode != HCF_SUCCESS) {
422 LOGE("md doFinal failed!");
423 napi_throw(env, GenerateBusinessError(env, errCode, "md doFinal failed!"));
424 HcfBlobDataClearAndFree(&outBlob);
425 return nullptr;
426 }
427
428 napi_value instance = nullptr;
429 errCode = ConvertDataBlobToNapiValue(env, &outBlob, &instance);
430 HcfBlobDataClearAndFree(&outBlob);
431 if (errCode != HCF_SUCCESS) {
432 LOGE("md convert dataBlob to napi_value failed!");
433 napi_throw(env, GenerateBusinessError(env, errCode, "md convert dataBlob to napi_value failed!"));
434 return nullptr;
435 }
436 return instance;
437 }
438
JsGetMdLength(napi_env env,napi_callback_info info)439 napi_value NapiMd::JsGetMdLength(napi_env env, napi_callback_info info)
440 {
441 napi_value thisVar = nullptr;
442 NapiMd *napiMd = nullptr;
443
444 napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
445
446 napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiMd));
447 if (status != napi_ok || napiMd == nullptr) {
448 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "failed to unwrap NapiMd obj!"));
449 LOGE("failed to unwrap NapiMd obj!");
450 return nullptr;
451 }
452
453 HcfMd *md = napiMd->GetMd();
454 if (md == nullptr) {
455 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "fail to get md obj!"));
456 LOGE("fail to get md obj!");
457 return nullptr;
458 }
459
460 uint32_t retLen = md->getMdLength(md);
461 napi_value napiLen = nullptr;
462 napi_create_uint32(env, retLen, &napiLen);
463 return napiLen;
464 }
465
MdConstructor(napi_env env,napi_callback_info info)466 napi_value NapiMd::MdConstructor(napi_env env, napi_callback_info info)
467 {
468 napi_value thisVar = nullptr;
469 napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
470 return thisVar;
471 }
472
NapiWrapMd(napi_env env,napi_value instance,NapiMd * mdNapiObj)473 static napi_value NapiWrapMd(napi_env env, napi_value instance, NapiMd *mdNapiObj)
474 {
475 napi_status status = napi_wrap(
476 env, instance, mdNapiObj,
477 [](napi_env env, void *data, void *hint) {
478 NapiMd *md = static_cast<NapiMd *>(data);
479 delete md;
480 return;
481 }, nullptr, nullptr);
482 if (status != napi_ok) {
483 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "failed to wrap NapiMd obj!"));
484 delete mdNapiObj;
485 LOGE("failed to wrap NapiMd obj!");
486 return nullptr;
487 }
488 return instance;
489 }
490
CreateMd(napi_env env,napi_callback_info info)491 napi_value NapiMd::CreateMd(napi_env env, napi_callback_info info)
492 {
493 size_t expectedArgc = ARGS_SIZE_ONE;
494 size_t argc = expectedArgc;
495 napi_value argv[ARGS_SIZE_ONE] = { nullptr };
496 napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
497 if (argc != expectedArgc) {
498 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "The input args num is invalid."));
499 LOGE("The input args num is invalid.");
500 return nullptr;
501 }
502 std::string algoName;
503 if (!GetStringFromJSParams(env, argv[PARAM0], algoName)) {
504 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "Failed to get algorithm."));
505 LOGE("Failed to get algorithm.");
506 return nullptr;
507 }
508 HcfMd *mdObj = nullptr;
509 HcfResult res = HcfMdCreate(algoName.c_str(), &mdObj);
510 if (res != HCF_SUCCESS) {
511 napi_throw(env, GenerateBusinessError(env, res, "create C obj failed."));
512 LOGE("create c mdObj failed.");
513 return nullptr;
514 }
515 napi_value napiAlgName = nullptr;
516 napi_create_string_utf8(env, algoName.c_str(), NAPI_AUTO_LENGTH, &napiAlgName);
517 napi_value instance = nullptr;
518 napi_value constructor = nullptr;
519 napi_get_reference_value(env, classRef_, &constructor);
520 napi_new_instance(env, constructor, argc, argv, &instance);
521 napi_set_named_property(env, instance, CRYPTO_TAG_ALG_NAME.c_str(), napiAlgName);
522 NapiMd *mdNapiObj = new (std::nothrow) NapiMd(mdObj);
523 if (mdNapiObj == nullptr) {
524 napi_throw(env, GenerateBusinessError(env, HCF_ERR_MALLOC, "new md napi obj failed!"));
525 HcfObjDestroy(mdObj);
526 mdObj = nullptr;
527 LOGE("create md napi obj failed!");
528 return nullptr;
529 }
530
531 return NapiWrapMd(env, instance, mdNapiObj);
532 }
533
DefineMdJSClass(napi_env env,napi_value exports)534 void NapiMd::DefineMdJSClass(napi_env env, napi_value exports)
535 {
536 napi_property_descriptor desc[] = {
537 DECLARE_NAPI_FUNCTION("createMd", NapiMd::CreateMd),
538 };
539 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
540 napi_property_descriptor classDesc[] = {
541 DECLARE_NAPI_FUNCTION("update", NapiMd::JsMdUpdate),
542 DECLARE_NAPI_FUNCTION("updateSync", NapiMd::JsMdUpdateSync),
543 DECLARE_NAPI_FUNCTION("digest", NapiMd::JsMdDoFinal),
544 DECLARE_NAPI_FUNCTION("digestSync", NapiMd::JsMdDoFinalSync),
545 DECLARE_NAPI_FUNCTION("getMdLength", NapiMd::JsGetMdLength),
546 };
547 napi_value constructor = nullptr;
548 napi_define_class(env, "Md", NAPI_AUTO_LENGTH, MdConstructor, nullptr,
549 sizeof(classDesc) / sizeof(classDesc[0]), classDesc, &constructor);
550 napi_create_reference(env, constructor, 1, &classRef_);
551 }
552 } // CryptoFramework
553 } // OHOS