• 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 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         LOGE("generate secret fail.");
158         ctx->errMsg = "generate secret fail.";
159     }
160 }
161 
KeyAgreementAsyncWorkReturn(napi_env env,napi_status status,void * data)162 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     size_t expectedArgc = PARAMS_NUM_ONE;
249     size_t argc = PARAMS_NUM_ONE;
250     napi_value argv[PARAMS_NUM_ONE] = { nullptr };
251     napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
252     if (argc != expectedArgc) {
253         napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "The input args num is invalid."));
254         LOGE("The input args num is invalid.");
255         return nullptr;
256     }
257 
258     napi_value instance = nullptr;
259     napi_value constructor = nullptr;
260     napi_get_reference_value(env, classRef_, &constructor);
261     napi_new_instance(env, constructor, argc, argv, &instance);
262 
263     std::string algName;
264     if (!GetStringFromJSParams(env, argv[0], algName)) {
265         napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "Get algName is invalid."));
266         return nullptr;
267     }
268 
269     HcfKeyAgreement *keyAgreement = nullptr;
270     HcfResult res = HcfKeyAgreementCreate(algName.c_str(), &keyAgreement);
271     if (res != HCF_SUCCESS) {
272         napi_throw(env, GenerateBusinessError(env, res, "create c keyAgreement fail."));
273         LOGE("create c keyAgreement fail.");
274         return nullptr;
275     }
276 
277     NapiKeyAgreement *napiKeyAgreement = new (std::nothrow) NapiKeyAgreement(keyAgreement);
278     if (napiKeyAgreement == nullptr) {
279         napi_throw(env, GenerateBusinessError(env, HCF_ERR_MALLOC, "new napi key agreement failed."));
280         LOGE("new napi key agreement failed");
281         HcfObjDestroy(keyAgreement);
282         return nullptr;
283     }
284 
285     napi_value napiAlgName = nullptr;
286     napi_create_string_utf8(env, algName.c_str(), NAPI_AUTO_LENGTH, &napiAlgName);
287     napi_set_named_property(env, instance, CRYPTO_TAG_ALG_NAME.c_str(), napiAlgName);
288 
289     napi_status status = napi_wrap(env, instance, napiKeyAgreement,
290         [](napi_env env, void *data, void *hint) {
291             NapiKeyAgreement *napiKeyAgreement = static_cast<NapiKeyAgreement *>(data);
292             delete napiKeyAgreement;
293             return;
294         }, nullptr, nullptr);
295     if (status != napi_ok) {
296         napi_throw(env, GenerateBusinessError(env, HCF_INVALID_PARAMS, "failed to wrap napiKeyAgreement obj!"));
297         LOGE("failed to wrap napiKeyAgreement obj!");
298         delete napiKeyAgreement;
299         return nullptr;
300     }
301 
302     return instance;
303 }
304 
DefineKeyAgreementJSClass(napi_env env,napi_value exports)305 void NapiKeyAgreement::DefineKeyAgreementJSClass(napi_env env, napi_value exports)
306 {
307     napi_property_descriptor desc[] = {
308         DECLARE_NAPI_FUNCTION("createKeyAgreement", NapiKeyAgreement::CreateJsKeyAgreement),
309     };
310     napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
311 
312     napi_property_descriptor classDesc[] = {
313         DECLARE_NAPI_FUNCTION("generateSecret", NapiKeyAgreement::JsGenerateSecret),
314     };
315     napi_value constructor = nullptr;
316     napi_define_class(env, "KeyAgreement", NAPI_AUTO_LENGTH, NapiKeyAgreement::KeyAgreementConstructor, nullptr,
317         sizeof(classDesc) / sizeof(classDesc[0]), classDesc, &constructor);
318     napi_create_reference(env, constructor, 1, &classRef_);
319 }
320 } // CryptoFramework
321 } // OHOS
322