• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022-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_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 
38     HcfKeyAgreement *keyAgreement = nullptr;
39     HcfPriKey *priKey = nullptr;
40     HcfPubKey *pubKey = nullptr;
41 
42     HcfResult errCode = HCF_SUCCESS;
43     const char *errMsg = nullptr;
44     HcfBlob returnSecret { .data = nullptr, .len = 0 };
45 };
46 
47 thread_local napi_ref NapiKeyAgreement::classRef_ = nullptr;
48 
FreeKeyAgreementCtx(napi_env env,KeyAgreementCtx * ctx)49 static void FreeKeyAgreementCtx(napi_env env, KeyAgreementCtx *ctx)
50 {
51     if (ctx == nullptr) {
52         return;
53     }
54 
55     if (ctx->asyncWork != nullptr) {
56         napi_delete_async_work(env, ctx->asyncWork);
57         ctx->asyncWork = nullptr;
58     }
59 
60     if (ctx->callback != nullptr) {
61         napi_delete_reference(env, ctx->callback);
62         ctx->callback = nullptr;
63     }
64 
65     if (ctx->returnSecret.data != nullptr) {
66         HcfFree(ctx->returnSecret.data);
67         ctx->returnSecret.data = nullptr;
68         ctx->returnSecret.len = 0;
69     }
70 
71     HcfFree(ctx);
72 }
73 
BuildKeyAgreementJsCtx(napi_env env,napi_callback_info info,KeyAgreementCtx * ctx)74 static bool BuildKeyAgreementJsCtx(napi_env env, napi_callback_info info, KeyAgreementCtx *ctx)
75 {
76     napi_value thisVar = nullptr;
77     size_t expectedArgc = PARAMS_NUM_THREE;
78     size_t argc = expectedArgc;
79     napi_value argv[PARAMS_NUM_THREE] = { nullptr };
80     napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
81     if (argc != expectedArgc && argc != expectedArgc - 1) {
82         LOGE("wrong argument num. require %zu or %zu arguments. [Argc]: %zu!", expectedArgc - 1, expectedArgc, argc);
83         return false;
84     }
85     ctx->asyncType = isCallback(env, argv[expectedArgc - 1], argc, expectedArgc) ? ASYNC_CALLBACK : ASYNC_PROMISE;
86 
87     NapiKeyAgreement *napiKeyAgreement = nullptr;
88     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiKeyAgreement));
89     if (status != napi_ok || napiKeyAgreement == nullptr) {
90         LOGE("failed to unwrap napi verify obj.");
91         return false;
92     }
93 
94     size_t index = 0;
95     NapiPriKey *napiPriKey = nullptr;
96     status = napi_unwrap(env, argv[index], reinterpret_cast<void **>(&napiPriKey));
97     if (status != napi_ok || napiPriKey == nullptr) {
98         LOGE("failed to unwrap priKey verify obj.");
99         return false;
100     }
101 
102     index++;
103     NapiPubKey *napiPubKey = nullptr;
104     status = napi_unwrap(env, argv[index], reinterpret_cast<void **>(&napiPubKey));
105     if (status != napi_ok || napiPubKey == nullptr) {
106         LOGE("failed to unwrap napi pubKey obj.");
107         return false;
108     }
109 
110     ctx->keyAgreement = napiKeyAgreement->GetKeyAgreement();
111     ctx->priKey = napiPriKey->GetPriKey();
112     ctx->pubKey = napiPubKey->GetPubKey();
113 
114     if (ctx->asyncType == ASYNC_PROMISE) {
115         napi_create_promise(env, &ctx->deferred, &ctx->promise);
116         return true;
117     } else {
118         return GetCallbackFromJSParams(env, argv[expectedArgc - 1], &ctx->callback);
119     }
120 }
121 
ReturnCallbackResult(napi_env env,KeyAgreementCtx * ctx,napi_value result)122 static void ReturnCallbackResult(napi_env env, KeyAgreementCtx *ctx, napi_value result)
123 {
124     napi_value businessError = nullptr;
125     if (ctx->errCode != HCF_SUCCESS) {
126         businessError = GenerateBusinessError(env, ctx->errCode, ctx->errMsg);
127     }
128 
129     napi_value params[ARGS_SIZE_TWO] = { businessError, result };
130 
131     napi_value func = nullptr;
132     napi_get_reference_value(env, ctx->callback, &func);
133 
134     napi_value recv = nullptr;
135     napi_value callFuncRet = nullptr;
136     napi_get_undefined(env, &recv);
137     napi_call_function(env, recv, func, ARGS_SIZE_TWO, params, &callFuncRet);
138 }
139 
ReturnPromiseResult(napi_env env,KeyAgreementCtx * ctx,napi_value result)140 static void ReturnPromiseResult(napi_env env, KeyAgreementCtx *ctx, napi_value result)
141 {
142     if (ctx->errCode == HCF_SUCCESS) {
143         napi_resolve_deferred(env, ctx->deferred, result);
144     } else {
145         napi_reject_deferred(env, ctx->deferred,
146             GenerateBusinessError(env, ctx->errCode, ctx->errMsg));
147     }
148 }
149 
KeyAgreementAsyncWorkProcess(napi_env env,void * data)150 static void KeyAgreementAsyncWorkProcess(napi_env env, void *data)
151 {
152     KeyAgreementCtx *ctx = static_cast<KeyAgreementCtx *>(data);
153 
154     ctx->errCode = ctx->keyAgreement->generateSecret(ctx->keyAgreement,
155         ctx->priKey, ctx->pubKey, &ctx->returnSecret);
156     if (ctx->errCode != HCF_SUCCESS) {
157         LOGD("[error] generate secret fail.");
158         ctx->errMsg = "generate secret fail.";
159     }
160 }
161 
KeyAgreementAsyncWorkReturn(napi_env env,napi_status status,void * data)162 static void KeyAgreementAsyncWorkReturn(napi_env env, napi_status status, void *data)
163 {
164     KeyAgreementCtx *ctx = static_cast<KeyAgreementCtx *>(data);
165 
166     napi_value dataBlob = nullptr;
167     if (ctx->errCode == HCF_SUCCESS) {
168         dataBlob = ConvertBlobToNapiValue(env, &ctx->returnSecret);
169     }
170 
171     if (ctx->asyncType == ASYNC_CALLBACK) {
172         ReturnCallbackResult(env, ctx, dataBlob);
173     } else {
174         ReturnPromiseResult(env, ctx, dataBlob);
175     }
176     FreeKeyAgreementCtx(env, ctx);
177 }
178 
NewKeyAgreementAsyncWork(napi_env env,KeyAgreementCtx * ctx)179 static napi_value NewKeyAgreementAsyncWork(napi_env env, KeyAgreementCtx *ctx)
180 {
181     napi_value resourceName = nullptr;
182     napi_create_string_utf8(env, "generateSecret", NAPI_AUTO_LENGTH, &resourceName);
183 
184     napi_create_async_work(
185         env, nullptr, resourceName,
186         [](napi_env env, void *data) {
187             KeyAgreementAsyncWorkProcess(env, data);
188             return;
189         },
190         [](napi_env env, napi_status status, void *data) {
191             KeyAgreementAsyncWorkReturn(env, status, data);
192             return;
193         },
194         static_cast<void *>(ctx),
195         &ctx->asyncWork);
196 
197     napi_queue_async_work(env, ctx->asyncWork);
198     if (ctx->asyncType == ASYNC_PROMISE) {
199         return ctx->promise;
200     } else {
201         return NapiGetNull(env);
202     }
203 }
204 
NapiKeyAgreement(HcfKeyAgreement * keyAgreement)205 NapiKeyAgreement::NapiKeyAgreement(HcfKeyAgreement *keyAgreement)
206 {
207     this->keyAgreement_ = keyAgreement;
208 }
209 
~NapiKeyAgreement()210 NapiKeyAgreement::~NapiKeyAgreement()
211 {
212     HcfObjDestroy(this->keyAgreement_);
213 }
214 
GetKeyAgreement()215 HcfKeyAgreement *NapiKeyAgreement::GetKeyAgreement()
216 {
217     return this->keyAgreement_;
218 }
219 
JsGenerateSecret(napi_env env,napi_callback_info info)220 napi_value NapiKeyAgreement::JsGenerateSecret(napi_env env, napi_callback_info info)
221 {
222     KeyAgreementCtx *ctx = static_cast<KeyAgreementCtx *>(HcfMalloc(sizeof(KeyAgreementCtx), 0));
223     if (ctx == nullptr) {
224         napi_throw(env, GenerateBusinessError(env, HCF_ERR_MALLOC, "create context fail."));
225         LOGE("create context fail.");
226         return nullptr;
227     }
228 
229     if (!BuildKeyAgreementJsCtx(env, info, ctx)) {
230         napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "build context fail."));
231         LOGE("build context fail.");
232         FreeKeyAgreementCtx(env, ctx);
233         return nullptr;
234     }
235 
236     return NewKeyAgreementAsyncWork(env, ctx);
237 }
238 
KeyAgreementConstructor(napi_env env,napi_callback_info info)239 napi_value NapiKeyAgreement::KeyAgreementConstructor(napi_env env, napi_callback_info info)
240 {
241     napi_value thisVar = nullptr;
242     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
243     return thisVar;
244 }
245 
CreateJsKeyAgreement(napi_env env,napi_callback_info info)246 napi_value NapiKeyAgreement::CreateJsKeyAgreement(napi_env env, napi_callback_info info)
247 {
248     LOGD("Enter CreateJsKeyAgreement...");
249     size_t expectedArgc = PARAMS_NUM_ONE;
250     size_t argc = PARAMS_NUM_ONE;
251     napi_value argv[PARAMS_NUM_ONE] = { nullptr };
252     napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
253     if (argc != expectedArgc) {
254         napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "The input args num is invalid."));
255         LOGE("The input args num is invalid.");
256         return nullptr;
257     }
258 
259     napi_value instance = nullptr;
260     napi_value constructor = nullptr;
261     napi_get_reference_value(env, classRef_, &constructor);
262     napi_new_instance(env, constructor, argc, argv, &instance);
263 
264     std::string algName;
265     if (!GetStringFromJSParams(env, argv[0], algName)) {
266         napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "Get algName is invalid."));
267         return nullptr;
268     }
269 
270     HcfKeyAgreement *keyAgreement = nullptr;
271     HcfResult res = HcfKeyAgreementCreate(algName.c_str(), &keyAgreement);
272     if (res != HCF_SUCCESS) {
273         napi_throw(env, GenerateBusinessError(env, res, "create c keyAgreement fail."));
274         LOGE("create c keyAgreement fail.");
275         return nullptr;
276     }
277 
278     NapiKeyAgreement *napiKeyAgreement = new (std::nothrow) NapiKeyAgreement(keyAgreement);
279     if (napiKeyAgreement == nullptr) {
280         napi_throw(env, GenerateBusinessError(env, HCF_ERR_MALLOC, "new napi key agreement failed."));
281         LOGE("new napi key agreement failed");
282         HcfObjDestroy(keyAgreement);
283         return nullptr;
284     }
285 
286     napi_status status = napi_wrap(env, instance, napiKeyAgreement,
287         [](napi_env env, void *data, void *hint) {
288             NapiKeyAgreement *napiKeyAgreement = static_cast<NapiKeyAgreement *>(data);
289             delete napiKeyAgreement;
290             return;
291         }, nullptr, nullptr);
292     if (status != napi_ok) {
293         napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "failed to wrap napiKeyAgreement obj!"));
294         LOGE("failed to wrap napiKeyAgreement obj!");
295         delete napiKeyAgreement;
296         return nullptr;
297     }
298 
299     return instance;
300 }
301 
JsGetAlgorithm(napi_env env,napi_callback_info info)302 napi_value NapiKeyAgreement::JsGetAlgorithm(napi_env env, napi_callback_info info)
303 {
304     napi_value thisVar = nullptr;
305     NapiKeyAgreement *napiKeyAgreement = nullptr;
306     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
307     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiKeyAgreement));
308     if (status != napi_ok || napiKeyAgreement == nullptr) {
309         napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "failed to unwrap napiKeyAgreement obj!"));
310         LOGE("failed to unwrap napiKeyAgreement obj!");
311         return nullptr;
312     }
313     HcfKeyAgreement *keyAgreement = napiKeyAgreement->GetKeyAgreement();
314 
315     const char *algo = keyAgreement->getAlgoName(keyAgreement);
316     napi_value instance = nullptr;
317     napi_create_string_utf8(env, algo, NAPI_AUTO_LENGTH, &instance);
318     return instance;
319 }
320 
DefineKeyAgreementJSClass(napi_env env,napi_value exports)321 void NapiKeyAgreement::DefineKeyAgreementJSClass(napi_env env, napi_value exports)
322 {
323     napi_property_descriptor desc[] = {
324         DECLARE_NAPI_FUNCTION("createKeyAgreement", NapiKeyAgreement::CreateJsKeyAgreement),
325     };
326     napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
327 
328     napi_property_descriptor classDesc[] = {
329         DECLARE_NAPI_FUNCTION("generateSecret", NapiKeyAgreement::JsGenerateSecret),
330         {.utf8name = "algName", .getter = NapiKeyAgreement::JsGetAlgorithm},
331     };
332     napi_value constructor = nullptr;
333     napi_define_class(env, "KeyAgreement", NAPI_AUTO_LENGTH, NapiKeyAgreement::KeyAgreementConstructor, nullptr,
334         sizeof(classDesc) / sizeof(classDesc[0]), classDesc, &constructor);
335     napi_create_reference(env, constructor, 1, &classRef_);
336 }
337 } // CryptoFramework
338 } // OHOS
339