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