• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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