1 /*
2  * Copyright (c) 2022 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 "huks_napi_update_finish.h"
17 
18 #include <vector>
19 
20 #include "securec.h"
21 
22 #include "hks_api.h"
23 #include "hks_log.h"
24 #include "hks_mem.h"
25 #include "hks_param.h"
26 #include "hks_type.h"
27 #include "huks_napi_common.h"
28 
29 namespace HuksNapi {
30 namespace {
31 constexpr int HUKS_NAPI_UPDATE_MIN_ARGS = 2;
32 constexpr int HUKS_NAPI_UPDATE_MAX_ARGS = 4;
33 }  // namespace
34 
35 struct UpdateAsyncContextT {
36     napi_async_work asyncWork = nullptr;
37     napi_deferred deferred = nullptr;
38     napi_ref callback = nullptr;
39 
40     int32_t result = 0;
41     struct HksBlob *handle = nullptr;
42     struct HksParamSet *paramSet = nullptr;
43     struct HksBlob *inData = nullptr;
44     struct HksBlob *outData = nullptr;
45     struct HksBlob *token = nullptr;
46     bool isUpdate = false;
47 };
48 using UpdateAsyncContext = UpdateAsyncContextT *;
49 
CreateUpdateAsyncContext()50 static UpdateAsyncContext CreateUpdateAsyncContext()
51 {
52     UpdateAsyncContext context = static_cast<UpdateAsyncContext>(HksMalloc(sizeof(UpdateAsyncContextT)));
53     if (context != nullptr) {
54         (void)memset_s(context, sizeof(UpdateAsyncContextT), 0, sizeof(UpdateAsyncContextT));
55     }
56     return context;
57 }
58 
DeleteUpdateAsyncContext(napi_env env,UpdateAsyncContext & context)59 static void DeleteUpdateAsyncContext(napi_env env, UpdateAsyncContext &context)
60 {
61     if (context == nullptr) {
62         return;
63     }
64 
65     DeleteCommonAsyncContext(env, context->asyncWork, context->callback, context->handle, context->paramSet);
66 
67     if (context->inData != nullptr) {
68         if (context->inData->data != nullptr && context->inData->size != 0) {
69             (void)memset_s(context->inData->data, context->inData->size, 0, context->inData->size);
70         }
71         FreeHksBlob(context->inData);
72     }
73 
74     if (context->outData != nullptr) {
75         if (context->outData->data != nullptr && context->outData->size != 0) {
76             (void)memset_s(context->outData->data, context->outData->size, 0, context->outData->size);
77         }
78         FreeHksBlob(context->outData);
79     }
80 
81     if (context->token != nullptr) {
82         FreeHksBlob(context->token);
83     }
84 
85     HKS_FREE(context);
86     context = nullptr;
87 }
88 
FillContextInDataAndOutData(napi_env env,napi_value * argv,UpdateAsyncContext context,size_t index)89 static int32_t FillContextInDataAndOutData(napi_env env, napi_value *argv, UpdateAsyncContext context, size_t index)
90 {
91     napi_value inData = nullptr;
92     bool hasInData = false;
93     napi_has_named_property(env, argv[index], HKS_OPTIONS_PROPERTY_INDATA.c_str(), &hasInData);
94     napi_status status = napi_get_named_property(env, argv[index], HKS_OPTIONS_PROPERTY_INDATA.c_str(), &inData);
95     if (status == napi_ok && inData != nullptr && hasInData) {
96         napi_value result = GetUint8Array(env, inData, *context->inData);
97         if (result == nullptr) {
98             HKS_LOG_E("could not get inData");
99             return HKS_ERROR_BAD_STATE;
100         }
101     } else {
102         context->inData->size = 0;
103         context->inData->data = nullptr;
104     }
105 
106     context->outData->size = context->inData->size + DATA_SIZE_64KB;
107     context->outData->data = static_cast<uint8_t *>(HksMalloc(context->outData->size));
108     if (context->outData->data == nullptr) {
109         HKS_LOG_E("malloc memory failed");
110         return HKS_ERROR_MALLOC_FAIL;
111     }
112 
113     return HKS_SUCCESS;
114 }
115 
FillContextInDataAndOutBlob(napi_env env,napi_value * argv,UpdateAsyncContext context,size_t index)116 static int32_t FillContextInDataAndOutBlob(napi_env env, napi_value *argv, UpdateAsyncContext context, size_t index)
117 {
118     context->outData = static_cast<HksBlob *>(HksMalloc(sizeof(HksBlob)));
119     if (context->outData == nullptr) {
120         HKS_LOG_E("could not alloc out blob memory");
121         return HKS_ERROR_MALLOC_FAIL;
122     }
123     (void)memset_s(context->outData, sizeof(HksBlob), 0, sizeof(HksBlob));
124 
125     context->inData = static_cast<HksBlob *>(HksMalloc(sizeof(HksBlob)));
126     if (context->inData == nullptr) {
127         HKS_LOG_E("could not alloc in blob memory");
128         return HKS_ERROR_MALLOC_FAIL;
129     }
130     (void)memset_s(context->inData, sizeof(HksBlob), 0, sizeof(HksBlob));
131 
132     int32_t ret = FillContextInDataAndOutData(env, argv, context, index);
133     if (ret != HKS_SUCCESS) {
134         HKS_LOG_E("fill data failed");
135     }
136     return ret;
137 }
138 
CheckIsCallbackFuction(napi_env env,napi_value object,bool & isFunc)139 static int32_t CheckIsCallbackFuction(napi_env env, napi_value object, bool &isFunc)
140 {
141     isFunc = false;
142     napi_valuetype valueType = napi_undefined;
143     napi_status status = napi_typeof(env, object, &valueType);
144     if (status != napi_ok) {
145         HKS_LOG_E("could not get object type");
146         return HKS_ERROR_INVALID_ARGUMENT;
147     }
148 
149     if (valueType == napi_function) {
150         isFunc = true;
151     }
152     return HKS_SUCCESS;
153 }
154 
GetCallBackFunction(napi_env env,napi_value object,UpdateAsyncContext context)155 static int32_t GetCallBackFunction(napi_env env, napi_value object, UpdateAsyncContext context)
156 {
157     napi_ref ref = nullptr;
158     napi_status status = napi_create_reference(env, object, 1, &ref);
159     if (status != napi_ok) {
160         HKS_LOG_E("could not create reference");
161         return HKS_ERROR_BAD_STATE;
162     }
163     context->callback = ref;
164     return HKS_SUCCESS;
165 }
166 
GetToken(napi_env env,napi_value object,UpdateAsyncContext context)167 static int32_t GetToken(napi_env env, napi_value object, UpdateAsyncContext context)
168 {
169     context->token = static_cast<HksBlob *>(HksMalloc(sizeof(HksBlob)));
170     if (context->token == nullptr) {
171         HKS_LOG_E("could not alloc token blob memory");
172         return HKS_ERROR_MALLOC_FAIL;
173     }
174     (void)memset_s(context->token, sizeof(HksBlob), 0, sizeof(HksBlob));
175 
176     napi_value result = GetUint8Array(env, object, *(context->token));
177     if (result == nullptr) {
178         HKS_LOG_E("could not get token data");
179         return HKS_ERROR_BAD_STATE;
180     }
181 
182     return HKS_SUCCESS;
183 }
184 
GetTokenOrCallback(napi_env env,napi_value * argv,UpdateAsyncContext context,size_t index,size_t maxIndex)185 static int32_t GetTokenOrCallback(napi_env env, napi_value *argv, UpdateAsyncContext context,
186     size_t index, size_t maxIndex)
187 {
188     if (index >= maxIndex) { /* only 2 input params */
189         return HKS_SUCCESS;
190     }
191 
192     /*
193      * check wether arg 3 is callback: if true, get callback function and return;
194      * else get token, then check wether has arg 4: if true, get arg 4 as callback function
195      */
196     bool isFunc = false;
197     int32_t ret = CheckIsCallbackFuction(env, argv[index], isFunc);
198     if (ret != HKS_SUCCESS) {
199         return ret;
200     }
201     if (isFunc) {
202         return GetCallBackFunction(env, argv[index], context); /* return if arg 3 is callback */
203     }
204 
205     /* get token */
206     ret = GetToken(env, argv[index], context);
207     if (ret != HKS_SUCCESS) {
208         return ret;
209     }
210 
211     index++;
212     if (index < maxIndex) { /* has arg 4: can only be callback */
213         ret = CheckIsCallbackFuction(env, argv[index], isFunc);
214         if (ret != HKS_SUCCESS || !isFunc) {
215             HKS_LOG_E("check param4 failed[ret = %" LOG_PUBLIC "d], or param4 is not func.", ret);
216             return HKS_ERROR_INVALID_ARGUMENT;
217         }
218         return GetCallBackFunction(env, argv[index], context);
219     }
220 
221     return HKS_SUCCESS;
222 }
223 
AddParams(const std::vector<HksParam> & params,struct HksParamSet * & paramSet)224 static int32_t AddParams(const std::vector<HksParam> &params, struct HksParamSet *&paramSet)
225 {
226     const HksParam *param = params.data();
227     size_t paramCount = params.size();
228     if (param == nullptr) {
229         return HKS_SUCCESS;
230     }
231 
232     for (uint32_t i = 0; i < paramCount; ++i) {
233         int32_t ret = HksAddParams(paramSet, param, 1);
234         if (ret != HKS_SUCCESS) {
235             HKS_LOG_E("add param[%" LOG_PUBLIC "u] failed", i);
236             return ret;
237         }
238         param++;
239     }
240     return HKS_SUCCESS;
241 }
242 
GetInputParamSet(napi_env env,napi_value object,struct HksBlob * & token,HksParamSet * & paramSet)243 static int32_t GetInputParamSet(napi_env env, napi_value object, struct HksBlob *&token, HksParamSet *&paramSet)
244 {
245     std::vector<HksParam> params;
246     napi_value result = ParseParams(env, object, params);
247     if (result == nullptr) {
248         HKS_LOG_E("parse params failed");
249         FreeParsedParams(params);
250         return HKS_ERROR_INVALID_ARGUMENT;
251     }
252 
253     HksParamSet *outParamSet = nullptr;
254     int32_t ret;
255     do {
256         ret = HksInitParamSet(&outParamSet);
257         if (ret != HKS_SUCCESS) {
258             HKS_LOG_E("init paramSet failed");
259             break;
260         }
261 
262         if (CheckBlob(token) == HKS_SUCCESS) { /* has token param */
263             HksParam tokenParam = {
264                 .tag = HKS_TAG_AUTH_TOKEN,
265                 .blob = *token
266             };
267             ret = HksAddParams(outParamSet, &tokenParam, 1);
268             if (ret != HKS_SUCCESS) {
269                 HKS_LOG_E("add token param failed.");
270                 break;
271             }
272         }
273 
274         ret = AddParams(params, outParamSet);
275         if (ret != HKS_SUCCESS) {
276             HKS_LOG_E("add params failed");
277             break;
278         }
279 
280         ret = HksBuildParamSet(&outParamSet);
281         if (ret != HKS_SUCCESS) {
282             HKS_LOG_E("build params failed");
283             break;
284         }
285     } while (0);
286     FreeParsedParams(params);
287     if (ret != HKS_SUCCESS) {
288         HksFreeParamSet(&outParamSet);
289     }
290     paramSet = outParamSet;
291     return ret;
292 }
293 
ParseUpdateParams(napi_env env,napi_callback_info info,UpdateAsyncContext context)294 static napi_value ParseUpdateParams(napi_env env, napi_callback_info info, UpdateAsyncContext context)
295 {
296     size_t argc = HUKS_NAPI_UPDATE_MAX_ARGS;
297     napi_value argv[HUKS_NAPI_UPDATE_MAX_ARGS] = { 0 };
298     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
299 
300     if (argc < HUKS_NAPI_UPDATE_MIN_ARGS) {
301         napi_throw_error(env, nullptr, "invalid arguments");
302         HKS_LOG_E("no enough params");
303         return nullptr;
304     }
305 
306     size_t index = 0;
307     napi_value result = GetHandleValue(env, argv[index], context->handle);
308     if (result == nullptr) {
309         HKS_LOG_E("update could not get handle value");
310         return nullptr;
311     }
312 
313     index++;
314     napi_value properties = nullptr;
315     napi_status status = napi_get_named_property(env, argv[index],
316         HKS_OPTIONS_PROPERTY_PROPERTIES.c_str(), &properties);
317     if (status != napi_ok || properties == nullptr) {
318         GET_AND_THROW_LAST_ERROR((env));
319         HKS_LOG_E("update could not get property %" LOG_PUBLIC "s", HKS_OPTIONS_PROPERTY_PROPERTIES.c_str());
320         return nullptr;
321     }
322 
323     if (FillContextInDataAndOutBlob(env, argv, context, index) != HKS_SUCCESS) {
324         HKS_LOG_E("fill in or out blob failed");
325         return nullptr;
326     }
327 
328     index++;
329     if (GetTokenOrCallback(env, argv, context, index, argc) != HKS_SUCCESS) {
330         HKS_LOG_E("get token or callback failed");
331         return nullptr;
332     }
333 
334     if (GetInputParamSet(env, properties, context->token, context->paramSet) != HKS_SUCCESS) {
335         HKS_LOG_E("could not get paramset");
336         return nullptr;
337     }
338 
339     return GetInt32(env, 0);
340 }
341 
UpdateWriteResult(napi_env env,UpdateAsyncContext context)342 static napi_value UpdateWriteResult(napi_env env, UpdateAsyncContext context)
343 {
344     return GenerateHksResult(env,
345         context->result,
346         ((context->result == HKS_SUCCESS && context->outData != nullptr) ? context->outData->data : nullptr),
347         (context->result == HKS_SUCCESS && context->outData != nullptr) ? context->outData->size : 0);
348 }
349 
UpdateFinishAsyncWork(napi_env env,UpdateAsyncContext & context)350 static napi_value UpdateFinishAsyncWork(napi_env env, UpdateAsyncContext &context)
351 {
352     napi_value promise = nullptr;
353     if (context->callback == nullptr) {
354         NAPI_CALL(env, napi_create_promise(env, &context->deferred, &promise));
355     }
356 
357     napi_value resourceName;
358     napi_create_string_latin1(env, "UpdateAsyncWork", NAPI_AUTO_LENGTH, &resourceName);
359 
360     napi_create_async_work(
361         env,
362         nullptr,
363         resourceName,
364         [](napi_env env, void *data) {
365             UpdateAsyncContext napiContext = static_cast<UpdateAsyncContext>(data);
366 
367             if (napiContext->isUpdate) {
368                 napiContext->result = HksUpdate(napiContext->handle,
369                     napiContext->paramSet, napiContext->inData, napiContext->outData);
370             } else {
371                 napiContext->result = HksFinish(napiContext->handle,
372                     napiContext->paramSet, napiContext->inData, napiContext->outData);
373             }
374         },
375         [](napi_env env, napi_status status, void *data) {
376             UpdateAsyncContext napiContext = static_cast<UpdateAsyncContext>(data);
377             napi_value result = UpdateWriteResult(env, napiContext);
378             if (napiContext->callback == nullptr) {
379                 napi_resolve_deferred(env, napiContext->deferred, result);
380             } else if (result != nullptr) {
381                 CallAsyncCallback(env, napiContext->callback, napiContext->result, result);
382             }
383             DeleteUpdateAsyncContext(env, napiContext);
384         },
385         static_cast<void *>(context),
386         &context->asyncWork);
387 
388     napi_status status = napi_queue_async_work(env, context->asyncWork);
389     if (status != napi_ok) {
390         GET_AND_THROW_LAST_ERROR((env));
391         DeleteUpdateAsyncContext(env, context);
392         HKS_LOG_E("could not queue async work");
393         return nullptr;
394     }
395 
396     if (context->callback == nullptr) {
397         return promise;
398     } else {
399         return GetNull(env);
400     }
401 }
402 
HuksNapiUpdate(napi_env env,napi_callback_info info)403 napi_value HuksNapiUpdate(napi_env env, napi_callback_info info)
404 {
405     UpdateAsyncContext context = CreateUpdateAsyncContext();
406     if (context == nullptr) {
407         HKS_LOG_E("could not create update async context");
408         return nullptr;
409     }
410 
411     napi_value result = ParseUpdateParams(env, info, context);
412     if (result == nullptr) {
413         HKS_LOG_E("could not parse params");
414         DeleteUpdateAsyncContext(env, context);
415         return nullptr;
416     }
417     context->isUpdate = true;
418 
419     result = UpdateFinishAsyncWork(env, context);
420     if (result == nullptr) {
421         HKS_LOG_E("could not start async work");
422         DeleteUpdateAsyncContext(env, context);
423         return nullptr;
424     }
425     return result;
426 }
427 
HuksNapiFinish(napi_env env,napi_callback_info info)428 napi_value HuksNapiFinish(napi_env env, napi_callback_info info)
429 {
430     UpdateAsyncContext context = CreateUpdateAsyncContext();
431     if (context == nullptr) {
432         HKS_LOG_E("could not create context");
433         return nullptr;
434     }
435 
436     napi_value result = ParseUpdateParams(env, info, context);
437     if (result == nullptr) {
438         HKS_LOG_E("could not parse params");
439         DeleteUpdateAsyncContext(env, context);
440         return nullptr;
441     }
442     context->isUpdate = false;
443 
444     result = UpdateFinishAsyncWork(env, context);
445     if (result == nullptr) {
446         HKS_LOG_E("could not start async work");
447         DeleteUpdateAsyncContext(env, context);
448         return nullptr;
449     }
450     return result;
451 }
452 }  // namespace HuksNapi
453