• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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