• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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 "asset_napi_common.h"
17 
18 #include <vector>
19 
20 #include "securec.h"
21 
22 #include "asset_api.h"
23 #include "asset_log.h"
24 #include "asset_mem.h"
25 #include "asset_napi_error_code.h"
26 #include "asset_type.h"
27 
28 namespace OHOS {
29 namespace Security {
30 namespace Asset {
31 namespace {
32 #define MAX_BUFFER_LEN 1024
33 #define MAX_MESSAGE_LEN 128
34 #define MAX_ARGS_NUM 5
35 
36 #define NAPI_THROW_BASE(env, condition, ret, code, message)             \
37 if ((condition)) {                                                      \
38     LOGE("[FATAL][NAPI]%{public}s", (message));                         \
39     napi_throw_error((env), std::to_string((code)).c_str(), message);   \
40     return (ret);                                                       \
41 }
42 
43 #define NAPI_THROW(env, condition, code, message)                       \
44     NAPI_THROW_BASE(env, condition, nullptr, code, message)
45 
46 #define NAPI_THROW_RETURN_ERR(env, condition, code, message)            \
47     NAPI_THROW_BASE(env, condition, napi_generic_failure, code, message)
48 
49 #define NAPI_CALL_BREAK(env, theCall)   \
50 if ((theCall) != napi_ok) {             \
51     GET_AND_THROW_LAST_ERROR((env));    \
52     break;                              \
53 }
54 
55 #define NAPI_CALL_RETURN_ERR(env, theCall)  \
56 if ((theCall) != napi_ok) {                 \
57     GET_AND_THROW_LAST_ERROR((env));        \
58     return napi_generic_failure;            \
59 }
60 
61 #define CHECK_ASSET_TAG(env, condition, tag, message)                                   \
62 if ((condition)) {                                                                      \
63     char msg[MAX_MESSAGE_LEN] = { 0 };                                                  \
64     (void)sprintf_s(msg, MAX_MESSAGE_LEN, "AssetTag(0x%08x) " message, tag);            \
65     LOGE("[FATAL][NAPI]%{public}s", (msg));                                             \
66     napi_throw_error((env), std::to_string(ASSET_INVALID_ARGUMENT).c_str(), msg);       \
67     return napi_invalid_arg;                                                            \
68 }
69 
FreeAssetAttrs(std::vector<Asset_Attr> & attrs)70 void FreeAssetAttrs(std::vector<Asset_Attr> &attrs)
71 {
72     for (auto attr : attrs) {
73         if ((attr.tag & ASSET_TAG_TYPE_MASK) == ASSET_TYPE_BYTES) {
74             OH_Asset_FreeBlob(&attr.value.blob);
75         }
76     }
77     attrs.clear();
78 }
79 
CreateAsyncContext()80 AsyncContext *CreateAsyncContext()
81 {
82     return static_cast<AsyncContext *>(AssetMalloc(sizeof(AsyncContext)));
83 }
84 
DestroyAsyncContext(napi_env env,AsyncContext * context)85 void DestroyAsyncContext(napi_env env, AsyncContext *context)
86 {
87     if (context == nullptr) {
88         return;
89     }
90     if (context->work != nullptr) {
91         napi_delete_async_work(env, context->work);
92         context->work = nullptr;
93     }
94 
95     OH_Asset_FreeResultSet(&context->resultSet);
96     OH_Asset_FreeBlob(&context->challenge);
97     FreeAssetAttrs(context->updateAttrs);
98     FreeAssetAttrs(context->attrs);
99     AssetFree(context);
100 }
101 
ParseByteArray(napi_env env,napi_value value,uint32_t tag,Asset_Blob & blob)102 napi_status ParseByteArray(napi_env env, napi_value value, uint32_t tag, Asset_Blob &blob)
103 {
104     napi_typedarray_type arrayType;
105     size_t length = 0;
106     void *rawData = nullptr;
107 
108     bool result = false;
109     NAPI_CALL_RETURN_ERR(env, napi_is_typedarray(env, value, &result));
110     CHECK_ASSET_TAG(env, !result, tag, "Expect type napi_typedarray.");
111     NAPI_CALL_RETURN_ERR(env, napi_get_typedarray_info(env, value, &arrayType, &length, &rawData, nullptr, nullptr));
112     CHECK_ASSET_TAG(env, arrayType != napi_uint8_array, tag, "Expect type napi_uint8_array.");
113     CHECK_ASSET_TAG(env, length == 0 || length > MAX_BUFFER_LEN, tag, "Invalid array length.");
114 
115     blob.data = static_cast<uint8_t *>(AssetMalloc(length));
116     NAPI_THROW_RETURN_ERR(env, blob.data == nullptr, ASSET_OUT_OF_MEMORY, "Unable to allocate memory for Asset_Blob.");
117 
118     (void)memcpy_s(blob.data, length, rawData, length);
119     blob.size = static_cast<uint32_t>(length);
120     return napi_ok;
121 }
122 
ParseAssetAttribute(napi_env env,napi_value tag,napi_value value,Asset_Attr & attr)123 napi_status ParseAssetAttribute(napi_env env, napi_value tag, napi_value value, Asset_Attr &attr)
124 {
125     // parse tag
126     napi_valuetype type = napi_undefined;
127     NAPI_CALL_RETURN_ERR(env, napi_typeof(env, tag, &type));
128     NAPI_THROW_RETURN_ERR(env, type != napi_number, ASSET_INVALID_ARGUMENT, "The tag type of map should be number.");
129     NAPI_CALL_RETURN_ERR(env, napi_get_value_uint32(env, tag, &attr.tag));
130 
131     // parse value
132     NAPI_CALL_RETURN_ERR(env, napi_typeof(env, value, &type));
133     switch (attr.tag & ASSET_TAG_TYPE_MASK) {
134         case ASSET_TYPE_BOOL:
135             CHECK_ASSET_TAG(env, type != napi_boolean, attr.tag, "Expect type napi_boolean.");
136             NAPI_CALL_RETURN_ERR(env, napi_get_value_bool(env, value, &attr.value.boolean));
137             break;
138         case ASSET_TYPE_NUMBER:
139             CHECK_ASSET_TAG(env, type != napi_number, attr.tag, "Expect type napi_number.");
140             NAPI_CALL_RETURN_ERR(env, napi_get_value_uint32(env, value, &attr.value.u32));
141             break;
142         case ASSET_TYPE_BYTES:
143             CHECK_ASSET_TAG(env, type != napi_object, attr.tag, "Expect type napi_object.");
144             NAPI_CALL_RETURN_ERR(env, ParseByteArray(env, value, attr.tag, attr.value.blob));
145             break;
146         default:
147             CHECK_ASSET_TAG(env, true, attr.tag, "Invalid tag argument.");
148     }
149     return napi_ok;
150 }
151 
GetIteratorNext(napi_env env,napi_value iterator,napi_value func,bool * done)152 napi_value GetIteratorNext(napi_env env, napi_value iterator, napi_value func, bool *done)
153 {
154     napi_value next = nullptr;
155     NAPI_CALL(env, napi_call_function(env, iterator, func, 0, nullptr, &next));
156 
157     napi_value doneValue = nullptr;
158     NAPI_CALL(env, napi_get_named_property(env, next, "done", &doneValue));
159     NAPI_CALL(env, napi_get_value_bool(env, doneValue, done));
160     return next;
161 }
162 
ParseMapParam(napi_env env,napi_value arg,std::vector<Asset_Attr> & attrs)163 napi_status ParseMapParam(napi_env env, napi_value arg, std::vector<Asset_Attr> &attrs)
164 {
165     // check map type
166     bool isMap = false;
167     NAPI_CALL_RETURN_ERR(env, napi_is_map(env, arg, &isMap));
168     NAPI_THROW_RETURN_ERR(env, !isMap, ASSET_INVALID_ARGUMENT, "Expect Map type.");
169 
170     // parse map object
171     napi_value entriesFunc = nullptr;
172     napi_value iterator = nullptr;
173     napi_value nextFunc = nullptr;
174     NAPI_CALL_RETURN_ERR(env, napi_get_named_property(env, arg, "entries", &entriesFunc));
175     NAPI_CALL_RETURN_ERR(env, napi_call_function(env, arg, entriesFunc, 0, nullptr, &iterator));
176     NAPI_CALL_RETURN_ERR(env, napi_get_named_property(env, iterator, "next", &nextFunc));
177 
178     bool done = false;
179     napi_value next = nullptr;
180     while ((next = GetIteratorNext(env, iterator, nextFunc, &done)) != nullptr && !done) {
181         napi_value entry = nullptr;
182         napi_value key = nullptr;
183         napi_value value = nullptr;
184         NAPI_CALL_BREAK(env, napi_get_named_property(env, next, "value", &entry));
185         NAPI_CALL_BREAK(env, napi_get_element(env, entry, 0, &key));
186         NAPI_CALL_BREAK(env, napi_get_element(env, entry, 1, &value));
187 
188         Asset_Attr param = { 0 };
189         NAPI_CALL_BREAK(env, ParseAssetAttribute(env, key, value, param));
190         attrs.push_back(param);
191     }
192 
193     NAPI_THROW_RETURN_ERR(env, !done, ASSET_INVALID_ARGUMENT, "Parse entry of map failed.");
194     return napi_ok;
195 }
196 
GetUndefinedValue(napi_env env)197 napi_value GetUndefinedValue(napi_env env)
198 {
199     napi_value value = nullptr;
200     NAPI_CALL(env, napi_get_undefined(env, &value));
201     return value;
202 }
203 
GetUint8Array(napi_env env,const Asset_Blob * blob)204 napi_value GetUint8Array(napi_env env, const Asset_Blob *blob)
205 {
206     if (blob->data == nullptr || blob->size == 0 || blob->size > MAX_BUFFER_LEN) {
207         return nullptr;
208     }
209 
210     void *data = nullptr;
211     napi_value buffer = nullptr;
212     NAPI_CALL(env, napi_create_arraybuffer(env, blob->size, &data, &buffer));
213     (void)memcpy_s(data, blob->size, blob->data, blob->size);
214 
215     napi_value result = nullptr;
216     NAPI_CALL(env, napi_create_typedarray(env, napi_uint8_array, blob->size, buffer, 0, &result));
217     return result;
218 }
219 
GetMapObject(napi_env env,Asset_Result * result)220 napi_value GetMapObject(napi_env env, Asset_Result *result)
221 {
222     napi_value global = nullptr;
223     napi_value mapFunc = nullptr;
224     napi_value map = nullptr;
225     NAPI_CALL(env, napi_get_global(env, &global));
226     NAPI_CALL(env, napi_get_named_property(env, global, "Map", &mapFunc));
227     NAPI_CALL(env, napi_new_instance(env, mapFunc, 0, nullptr, &map));
228     napi_value setFunc = nullptr;
229     NAPI_CALL(env, napi_get_named_property(env, map, "set", &setFunc));
230     for (uint32_t i = 0; i < result->count; i++) {
231         napi_value key = nullptr;
232         napi_value value = nullptr;
233         NAPI_CALL(env, napi_create_uint32(env, result->attrs[i].tag, &key));
234         switch (result->attrs[i].tag & ASSET_TAG_TYPE_MASK) {
235             case ASSET_TYPE_BOOL:
236                 NAPI_CALL(env, napi_get_boolean(env, result->attrs[i].value.boolean, &value));
237                 break;
238             case ASSET_TYPE_NUMBER:
239                 NAPI_CALL(env, napi_create_uint32(env, result->attrs[i].value.u32, &value));
240                 break;
241             case ASSET_TYPE_BYTES:
242                 value = GetUint8Array(env, &result->attrs[i].value.blob);
243                 break;
244             default:
245                 return nullptr;
246         }
247 
248         napi_value setArgs[] = { key, value };
249         NAPI_CALL(env, napi_call_function(env, map, setFunc, sizeof(setArgs) / sizeof(setArgs[0]), setArgs, nullptr));
250     }
251     return map;
252 }
253 
GetMapArray(napi_env env,Asset_ResultSet * resultSet)254 napi_value GetMapArray(napi_env env, Asset_ResultSet *resultSet)
255 {
256     napi_value array = nullptr;
257     NAPI_CALL(env, napi_create_array(env, &array));
258     for (uint32_t i = 0; i < resultSet->count; i++) {
259         if (resultSet->results[i].attrs == nullptr || resultSet->results[i].count == 0) {
260             return nullptr;
261         }
262         napi_value map = GetMapObject(env, &resultSet->results[i]);
263         NAPI_CALL(env, napi_set_element(env, array, i, map));
264     }
265     return array;
266 }
267 
GetBusinessValue(napi_env env,AsyncContext * context)268 napi_value GetBusinessValue(napi_env env, AsyncContext *context)
269 {
270     // Processing the return value of the PreQueryAsset function.
271     if (context->challenge.data != nullptr && context->challenge.size != 0) {
272         return GetUint8Array(env, &context->challenge);
273     }
274 
275     // Processing the return value of the QueryAsset function.
276     if (context->resultSet.results != nullptr && context->resultSet.count != 0) {
277         return GetMapArray(env, &context->resultSet);
278     }
279 
280     return GetUndefinedValue(env);
281 }
282 
GetBusinessError(napi_env env,int32_t errCode)283 napi_value GetBusinessError(napi_env env, int32_t errCode)
284 {
285     napi_value code = nullptr;
286     NAPI_CALL(env, napi_create_int32(env, errCode, &code));
287 
288     napi_value message = nullptr;
289     const char *errorMsg = GetErrorMessage(errCode);
290     NAPI_CALL(env, napi_create_string_utf8(env, errorMsg, strlen(errorMsg), &message));
291 
292     napi_value result = nullptr;
293     NAPI_CALL(env, napi_create_error(env, code, message, &result));
294     return result;
295 }
296 
ResolvePromise(napi_env env,AsyncContext * context)297 void ResolvePromise(napi_env env, AsyncContext *context)
298 {
299     napi_value result = nullptr;
300     if (context->result == ASSET_SUCCESS) {
301         result = GetBusinessValue(env, context);
302         NAPI_CALL_RETURN_VOID(env, napi_resolve_deferred(env, context->deferred, result));
303     } else {
304         result = GetBusinessError(env, context->result);
305         NAPI_CALL_RETURN_VOID(env, napi_reject_deferred(env, context->deferred, result));
306     }
307 }
308 
CreateAsyncWork(napi_env env,AsyncContext * context,const char * funcName,napi_async_execute_callback execute)309 napi_value CreateAsyncWork(napi_env env, AsyncContext *context, const char *funcName,
310     napi_async_execute_callback execute)
311 {
312     napi_value result;
313     NAPI_CALL(env, napi_create_promise(env, &context->deferred, &result));
314 
315     napi_value resource = nullptr;
316     NAPI_CALL(env, napi_create_string_utf8(env, funcName, NAPI_AUTO_LENGTH, &resource));
317     NAPI_CALL(env, napi_create_async_work(
318         env, nullptr, resource, execute,
319         [](napi_env env, napi_status status, void *data) {
320             AsyncContext *asyncContext = static_cast<AsyncContext *>(data);
321             ResolvePromise(env, asyncContext);
322             DestroyAsyncContext(env, asyncContext);
323         },
324         static_cast<void *>(context), &context->work));
325     NAPI_CALL(env, napi_queue_async_work(env, context->work));
326     return result;
327 }
328 
329 } // anonymous namespace
330 
NapiEntry(napi_env env,napi_callback_info info,const char * funcName,napi_async_execute_callback execute,size_t expectArgNum)331 napi_value NapiEntry(napi_env env, napi_callback_info info, const char *funcName, napi_async_execute_callback execute,
332     size_t expectArgNum)
333 {
334     size_t argc = expectArgNum;
335     napi_value argv[MAX_ARGS_NUM] = { 0 };
336     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
337     NAPI_THROW(env, argc < expectArgNum, ASSET_INVALID_ARGUMENT, "The number of arguments is insufficient.");
338 
339     AsyncContext *context = CreateAsyncContext();
340     NAPI_THROW(env, context == nullptr, ASSET_OUT_OF_MEMORY, "Unable to allocate memory for AsyncContext.");
341 
342     do {
343         size_t index = 0;
344         if (ParseMapParam(env, argv[index++], context->attrs) != napi_ok) {
345             LOGE("Parse first map parameter failed.");
346             break;
347         }
348 
349         if (expectArgNum == UPDATE_ARGS_NUM &&
350             ParseMapParam(env, argv[index++], context->updateAttrs) != napi_ok) {
351             LOGE("Parse second map parameter failed.");
352             break;
353         }
354 
355         napi_value promise = CreateAsyncWork(env, context, funcName, execute);
356         if (promise == nullptr) {
357             LOGE("Create async work failed.");
358             break;
359         }
360         return promise;
361     } while (0);
362     DestroyAsyncContext(env, context);
363     return nullptr;
364 }
365 
366 } // Asset
367 } // Security
368 } // OHOS
369