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