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