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 LOGD("[error] doFinal failed!");
137 context->errMsg = "doFinal failed";
138 return;
139 }
140 context->outBlob = outBlob;
141 }
142
MdUpdateComplete(napi_env env,napi_status status,void * data)143 static void MdUpdateComplete(napi_env env, napi_status status, void *data)
144 {
145 MdCtx *context = static_cast<MdCtx *>(data);
146 napi_value nullInstance = nullptr;
147 napi_get_null(env, &nullInstance);
148 if (context->asyncType == ASYNC_CALLBACK) {
149 ReturnCallbackResult(env, context, nullInstance);
150 } else {
151 ReturnPromiseResult(env, context, nullInstance);
152 }
153 FreeCryptoFwkCtx(env, context);
154 }
155
MdDoFinalComplete(napi_env env,napi_status status,void * data)156 static void MdDoFinalComplete(napi_env env, napi_status status, void *data)
157 {
158 MdCtx *context = static_cast<MdCtx *>(data);
159 napi_value returnOutBlob = ConvertBlobToNapiValue(env, context->outBlob);
160 if (returnOutBlob == nullptr) {
161 LOGE("returnOutBlob is nullptr!");
162 returnOutBlob = NapiGetNull(env);
163 }
164 if (context->asyncType == ASYNC_CALLBACK) {
165 ReturnCallbackResult(env, context, returnOutBlob);
166 } else {
167 ReturnPromiseResult(env, context, returnOutBlob);
168 }
169 FreeCryptoFwkCtx(env, context);
170 }
171
BuildMdJsUpdateCtx(napi_env env,napi_callback_info info,MdCtx * context)172 static bool BuildMdJsUpdateCtx(napi_env env, napi_callback_info info, MdCtx *context)
173 {
174 napi_value thisVar = nullptr;
175 NapiMd *napiMd = nullptr;
176 size_t expectedArgsCount = ARGS_SIZE_TWO;
177 size_t argc = expectedArgsCount;
178 napi_value argv[ARGS_SIZE_TWO] = { nullptr };
179 napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
180 if (!CheckArgsCount(env, argc, ARGS_SIZE_TWO, false)) {
181 return false;
182 }
183
184 context->asyncType = isCallback(env, argv[expectedArgsCount - 1], argc, expectedArgsCount) ?
185 ASYNC_CALLBACK : ASYNC_PROMISE;
186 context->inBlob = GetBlobFromNapiDataBlob(env, argv[PARAM0]);
187 if (context->inBlob == nullptr) {
188 LOGE("inBlob is null!");
189 return false;
190 }
191 napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiMd));
192 if (status != napi_ok || napiMd == nullptr) {
193 LOGE("failed to unwrap NapiMd obj!");
194 return false;
195 }
196
197 context->md = napiMd->GetMd();
198
199 if (napi_create_reference(env, thisVar, 1, &context->mdRef) != napi_ok) {
200 LOGE("create md ref failed when do md update!");
201 return false;
202 }
203
204 if (context->asyncType == ASYNC_PROMISE) {
205 napi_create_promise(env, &context->deferred, &context->promise);
206 return true;
207 } else {
208 return GetCallbackFromJSParams(env, argv[PARAM1], &context->callback);
209 }
210 }
211
BuildMdJsDoFinalCtx(napi_env env,napi_callback_info info,MdCtx * context)212 static bool BuildMdJsDoFinalCtx(napi_env env, napi_callback_info info, MdCtx *context)
213 {
214 napi_value thisVar = nullptr;
215 NapiMd *napiMd = nullptr;
216 size_t expectedArgsCount = ARGS_SIZE_ONE;
217 size_t argc = expectedArgsCount;
218 napi_value argv[ARGS_SIZE_ONE] = { nullptr };
219 napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
220 if (!CheckArgsCount(env, argc, ARGS_SIZE_ONE, false)) {
221 return false;
222 }
223
224 context->asyncType = isCallback(env, argv[expectedArgsCount - 1], argc, expectedArgsCount) ?
225 ASYNC_CALLBACK : ASYNC_PROMISE;
226
227 napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiMd));
228 if (status != napi_ok || napiMd == nullptr) {
229 LOGE("failed to unwrap NapiMd obj!");
230 return false;
231 }
232
233 context->md = napiMd->GetMd();
234
235 if (napi_create_reference(env, thisVar, 1, &context->mdRef) != napi_ok) {
236 LOGE("create md ref failed when do md final!");
237 return false;
238 }
239
240 if (context->asyncType == ASYNC_PROMISE) {
241 napi_create_promise(env, &context->deferred, &context->promise);
242 return true;
243 } else {
244 return GetCallbackFromJSParams(env, argv[PARAM0], &context->callback);
245 }
246 }
247
NewMdJsUpdateAsyncWork(napi_env env,MdCtx * context)248 static napi_value NewMdJsUpdateAsyncWork(napi_env env, MdCtx *context)
249 {
250 napi_create_async_work(
251 env, nullptr, GetResourceName(env, "MdUpdate"),
252 [](napi_env env, void *data) {
253 MdUpdateExecute(env, data);
254 return;
255 },
256 [](napi_env env, napi_status status, void *data) {
257 MdUpdateComplete(env, status, data);
258 return;
259 },
260 static_cast<void *>(context),
261 &context->asyncWork);
262
263 napi_queue_async_work(env, context->asyncWork);
264 if (context->asyncType == ASYNC_PROMISE) {
265 return context->promise;
266 } else {
267 return NapiGetNull(env);
268 }
269 }
270
NewMdJsDoFinalAsyncWork(napi_env env,MdCtx * context)271 static napi_value NewMdJsDoFinalAsyncWork(napi_env env, MdCtx *context)
272 {
273 napi_create_async_work(
274 env, nullptr, GetResourceName(env, "MdDoFinal"),
275 [](napi_env env, void *data) {
276 MdDoFinalExecute(env, data);
277 return;
278 },
279 [](napi_env env, napi_status status, void *data) {
280 MdDoFinalComplete(env, status, data);
281 return;
282 },
283 static_cast<void *>(context),
284 &context->asyncWork);
285
286 napi_queue_async_work(env, context->asyncWork);
287 if (context->asyncType == ASYNC_PROMISE) {
288 return context->promise;
289 } else {
290 return NapiGetNull(env);
291 }
292 }
293
NapiMd(HcfMd * mdObj)294 NapiMd::NapiMd(HcfMd *mdObj)
295 {
296 this->mdObj_ = mdObj;
297 }
298
~NapiMd()299 NapiMd::~NapiMd()
300 {
301 HcfObjDestroy(this->mdObj_);
302 }
303
GetMd()304 HcfMd *NapiMd::GetMd()
305 {
306 return this->mdObj_;
307 }
308
JsMdUpdate(napi_env env,napi_callback_info info)309 napi_value NapiMd::JsMdUpdate(napi_env env, napi_callback_info info)
310 {
311 MdCtx *context = static_cast<MdCtx *>(HcfMalloc(sizeof(MdCtx), 0));
312 if (context == nullptr) {
313 napi_throw(env, GenerateBusinessError(env, HCF_ERR_MALLOC, "malloc context failed"));
314 LOGE("malloc context failed!");
315 return nullptr;
316 }
317
318 if (!BuildMdJsUpdateCtx(env, info, context)) {
319 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "build context fail."));
320 LOGE("build context fail.");
321 FreeCryptoFwkCtx(env, context);
322 return nullptr;
323 }
324
325 return NewMdJsUpdateAsyncWork(env, context);
326 }
327
JsMdUpdateSync(napi_env env,napi_callback_info info)328 napi_value NapiMd::JsMdUpdateSync(napi_env env, napi_callback_info info)
329 {
330 napi_value thisVar = nullptr;
331 NapiMd *napiMd = nullptr;
332 size_t expectedArgsCount = ARGS_SIZE_ONE;
333 size_t argc = expectedArgsCount;
334 napi_value argv[ARGS_SIZE_ONE] = { nullptr };
335 napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
336 if (argc != expectedArgsCount) {
337 LOGE("The input args num is invalid.");
338 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "invalid parameters."));
339 return nullptr;
340 }
341 HcfBlob *inBlob = GetBlobFromNapiDataBlob(env, argv[PARAM0]);
342 if (inBlob == nullptr) {
343 LOGE("inBlob is null!");
344 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "invalid parameters."));
345 return nullptr;
346 }
347 napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiMd));
348 if (status != napi_ok || napiMd == nullptr) {
349 LOGE("failed to unwrap NapiMd obj!");
350 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "invalid parameters."));
351 HcfBlobDataClearAndFree(inBlob);
352 HcfFree(inBlob);
353 return nullptr;
354 }
355 HcfMd *md = napiMd->GetMd();
356 if (md == nullptr) {
357 LOGE("md is nullptr!");
358 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "md is nullptr!"));
359 HcfBlobDataClearAndFree(inBlob);
360 HcfFree(inBlob);
361 return nullptr;
362 }
363 HcfResult errCode = md->update(md, inBlob);
364 if (errCode != HCF_SUCCESS) {
365 LOGE("update failed!");
366 napi_throw(env, GenerateBusinessError(env, HCF_ERR_CRYPTO_OPERATION, "crypto operation error."));
367 HcfBlobDataClearAndFree(inBlob);
368 HcfFree(inBlob);
369 return nullptr;
370 }
371 napi_value nullInstance = nullptr;
372 napi_get_null(env, &nullInstance);
373 HcfBlobDataClearAndFree(inBlob);
374 HcfFree(inBlob);
375 return nullInstance;
376 }
377
JsMdDoFinal(napi_env env,napi_callback_info info)378 napi_value NapiMd::JsMdDoFinal(napi_env env, napi_callback_info info)
379 {
380 MdCtx *context = static_cast<MdCtx *>(HcfMalloc(sizeof(MdCtx), 0));
381 if (context == nullptr) {
382 napi_throw(env, GenerateBusinessError(env, HCF_ERR_MALLOC, "malloc context failed"));
383 LOGE("malloc context failed!");
384 return nullptr;
385 }
386
387 if (!BuildMdJsDoFinalCtx(env, info, context)) {
388 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "build context fail."));
389 LOGE("build context fail.");
390 FreeCryptoFwkCtx(env, context);
391 return nullptr;
392 }
393
394 return NewMdJsDoFinalAsyncWork(env, context);
395 }
396
JsMdDoFinalSync(napi_env env,napi_callback_info info)397 napi_value NapiMd::JsMdDoFinalSync(napi_env env, napi_callback_info info)
398 {
399 NapiMd *napiMd = nullptr;
400 napi_value thisVar = nullptr;
401 napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
402
403 napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiMd));
404 if (status != napi_ok || napiMd == nullptr) {
405 LOGE("failed to unwrap NapiMd obj!");
406 napi_throw(env, GenerateBusinessError(env, HCF_ERR_NAPI, "failed to unwrap NapiMd obj!"));
407 return nullptr;
408 }
409
410 HcfMd *md = napiMd->GetMd();
411 if (md == nullptr) {
412 LOGE("md is nullptr!");
413 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "md is nullptr!"));
414 return nullptr;
415 }
416
417 HcfBlob outBlob = { .data = nullptr, .len = 0 };
418 HcfResult errCode = md->doFinal(md, &outBlob);
419 if (errCode != HCF_SUCCESS) {
420 LOGE("md doFinal failed!");
421 napi_throw(env, GenerateBusinessError(env, errCode, "md doFinal failed!"));
422 HcfBlobDataClearAndFree(&outBlob);
423 return nullptr;
424 }
425
426 napi_value instance = nullptr;
427 errCode = ConvertDataBlobToNapiValue(env, &outBlob, &instance);
428 HcfBlobDataClearAndFree(&outBlob);
429 if (errCode != HCF_SUCCESS) {
430 LOGE("md convert dataBlob to napi_value failed!");
431 napi_throw(env, GenerateBusinessError(env, errCode, "md convert dataBlob to napi_value failed!"));
432 return nullptr;
433 }
434 return instance;
435 }
436
JsGetMdLength(napi_env env,napi_callback_info info)437 napi_value NapiMd::JsGetMdLength(napi_env env, napi_callback_info info)
438 {
439 napi_value thisVar = nullptr;
440 NapiMd *napiMd = nullptr;
441
442 napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
443
444 napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiMd));
445 if (status != napi_ok || napiMd == nullptr) {
446 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "failed to unwrap NapiMd obj!"));
447 LOGE("failed to unwrap NapiMd obj!");
448 return nullptr;
449 }
450
451 HcfMd *md = napiMd->GetMd();
452 if (md == nullptr) {
453 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "fail to get md obj!"));
454 LOGE("fail to get md obj!");
455 return nullptr;
456 }
457
458 uint32_t retLen = md->getMdLength(md);
459 napi_value napiLen = nullptr;
460 napi_create_uint32(env, retLen, &napiLen);
461 return napiLen;
462 }
463
MdConstructor(napi_env env,napi_callback_info info)464 napi_value NapiMd::MdConstructor(napi_env env, napi_callback_info info)
465 {
466 napi_value thisVar = nullptr;
467 napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
468 return thisVar;
469 }
470
NapiWrapMd(napi_env env,napi_value instance,NapiMd * mdNapiObj)471 static napi_value NapiWrapMd(napi_env env, napi_value instance, NapiMd *mdNapiObj)
472 {
473 napi_status status = napi_wrap(
474 env, instance, mdNapiObj,
475 [](napi_env env, void *data, void *hint) {
476 NapiMd *md = static_cast<NapiMd *>(data);
477 delete md;
478 return;
479 }, nullptr, nullptr);
480 if (status != napi_ok) {
481 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "failed to wrap NapiMd obj!"));
482 delete mdNapiObj;
483 LOGE("failed to wrap NapiMd obj!");
484 return nullptr;
485 }
486 return instance;
487 }
488
CreateMd(napi_env env,napi_callback_info info)489 napi_value NapiMd::CreateMd(napi_env env, napi_callback_info info)
490 {
491 size_t expectedArgc = ARGS_SIZE_ONE;
492 size_t argc = expectedArgc;
493 napi_value argv[ARGS_SIZE_ONE] = { nullptr };
494 napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
495 if (argc != expectedArgc) {
496 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "The input args num is invalid."));
497 LOGE("The input args num is invalid.");
498 return nullptr;
499 }
500 std::string algoName;
501 if (!GetStringFromJSParams(env, argv[PARAM0], algoName)) {
502 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "Failed to get algorithm."));
503 LOGE("Failed to get algorithm.");
504 return nullptr;
505 }
506 HcfMd *mdObj = nullptr;
507 HcfResult res = HcfMdCreate(algoName.c_str(), &mdObj);
508 if (res != HCF_SUCCESS) {
509 napi_throw(env, GenerateBusinessError(env, res, "create C obj failed."));
510 LOGE("create c mdObj failed.");
511 return nullptr;
512 }
513 napi_value napiAlgName = nullptr;
514 napi_create_string_utf8(env, algoName.c_str(), NAPI_AUTO_LENGTH, &napiAlgName);
515 napi_value instance = nullptr;
516 napi_value constructor = nullptr;
517 napi_get_reference_value(env, classRef_, &constructor);
518 napi_new_instance(env, constructor, argc, argv, &instance);
519 napi_set_named_property(env, instance, CRYPTO_TAG_ALG_NAME.c_str(), napiAlgName);
520 NapiMd *mdNapiObj = new (std::nothrow) NapiMd(mdObj);
521 if (mdNapiObj == nullptr) {
522 napi_throw(env, GenerateBusinessError(env, HCF_ERR_MALLOC, "new md napi obj failed!"));
523 HcfObjDestroy(mdObj);
524 LOGE("create md napi obj failed!");
525 return nullptr;
526 }
527
528 return NapiWrapMd(env, instance, mdNapiObj);
529 }
530
DefineMdJSClass(napi_env env,napi_value exports)531 void NapiMd::DefineMdJSClass(napi_env env, napi_value exports)
532 {
533 napi_property_descriptor desc[] = {
534 DECLARE_NAPI_FUNCTION("createMd", NapiMd::CreateMd),
535 };
536 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
537 napi_property_descriptor classDesc[] = {
538 DECLARE_NAPI_FUNCTION("update", NapiMd::JsMdUpdate),
539 DECLARE_NAPI_FUNCTION("updateSync", NapiMd::JsMdUpdateSync),
540 DECLARE_NAPI_FUNCTION("digest", NapiMd::JsMdDoFinal),
541 DECLARE_NAPI_FUNCTION("digestSync", NapiMd::JsMdDoFinalSync),
542 DECLARE_NAPI_FUNCTION("getMdLength", NapiMd::JsGetMdLength),
543 };
544 napi_value constructor = nullptr;
545 napi_define_class(env, "Md", NAPI_AUTO_LENGTH, MdConstructor, nullptr,
546 sizeof(classDesc) / sizeof(classDesc[0]), classDesc, &constructor);
547 napi_create_reference(env, constructor, 1, &classRef_);
548 }
549 } // CryptoFramework
550 } // OHOS