• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 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_kdf.h"
17 
18 #include "securec.h"
19 #include "memory.h"
20 
21 #include "napi_utils.h"
22 #include "napi_crypto_framework_defines.h"
23 #include "detailed_pbkdf2_params.h"
24 
25 #define PBKDF2_ALG_SIZE 6
26 
27 namespace OHOS {
28 namespace CryptoFramework {
29 thread_local napi_ref NapiKdf::classRef_ = nullptr;
30 
31 struct KdfCtx {
32     napi_env env = nullptr;
33 
34     AsyncType asyncType = ASYNC_CALLBACK;
35     napi_ref callback = nullptr;
36     napi_deferred deferred = nullptr;
37     napi_value promise = nullptr;
38     napi_async_work asyncWork = nullptr;
39 
40     HcfResult errCode = HCF_SUCCESS;
41     const char *errMsg = nullptr;
42     HcfKdfParamsSpec *paramsSpec = nullptr;
43     HcfKdf *kdf = nullptr;
44 };
45 
FreeKdfParamsSpec(HcfKdfParamsSpec * params)46 static void FreeKdfParamsSpec(HcfKdfParamsSpec *params)
47 {
48     if (params == nullptr) {
49         return;
50     }
51     if (PBKDF2_ALG_NAME.compare(params->algName) == 0) {
52         HcfPBKDF2ParamsSpec *tmp = reinterpret_cast<HcfPBKDF2ParamsSpec *>(params);
53         HcfBlobDataClearAndFree(&(tmp->password));
54         HcfBlobDataClearAndFree(&(tmp->salt));
55         HcfBlobDataClearAndFree(&(tmp->output));
56         tmp->base.algName = nullptr;
57     }
58     HcfFree(params);
59 }
60 
FreeCryptoFwkCtx(napi_env env,KdfCtx * context)61 static void FreeCryptoFwkCtx(napi_env env, KdfCtx *context)
62 {
63     if (context == nullptr) {
64         return;
65     }
66     if (context->asyncWork != nullptr) {
67         napi_delete_async_work(env, context->asyncWork);
68         context->asyncWork = nullptr;
69     }
70 
71     if (context->callback != nullptr) {
72         napi_delete_reference(env, context->callback);
73         context->callback = nullptr;
74     }
75     FreeKdfParamsSpec(context->paramsSpec);
76     context->paramsSpec = nullptr;
77     context->errMsg = nullptr;
78     context->kdf = nullptr;
79     HcfFree(context);
80 }
81 
ReturnCallbackResult(napi_env env,KdfCtx * context,napi_value result)82 static void ReturnCallbackResult(napi_env env, KdfCtx *context, napi_value result)
83 {
84     napi_value businessError = nullptr;
85     if (context->errCode != HCF_SUCCESS) {
86         businessError = GenerateBusinessError(env, context->errCode, context->errMsg);
87     }
88     napi_value params[ARGS_SIZE_TWO] = { businessError, result };
89 
90     napi_value func = nullptr;
91     napi_get_reference_value(env, context->callback, &func);
92 
93     napi_value recv = nullptr;
94     napi_value callFuncRet = nullptr;
95     napi_get_undefined(env, &recv);
96     napi_call_function(env, recv, func, ARGS_SIZE_TWO, params, &callFuncRet);
97 }
98 
ReturnPromiseResult(napi_env env,KdfCtx * context,napi_value result)99 static void ReturnPromiseResult(napi_env env, KdfCtx *context, napi_value result)
100 {
101     if (context->errCode == HCF_SUCCESS) {
102         napi_resolve_deferred(env, context->deferred, result);
103     } else {
104         napi_reject_deferred(env, context->deferred,
105             GenerateBusinessError(env, context->errCode, context->errMsg));
106     }
107 }
108 
KdfGenSecretExecute(napi_env env,void * data)109 static void KdfGenSecretExecute(napi_env env, void *data)
110 {
111     KdfCtx *context = static_cast<KdfCtx *>(data);
112     HcfKdf *kdf = context->kdf;
113     context->errCode = kdf->generateSecret(kdf, context->paramsSpec);
114     if (context->errCode != HCF_SUCCESS) {
115         LOGD("[error] KDF generateSecret failed!");
116         context->errMsg = "KDF generateSecret failed";
117         return;
118     }
119 }
120 
KdfGenSecretComplete(napi_env env,napi_status status,void * data)121 static void KdfGenSecretComplete(napi_env env, napi_status status, void *data)
122 {
123     KdfCtx *context = static_cast<KdfCtx *>(data);
124     napi_value returnBlob = nullptr;
125     if (PBKDF2_ALG_NAME.compare(context->paramsSpec->algName) == 0) {
126         HcfPBKDF2ParamsSpec *params = reinterpret_cast<HcfPBKDF2ParamsSpec *>(context->paramsSpec);
127         returnBlob = ConvertBlobToNapiValue(env, &(params->output));
128     }
129     if (returnBlob == nullptr) {
130         LOGE("returnOutBlob is nullptr!");
131         returnBlob = NapiGetNull(env);
132     }
133     if (context->asyncType == ASYNC_CALLBACK) {
134         ReturnCallbackResult(env, context, returnBlob);
135     } else {
136         ReturnPromiseResult(env, context, returnBlob);
137     }
138     FreeCryptoFwkCtx(env, context);
139 }
140 
GetInt32FromPBKDF2Params(napi_env env,napi_value arg,const std::string & name,int32_t & retInt)141 static bool GetInt32FromPBKDF2Params(napi_env env, napi_value arg, const std::string &name, int32_t &retInt)
142 {
143     // int attribute
144     napi_value dataInt = nullptr;
145     napi_valuetype valueType = napi_undefined;
146     napi_status status = napi_get_named_property(env, arg, name.c_str(), &dataInt);
147     napi_typeof(env, dataInt, &valueType);
148     if ((status != napi_ok) || (dataInt == nullptr) || (valueType == napi_undefined)) {
149         LOGE("failed to get valid napi int");
150         return false;
151     }
152     return GetInt32FromJSParams(env, dataInt, retInt);
153 }
154 
GetCharArrayFromUint8Arr(napi_env env,napi_value data,HcfBlob * retBlob)155 static bool GetCharArrayFromUint8Arr(napi_env env, napi_value data, HcfBlob *retBlob)
156 {
157     size_t length = 0;
158     size_t offset = 0;
159     void *rawData = nullptr;
160     napi_value arrayBuffer = nullptr;
161     napi_typedarray_type arrayType;
162     // Warning: Do not release the rawData returned by this interface because the rawData is managed by VM.
163     napi_status status = napi_get_typedarray_info(env, data, &arrayType, &length,
164         reinterpret_cast<void **>(&rawData), &arrayBuffer, &offset);
165     if ((status != napi_ok)) {
166         LOGE("failed to get valid rawData.");
167         return false;
168     }
169     if (arrayType != napi_uint8_array) {
170         LOGE("input data is not uint8 array.");
171         return false;
172     }
173     // input empty uint8Arr, ex: new Uint8Arr(), the length is 0 and rawData is nullptr;
174     if ((length == 0) || (rawData == nullptr)) {
175         LOGD("napi Uint8Arr is null");
176         return true;
177     }
178     if (length > INT_MAX) {
179         LOGE("Beyond the size");
180         return false;
181     }
182     uint8_t *tmp = static_cast<uint8_t *>(HcfMalloc(length, 0));
183     if (tmp == nullptr) {
184         LOGE("malloc blob data failed!");
185         return false;
186     }
187     (void)memcpy_s(tmp, length, rawData, length);
188     retBlob->data = tmp;
189     retBlob->len = length;
190     return true;
191 }
192 
GetCharArrayFromJsString(napi_env env,napi_value arg,HcfBlob * retPassword)193 static bool GetCharArrayFromJsString(napi_env env, napi_value arg, HcfBlob *retPassword)
194 {
195     size_t length = 0;
196     if (napi_get_value_string_utf8(env, arg, nullptr, 0, &length) != napi_ok) {
197         LOGE("can not get char string length");
198         return false;
199     }
200     if (length > INT_MAX) {
201         LOGE("password length should not exceed INT_MAX");
202         return false;
203     }
204     if (length == 0) {
205         LOGD("empty string");
206         return true;
207     }
208     char *tmpPassword = static_cast<char *>(HcfMalloc(length + 1, 0));
209     if (tmpPassword == nullptr) {
210         LOGE("malloc string failed");
211         return false;
212     }
213     if (napi_get_value_string_utf8(env, arg, tmpPassword, (length + 1), &length) != napi_ok) {
214         LOGE("can not get char string value");
215         HcfFree(tmpPassword);
216         return false;
217     }
218     retPassword->data = reinterpret_cast<uint8_t *>(tmpPassword);
219     retPassword->len = length;
220     return true;
221 }
222 
GetPasswordFromPBKDF2Params(napi_env env,napi_value arg,const std::string & name,HcfBlob * retPassword)223 static bool GetPasswordFromPBKDF2Params(napi_env env, napi_value arg, const std::string &name, HcfBlob *retPassword)
224 {
225     napi_value data = nullptr;
226     napi_valuetype valueType = napi_undefined;
227     napi_status status = napi_get_named_property(env, arg, name.c_str(), &data);
228     napi_typeof(env, data, &valueType);
229     if ((status != napi_ok) || (data == nullptr) || (valueType == napi_undefined)) {
230         LOGE("failed to get valid password");
231         return false;
232     }
233     if (valueType == napi_string) {
234         if (GetCharArrayFromJsString(env, data, retPassword) != true) {
235             LOGE("get char string failed");
236             return false;
237         }
238     } else {
239         if (GetCharArrayFromUint8Arr(env, data, retPassword) != true) {
240             LOGE("get uint8arr failed");
241             return false;
242         }
243     }
244     return true;
245 }
246 
GetBlobFromPBKDF2ParamsSpec(napi_env env,napi_value arg,const std::string & name)247 static HcfBlob *GetBlobFromPBKDF2ParamsSpec(napi_env env, napi_value arg, const std::string &name)
248 {
249     // get uint8Array attribute
250     napi_value data = nullptr;
251     napi_valuetype valueType = napi_undefined;
252     napi_status status = napi_get_named_property(env, arg, name.c_str(), &data);
253     napi_typeof(env, data, &valueType);
254     if ((status != napi_ok) || (data == nullptr) || (valueType == napi_undefined)) {
255         LOGE("failed to get valid salt");
256         return nullptr;
257     }
258 
259     return GetBlobFromNapiUint8Arr(env, data);
260 }
261 
SetPBKDF2ParamsSpecAttribute(int iter,const HcfBlob & out,HcfBlob * salt,const HcfBlob & password,HcfPBKDF2ParamsSpec * tmp)262 static void SetPBKDF2ParamsSpecAttribute(int iter, const HcfBlob &out, HcfBlob *salt, const HcfBlob &password,
263     HcfPBKDF2ParamsSpec *tmp)
264 {
265     tmp->iterations = iter;
266     tmp->output = out;
267     tmp->salt.data = salt->data;
268     tmp->salt.len = salt->len;
269     tmp->password = password;
270     tmp->base.algName = PBKDF2_ALG_NAME.c_str();
271 }
272 
GetPBKDF2ParamsSpec(napi_env env,napi_value arg,HcfKdfParamsSpec ** params)273 static bool GetPBKDF2ParamsSpec(napi_env env, napi_value arg, HcfKdfParamsSpec **params)
274 {
275     // get attribute from params
276     // int attribute
277     int iter = -1;
278     int keySize = -1;
279     if (!GetInt32FromPBKDF2Params(env, arg, PBKDF2_PARAMS_ITER, iter) ||
280         !GetInt32FromPBKDF2Params(env, arg, PBKDF2_PARAMS_KEY_SIZE, keySize)) {
281         LOGE("failed to get valid num");
282         return false;
283     }
284     if (iter <= 0 || keySize <= 0) {
285         LOGE("iter and keySize should larger than 0");
286         return false;
287     }
288     HcfBlob out = { .data = static_cast<uint8_t *>(HcfMalloc(keySize, 0)), .len = keySize };
289     if (out.data == nullptr) {
290         LOGE("output malloc failed!");
291         return false;
292     }
293     HcfBlob tmpPassword = { .data = nullptr, .len = 0 };
294     HcfBlob *salt = nullptr;
295     HcfPBKDF2ParamsSpec *tmp = nullptr;
296     do {
297         // get password
298         if (!GetPasswordFromPBKDF2Params(env, arg, PBKDF2_PARAMS_PASSWORD, &tmpPassword)) {
299             LOGE("failed to get password");
300             break;
301         }
302         // get salt attribute
303         salt = GetBlobFromPBKDF2ParamsSpec(env, arg, PBKDF2_PARAMS_SALT);
304         if (salt == nullptr) {
305             LOGE("fail to get salt");
306             break;
307         }
308         // malloc params
309         tmp = static_cast<HcfPBKDF2ParamsSpec *>(HcfMalloc(sizeof(HcfPBKDF2ParamsSpec), 0));
310         if (tmp == nullptr) {
311             LOGE("pbkdf2 spec malloc failed!");
312             break;
313         }
314         SetPBKDF2ParamsSpecAttribute(iter, out, salt, tmpPassword, tmp);
315         // only need the data and data length of the salt, so free the blob pointer.
316         HcfFree(salt);
317         *params = reinterpret_cast<HcfKdfParamsSpec *>(tmp);
318         return true;
319     } while (0);
320     HcfBlobDataClearAndFree(&tmpPassword);
321     HcfBlobDataClearAndFree(salt);
322     HcfFree(salt);
323     HcfFree(out.data);
324     return false;
325 }
326 
GetKdfParamsSpec(napi_env env,napi_value arg,HcfKdfParamsSpec ** params)327 static bool GetKdfParamsSpec(napi_env env, napi_value arg, HcfKdfParamsSpec **params)
328 {
329     napi_value data = nullptr;
330     napi_valuetype valueType = napi_undefined;
331     if ((env == nullptr) || (arg == nullptr) || (params == nullptr)) {
332         LOGE("Invalid params!");
333         return false;
334     }
335 
336     napi_status status = napi_get_named_property(env, arg, ALGO_PARAMS.c_str(), &data);
337     napi_typeof(env, data, &valueType);
338     if ((status != napi_ok) || (data == nullptr) || (valueType == napi_undefined)) {
339         LOGE("failed to get valid algo name!");
340         return false;
341     }
342     std::string algoName;
343     if (!GetStringFromJSParams(env, data, algoName)) {
344         LOGE("GetStringFromJSParams failed!");
345         return false;
346     }
347     if (algoName.compare(PBKDF2_ALG_NAME) == 0) {
348         return GetPBKDF2ParamsSpec(env, arg, params);
349     } else {
350         LOGE("Not support that alg");
351         return false;
352     }
353 }
354 
BuildKdfGenSecretCtx(napi_env env,napi_callback_info info,KdfCtx * context)355 static bool BuildKdfGenSecretCtx(napi_env env, napi_callback_info info, KdfCtx *context)
356 {
357     napi_value thisVar = nullptr;
358     size_t expectedArgsCount = ARGS_SIZE_TWO;
359     size_t argc = expectedArgsCount;
360     napi_value argv[ARGS_SIZE_TWO] = { nullptr };
361     napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
362     if ((argc != expectedArgsCount) && (argc != expectedArgsCount - CALLBACK_SIZE)) {
363         LOGE("The arguments count is not expected!");
364         return false;
365     }
366 
367     NapiKdf *napiKdf = nullptr;
368     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiKdf));
369     if (status != napi_ok || napiKdf == nullptr) {
370         LOGE("failed to unwrap NapiKdf obj!");
371         return false;
372     }
373 
374     context->kdf = napiKdf->GetKdf();
375     if (!GetKdfParamsSpec(env, argv[PARAM0], &(context->paramsSpec))) {
376         LOGE("get kdf paramsspec failed!");
377         return false;
378     }
379     context->asyncType = isCallback(env, argv[expectedArgsCount - 1], argc, expectedArgsCount) ?
380         ASYNC_CALLBACK : ASYNC_PROMISE;
381 
382     if (context->asyncType == ASYNC_PROMISE) {
383         napi_create_promise(env, &context->deferred, &context->promise);
384         return true;
385     } else {
386         return GetCallbackFromJSParams(env, argv[PARAM1], &context->callback);
387     }
388 }
389 
NewKdfJsGenSecretAsyncWork(napi_env env,KdfCtx * context)390 static napi_value NewKdfJsGenSecretAsyncWork(napi_env env, KdfCtx *context)
391 {
392     napi_create_async_work(
393         env, nullptr, GetResourceName(env, "KdfGenerateSecret"),
394         [](napi_env env, void *data) {
395             KdfGenSecretExecute(env, data);
396             return;
397         },
398         [](napi_env env, napi_status status, void *data) {
399             KdfGenSecretComplete(env, status, data);
400             return;
401         },
402         static_cast<void *>(context),
403         &context->asyncWork);
404 
405     napi_queue_async_work(env, context->asyncWork);
406     if (context->asyncType == ASYNC_PROMISE) {
407         return context->promise;
408     } else {
409         return NapiGetNull(env);
410     }
411 }
412 
NapiKdf(HcfKdf * kdfObj)413 NapiKdf::NapiKdf(HcfKdf *kdfObj)
414 {
415     this->kdf = kdfObj;
416 }
417 
~NapiKdf()418 NapiKdf::~NapiKdf()
419 {
420     HcfObjDestroy(this->kdf);
421 }
422 
GetKdf() const423 HcfKdf *NapiKdf::GetKdf() const
424 {
425     return this->kdf;
426 }
427 
JsKdfGenerateSecret(napi_env env,napi_callback_info info)428 napi_value NapiKdf::JsKdfGenerateSecret(napi_env env, napi_callback_info info)
429 {
430     KdfCtx *context = static_cast<KdfCtx *>(HcfMalloc(sizeof(KdfCtx), 0));
431     if (context == nullptr) {
432         napi_throw(env, GenerateBusinessError(env, HCF_ERR_MALLOC, "malloc context failed"));
433         LOGE("malloc context failed!");
434         return nullptr;
435     }
436 
437     if (!BuildKdfGenSecretCtx(env, info, context)) {
438         napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "build context fail."));
439         LOGE("build context fail.");
440         FreeCryptoFwkCtx(env, context);
441         return nullptr;
442     }
443 
444     return NewKdfJsGenSecretAsyncWork(env, context);
445 }
446 
JsGetAlgorithm(napi_env env,napi_callback_info info)447 napi_value NapiKdf::JsGetAlgorithm(napi_env env, napi_callback_info info)
448 {
449     napi_value thisVar = nullptr;
450     NapiKdf *napiKdf = nullptr;
451 
452     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
453     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiKdf));
454     if (status != napi_ok || napiKdf == nullptr) {
455         LOGE("failed to unwrap NapiKdf obj!");
456         return nullptr;
457     }
458 
459     HcfKdf *kdf = napiKdf->GetKdf();
460     if (kdf == nullptr) {
461         LOGE("fail to get kdf obj!");
462         return nullptr;
463     }
464 
465     const char *algoName = kdf->getAlgorithm(kdf);
466     napi_value instance = nullptr;
467     napi_create_string_utf8(env, algoName, NAPI_AUTO_LENGTH, &instance);
468     return instance;
469 }
470 
KdfConstructor(napi_env env,napi_callback_info info)471 napi_value NapiKdf::KdfConstructor(napi_env env, napi_callback_info info)
472 {
473     napi_value thisVar = nullptr;
474     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
475     return thisVar;
476 }
477 
CreateJsKdf(napi_env env,napi_callback_info info)478 napi_value NapiKdf::CreateJsKdf(napi_env env, napi_callback_info info)
479 {
480     LOGD("Enter CreateKdf...");
481     size_t expectedArgc = ARGS_SIZE_ONE;
482     size_t argc = expectedArgc;
483     napi_value argv[ARGS_SIZE_ONE] = { nullptr };
484     napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
485     if (argc != expectedArgc) {
486         napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "The input args num is invalid."));
487         LOGE("The input args num is invalid.");
488         return nullptr;
489     }
490     std::string algoName;
491     if (!GetStringFromJSParams(env, argv[PARAM0], algoName)) {
492         napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "Failed to get algorithm."));
493         LOGE("Failed to get algorithm.");
494         return nullptr;
495     }
496     HcfKdf *kdf = nullptr;
497     HcfResult res = HcfKdfCreate(algoName.c_str(), &kdf);
498     if (res != HCF_SUCCESS) {
499         napi_throw(env, GenerateBusinessError(env, res, "create C obj failed."));
500         LOGE("create c kdf obj failed.");
501         return nullptr;
502     }
503     napi_value instance = nullptr;
504     napi_value constructor = nullptr;
505     napi_get_reference_value(env, classRef_, &constructor);
506     napi_new_instance(env, constructor, 0, nullptr, &instance);
507     NapiKdf *napiKdf = new (std::nothrow) NapiKdf(kdf);
508     if (napiKdf == nullptr) {
509         napi_throw(env, GenerateBusinessError(env, HCF_ERR_MALLOC, "new kdf napi obj failed."));
510         HcfObjDestroy(kdf);
511         LOGE("create kdf napi obj failed");
512         return nullptr;
513     }
514     napi_status status = napi_wrap(env, instance, napiKdf,
515         [](napi_env env, void *data, void *hint) {
516             NapiKdf *kdf = static_cast<NapiKdf *>(data);
517             delete kdf;
518             return;
519         }, nullptr, nullptr);
520     if (status != napi_ok) {
521         napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "failed to wrap NapiKdf obj!"));
522         delete napiKdf;
523         LOGE("failed to wrap NapiKdf obj!");
524         return nullptr;
525     }
526     return instance;
527 }
528 
DefineKdfJSClass(napi_env env,napi_value exports)529 void NapiKdf::DefineKdfJSClass(napi_env env, napi_value exports)
530 {
531     napi_property_descriptor desc[] = {
532         DECLARE_NAPI_FUNCTION("createKdf", NapiKdf::CreateJsKdf),
533     };
534     napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
535     napi_property_descriptor classDesc[] = {
536         DECLARE_NAPI_FUNCTION("generateSecret", NapiKdf::JsKdfGenerateSecret),
537         {.utf8name = "algName", .getter = NapiKdf::JsGetAlgorithm},
538     };
539     napi_value constructor = nullptr;
540     napi_define_class(env, "Kdf", NAPI_AUTO_LENGTH, KdfConstructor, nullptr,
541         sizeof(classDesc) / sizeof(classDesc[0]), classDesc, &constructor);
542     napi_create_reference(env, constructor, 1, &classRef_);
543 }
544 } // CryptoFramework
545 } // OHOS
546