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