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