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_rand.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 NapiRand::classRef_ = nullptr;
28
29 struct RandCtx {
30 napi_env env = nullptr;
31
32 CfAsyncType asyncType = ASYNC_TYPE_CALLBACK;
33 napi_ref callback = nullptr;
34 napi_deferred deferred = nullptr;
35 napi_value promise = nullptr;
36 napi_async_work asyncWork = nullptr;
37
38 NapiRand *randClass = nullptr;
39 uint32_t numBytes = 0;
40 HcfBlob *seedBlob = nullptr;
41
42 HcfResult errCode = HCF_SUCCESS;
43 const char *errMsg = nullptr;
44 HcfBlob *randBlob = nullptr;
45 };
46
FreeCryptoFwkCtx(napi_env env,RandCtx * context)47 static void FreeCryptoFwkCtx(napi_env env, RandCtx *context)
48 {
49 if (context == nullptr) {
50 return;
51 }
52 if (context->asyncWork != nullptr) {
53 napi_delete_async_work(env, context->asyncWork);
54 context->asyncWork = nullptr;
55 }
56
57 if (context->callback != nullptr) {
58 napi_delete_reference(env, context->callback);
59 context->callback = nullptr;
60 }
61 if (context->seedBlob != nullptr) {
62 HcfFree(context->seedBlob->data);
63 context->seedBlob->data = nullptr;
64 context->seedBlob->len = 0;
65 HcfFree(context->seedBlob);
66 context->seedBlob = nullptr;
67 }
68 if (context->randBlob != nullptr) {
69 HcfFree(context->randBlob->data);
70 context->randBlob->data = nullptr;
71 context->randBlob->len = 0;
72 HcfFree(context->randBlob);
73 context->randBlob = nullptr;
74 }
75 context->errMsg = nullptr;
76 HcfFree(context);
77 context = nullptr;
78 }
79
ReturnCallbackResult(napi_env env,RandCtx * context,napi_value result)80 static void ReturnCallbackResult(napi_env env, RandCtx *context, napi_value result)
81 {
82 napi_value businessError = nullptr;
83 if (context->errCode != HCF_SUCCESS) {
84 businessError = GenerateBusinessError(env, context->errCode, context->errMsg, false);
85 }
86 napi_value params[ARGS_SIZE_TWO] = { businessError, result };
87
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,RandCtx * context,napi_value result)97 static void ReturnPromiseResult(napi_env env, RandCtx *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, false));
104 }
105 }
106
CreateCallbackAndPromise(napi_env env,RandCtx * context,size_t argc,size_t maxCount,napi_value callbackValue)107 static bool CreateCallbackAndPromise(napi_env env, RandCtx *context, size_t argc,
108 size_t maxCount, napi_value callbackValue)
109 {
110 context->asyncType = (argc == maxCount) ? ASYNC_TYPE_CALLBACK : ASYNC_TYPE_PROMISE;
111 if (context->asyncType == ASYNC_TYPE_CALLBACK) {
112 if (!GetCallbackFromJSParams(env, callbackValue, &context->callback, false)) {
113 LOGE("get callback failed!");
114 return false;
115 }
116 } else {
117 napi_create_promise(env, &context->deferred, &context->promise);
118 }
119 return true;
120 }
121
NapiRand(HcfRand * randObj)122 NapiRand::NapiRand(HcfRand *randObj)
123 {
124 this->randObj_ = randObj;
125 }
126
~NapiRand()127 NapiRand::~NapiRand()
128 {
129 HcfObjDestroy(this->randObj_);
130 }
131
GenerateRandomExecute(napi_env env,void * data)132 static void GenerateRandomExecute(napi_env env, void *data)
133 {
134 RandCtx *context = static_cast<RandCtx *>(data);
135 NapiRand *randClass = context->randClass;
136 HcfRand *randObj = randClass->GetRand();
137 HcfBlob *randBlob = reinterpret_cast<HcfBlob *>(HcfMalloc(sizeof(HcfBlob), 0));
138 if (randBlob == nullptr) {
139 LOGE("randBlob is null!");
140 context->errCode = HCF_ERR_MALLOC;
141 context->errMsg = "malloc data blob failed";
142 return;
143 }
144 uint32_t numBytes = context->numBytes;
145 context->errCode = randObj->generateRandom(randObj, numBytes, randBlob);
146 if (context->errCode != HCF_SUCCESS) {
147 LOGE("generateRandom failed!");
148 context->errMsg = "generateRandom failed";
149 HcfFree(randBlob);
150 randBlob = nullptr;
151 return;
152 }
153 context->randBlob = randBlob;
154 }
155
GenerateRandomComplete(napi_env env,napi_status status,void * data)156 static void GenerateRandomComplete(napi_env env, napi_status status, void *data)
157 {
158 RandCtx *context = static_cast<RandCtx *>(data);
159 napi_value returnRandBlob = ConvertBlobToNapiValue(env, context->randBlob);
160 if (returnRandBlob == nullptr) {
161 LOGE("returnOutBlob is nullptr!");
162 returnRandBlob = NapiGetNull(env);
163 }
164 if (context->asyncType == ASYNC_TYPE_CALLBACK) {
165 ReturnCallbackResult(env, context, returnRandBlob);
166 } else {
167 ReturnPromiseResult(env, context, returnRandBlob);
168 }
169 FreeCryptoFwkCtx(env, context);
170 }
171
GenerateRandom(napi_env env,napi_callback_info info)172 napi_value NapiRand::GenerateRandom(napi_env env, napi_callback_info info)
173 {
174 size_t expectedArgsCount = ARGS_SIZE_TWO;
175 size_t argc = expectedArgsCount;
176 napi_value argv[ARGS_SIZE_TWO] = { nullptr };
177 napi_value thisVar = nullptr;
178 napi_value ret = NapiGetNull(env);
179 napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
180 if ((argc != expectedArgsCount) && (argc != expectedArgsCount - CALLBACK_SIZE)) {
181 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "invalid params count", false));
182 LOGE("The arguments count is not expected!");
183 return ret;
184 }
185 RandCtx *context = static_cast<RandCtx *>(HcfMalloc(sizeof(RandCtx), 0));
186 if (context == nullptr) {
187 napi_throw(env, GenerateBusinessError(env, HCF_ERR_MALLOC, "malloc context failed", false));
188 LOGE("malloc context failed!");
189 return ret;
190 }
191 context->randClass = this;
192 if (!GetUint32FromJSParams(env, argv[PARAM0], context->numBytes, false)) {
193 LOGE("get numBytes failed!");
194 FreeCryptoFwkCtx(env, context);
195 return ret;
196 }
197 if (!CreateCallbackAndPromise(env, context, argc, ARGS_SIZE_TWO, argv[PARAM1])) {
198 FreeCryptoFwkCtx(env, context);
199 return nullptr;
200 }
201 napi_create_async_work(
202 env, nullptr, GetResourceName(env, "GenerateRandom"),
203 GenerateRandomExecute,
204 GenerateRandomComplete,
205 static_cast<void *>(context),
206 &context->asyncWork);
207 napi_queue_async_work(env, context->asyncWork);
208 if (context->asyncType == ASYNC_TYPE_PROMISE) {
209 return context->promise;
210 } else {
211 return NapiGetNull(env);
212 }
213 }
214
SetSeed(napi_env env,napi_callback_info info)215 napi_value NapiRand::SetSeed(napi_env env, napi_callback_info info)
216 {
217 size_t expectedArgsCount = ARGS_SIZE_ONE;
218 size_t argc = expectedArgsCount;
219 napi_value argv[ARGS_SIZE_ONE] = { nullptr };
220 napi_value thisVar = nullptr;
221 napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
222 if (argc != expectedArgsCount) {
223 napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "invalid params count", false));
224 LOGE("The arguments count is not expected!");
225 return nullptr;
226 }
227 HcfBlob *seedBlob = GetBlobFromNapiValue(env, argv[PARAM0]);
228 HcfRand *randObj = GetRand();
229 HcfResult res = randObj->setSeed(randObj, seedBlob);
230 if (res != HCF_SUCCESS) {
231 napi_throw(env, GenerateBusinessError(env, res, "set seed failed.", false));
232 LOGE("set seed failed.");
233 }
234 return nullptr;
235 }
236
NapiGenerateRandom(napi_env env,napi_callback_info info)237 static napi_value NapiGenerateRandom(napi_env env, napi_callback_info info)
238 {
239 napi_value thisVar = nullptr;
240 napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
241 NapiRand *randObj = nullptr;
242 napi_unwrap(env, thisVar, reinterpret_cast<void **>(&randObj));
243 if (randObj == nullptr) {
244 LOGE("randObj is nullptr!");
245 return NapiGetNull(env);
246 }
247 return randObj->GenerateRandom(env, info);
248 }
249
NapiSetSeed(napi_env env,napi_callback_info info)250 static napi_value NapiSetSeed(napi_env env, napi_callback_info info)
251 {
252 napi_value thisVar = nullptr;
253 napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
254 NapiRand *randObj = nullptr;
255 napi_unwrap(env, thisVar, reinterpret_cast<void **>(&randObj));
256 if (randObj == nullptr) {
257 LOGE("randObj is nullptr!");
258 return nullptr;
259 }
260 return randObj->SetSeed(env, info);
261 }
262
RandConstructor(napi_env env,napi_callback_info info)263 napi_value NapiRand::RandConstructor(napi_env env, napi_callback_info info)
264 {
265 napi_value thisVar = nullptr;
266 napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
267 return thisVar;
268 }
269
CreateRand(napi_env env,napi_callback_info info)270 napi_value NapiRand::CreateRand(napi_env env, napi_callback_info info)
271 {
272 HcfRand *randObj = nullptr;
273 HcfResult res = HcfRandCreate(&randObj);
274 if (res != HCF_SUCCESS) {
275 napi_throw(env, GenerateBusinessError(env, res, "create C obj failed.", false));
276 LOGE("create c randObj failed.");
277 return nullptr;
278 }
279 napi_value instance = nullptr;
280 napi_value constructor = nullptr;
281 napi_get_reference_value(env, classRef_, &constructor);
282 napi_new_instance(env, constructor, 0, nullptr, &instance);
283 NapiRand *randNapiObj = new (std::nothrow) NapiRand(randObj);
284 if (randNapiObj == nullptr) {
285 LOGE("create napi obj failed");
286 return nullptr;
287 }
288 napi_wrap(
289 env, instance, randNapiObj,
290 [](napi_env env, void *data, void *hint) {
291 NapiRand *rand = static_cast<NapiRand *>(data);
292 delete rand;
293 return;
294 },
295 nullptr,
296 nullptr);
297 return instance;
298 }
299
DefineRandJSClass(napi_env env,napi_value exports)300 void NapiRand::DefineRandJSClass(napi_env env, napi_value exports)
301 {
302 napi_property_descriptor desc[] = {
303 DECLARE_NAPI_FUNCTION("createRandom", CreateRand),
304 };
305 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
306 napi_property_descriptor classDesc[] = {
307 DECLARE_NAPI_FUNCTION("generateRandom", NapiGenerateRandom),
308 DECLARE_NAPI_FUNCTION("setSeed", NapiSetSeed),
309 };
310 napi_value constructor = nullptr;
311 napi_define_class(env, "Random", NAPI_AUTO_LENGTH, RandConstructor, nullptr,
312 sizeof(classDesc) / sizeof(classDesc[0]), classDesc, &constructor);
313 napi_create_reference(env, constructor, 1, &classRef_);
314 }
315 } // CryptoFramework
316 } // OHOS
317