1 /*
2 * Copyright (c) 2022-2024 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 "user_auth_napi_helper.h"
17
18 #include <cinttypes>
19 #include <uv.h>
20
21 #include "securec.h"
22
23 #include "napi/native_api.h"
24 #include "napi/native_common.h"
25 #include "napi/native_node_api.h"
26
27 #include "iam_logger.h"
28 #include "iam_ptr.h"
29 #include "user_auth_helper.h"
30
31 #define LOG_TAG "USER_AUTH_NAPI"
32
33 namespace OHOS {
34 namespace UserIam {
35 namespace UserAuth {
36 namespace {
37 static constexpr const int MAX_STRING_LENGTH = 65536;
38 struct DeleteRefHolder {
39 napi_env env {nullptr};
40 napi_ref ref {nullptr};
41 };
42 }
43
JsRefHolder(napi_env env,napi_value value)44 JsRefHolder::JsRefHolder(napi_env env, napi_value value)
45 {
46 if (env == nullptr || value == nullptr) {
47 IAM_LOGE("get null ptr");
48 return;
49 }
50 napi_status ret = UserAuthNapiHelper::GetFunctionRef(env, value, ref_);
51 if (ret != napi_ok) {
52 IAM_LOGE("GetFunctionRef fail %{public}d", ret);
53 ref_ = nullptr;
54 return;
55 }
56 env_ = env;
57 }
58
~JsRefHolder()59 JsRefHolder::~JsRefHolder()
60 {
61 if (!IsValid()) {
62 IAM_LOGI("invalid");
63 return;
64 }
65 IAM_LOGD("delete reference");
66 uv_loop_s *loop = nullptr;
67 napi_status napiStatus = napi_get_uv_event_loop(env_, &loop);
68 if (napiStatus != napi_ok || loop == nullptr) {
69 IAM_LOGE("napi_get_uv_event_loop fail");
70 return;
71 }
72 std::shared_ptr<DeleteRefHolder> deleteRefHolder = Common::MakeShared<DeleteRefHolder>();
73 if (deleteRefHolder == nullptr) {
74 IAM_LOGE("deleteRefHolder is null");
75 return;
76 }
77 deleteRefHolder->env = env_;
78 deleteRefHolder->ref = ref_;
79 auto task = [deleteRefHolder] () {
80 IAM_LOGI("start");
81 if (deleteRefHolder == nullptr) {
82 IAM_LOGE("deleteRefHolder is invalid");
83 return;
84 }
85 napi_status ret = napi_delete_reference(deleteRefHolder->env, deleteRefHolder->ref);
86 if (ret != napi_ok) {
87 IAM_LOGE("napi_delete_reference fail %{public}d", ret);
88 return;
89 }
90 };
91 if (napi_status::napi_ok != napi_send_event(env_, task, napi_eprio_immediate)) {
92 IAM_LOGE("napi_send_event: Failed to SendEvent");
93 }
94 }
95
IsValid() const96 bool JsRefHolder::IsValid() const
97 {
98 return (env_ != nullptr && ref_ != nullptr);
99 }
100
Get() const101 napi_ref JsRefHolder::Get() const
102 {
103 return ref_;
104 }
105
GetResultCodeV8(int32_t result)106 int32_t UserAuthNapiHelper::GetResultCodeV8(int32_t result)
107 {
108 if (result == CHECK_PERMISSION_FAILED) {
109 return static_cast<int32_t>(UserAuthResultCode::OHOS_CHECK_PERMISSION_FAILED);
110 }
111 if (result == PIN_EXPIRED) {
112 return GENERAL_ERROR;
113 }
114 if ((result < SUCCESS) || (result > NOT_ENROLLED)) {
115 return GENERAL_ERROR;
116 }
117 return result;
118 }
119
GetResultCodeV9(int32_t result)120 int32_t UserAuthNapiHelper::GetResultCodeV9(int32_t result)
121 {
122 if (result == CHECK_PERMISSION_FAILED) {
123 return static_cast<int32_t>(UserAuthResultCode::OHOS_CHECK_PERMISSION_FAILED);
124 }
125 if (result == INVALID_PARAMETERS) {
126 return static_cast<int32_t>(UserAuthResultCode::OHOS_INVALID_PARAM);
127 }
128 if (result == PIN_EXPIRED) {
129 return static_cast<int32_t>(UserAuthResultCode::GENERAL_ERROR);
130 }
131 if (result > (INT32_MAX - static_cast<int32_t>(UserAuthResultCode::RESULT_CODE_V9_MIN))) {
132 return static_cast<int32_t>(UserAuthResultCode::GENERAL_ERROR);
133 }
134 int32_t resultCodeV9 = result + static_cast<int32_t>(UserAuthResultCode::RESULT_CODE_V9_MIN);
135 if (resultCodeV9 >= static_cast<int32_t>(UserAuthResultCode::RESULT_CODE_V9_MIN) &&
136 resultCodeV9 <= static_cast<int32_t>(UserAuthResultCode::RESULT_CODE_V9_MAX)) {
137 return resultCodeV9;
138 }
139 return static_cast<int32_t>(UserAuthResultCode::GENERAL_ERROR);
140 }
141
GenerateBusinessErrorV9(napi_env env,UserAuthResultCode result)142 napi_value UserAuthNapiHelper::GenerateBusinessErrorV9(napi_env env, UserAuthResultCode result)
143 {
144 napi_value code;
145 std::string msgStr;
146 auto res = g_resultV92Str.find(result);
147 if (res == g_resultV92Str.end()) {
148 IAM_LOGE("result %{public}d not found", static_cast<int32_t>(result));
149 msgStr = g_resultV92Str.at(UserAuthResultCode::GENERAL_ERROR);
150 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(UserAuthResultCode::GENERAL_ERROR), &code));
151 } else {
152 msgStr = res->second;
153 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(result), &code));
154 }
155 IAM_LOGI("get msg %{public}s", msgStr.c_str());
156
157 napi_value msg;
158 NAPI_CALL(env, napi_create_string_utf8(env, msgStr.c_str(), NAPI_AUTO_LENGTH, &msg));
159
160 napi_value businessError;
161 NAPI_CALL(env, napi_create_error(env, nullptr, msg, &businessError));
162 NAPI_CALL(env, napi_set_named_property(env, businessError, "code", code));
163
164 return businessError;
165 }
166
GenerateErrorMsg(napi_env env,UserAuthResultCode result,std::string errorMsg)167 napi_value UserAuthNapiHelper::GenerateErrorMsg(napi_env env, UserAuthResultCode result, std::string errorMsg)
168 {
169 napi_value code;
170 std::string msgStr;
171 auto res = g_resultV92Str.find(result);
172 if (res == g_resultV92Str.end()) {
173 IAM_LOGE("result %{public}d not found", static_cast<int32_t>(result));
174 msgStr = g_resultV92Str.at(UserAuthResultCode::GENERAL_ERROR);
175 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(UserAuthResultCode::GENERAL_ERROR), &code));
176 } else {
177 msgStr = errorMsg;
178 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(result), &code));
179 }
180 IAM_LOGI("error msg %{public}s", msgStr.c_str());
181
182 napi_value msg;
183 NAPI_CALL(env, napi_create_string_utf8(env, msgStr.c_str(), NAPI_AUTO_LENGTH, &msg));
184
185 napi_value businessError;
186 NAPI_CALL(env, napi_create_error(env, nullptr, msg, &businessError));
187 NAPI_CALL(env, napi_set_named_property(env, businessError, "code", code));
188
189 return businessError;
190 }
191
ThrowErrorMsg(napi_env env,UserAuthResultCode errorCode,std::string errorMsg)192 UserAuthResultCode UserAuthNapiHelper::ThrowErrorMsg(napi_env env, UserAuthResultCode errorCode, std::string errorMsg)
193 {
194 napi_throw(env, UserAuthNapiHelper::GenerateErrorMsg(env, errorCode, errorMsg));
195 return errorCode;
196 }
197
CheckNapiType(napi_env env,napi_value value,napi_valuetype type)198 napi_status UserAuthNapiHelper::CheckNapiType(napi_env env, napi_value value, napi_valuetype type)
199 {
200 napi_valuetype valuetype;
201 napi_status result = napi_typeof(env, value, &valuetype);
202 if (result != napi_ok) {
203 IAM_LOGE("napi_typeof fail");
204 return result;
205 }
206 if (valuetype != type) {
207 IAM_LOGE("check valuetype fail");
208 return napi_generic_failure;
209 }
210 return napi_ok;
211 }
212
GetBoolValue(napi_env env,napi_value value,bool & out)213 napi_status UserAuthNapiHelper::GetBoolValue(napi_env env, napi_value value, bool &out)
214 {
215 napi_status result = CheckNapiType(env, value, napi_boolean);
216 if (result != napi_ok) {
217 IAM_LOGE("CheckNapiType fail");
218 return result;
219 }
220 result = napi_get_value_bool(env, value, &out);
221 if (result != napi_ok) {
222 IAM_LOGE("napi_get_value_bool fail");
223 }
224 return result;
225 }
226
GetInt32Value(napi_env env,napi_value value,int32_t & out)227 napi_status UserAuthNapiHelper::GetInt32Value(napi_env env, napi_value value, int32_t &out)
228 {
229 napi_status result = CheckNapiType(env, value, napi_number);
230 if (result != napi_ok) {
231 IAM_LOGE("CheckNapiType fail");
232 return result;
233 }
234 result = napi_get_value_int32(env, value, &out);
235 if (result != napi_ok) {
236 IAM_LOGE("napi_get_value_int32 fail");
237 }
238 return result;
239 }
240
GetUint32Value(napi_env env,napi_value value,uint32_t & out)241 napi_status UserAuthNapiHelper::GetUint32Value(napi_env env, napi_value value, uint32_t &out)
242 {
243 napi_status result = CheckNapiType(env, value, napi_number);
244 if (result != napi_ok) {
245 IAM_LOGE("CheckNapiType fail");
246 return result;
247 }
248 result = napi_get_value_uint32(env, value, &out);
249 if (result != napi_ok) {
250 IAM_LOGE("napi_get_value_uint32 fail");
251 }
252 return result;
253 }
254
GetStrValue(napi_env env,napi_value value,char * out,size_t & len)255 napi_status UserAuthNapiHelper::GetStrValue(napi_env env, napi_value value, char *out, size_t &len)
256 {
257 if (out == nullptr) {
258 IAM_LOGE("invalid out parameter");
259 return napi_invalid_arg;
260 }
261 napi_status result = CheckNapiType(env, value, napi_string);
262 if (result != napi_ok) {
263 IAM_LOGE("CheckNapiType fail");
264 return result;
265 }
266 size_t maxLen = len;
267 result = napi_get_value_string_utf8(env, value, out, maxLen, &len);
268 if (result != napi_ok) {
269 IAM_LOGE("napi_get_value_string_utf8 fail");
270 }
271 if (maxLen > 0) {
272 out[maxLen - 1] = '\0';
273 }
274 return result;
275 }
276
GetFunctionRef(napi_env env,napi_value value,napi_ref & ref)277 napi_status UserAuthNapiHelper::GetFunctionRef(napi_env env, napi_value value, napi_ref &ref)
278 {
279 napi_status result = CheckNapiType(env, value, napi_function);
280 if (result != napi_ok) {
281 IAM_LOGE("CheckNapiType fail");
282 return result;
283 }
284 result = napi_create_reference(env, value, 1, &ref);
285 if (result != napi_ok) {
286 IAM_LOGE("napi_create_reference fail");
287 }
288 return result;
289 }
290
GetUint8ArrayValue(napi_env env,napi_value value,size_t limitLen,std::vector<uint8_t> & array)291 napi_status UserAuthNapiHelper::GetUint8ArrayValue(napi_env env, napi_value value,
292 size_t limitLen, std::vector<uint8_t> &array)
293 {
294 bool isTypedarray;
295 napi_status result = napi_is_typedarray(env, value, &isTypedarray);
296 if (result != napi_ok) {
297 IAM_LOGE("napi_is_typedarray fail");
298 return result;
299 }
300 if (!isTypedarray) {
301 IAM_LOGE("value is not typedarray");
302 return napi_array_expected;
303 }
304 napi_typedarray_type type;
305 size_t length;
306 void *data;
307 napi_value buffer;
308 size_t offset;
309 result = napi_get_typedarray_info(env, value, &type, &length, &data, &buffer, &offset);
310 if (result != napi_ok) {
311 IAM_LOGE("napi_get_typedarray_info fail");
312 return result;
313 }
314 if (type != napi_uint8_array) {
315 IAM_LOGE("value is not napi_uint8_array");
316 return napi_invalid_arg;
317 }
318 if (length > limitLen) {
319 IAM_LOGE("array length reach limit");
320 return napi_generic_failure;
321 }
322 array.resize(length);
323 if (memcpy_s(array.data(), length, data, length) != EOK) {
324 IAM_LOGE("memcpy_s fail");
325 return napi_generic_failure;
326 }
327 return result;
328 }
329
Uint64ToNapiUint8Array(napi_env env,uint64_t value)330 napi_value UserAuthNapiHelper::Uint64ToNapiUint8Array(napi_env env, uint64_t value)
331 {
332 void *data = nullptr;
333 napi_value arraybuffer = nullptr;
334 size_t length = sizeof(value);
335 NAPI_CALL(env, napi_create_arraybuffer(env, length, &data, &arraybuffer));
336 if (memcpy_s(data, length, reinterpret_cast<const void *>(&value), length) != EOK) {
337 IAM_LOGE("memcpy_s fail");
338 return nullptr;
339 }
340 napi_value result = nullptr;
341 NAPI_CALL(env, napi_create_typedarray(env, napi_uint8_array, length, arraybuffer, 0, &result));
342 return result;
343 }
344
Uint8VectorToNapiUint8Array(napi_env env,std::vector<uint8_t> & value)345 napi_value UserAuthNapiHelper::Uint8VectorToNapiUint8Array(napi_env env, std::vector<uint8_t> &value)
346 {
347 void *data = nullptr;
348 napi_value arraybuffer = nullptr;
349 size_t length = value.size();
350 NAPI_CALL(env, napi_create_arraybuffer(env, length, &data, &arraybuffer));
351 if (memcpy_s(data, length, reinterpret_cast<const void *>(value.data()), length) != EOK) {
352 IAM_LOGE("memcpy_s fail");
353 return nullptr;
354 }
355 napi_value result = nullptr;
356 NAPI_CALL(env, napi_create_typedarray(env, napi_uint8_array, length, arraybuffer, 0, &result));
357 return result;
358 }
359
CallVoidNapiFunc(napi_env env,napi_ref funcRef,size_t argc,const napi_value * argv)360 napi_status UserAuthNapiHelper::CallVoidNapiFunc(napi_env env, napi_ref funcRef, size_t argc, const napi_value *argv)
361 {
362 napi_value funcVal;
363 napi_status ret = napi_get_reference_value(env, funcRef, &funcVal);
364 if (ret != napi_ok) {
365 IAM_LOGE("napi_get_reference_value failed %{public}d", ret);
366 return ret;
367 }
368 napi_value undefined;
369 ret = napi_get_undefined(env, &undefined);
370 if (ret != napi_ok) {
371 IAM_LOGE("napi_get_undefined failed %{public}d", ret);
372 return ret;
373 }
374 napi_value callResult;
375 ret = napi_call_function(env, undefined, funcVal, argc, argv, &callResult);
376 if (ret != napi_ok) {
377 IAM_LOGE("napi_call_function failed %{public}d", ret);
378 }
379 return ret;
380 }
381
SetInt32Property(napi_env env,napi_value obj,const char * name,int32_t value)382 napi_status UserAuthNapiHelper::SetInt32Property(napi_env env, napi_value obj, const char *name, int32_t value)
383 {
384 napi_value napiValue = nullptr;
385 napi_status ret = napi_create_int32(env, value, &napiValue);
386 if (ret != napi_ok) {
387 IAM_LOGE("napi_create_int32 failed %{public}d", ret);
388 return ret;
389 }
390 ret = napi_set_named_property(env, obj, name, napiValue);
391 if (ret != napi_ok) {
392 IAM_LOGE("napi_set_named_property failed %{public}d", ret);
393 }
394 return ret;
395 }
396
SetUint32Property(napi_env env,napi_value obj,const char * name,uint32_t value)397 napi_status UserAuthNapiHelper::SetUint32Property(napi_env env, napi_value obj, const char *name, uint32_t value)
398 {
399 napi_value napiValue = nullptr;
400 napi_status ret = napi_create_uint32(env, value, &napiValue);
401 if (ret != napi_ok) {
402 IAM_LOGE("napi_create_uint32 failed %{public}d", ret);
403 return ret;
404 }
405 ret = napi_set_named_property(env, obj, name, napiValue);
406 if (ret != napi_ok) {
407 IAM_LOGE("napi_set_named_property failed %{public}d", ret);
408 }
409 return ret;
410 }
411
SetUint8ArrayProperty(napi_env env,napi_value obj,const char * name,const std::vector<uint8_t> & value)412 napi_status UserAuthNapiHelper::SetUint8ArrayProperty(napi_env env,
413 napi_value obj, const char *name, const std::vector<uint8_t> &value)
414 {
415 size_t size = value.size();
416 void *data;
417 napi_value buffer;
418 napi_status ret = napi_create_arraybuffer(env, size, &data, &buffer);
419 if (ret != napi_ok) {
420 IAM_LOGE("napi_create_arraybuffer failed %{public}d", ret);
421 return ret;
422 }
423 if (size != 0) {
424 if (memcpy_s(data, size, value.data(), value.size()) != EOK) {
425 IAM_LOGE("memcpy_s failed");
426 return napi_generic_failure;
427 }
428 }
429 napi_value napiValue;
430 ret = napi_create_typedarray(env, napi_uint8_array, size, buffer, 0, &napiValue);
431 if (ret != napi_ok) {
432 IAM_LOGE("napi_create_typedarray failed %{public}d", ret);
433 return ret;
434 }
435 ret = napi_set_named_property(env, obj, name, napiValue);
436 if (ret != napi_ok) {
437 IAM_LOGE("napi_set_named_property failed %{public}d", ret);
438 }
439 return ret;
440 }
441
SetEnrolledStateProperty(napi_env env,napi_value obj,const char * name,EnrolledState & value)442 napi_status UserAuthNapiHelper::SetEnrolledStateProperty(napi_env env, napi_value obj,
443 const char *name, EnrolledState &value)
444 {
445 napi_value napiValue = nullptr;
446 napi_status ret = napi_create_object(env, &napiValue);
447 if (ret != napi_ok) {
448 IAM_LOGE("napi_create_object failed %{public}d", ret);
449 return ret;
450 }
451 int32_t credentialDigest = static_cast<int32_t>(value.credentialDigest);
452 int32_t credentialCount = static_cast<int32_t>(value.credentialCount);
453 IAM_LOGI("get enrolled state success, credentialDigest = %{public}d, credentialCount = %{public}d",
454 credentialDigest, credentialCount);
455 ret = UserAuthNapiHelper::SetInt32Property(env, napiValue, "credentialDigest", credentialDigest);
456 if (ret != napi_ok) {
457 IAM_LOGE("napi_create_int32 failed %{public}d", ret);
458 return ret;
459 }
460 ret = UserAuthNapiHelper::SetInt32Property(env, napiValue, "credentialCount", credentialCount);
461 if (ret != napi_ok) {
462 IAM_LOGE("napi_create_int32 failed %{public}d", ret);
463 return ret;
464 }
465 ret = napi_set_named_property(env, obj, name, napiValue);
466 if (ret != napi_ok) {
467 IAM_LOGE("napi_set_named_property failed %{public}d", ret);
468 }
469 return ret;
470 }
471
GetNamedProperty(napi_env env,napi_value object,const std::string & propertyName)472 napi_value UserAuthNapiHelper::GetNamedProperty(napi_env env, napi_value object, const std::string &propertyName)
473 {
474 napi_value value = nullptr;
475 bool hasProperty = false;
476 NAPI_CALL(env, napi_has_named_property(env, object, propertyName.c_str(), &hasProperty));
477 if (!hasProperty) {
478 return value;
479 }
480 NAPI_CALL(env, napi_get_named_property(env, object, propertyName.c_str(), &value));
481 return value;
482 }
483
GetStringFromValueUtf8(napi_env env,napi_value value)484 std::string UserAuthNapiHelper::GetStringFromValueUtf8(napi_env env, napi_value value)
485 {
486 if (CheckNapiType(env, value, napi_string) != napi_ok) {
487 return "";
488 }
489 std::string result;
490 std::vector<char> str(MAX_STRING_LENGTH + 1, '\0');
491 size_t length = 0;
492 NAPI_CALL(env, napi_get_value_string_utf8(env, value, &str[0], MAX_STRING_LENGTH, &length));
493 if (length > 0) {
494 return result.append(&str[0], length);
495 }
496 return result;
497 }
498
HasNamedProperty(napi_env env,napi_value object,const std::string & propertyName)499 bool UserAuthNapiHelper::HasNamedProperty(napi_env env, napi_value object, const std::string &propertyName)
500 {
501 bool hasProperty = false;
502 NAPI_CALL_BASE(env, napi_has_named_property(env, object, propertyName.c_str(), &hasProperty), false);
503 return hasProperty;
504 }
505
GetStringPropertyUtf8(napi_env env,napi_value object,const std::string & propertyName)506 std::string UserAuthNapiHelper::GetStringPropertyUtf8(napi_env env, napi_value object, const std::string &propertyName)
507 {
508 napi_value value = GetNamedProperty(env, object, propertyName);
509 napi_status result = CheckNapiType(env, value, napi_string);
510 if (result != napi_ok) {
511 return "";
512 }
513 return GetStringFromValueUtf8(env, value);
514 }
515
SetStringPropertyUtf8(napi_env env,napi_value object,const std::string & name,const std::string & value)516 bool UserAuthNapiHelper::SetStringPropertyUtf8(
517 napi_env env, napi_value object, const std::string &name, const std::string &value)
518 {
519 napi_value jsValue = nullptr;
520 if (napi_create_string_utf8(env, value.c_str(), strlen(value.c_str()), &jsValue) != napi_ok) {
521 IAM_LOGE("get string error");
522 return false;
523 }
524 napi_valuetype valueType = napi_undefined;
525 NAPI_CALL_BASE(env, napi_typeof(env, jsValue, &valueType), napi_undefined);
526 napi_set_named_property(env, object, name.c_str(), jsValue);
527 return true;
528 }
529
GetInt32Array(napi_env env,napi_value obj,std::vector<uint32_t> vec)530 bool UserAuthNapiHelper::GetInt32Array(napi_env env, napi_value obj, std::vector<uint32_t> vec)
531 {
532 vec.clear();
533 uint32_t len;
534 napi_get_array_length(env, obj, &len);
535 IAM_LOGI("GetInt32Array length: %{public}d", len);
536 for (uint32_t index = 0; index < len; index++) {
537 napi_value value;
538 uint32_t getValue;
539 NAPI_CALL_BASE(env, napi_get_element(env, obj, index, &value), napi_undefined);
540 NAPI_CALL_BASE(env, napi_get_value_uint32(env, value, &getValue), napi_undefined);
541 IAM_LOGI("vec[%{public}d]: %{public}d", index, len);
542 vec.emplace_back(getValue);
543 }
544 return true;
545 }
546
CheckAuthType(int32_t authType)547 bool UserAuthNapiHelper::CheckAuthType(int32_t authType)
548 {
549 if (authType != AuthType::FACE && authType != AuthType::FINGERPRINT) {
550 IAM_LOGE("authType check fail:%{public}d", authType);
551 return false;
552 }
553 return true;
554 }
555 } // namespace UserAuth
556 } // namespace UserIam
557 } // namespace OHOS