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