1 /*
2 * Copyright (c) 2023-2025 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_log.h"
23 #include "asset_mem.h"
24 #include "asset_system_type.h"
25
26 #include "asset_napi_context.h"
27 #include "asset_napi_error_code.h"
28
29 namespace OHOS {
30 namespace Security {
31 namespace Asset {
32 namespace {
33 #define MAX_BUFFER_LEN 2048
34
35 #define NAPI_THROW_RETURN_ERR(env, condition, code, message) \
36 NAPI_THROW_BASE(env, condition, napi_generic_failure, code, message)
37
38 #define NAPI_CALL_BREAK(env, theCall) \
39 if ((theCall) != napi_ok) { \
40 GET_AND_THROW_LAST_ERROR((env)); \
41 break; \
42 }
43
44 #define NAPI_CALL_RETURN_ERR(env, theCall) \
45 if ((theCall) != napi_ok) { \
46 GET_AND_THROW_LAST_ERROR((env)); \
47 return napi_generic_failure; \
48 }
49
50 #define CHECK_ASSET_TAG(env, condition, tag, message) \
51 if ((condition)) { \
52 char msg[MAX_MESSAGE_LEN] = { 0 }; \
53 (void)sprintf_s(msg, MAX_MESSAGE_LEN, "AssetTag(0x%08x) " message, tag); \
54 LOGE("[FATAL][NAPI]%{public}s", (msg)); \
55 napi_throw((env), CreateJsError((env), SEC_ASSET_INVALID_ARGUMENT, (msg))); \
56 return napi_invalid_arg; \
57 }
58
IsBlobValid(const AssetBlob & blob)59 bool IsBlobValid(const AssetBlob &blob)
60 {
61 return blob.data != nullptr && blob.size != 0;
62 }
63
ParseByteArray(const napi_env env,napi_value value,uint32_t tag,AssetBlob & blob)64 napi_status ParseByteArray(const napi_env env, napi_value value, uint32_t tag, AssetBlob &blob)
65 {
66 napi_typedarray_type arrayType;
67 size_t length = 0;
68 void *rawData = nullptr;
69
70 bool result = false;
71 NAPI_CALL_RETURN_ERR(env, napi_is_typedarray(env, value, &result));
72 CHECK_ASSET_TAG(env, !result, tag, "Expect type napi_typedarray.");
73 NAPI_CALL_RETURN_ERR(env, napi_get_typedarray_info(env, value, &arrayType, &length, &rawData, nullptr, nullptr));
74 CHECK_ASSET_TAG(env, arrayType != napi_uint8_array, tag, "Expect type napi_uint8_array.");
75 CHECK_ASSET_TAG(env, length == 0 || length > MAX_BUFFER_LEN, tag, "Invalid array length.");
76
77 blob.data = static_cast<uint8_t *>(AssetMalloc(length));
78 NAPI_THROW_RETURN_ERR(
79 env, blob.data == nullptr, SEC_ASSET_OUT_OF_MEMORY, "Unable to allocate memory for AssetBlob.");
80
81 (void)memcpy_s(blob.data, length, rawData, length);
82 blob.size = static_cast<uint32_t>(length);
83 return napi_ok;
84 }
85
ParseAssetAttribute(const napi_env env,napi_value tag,napi_value value,AssetAttr & attr)86 napi_status ParseAssetAttribute(const napi_env env, napi_value tag, napi_value value, AssetAttr &attr)
87 {
88 // parse tag
89 napi_valuetype type = napi_undefined;
90 NAPI_CALL_RETURN_ERR(env, napi_typeof(env, tag, &type));
91 NAPI_THROW_RETURN_ERR(
92 env, type != napi_number, SEC_ASSET_INVALID_ARGUMENT, "The tag type of map should be number.");
93 NAPI_CALL_RETURN_ERR(env, napi_get_value_uint32(env, tag, &attr.tag));
94
95 // parse value
96 NAPI_CALL_RETURN_ERR(env, napi_typeof(env, value, &type));
97 switch (attr.tag & SEC_ASSET_TAG_TYPE_MASK) {
98 case SEC_ASSET_TYPE_BOOL:
99 CHECK_ASSET_TAG(env, type != napi_boolean, attr.tag, "Expect type napi_boolean.");
100 NAPI_CALL_RETURN_ERR(env, napi_get_value_bool(env, value, &attr.value.boolean));
101 break;
102 case SEC_ASSET_TYPE_NUMBER:
103 CHECK_ASSET_TAG(env, type != napi_number, attr.tag, "Expect type napi_number.");
104 NAPI_CALL_RETURN_ERR(env, napi_get_value_uint32(env, value, &attr.value.u32));
105 break;
106 case SEC_ASSET_TYPE_BYTES:
107 CHECK_ASSET_TAG(env, type != napi_object, attr.tag, "Expect type napi_object.");
108 NAPI_CALL_RETURN_ERR(env, ParseByteArray(env, value, attr.tag, attr.value.blob));
109 break;
110 default:
111 CHECK_ASSET_TAG(env, true, attr.tag, "Invalid tag argument.");
112 }
113 return napi_ok;
114 }
115
GetIteratorNext(const napi_env env,napi_value iterator,napi_value func,bool * done)116 napi_value GetIteratorNext(const napi_env env, napi_value iterator, napi_value func, bool *done)
117 {
118 napi_value next = nullptr;
119 NAPI_CALL(env, napi_call_function(env, iterator, func, 0, nullptr, &next));
120
121 napi_value doneValue = nullptr;
122 NAPI_CALL(env, napi_get_named_property(env, next, "done", &doneValue));
123 NAPI_CALL(env, napi_get_value_bool(env, doneValue, done));
124 return next;
125 }
126
CreateJsMap(const napi_env env,const AssetResult & result)127 napi_value CreateJsMap(const napi_env env, const AssetResult &result)
128 {
129 napi_value global = nullptr;
130 napi_value mapFunc = nullptr;
131 napi_value map = nullptr;
132 NAPI_CALL(env, napi_get_global(env, &global));
133 NAPI_CALL(env, napi_get_named_property(env, global, "Map", &mapFunc));
134 NAPI_CALL(env, napi_new_instance(env, mapFunc, 0, nullptr, &map));
135 napi_value setFunc = nullptr;
136 NAPI_CALL(env, napi_get_named_property(env, map, "set", &setFunc));
137 for (uint32_t i = 0; i < result.count; i++) {
138 napi_value key = nullptr;
139 napi_value value = nullptr;
140 NAPI_CALL(env, napi_create_uint32(env, result.attrs[i].tag, &key));
141 switch (result.attrs[i].tag & SEC_ASSET_TAG_TYPE_MASK) {
142 case SEC_ASSET_TYPE_BOOL:
143 NAPI_CALL(env, napi_get_boolean(env, result.attrs[i].value.boolean, &value));
144 break;
145 case SEC_ASSET_TYPE_NUMBER:
146 NAPI_CALL(env, napi_create_uint32(env, result.attrs[i].value.u32, &value));
147 break;
148 case SEC_ASSET_TYPE_BYTES:
149 value = CreateJsUint8Array(env, result.attrs[i].value.blob);
150 break;
151 default:
152 return nullptr;
153 }
154
155 napi_value setArgs[] = { key, value };
156 NAPI_CALL(env, napi_call_function(env, map, setFunc, sizeof(setArgs) / sizeof(setArgs[0]), setArgs, nullptr));
157 }
158 return map;
159 }
160
ResolvePromise(const napi_env env,BaseContext * context)161 void ResolvePromise(const napi_env env, BaseContext *context)
162 {
163 if (context->result == SEC_ASSET_SUCCESS) {
164 napi_value result = context->resolve(env, context);
165 NAPI_CALL_RETURN_VOID(env, napi_resolve_deferred(env, context->deferred, result));
166 } else {
167 napi_value result = CreateJsError(env, context->result);
168 NAPI_CALL_RETURN_VOID(env, napi_reject_deferred(env, context->deferred, result));
169 }
170 }
171 } // anonymous namespace
172
ParseJsArgs(const napi_env env,napi_callback_info info,napi_value * value,size_t valueSize)173 napi_status ParseJsArgs(const napi_env env, napi_callback_info info, napi_value *value, size_t valueSize)
174 {
175 size_t argc = valueSize;
176 NAPI_CALL_RETURN_ERR(env, napi_get_cb_info(env, info, &argc, value, nullptr, nullptr));
177 NAPI_THROW_RETURN_ERR(env, argc < valueSize, SEC_ASSET_INVALID_ARGUMENT,
178 "Mandatory parameters are left unspecified.");
179 return napi_ok;
180 }
181
ParseJsMap(const napi_env env,napi_value arg,std::vector<AssetAttr> & attrs)182 napi_status ParseJsMap(const napi_env env, napi_value arg, std::vector<AssetAttr> &attrs)
183 {
184 // check map type
185 bool isMap = false;
186 NAPI_CALL_RETURN_ERR(env, napi_is_map(env, arg, &isMap));
187 NAPI_THROW_RETURN_ERR(env, !isMap, SEC_ASSET_INVALID_ARGUMENT, "Expect Map type.");
188
189 // parse map object
190 napi_value entriesFunc = nullptr;
191 napi_value iterator = nullptr;
192 napi_value nextFunc = nullptr;
193 NAPI_CALL_RETURN_ERR(env, napi_get_named_property(env, arg, "entries", &entriesFunc));
194 NAPI_CALL_RETURN_ERR(env, napi_call_function(env, arg, entriesFunc, 0, nullptr, &iterator));
195 NAPI_CALL_RETURN_ERR(env, napi_get_named_property(env, iterator, "next", &nextFunc));
196
197 bool done = false;
198 napi_value next = nullptr;
199 while ((next = GetIteratorNext(env, iterator, nextFunc, &done)) != nullptr && !done) {
200 napi_value entry = nullptr;
201 napi_value key = nullptr;
202 napi_value value = nullptr;
203 NAPI_CALL_BREAK(env, napi_get_named_property(env, next, "value", &entry));
204 NAPI_CALL_BREAK(env, napi_get_element(env, entry, 0, &key));
205 NAPI_CALL_BREAK(env, napi_get_element(env, entry, 1, &value));
206
207 AssetAttr param = { 0 };
208 NAPI_CALL_BREAK(env, ParseAssetAttribute(env, key, value, param));
209 attrs.push_back(param);
210 }
211
212 NAPI_THROW_RETURN_ERR(env, !done, SEC_ASSET_INVALID_ARGUMENT, "Parse entry of map failed.");
213 return napi_ok;
214 }
215
ParseJsUserId(const napi_env env,napi_value arg,std::vector<AssetAttr> & attrs)216 napi_status ParseJsUserId(const napi_env env, napi_value arg, std::vector<AssetAttr> &attrs)
217 {
218 napi_valuetype type = napi_undefined;
219 NAPI_CALL_RETURN_ERR(env, napi_typeof(env, arg, &type));
220 NAPI_THROW_RETURN_ERR(env, type != napi_number, SEC_ASSET_INVALID_ARGUMENT, "The type of userId should be number.");
221
222 AssetAttr param = { 0 };
223 param.tag = SEC_ASSET_TAG_USER_ID;
224 NAPI_CALL_RETURN_ERR(env, napi_get_value_uint32(env, arg, ¶m.value.u32));
225 attrs.push_back(param);
226 return napi_ok;
227 }
228
CreateJsError(const napi_env env,int32_t errCode)229 napi_value CreateJsError(const napi_env env, int32_t errCode)
230 {
231 return CreateJsError(env, errCode, GetErrorMessage(errCode));
232 }
233
CreateJsError(const napi_env env,int32_t errCode,const char * errorMsg)234 napi_value CreateJsError(const napi_env env, int32_t errCode, const char *errorMsg)
235 {
236 napi_value code = nullptr;
237 NAPI_CALL(env, napi_create_int32(env, errCode, &code));
238
239 napi_value message = nullptr;
240 NAPI_CALL(env, napi_create_string_utf8(env, errorMsg, strlen(errorMsg), &message));
241
242 napi_value result = nullptr;
243 NAPI_CALL(env, napi_create_error(env, code, message, &result));
244 return result;
245 }
246
CreateJsUint8Array(const napi_env env,const AssetBlob & blob)247 napi_value CreateJsUint8Array(const napi_env env, const AssetBlob &blob)
248 {
249 if (!IsBlobValid(blob) || blob.size > MAX_BUFFER_LEN) {
250 return nullptr;
251 }
252
253 void *data = nullptr;
254 napi_value buffer = nullptr;
255 NAPI_CALL(env, napi_create_arraybuffer(env, blob.size, &data, &buffer));
256 (void)memcpy_s(data, blob.size, blob.data, blob.size);
257
258 napi_value result = nullptr;
259 NAPI_CALL(env, napi_create_typedarray(env, napi_uint8_array, blob.size, buffer, 0, &result));
260 return result;
261 }
262
CreateJsMapArray(const napi_env env,const AssetResultSet & resultSet)263 napi_value CreateJsMapArray(const napi_env env, const AssetResultSet &resultSet)
264 {
265 napi_value array = nullptr;
266 NAPI_CALL(env, napi_create_array(env, &array));
267 for (uint32_t i = 0; i < resultSet.count; i++) {
268 if (resultSet.results[i].attrs == nullptr || resultSet.results[i].count == 0) {
269 return nullptr;
270 }
271 napi_value map = CreateJsMap(env, resultSet.results[i]);
272 NAPI_CALL(env, napi_set_element(env, array, i, map));
273 }
274 return array;
275 }
276
CreateJsUndefined(const napi_env env)277 napi_value CreateJsUndefined(const napi_env env)
278 {
279 napi_value value = nullptr;
280 NAPI_CALL(env, napi_get_undefined(env, &value));
281 return value;
282 }
283
NapiSetProperty(const napi_env env,napi_value object,const char * propertyName,uint32_t propertyValue)284 napi_status NapiSetProperty(const napi_env env, napi_value object, const char *propertyName, uint32_t propertyValue)
285 {
286 napi_value property = nullptr;
287 NAPI_CALL_RETURN_ERR(env, napi_create_uint32(env, propertyValue, &property));
288 NAPI_CALL_RETURN_ERR(env, napi_set_named_property(env, object, propertyName, property));
289 return napi_ok;
290 }
291
CreateAsyncWork(const napi_env env,napi_callback_info info,std::unique_ptr<BaseContext> context,const char * resourceName)292 napi_value CreateAsyncWork(const napi_env env, napi_callback_info info, std::unique_ptr<BaseContext> context,
293 const char *resourceName)
294 {
295 if (context->parse != nullptr) {
296 NAPI_CALL(env, context->parse(env, info, context.get()));
297 }
298
299 napi_value promise;
300 NAPI_CALL(env, napi_create_promise(env, &context->deferred, &promise));
301 if (context->check != nullptr) {
302 napi_value error = context->check(env, context.get());
303 if (error != nullptr) {
304 NAPI_CALL(env, napi_reject_deferred(env, context->deferred, error));
305 return promise;
306 }
307 }
308 napi_value resource = nullptr;
309 NAPI_CALL(env, napi_create_string_utf8(env, resourceName, NAPI_AUTO_LENGTH, &resource));
310 NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, context->execute,
311 [](napi_env env, napi_status status, void *data) {
312 BaseContext *context = static_cast<BaseContext *>(data);
313 ResolvePromise(env, context);
314 delete context;
315 }, static_cast<void *>(context.get()), &context->work));
316 context->env = env;
317 NAPI_CALL(env, napi_queue_async_work(env, context->work));
318 context.release();
319 return promise;
320 }
321
CreateSyncWork(const napi_env env,napi_callback_info info,BaseContext * context)322 napi_value CreateSyncWork(const napi_env env, napi_callback_info info, BaseContext *context)
323 {
324 if (context->parse != nullptr) {
325 NAPI_CALL(env, context->parse(env, info, context));
326 }
327
328 context->execute(env, context);
329 if (context->result != SEC_ASSET_SUCCESS) {
330 napi_throw(env, CreateJsError(env, context->result));
331 return nullptr;
332 }
333 return context->resolve(env, context);
334 }
335
336 } // Asset
337 } // Security
338 } // OHOS