1 /*
2 * Copyright (C) 2022-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_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
37 napi_async_work asyncWork = nullptr;
38
39 std::string algoName = "";
40 HcfBlob *inBlob = nullptr;
41
42 HcfResult errCode = HCF_SUCCESS;
43 const char *errMsg = nullptr;
44 HcfBlob *outBlob = nullptr;
45 HcfMd *md = nullptr;
46 };
47
FreeCryptoFwkCtx(napi_env env,MdCtx * context)48 static void FreeCryptoFwkCtx(napi_env env, MdCtx *context)
49 {
50 if (context == nullptr) {
51 return;
52 }
53 if (context->asyncWork != nullptr) {
54 napi_delete_async_work(env, context->asyncWork);
55 context->asyncWork = nullptr;
56 }
57 if (context->callback != nullptr) {
58 napi_delete_reference(env, context->callback);
59 context->callback = nullptr;
60 }
61 if (context->inBlob != nullptr) {
62 HcfFree(context->inBlob->data);
63 context->inBlob->data = nullptr;
64 context->inBlob->len = 0;
65 HcfFree(context->inBlob);
66 context->inBlob = nullptr;
67 }
68 if (context->outBlob != nullptr) {
69 HcfFree(context->outBlob->data);
70 context->outBlob->data = nullptr;
71 context->outBlob->len = 0;
72 HcfFree(context->outBlob);
73 context->outBlob = nullptr;
74 }
75 context->errMsg = nullptr;
76 context->md = nullptr;
77 HcfFree(context);
78 context = nullptr;
79 }
80
ReturnCallbackResult(napi_env env,MdCtx * context,napi_value result)81 static void ReturnCallbackResult(napi_env env, MdCtx *context, napi_value result)
82 {
83 napi_value businessError = nullptr;
84 if (context->errCode != HCF_SUCCESS) {
85 businessError = GenerateBusinessError(env, context->errCode, context->errMsg);
86 }
87 napi_value params[ARGS_SIZE_TWO] = { businessError, result };
88 napi_value func = nullptr;
89 napi_get_reference_value(env, context->callback, &func);
90
91 napi_value recv = nullptr;
92 napi_value callFuncRet = nullptr;
93 napi_get_undefined(env, &recv);
94 napi_call_function(env, recv, func, ARGS_SIZE_TWO, params, &callFuncRet);
95 }
96
ReturnPromiseResult(napi_env env,MdCtx * context,napi_value result)97 static void ReturnPromiseResult(napi_env env, MdCtx *context, napi_value result)
98 {
99 if (context->errCode == HCF_SUCCESS) {
100 napi_resolve_deferred(env, context->deferred, result);
101 } else {
102 napi_reject_deferred(env, context->deferred,
103 GenerateBusinessError(env, context->errCode, context->errMsg));
104 }
105 }
106
MdUpdateExecute(napi_env env,void * data)107 static void MdUpdateExecute(napi_env env, void *data)
108 {
109 MdCtx *context = static_cast<MdCtx *>(data);
110 HcfMd *mdObj = context->md;
111 context->errCode = mdObj->update(mdObj, context->inBlob);
112 if (context->errCode != HCF_SUCCESS) {
113 LOGD("[error] update failed!");
114 context->errMsg = "update failed";
115 }
116 }
117
MdDoFinalExecute(napi_env env,void * data)118 static void MdDoFinalExecute(napi_env env, void *data)
119 {
120 MdCtx *context = static_cast<MdCtx *>(data);
121 HcfMd *mdObj = context->md;
122 HcfBlob *outBlob = reinterpret_cast<HcfBlob *>(HcfMalloc(sizeof(HcfBlob), 0));
123 if (outBlob == nullptr) {
124 LOGE("outBlob is null!");
125 context->errCode = HCF_ERR_MALLOC;
126 context->errMsg = "malloc data blob failed";
127 return;
128 }
129 context->errCode = mdObj->doFinal(mdObj, outBlob);
130 if (context->errCode != HCF_SUCCESS) {
131 HcfFree(outBlob);
132 LOGD("[error] doFinal failed!");
133 context->errMsg = "doFinal failed";
134 return;
135 }
136 context->outBlob = outBlob;
137 }
138
MdUpdateComplete(napi_env env,napi_status status,void * data)139 static void MdUpdateComplete(napi_env env, napi_status status, void *data)
140 {
141 MdCtx *context = static_cast<MdCtx *>(data);
142 napi_value nullInstance = nullptr;
143 napi_get_null(env, &nullInstance);
144 if (context->asyncType == ASYNC_CALLBACK) {
145 ReturnCallbackResult(env, context, nullInstance);
146 } else {
147 ReturnPromiseResult(env, context, nullInstance);
148 }
149 FreeCryptoFwkCtx(env, context);
150 }
151
MdDoFinalComplete(napi_env env,napi_status status,void * data)152 static void MdDoFinalComplete(napi_env env, napi_status status, void *data)
153 {
154 MdCtx *context = static_cast<MdCtx *>(data);
155 napi_value returnOutBlob = ConvertBlobToNapiValue(env, context->outBlob);
156 if (returnOutBlob == nullptr) {
157 LOGE("returnOutBlob is nullptr!");
158 returnOutBlob = NapiGetNull(env);
159 }
160 if (context->asyncType == ASYNC_CALLBACK) {
161 ReturnCallbackResult(env, context, returnOutBlob);
162 } else {
163 ReturnPromiseResult(env, context, returnOutBlob);
164 }
165 FreeCryptoFwkCtx(env, context);
166 }
167
BuildMdJsUpdateCtx(napi_env env,napi_callback_info info,MdCtx * context)168 static bool BuildMdJsUpdateCtx(napi_env env, napi_callback_info info, MdCtx *context)
169 {
170 napi_value thisVar = nullptr;
171 NapiMd *napiMd = nullptr;
172 size_t expectedArgsCount = ARGS_SIZE_TWO;
173 size_t argc = expectedArgsCount;
174 napi_value argv[ARGS_SIZE_TWO] = { nullptr };
175 napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
176 if (!CheckArgsCount(env, argc, ARGS_SIZE_TWO, false)) {
177 return false;
178 }
179
180 context->asyncType = isCallback(env, argv[expectedArgsCount - 1], argc, expectedArgsCount) ?
181 ASYNC_CALLBACK : ASYNC_PROMISE;
182 context->inBlob = GetBlobFromNapiDataBlob(env, argv[PARAM0]);
183 if (context->inBlob == nullptr) {
184 LOGE("inBlob is null!");
185 return false;
186 }
187 napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiMd));
188 if (status != napi_ok || napiMd == nullptr) {
189 LOGE("failed to unwrap NapiMd obj!");
190 return false;
191 }
192
193 context->md = napiMd->GetMd();
194
195 if (context->asyncType == ASYNC_PROMISE) {
196 napi_create_promise(env, &context->deferred, &context->promise);
197 return true;
198 } else {
199 return GetCallbackFromJSParams(env, argv[PARAM1], &context->callback);
200 }
201 }
202
BuildMdJsDoFinalCtx(napi_env env,napi_callback_info info,MdCtx * context)203 static bool BuildMdJsDoFinalCtx(napi_env env, napi_callback_info info, MdCtx *context)
204 {
205 napi_value thisVar = nullptr;
206 NapiMd *napiMd = nullptr;
207 size_t expectedArgsCount = ARGS_SIZE_ONE;
208 size_t argc = expectedArgsCount;
209 napi_value argv[ARGS_SIZE_ONE] = { nullptr };
210 napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
211 if (!CheckArgsCount(env, argc, ARGS_SIZE_ONE, false)) {
212 return false;
213 }
214
215 context->asyncType = isCallback(env, argv[expectedArgsCount - 1], argc, expectedArgsCount) ?
216 ASYNC_CALLBACK : ASYNC_PROMISE;
217
218 napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiMd));
219 if (status != napi_ok || napiMd == nullptr) {
220 LOGE("failed to unwrap NapiMd obj!");
221 return false;
222 }
223
224 context->md = napiMd->GetMd();
225
226 if (context->asyncType == ASYNC_PROMISE) {
227 napi_create_promise(env, &context->deferred, &context->promise);
228 return true;
229 } else {
230 return GetCallbackFromJSParams(env, argv[PARAM0], &context->callback);
231 }
232 }
233
NewMdJsUpdateAsyncWork(napi_env env,MdCtx * context)234 static napi_value NewMdJsUpdateAsyncWork(napi_env env, MdCtx *context)
235 {
236 napi_create_async_work(
237 env, nullptr, GetResourceName(env, "MdUpdate"),
238 [](napi_env env, void *data) {
239 MdUpdateExecute(env, data);
240 return;
241 },
242 [](napi_env env, napi_status status, void *data) {
243 MdUpdateComplete(env, status, data);
244 return;
245 },
246 static_cast<void *>(context),
247 &context->asyncWork);
248
249 napi_queue_async_work(env, context->asyncWork);
250 if (context->asyncType == ASYNC_PROMISE) {
251 return context->promise;
252 } else {
253 return NapiGetNull(env);
254 }
255 }
256
NewMdJsDoFinalAsyncWork(napi_env env,MdCtx * context)257 static napi_value NewMdJsDoFinalAsyncWork(napi_env env, MdCtx *context)
258 {
259 napi_create_async_work(
260 env, nullptr, GetResourceName(env, "MdDoFinal"),
261 [](napi_env env, void *data) {
262 MdDoFinalExecute(env, data);
263 return;
264 },
265 [](napi_env env, napi_status status, void *data) {
266 MdDoFinalComplete(env, status, data);
267 return;
268 },
269 static_cast<void *>(context),
270 &context->asyncWork);
271
272 napi_queue_async_work(env, context->asyncWork);
273 if (context->asyncType == ASYNC_PROMISE) {
274 return context->promise;
275 } else {
276 return NapiGetNull(env);
277 }
278 }
279
NapiMd(HcfMd * mdObj)280 NapiMd::NapiMd(HcfMd *mdObj)
281 {
282 this->mdObj_ = mdObj;
283 }
284
~NapiMd()285 NapiMd::~NapiMd()
286 {
287 HcfObjDestroy(this->mdObj_);
288 }
289
GetMd()290 HcfMd *NapiMd::GetMd()
291 {
292 return this->mdObj_;
293 }
294
JsMdUpdate(napi_env env,napi_callback_info info)295 napi_value NapiMd::JsMdUpdate(napi_env env, napi_callback_info info)
296 {
297 MdCtx *context = static_cast<MdCtx *>(HcfMalloc(sizeof(MdCtx), 0));
298 if (context == nullptr) {
299 napi_throw(env, GenerateBusinessError(env, HCF_ERR_MALLOC, "malloc context failed"));
300 LOGE("malloc context failed!");
301 return nullptr;
302 }
303
304 if (!BuildMdJsUpdateCtx(env, info, context)) {
305 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "build context fail."));
306 LOGE("build context fail.");
307 FreeCryptoFwkCtx(env, context);
308 return nullptr;
309 }
310
311 return NewMdJsUpdateAsyncWork(env, context);
312 }
313
JsMdDoFinal(napi_env env,napi_callback_info info)314 napi_value NapiMd::JsMdDoFinal(napi_env env, napi_callback_info info)
315 {
316 MdCtx *context = static_cast<MdCtx *>(HcfMalloc(sizeof(MdCtx), 0));
317 if (context == nullptr) {
318 napi_throw(env, GenerateBusinessError(env, HCF_ERR_MALLOC, "malloc context failed"));
319 LOGE("malloc context failed!");
320 return nullptr;
321 }
322
323 if (!BuildMdJsDoFinalCtx(env, info, context)) {
324 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "build context fail."));
325 LOGE("build context fail.");
326 FreeCryptoFwkCtx(env, context);
327 return nullptr;
328 }
329
330 return NewMdJsDoFinalAsyncWork(env, context);
331 }
332
JsGetMdLength(napi_env env,napi_callback_info info)333 napi_value NapiMd::JsGetMdLength(napi_env env, napi_callback_info info)
334 {
335 napi_value thisVar = nullptr;
336 NapiMd *napiMd = nullptr;
337
338 napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
339
340 napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiMd));
341 if (status != napi_ok || napiMd == nullptr) {
342 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "failed to unwrap NapiMd obj!"));
343 LOGE("failed to unwrap NapiMd obj!");
344 return nullptr;
345 }
346
347 HcfMd *md = napiMd->GetMd();
348 if (md == nullptr) {
349 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "fail to get md obj!"));
350 LOGE("fail to get md obj!");
351 return nullptr;
352 }
353
354 uint32_t retLen = md->getMdLength(md);
355 napi_value napiLen = nullptr;
356 napi_create_uint32(env, retLen, &napiLen);
357 return napiLen;
358 }
359
MdConstructor(napi_env env,napi_callback_info info)360 napi_value NapiMd::MdConstructor(napi_env env, napi_callback_info info)
361 {
362 napi_value thisVar = nullptr;
363 napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
364 return thisVar;
365 }
366
NapiWrapMd(napi_env env,napi_value instance,NapiMd * mdNapiObj)367 static napi_value NapiWrapMd(napi_env env, napi_value instance, NapiMd *mdNapiObj)
368 {
369 napi_status status = napi_wrap(
370 env, instance, mdNapiObj,
371 [](napi_env env, void *data, void *hint) {
372 NapiMd *md = static_cast<NapiMd *>(data);
373 delete md;
374 return;
375 }, nullptr, nullptr);
376 if (status != napi_ok) {
377 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "failed to wrap NapiMd obj!"));
378 delete mdNapiObj;
379 mdNapiObj = nullptr;
380 LOGE("failed to wrap NapiMd obj!");
381 return nullptr;
382 }
383 return instance;
384 }
385
CreateMd(napi_env env,napi_callback_info info)386 napi_value NapiMd::CreateMd(napi_env env, napi_callback_info info)
387 {
388 LOGD("Enter CreateMd...");
389 size_t expectedArgc = ARGS_SIZE_ONE;
390 size_t argc = expectedArgc;
391 napi_value argv[ARGS_SIZE_ONE] = { nullptr };
392 napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
393 if (argc != expectedArgc) {
394 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "The input args num is invalid."));
395 LOGE("The input args num is invalid.");
396 return nullptr;
397 }
398 std::string algoName;
399 if (!GetStringFromJSParams(env, argv[PARAM0], algoName)) {
400 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "Failed to get algorithm."));
401 LOGE("Failed to get algorithm.");
402 return nullptr;
403 }
404 HcfMd *mdObj = nullptr;
405 HcfResult res = HcfMdCreate(algoName.c_str(), &mdObj);
406 if (res != HCF_SUCCESS) {
407 napi_throw(env, GenerateBusinessError(env, res, "create C obj failed."));
408 LOGE("create c mdObj failed.");
409 return nullptr;
410 }
411 napi_value napiAlgName = nullptr;
412 napi_create_string_utf8(env, algoName.c_str(), NAPI_AUTO_LENGTH, &napiAlgName);
413 napi_value instance = nullptr;
414 napi_value constructor = nullptr;
415 napi_get_reference_value(env, classRef_, &constructor);
416 napi_new_instance(env, constructor, argc, argv, &instance);
417 napi_set_named_property(env, instance, CRYPTO_TAG_ALG_NAME.c_str(), napiAlgName);
418 NapiMd *mdNapiObj = new (std::nothrow) NapiMd(mdObj);
419 if (mdNapiObj == nullptr) {
420 napi_throw(env, GenerateBusinessError(env, HCF_ERR_MALLOC, "new md napi obj failed!"));
421 HcfObjDestroy(mdObj);
422 LOGE("create md napi obj failed!");
423 return nullptr;
424 }
425
426 return NapiWrapMd(env, instance, mdNapiObj);
427 }
428
DefineMdJSClass(napi_env env,napi_value exports)429 void NapiMd::DefineMdJSClass(napi_env env, napi_value exports)
430 {
431 napi_property_descriptor desc[] = {
432 DECLARE_NAPI_FUNCTION("createMd", NapiMd::CreateMd),
433 };
434 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
435 napi_property_descriptor classDesc[] = {
436 DECLARE_NAPI_FUNCTION("update", NapiMd::JsMdUpdate),
437 DECLARE_NAPI_FUNCTION("digest", NapiMd::JsMdDoFinal),
438 DECLARE_NAPI_FUNCTION("getMdLength", NapiMd::JsGetMdLength),
439 };
440 napi_value constructor = nullptr;
441 napi_define_class(env, "Md", NAPI_AUTO_LENGTH, MdConstructor, nullptr,
442 sizeof(classDesc) / sizeof(classDesc[0]), classDesc, &constructor);
443 napi_create_reference(env, constructor, 1, &classRef_);
444 }
445 } // CryptoFramework
446 } // OHOS