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_key_agreement.h"
17 
18 #include "securec.h"
19 #include "log.h"
20 #include "memory.h"
21 
22 #include "napi_crypto_framework_defines.h"
23 #include "napi_pri_key.h"
24 #include "napi_pub_key.h"
25 #include "napi_utils.h"
26 
27 namespace OHOS {
28 namespace CryptoFramework {
29 struct KeyAgreementCtx {
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_async_work asyncWork = nullptr;
37     napi_ref keyAgreementRef = nullptr;
38     napi_ref priKeyRef = nullptr;
39     napi_ref pubKeyRef = nullptr;
40 
41     HcfKeyAgreement *keyAgreement = nullptr;
42     HcfPriKey *priKey = nullptr;
43     HcfPubKey *pubKey = nullptr;
44 
45     HcfResult errCode = HCF_SUCCESS;
46     const char *errMsg = nullptr;
47     HcfBlob returnSecret { .data = nullptr, .len = 0 };
48 };
49 
50 thread_local napi_ref NapiKeyAgreement::classRef_ = nullptr;
51 
FreeKeyAgreementCtx(napi_env env,KeyAgreementCtx * ctx)52 static void FreeKeyAgreementCtx(napi_env env, KeyAgreementCtx *ctx)
53 {
54     if (ctx == nullptr) {
55         return;
56     }
57 
58     if (ctx->asyncWork != nullptr) {
59         napi_delete_async_work(env, ctx->asyncWork);
60         ctx->asyncWork = nullptr;
61     }
62 
63     if (ctx->callback != nullptr) {
64         napi_delete_reference(env, ctx->callback);
65         ctx->callback = nullptr;
66     }
67 
68     if (ctx->keyAgreementRef != nullptr) {
69         napi_delete_reference(env, ctx->keyAgreementRef);
70         ctx->keyAgreementRef = nullptr;
71     }
72 
73     if (ctx->priKeyRef != nullptr) {
74         napi_delete_reference(env, ctx->priKeyRef);
75         ctx->priKeyRef = nullptr;
76     }
77 
78     if (ctx->pubKeyRef != nullptr) {
79         napi_delete_reference(env, ctx->pubKeyRef);
80         ctx->pubKeyRef = nullptr;
81     }
82 
83     if (ctx->returnSecret.data != nullptr) {
84         HcfFree(ctx->returnSecret.data);
85         ctx->returnSecret.data = nullptr;
86         ctx->returnSecret.len = 0;
87     }
88 
89     HcfFree(ctx);
90 }
91 
CreateKeyAgreementRef(napi_env env,napi_value thisVar,napi_value priKey,napi_value pubKey,KeyAgreementCtx * ctx)92 static bool CreateKeyAgreementRef(napi_env env, napi_value thisVar, napi_value priKey, napi_value pubKey,
93                                   KeyAgreementCtx *ctx)
94 {
95     if (napi_create_reference(env, thisVar, 1, &ctx->keyAgreementRef) != napi_ok) {
96         LOGE("create key agreement ref failed when derive secret key using key agreement!");
97         return false;
98     }
99 
100     if (napi_create_reference(env, priKey, 1, &ctx->priKeyRef) != napi_ok) {
101         LOGE("create private key ref failed when derive secret key using key agreement!");
102         return false;
103     }
104 
105     if (napi_create_reference(env, pubKey, 1, &ctx->pubKeyRef) != napi_ok) {
106         LOGE("create public key ref failed when derive secret key using key agreement!");
107         return false;
108     }
109 
110     return true;
111 }
112 
BuildKeyAgreementJsCtx(napi_env env,napi_callback_info info,KeyAgreementCtx * ctx)113 static bool BuildKeyAgreementJsCtx(napi_env env, napi_callback_info info, KeyAgreementCtx *ctx)
114 {
115     napi_value thisVar = nullptr;
116     size_t expectedArgc = PARAMS_NUM_THREE;
117     size_t argc = expectedArgc;
118     napi_value argv[PARAMS_NUM_THREE] = { nullptr };
119     napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
120     if (argc != expectedArgc && argc != expectedArgc - 1) {
121         LOGE("wrong argument num. require %zu or %zu arguments. [Argc]: %zu!", expectedArgc - 1, expectedArgc, argc);
122         return false;
123     }
124     ctx->asyncType = isCallback(env, argv[expectedArgc - 1], argc, expectedArgc) ? ASYNC_CALLBACK : ASYNC_PROMISE;
125 
126     NapiKeyAgreement *napiKeyAgreement = nullptr;
127     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiKeyAgreement));
128     if (status != napi_ok || napiKeyAgreement == nullptr) {
129         LOGE("failed to unwrap napi verify obj.");
130         return false;
131     }
132 
133     size_t index = 0;
134     NapiPriKey *napiPriKey = nullptr;
135     status = napi_unwrap(env, argv[index], reinterpret_cast<void **>(&napiPriKey));
136     if (status != napi_ok || napiPriKey == nullptr) {
137         LOGE("failed to unwrap priKey verify obj.");
138         return false;
139     }
140 
141     index++;
142     NapiPubKey *napiPubKey = nullptr;
143     status = napi_unwrap(env, argv[index], reinterpret_cast<void **>(&napiPubKey));
144     if (status != napi_ok || napiPubKey == nullptr) {
145         LOGE("failed to unwrap napi pubKey obj.");
146         return false;
147     }
148 
149     ctx->keyAgreement = napiKeyAgreement->GetKeyAgreement();
150     ctx->priKey = napiPriKey->GetPriKey();
151     ctx->pubKey = napiPubKey->GetPubKey();
152 
153     if (!CreateKeyAgreementRef(env, thisVar, argv[PARAM0], argv[PARAM1], ctx)) {
154         return false;
155     }
156 
157     if (ctx->asyncType == ASYNC_PROMISE) {
158         napi_create_promise(env, &ctx->deferred, &ctx->promise);
159         return true;
160     } else {
161         return GetCallbackFromJSParams(env, argv[expectedArgc - 1], &ctx->callback);
162     }
163 }
164 
ReturnCallbackResult(napi_env env,KeyAgreementCtx * ctx,napi_value result)165 static void ReturnCallbackResult(napi_env env, KeyAgreementCtx *ctx, napi_value result)
166 {
167     napi_value businessError = nullptr;
168     if (ctx->errCode != HCF_SUCCESS) {
169         businessError = GenerateBusinessError(env, ctx->errCode, ctx->errMsg);
170     }
171 
172     napi_value params[ARGS_SIZE_TWO] = { businessError, result };
173 
174     napi_value func = nullptr;
175     napi_get_reference_value(env, ctx->callback, &func);
176 
177     napi_value recv = nullptr;
178     napi_value callFuncRet = nullptr;
179     napi_get_undefined(env, &recv);
180     napi_call_function(env, recv, func, ARGS_SIZE_TWO, params, &callFuncRet);
181 }
182 
ReturnPromiseResult(napi_env env,KeyAgreementCtx * ctx,napi_value result)183 static void ReturnPromiseResult(napi_env env, KeyAgreementCtx *ctx, napi_value result)
184 {
185     if (ctx->errCode == HCF_SUCCESS) {
186         napi_resolve_deferred(env, ctx->deferred, result);
187     } else {
188         napi_reject_deferred(env, ctx->deferred,
189             GenerateBusinessError(env, ctx->errCode, ctx->errMsg));
190     }
191 }
192 
KeyAgreementAsyncWorkProcess(napi_env env,void * data)193 static void KeyAgreementAsyncWorkProcess(napi_env env, void *data)
194 {
195     KeyAgreementCtx *ctx = static_cast<KeyAgreementCtx *>(data);
196 
197     ctx->errCode = ctx->keyAgreement->generateSecret(ctx->keyAgreement,
198         ctx->priKey, ctx->pubKey, &ctx->returnSecret);
199     if (ctx->errCode != HCF_SUCCESS) {
200         LOGD("[error] generate secret fail.");
201         ctx->errMsg = "generate secret fail.";
202     }
203 }
204 
KeyAgreementAsyncWorkReturn(napi_env env,napi_status status,void * data)205 static void KeyAgreementAsyncWorkReturn(napi_env env, napi_status status, void *data)
206 {
207     KeyAgreementCtx *ctx = static_cast<KeyAgreementCtx *>(data);
208 
209     napi_value dataBlob = nullptr;
210     if (ctx->errCode == HCF_SUCCESS) {
211         dataBlob = ConvertBlobToNapiValue(env, &ctx->returnSecret);
212     }
213 
214     if (ctx->asyncType == ASYNC_CALLBACK) {
215         ReturnCallbackResult(env, ctx, dataBlob);
216     } else {
217         ReturnPromiseResult(env, ctx, dataBlob);
218     }
219     FreeKeyAgreementCtx(env, ctx);
220 }
221 
NewKeyAgreementAsyncWork(napi_env env,KeyAgreementCtx * ctx)222 static napi_value NewKeyAgreementAsyncWork(napi_env env, KeyAgreementCtx *ctx)
223 {
224     napi_value resourceName = nullptr;
225     napi_create_string_utf8(env, "generateSecret", NAPI_AUTO_LENGTH, &resourceName);
226 
227     napi_create_async_work(
228         env, nullptr, resourceName,
229         [](napi_env env, void *data) {
230             KeyAgreementAsyncWorkProcess(env, data);
231             return;
232         },
233         [](napi_env env, napi_status status, void *data) {
234             KeyAgreementAsyncWorkReturn(env, status, data);
235             return;
236         },
237         static_cast<void *>(ctx),
238         &ctx->asyncWork);
239 
240     napi_queue_async_work(env, ctx->asyncWork);
241     if (ctx->asyncType == ASYNC_PROMISE) {
242         return ctx->promise;
243     } else {
244         return NapiGetNull(env);
245     }
246 }
247 
NapiKeyAgreement(HcfKeyAgreement * keyAgreement)248 NapiKeyAgreement::NapiKeyAgreement(HcfKeyAgreement *keyAgreement)
249 {
250     this->keyAgreement_ = keyAgreement;
251 }
252 
~NapiKeyAgreement()253 NapiKeyAgreement::~NapiKeyAgreement()
254 {
255     HcfObjDestroy(this->keyAgreement_);
256 }
257 
GetKeyAgreement()258 HcfKeyAgreement *NapiKeyAgreement::GetKeyAgreement()
259 {
260     return this->keyAgreement_;
261 }
262 
JsGenerateSecret(napi_env env,napi_callback_info info)263 napi_value NapiKeyAgreement::JsGenerateSecret(napi_env env, napi_callback_info info)
264 {
265     KeyAgreementCtx *ctx = static_cast<KeyAgreementCtx *>(HcfMalloc(sizeof(KeyAgreementCtx), 0));
266     if (ctx == nullptr) {
267         napi_throw(env, GenerateBusinessError(env, HCF_ERR_MALLOC, "create context fail."));
268         LOGE("create context fail.");
269         return nullptr;
270     }
271 
272     if (!BuildKeyAgreementJsCtx(env, info, ctx)) {
273         napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "build context fail."));
274         LOGE("build context fail.");
275         FreeKeyAgreementCtx(env, ctx);
276         return nullptr;
277     }
278 
279     return NewKeyAgreementAsyncWork(env, ctx);
280 }
281 
GetPriKeyAndPubKeyFromParam(napi_env env,napi_value priKeyParam,napi_value pubKeyParam,NapiPriKey ** napiPriKey,NapiPubKey ** napiPubKey)282 static HcfResult GetPriKeyAndPubKeyFromParam(napi_env env, napi_value priKeyParam,  napi_value pubKeyParam,
283     NapiPriKey **napiPriKey, NapiPubKey **napiPubKey)
284 {
285     napi_status status = napi_unwrap(env, priKeyParam, reinterpret_cast<void **>(napiPriKey));
286     if (status != napi_ok) {
287         LOGE("failed to unwrap priKey verify obj.");
288         return HCF_ERR_NAPI;
289     }
290 
291     if (*napiPriKey == nullptr) {
292         LOGE("priKey param is nullptr.");
293         return HCF_ERR_NAPI;
294     }
295 
296     status = napi_unwrap(env, pubKeyParam, reinterpret_cast<void **>(napiPubKey));
297     if (status != napi_ok) {
298         LOGE("failed to unwrap pubKey verify obj.");
299         return HCF_ERR_NAPI;
300     }
301 
302     if (*napiPubKey == nullptr) {
303         LOGE("pubKey param is nullptr.");
304         return HCF_ERR_NAPI;
305     }
306 
307     return HCF_SUCCESS;
308 }
309 
JsGenerateSecretSync(napi_env env,napi_callback_info info)310 napi_value NapiKeyAgreement::JsGenerateSecretSync(napi_env env, napi_callback_info info)
311 {
312     napi_value thisVar = nullptr;
313     size_t argc = PARAMS_NUM_TWO;
314     napi_value argv[PARAMS_NUM_TWO] = { nullptr };
315     napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
316     if (argc != PARAMS_NUM_TWO) {
317         LOGE("wrong argument num. require %d arguments. [Argc]: %zu!", PARAMS_NUM_TWO, argc);
318         napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "wrong argument num."));
319         return nullptr;
320     }
321 
322     NapiKeyAgreement *napiKeyAgreement = nullptr;
323     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiKeyAgreement));
324     if (status != napi_ok || napiKeyAgreement == nullptr) {
325         LOGE("failed to unwrap napi verify obj.");
326         napi_throw(env, GenerateBusinessError(env, HCF_ERR_NAPI, "failed to unwrap napi verify obj."));
327         return nullptr;
328     }
329 
330     NapiPriKey *napiPriKey = nullptr;
331     NapiPubKey *napiPubKey = nullptr;
332     HcfResult ret = GetPriKeyAndPubKeyFromParam(env, argv[PARAM0], argv[PARAM1], &napiPriKey, &napiPubKey);
333     if (ret != HCF_SUCCESS) {
334         napi_throw(env, GenerateBusinessError(env, ret, "failed to parse priKey or pubKey."));
335         return nullptr;
336     }
337 
338     HcfKeyAgreement *keyAgreement = napiKeyAgreement->GetKeyAgreement();
339     HcfPriKey *priKey = napiPriKey->GetPriKey();
340     HcfPubKey *pubKey = napiPubKey->GetPubKey();
341     HcfBlob returnSecret = { .data = nullptr, .len = 0 };
342     ret = keyAgreement->generateSecret(keyAgreement, priKey, pubKey, &returnSecret);
343     if (ret != HCF_SUCCESS) {
344         LOGE("generate secret fail.");
345         napi_throw(env, GenerateBusinessError(env, ret, "generate secret fail."));
346         return nullptr;
347     }
348 
349     napi_value instance = nullptr;
350     ret = ConvertDataBlobToNapiValue(env, &returnSecret, &instance);
351     HcfBlobDataClearAndFree(&returnSecret);
352     if (ret != HCF_SUCCESS) {
353         LOGE("key agreement convert dataBlob to napi_value failed!");
354         napi_throw(env, GenerateBusinessError(env, ret, "key agreement convert dataBlob to napi_value failed!"));
355         return nullptr;
356     }
357 
358     return instance;
359 }
360 
KeyAgreementConstructor(napi_env env,napi_callback_info info)361 napi_value NapiKeyAgreement::KeyAgreementConstructor(napi_env env, napi_callback_info info)
362 {
363     napi_value thisVar = nullptr;
364     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
365     return thisVar;
366 }
367 
CreateJsKeyAgreement(napi_env env,napi_callback_info info)368 napi_value NapiKeyAgreement::CreateJsKeyAgreement(napi_env env, napi_callback_info info)
369 {
370     LOGD("Enter CreateJsKeyAgreement...");
371     size_t expectedArgc = PARAMS_NUM_ONE;
372     size_t argc = PARAMS_NUM_ONE;
373     napi_value argv[PARAMS_NUM_ONE] = { nullptr };
374     napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
375     if (argc != expectedArgc) {
376         napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "The input args num is invalid."));
377         LOGE("The input args num is invalid.");
378         return nullptr;
379     }
380 
381     napi_value instance = nullptr;
382     napi_value constructor = nullptr;
383     napi_get_reference_value(env, classRef_, &constructor);
384     napi_new_instance(env, constructor, argc, argv, &instance);
385 
386     std::string algName;
387     if (!GetStringFromJSParams(env, argv[0], algName)) {
388         napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "Get algName is invalid."));
389         return nullptr;
390     }
391 
392     HcfKeyAgreement *keyAgreement = nullptr;
393     HcfResult res = HcfKeyAgreementCreate(algName.c_str(), &keyAgreement);
394     if (res != HCF_SUCCESS) {
395         napi_throw(env, GenerateBusinessError(env, res, "create c keyAgreement fail."));
396         LOGE("create c keyAgreement fail.");
397         return nullptr;
398     }
399 
400     NapiKeyAgreement *napiKeyAgreement = new (std::nothrow) NapiKeyAgreement(keyAgreement);
401     if (napiKeyAgreement == nullptr) {
402         napi_throw(env, GenerateBusinessError(env, HCF_ERR_MALLOC, "new napi key agreement failed."));
403         LOGE("new napi key agreement failed");
404         HcfObjDestroy(keyAgreement);
405         return nullptr;
406     }
407 
408     napi_status status = napi_wrap(env, instance, napiKeyAgreement,
409         [](napi_env env, void *data, void *hint) {
410             NapiKeyAgreement *napiKeyAgreement = static_cast<NapiKeyAgreement *>(data);
411             delete napiKeyAgreement;
412             return;
413         }, nullptr, nullptr);
414     if (status != napi_ok) {
415         napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "failed to wrap napiKeyAgreement obj!"));
416         LOGE("failed to wrap napiKeyAgreement obj!");
417         delete napiKeyAgreement;
418         return nullptr;
419     }
420 
421     return instance;
422 }
423 
JsGetAlgorithm(napi_env env,napi_callback_info info)424 napi_value NapiKeyAgreement::JsGetAlgorithm(napi_env env, napi_callback_info info)
425 {
426     napi_value thisVar = nullptr;
427     NapiKeyAgreement *napiKeyAgreement = nullptr;
428     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
429     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiKeyAgreement));
430     if (status != napi_ok || napiKeyAgreement == nullptr) {
431         napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "failed to unwrap napiKeyAgreement obj!"));
432         LOGE("failed to unwrap napiKeyAgreement obj!");
433         return nullptr;
434     }
435     HcfKeyAgreement *keyAgreement = napiKeyAgreement->GetKeyAgreement();
436 
437     const char *algo = keyAgreement->getAlgoName(keyAgreement);
438     napi_value instance = nullptr;
439     napi_create_string_utf8(env, algo, NAPI_AUTO_LENGTH, &instance);
440     return instance;
441 }
442 
DefineKeyAgreementJSClass(napi_env env,napi_value exports)443 void NapiKeyAgreement::DefineKeyAgreementJSClass(napi_env env, napi_value exports)
444 {
445     napi_property_descriptor desc[] = {
446         DECLARE_NAPI_FUNCTION("createKeyAgreement", NapiKeyAgreement::CreateJsKeyAgreement),
447     };
448     napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
449 
450     napi_property_descriptor classDesc[] = {
451         DECLARE_NAPI_FUNCTION("generateSecret", NapiKeyAgreement::JsGenerateSecret),
452         DECLARE_NAPI_FUNCTION("generateSecretSync", NapiKeyAgreement::JsGenerateSecretSync),
453         {.utf8name = "algName", .getter = NapiKeyAgreement::JsGetAlgorithm},
454     };
455     napi_value constructor = nullptr;
456     napi_define_class(env, "KeyAgreement", NAPI_AUTO_LENGTH, NapiKeyAgreement::KeyAgreementConstructor, nullptr,
457         sizeof(classDesc) / sizeof(classDesc[0]), classDesc, &constructor);
458     napi_create_reference(env, constructor, 1, &classRef_);
459 }
460 } // CryptoFramework
461 } // OHOS
462