已合并
新增远程授权验证接口 #436
LinShen创建于 7月4日
新增远程授权验证接口 #436
已合并
共 66 个文件变更+6538-246
| @@ -63,18 +63,19 @@ group("asset_bin_test") { | |||
| 63 | #"test/secure_access_fence/agent_fence:saf_agent_fence_grant_tool_permissions_by_user_local_test" | 63 | #"test/secure_access_fence/agent_fence:saf_agent_fence_grant_tool_permissions_by_user_local_test" |
| 64 | ] | 64 | ] |
| 65 | if (enable_local_test) { | 65 | if (enable_local_test) { |
| 66 | - deps += [ "test/asset/unittest/inner_kits_rust:rust_asset_inner_test" ] | 66 | + #deps += [ "test/asset/unittest/inner_kits_rust:rust_asset_inner_test" ] |
| 67 | - deps += [ "test/asset/unittest/module_test:rust_asset_module_test" ] | 67 | + #deps += [ "test/asset/unittest/module_test:rust_asset_module_test" ] |
| 68 | - deps += [ "services/asset/common:rust_asset_calling_info_test" ] | 68 | + #deps += [ "services/asset/common:rust_asset_calling_info_test" ] |
| 69 | - deps += [ "services/asset/db_operator:rust_asset_db_operator_test" ] | 69 | + #deps += [ "services/asset/db_operator:rust_asset_db_operator_test" ] |
| 70 | - deps += [ "test/asset/unittest/ut_test/services/core_service:rust_asset_core_service_ut_test" ] | 70 | + #deps += [ "test/asset/unittest/ut_test/services/core_service:rust_asset_core_service_ut_test" ] |
| 71 | - deps += [ "test/secure_access_fence/agent_fence:saf_agent_fence_test" ] | 71 | + deps += [ "test/secure_access_fence/unittest/ut_test/services/core_service:rust_saf_core_service_ut_test" ] |
| 72 | - deps += [ "test/secure_access_fence/agent_fence:saf_agent_fence_manual_test" ] | 72 | + #deps += [ "test/secure_access_fence/agent_fence:saf_agent_fence_test" ] |
| 73 | - deps += [ "test/secure_access_fence/agent_fence:saf_agent_fence_stress_test" ] | 73 | + #deps += [ "test/secure_access_fence/agent_fence:saf_agent_fence_manual_test" ] |
| 74 | - deps += [ "test/secure_access_fence/agent_fence:saf_agent_fence_no_permission_test" ] | 74 | + #deps += [ "test/secure_access_fence/agent_fence:saf_agent_fence_stress_test" ] |
| 75 | - deps += [ "test/secure_access_fence/agent_fence:saf_agent_fence_screen_lock_test" ] | 75 | + #deps += [ "test/secure_access_fence/agent_fence:saf_agent_fence_no_permission_test" ] |
| 76 | - deps += [ "test/secure_access_fence/agent_fence:saf_agent_fence_screen_lock_hap_identity_test" ] | 76 | + #deps += [ "test/secure_access_fence/agent_fence:saf_agent_fence_screen_lock_test" ] |
| 77 | - deps += [ "test/secure_access_fence/agent_fence:saf_agent_fence_request_tool_permissions_local_test" ] | 77 | + #deps += [ "test/secure_access_fence/agent_fence:saf_agent_fence_screen_lock_hap_identity_test" ] |
| 78 | - deps += [ "test/secure_access_fence/agent_fence:saf_agent_fence_grant_tool_permissions_by_user_local_test" ] | 78 | + #deps += [ "test/secure_access_fence/agent_fence:saf_agent_fence_request_tool_permissions_local_test" ] |
| 79 | + #deps += [ "test/secure_access_fence/agent_fence:saf_agent_fence_grant_tool_permissions_by_user_local_test" ] | ||
| 79 | } | 80 | } |
| 80 | } | 81 | } |
| @@ -66,7 +66,8 @@ | |||
| 66 | "ylong_runtime", | 66 | "ylong_runtime", |
| 67 | "openssl", | 67 | "openssl", |
| 68 | "screenlock_mgr", | 68 | "screenlock_mgr", |
| 69 | - "time_service" | 69 | + "time_service", |
| 70 | + "init" | ||
| 70 | ], | 71 | ], |
| 71 | "third_party": [] | 72 | "third_party": [] |
| 72 | }, | 73 | }, |
| @@ -26,7 +26,8 @@ | |||
| 26 | "ohos.permission.MANAGE_TRUSTED_RING", | 26 | "ohos.permission.MANAGE_TRUSTED_RING", |
| 27 | "ohos.permission.USE_TRUSTED_RING", | 27 | "ohos.permission.USE_TRUSTED_RING", |
| 28 | "ohos.permission.INTERACT_ACROSS_LOCAL_ACCOUNTS", | 28 | "ohos.permission.INTERACT_ACROSS_LOCAL_ACCOUNTS", |
| 29 | - "ohos.permission.MANAGE_LOCAL_ACCOUNTS" | 29 | + "ohos.permission.MANAGE_LOCAL_ACCOUNTS", |
| 30 | + "ohos.permission.sec.ACCESS_UDID" | ||
| 30 | ], | 31 | ], |
| 31 | "permission_acls" : [ | 32 | "permission_acls" : [ |
| 32 | "ohos.permission.REMOVE_PERMISSION_USED_RECORD" | 33 | "ohos.permission.REMOVE_PERMISSION_USED_RECORD" |
| @@ -84,6 +84,8 @@ napi_status NapiGetProperty(const napi_env env, napi_value object, bool &value); | |||
| 84 | 84 | ||
| 85 | napi_status NapiGetProperty(const napi_env env, napi_value object, int32_t &value); | 85 | napi_status NapiGetProperty(const napi_env env, napi_value object, int32_t &value); |
| 86 | 86 | ||
| 87 | +napi_status NapiGetProperty(const napi_env env, napi_value object, int64_t &value); | ||
| 88 | + | ||
| 87 | napi_status NapiGetProperty(const napi_env env, napi_value object, std::string &value); | 89 | napi_status NapiGetProperty(const napi_env env, napi_value object, std::string &value); |
| 88 | 90 | ||
| 89 | napi_status NapiGetProperty(const napi_env env, napi_value object, std::vector<SAF::OperationInfo> &operationInfo); | 91 | napi_status NapiGetProperty(const napi_env env, napi_value object, std::vector<SAF::OperationInfo> &operationInfo); |
| @@ -104,6 +106,30 @@ napi_status NapiGetProperty(const napi_env env, napi_value object, SAF::RemoteCo | |||
| 104 | 106 | ||
| 105 | napi_status NapiGetProperty(const napi_env env, napi_value object, SAF::RemoteInfo &remoteInfo); | 107 | napi_status NapiGetProperty(const napi_env env, napi_value object, SAF::RemoteInfo &remoteInfo); |
| 106 | 108 | ||
| 109 | +napi_status NapiGetProperty(const napi_env env, napi_value object, | ||
| 110 | + std::vector<SAF::PermissionQuery> &permissionQueryVector); | ||
| 111 | + | ||
| 112 | +napi_status NapiGetProperty(const napi_env env, napi_value object, | ||
| 113 | + std::vector<SAF::RemoteAuthPackage> &remoteAuthPackageVector); | ||
| 114 | + | ||
| 115 | +napi_status NapiGetProperty(const napi_env env, napi_value object, | ||
| 116 | + SAF::RemoteAuthPackage &remoteAuthPackage); | ||
| 117 | + | ||
| 118 | +napi_status NapiGetProperty(const napi_env env, napi_value object, | ||
| 119 | + SAF::RemoteUserAuthItem &remoteUserAuthItem); | ||
| 120 | + | ||
| 121 | +napi_status NapiGetProperty(const napi_env env, napi_value object, | ||
| 122 | + std::vector<SAF::RemoteUserAuthItem> &remoteUserAuthItemVector); | ||
| 123 | + | ||
| 124 | +napi_status NapiGetProperty(const napi_env env, napi_value object, | ||
| 125 | + SAF::RemoteUserAuthResults &remoteUserAuthResults); | ||
| 126 | + | ||
| 127 | +napi_status NapiGetProperty(const napi_env env, napi_value object, | ||
| 128 | + std::vector<SAF::RemoteUserAuthResults> &remoteUserAuthResultsVector); | ||
| 129 | + | ||
| 130 | +napi_status NapiSetProperty(const napi_env env, napi_value object, const char *propertyName, | ||
| 131 | + const SAF::RemoteAuthPackage &remoteAuthPackage); | ||
| 132 | + | ||
| 107 | napi_status NapiSetProperty(const napi_env env, napi_value object, const char *propertyName, const bool value); | 133 | napi_status NapiSetProperty(const napi_env env, napi_value object, const char *propertyName, const bool value); |
| 108 | 134 | ||
| 109 | napi_status NapiSetProperty(const napi_env env, napi_value object, const char *propertyName, const int32_t value); | 135 | napi_status NapiSetProperty(const napi_env env, napi_value object, const char *propertyName, const int32_t value); |
| @@ -12,6 +12,8 @@ | |||
| 12 | * See the License for the specific language governing permissions and | 12 | * See the License for the specific language governing permissions and |
| 13 | * limitations under the License. | 13 | * limitations under the License. |
| 14 | */ | 14 | */ |
| 15 | + | ||
| 16 | + | ||
| 15 | 17 | ||
| 16 | 18 | ||
| 17 | 19 | ||
| @@ -22,8 +24,9 @@ namespace OHOS { | |||
| 22 | namespace Security { | 24 | namespace Security { |
| 23 | namespace SAF_ASSET_COMMON { | 25 | namespace SAF_ASSET_COMMON { |
| 24 | namespace { | 26 | namespace { |
| 25 | - #define MAX_BUFF_SIZE 4096 // 4KB | 27 | + constexpr uint32_t MAX_BUFF_SIZE = 1024 * 25; // 25KB |
| 26 | constexpr uint32_t MAX_PERMISSION_NAME_SIZE = 256; | 28 | constexpr uint32_t MAX_PERMISSION_NAME_SIZE = 256; |
| 29 | + constexpr uint32_t MAX_REMOTE_QUERY_ARRAY_SIZE = 10; | ||
| 27 | AgentFenceErrorCode MapErrorCode(const int32_t safResult) | 30 | AgentFenceErrorCode MapErrorCode(const int32_t safResult) |
| 28 | { | 31 | { |
| 29 | switch (safResult) { | 32 | switch (safResult) { |
| @@ -156,14 +159,24 @@ napi_status NapiGetProperty(const napi_env env, napi_value object, int32_t &valu | |||
| 156 | return napi_ok; | 159 | return napi_ok; |
| 157 | } | 160 | } |
| 158 | 161 | ||
| 162 | +napi_status NapiGetProperty(const napi_env env, napi_value object, int64_t &value) | ||
| 163 | +{ | ||
| 164 | + NAPI_RETURN_IF_VALUE_UNDEFINED(env, object); | ||
| 165 | + NAPI_CALL_RETURN_ERR(env, napi_get_value_int64(env, object, &value)); | ||
| 166 | + return napi_ok; | ||
| 167 | +} | ||
| 168 | + | ||
| 159 | napi_status NapiGetProperty(const napi_env env, napi_value object, std::string &value) | 169 | napi_status NapiGetProperty(const napi_env env, napi_value object, std::string &value) |
| 160 | { | 170 | { |
| 161 | NAPI_RETURN_IF_VALUE_UNDEFINED(env, object); | 171 | NAPI_RETURN_IF_VALUE_UNDEFINED(env, object); |
| 162 | NAPI_THROW_RETURN_ERR(env, type != napi_string, INVALID_PARAMETER, "Invalid type. Expect string"); | 172 | NAPI_THROW_RETURN_ERR(env, type != napi_string, INVALID_PARAMETER, "Invalid type. Expect string"); |
| 163 | - char buffer[MAX_BUFF_SIZE] = { 0 }; | ||
| 164 | size_t length = 0; | 173 | size_t length = 0; |
| 165 | - NAPI_CALL_RETURN_ERR(env, napi_get_value_string_utf8(env, object, buffer, MAX_BUFF_SIZE, &length)); | 174 | + NAPI_CALL_RETURN_ERR(env, napi_get_value_string_utf8(env, object, nullptr, 0, &length)); |
| 166 | - value = buffer; | 175 | + NAPI_THROW_RETURN_ERR(env, length >= MAX_BUFF_SIZE, INVALID_PARAMETER, |
| 176 | + "String length exceeds MAX_BUFF_SIZE"); | ||
| 177 | + std::vector<char> buffer(length + 1, 0); | ||
| 178 | + NAPI_CALL_RETURN_ERR(env, napi_get_value_string_utf8(env, object, buffer.data(), length + 1, &length)); | ||
| 179 | + value = buffer.data(); | ||
| 167 | return napi_ok; | 180 | return napi_ok; |
| 168 | } | 181 | } |
| 169 | 182 | ||
| @@ -439,6 +452,145 @@ napi_status NapiSetProperty(const napi_env env, napi_value object, const char *p | |||
| 439 | return napi_ok; | 452 | return napi_ok; |
| 440 | } | 453 | } |
| 441 | 454 | ||
| 455 | +napi_status NapiGetProperty(const napi_env env, napi_value object, | ||
| 456 | + std::vector<SAF::PermissionQuery> &permissionQueryVector) | ||
| 457 | +{ | ||
| 458 | + NAPI_RETURN_IF_VALUE_UNDEFINED(env, object); | ||
| 459 | + uint32_t count = 0; | ||
| 460 | + NAPI_CALL_RETURN_ERR(env, napi_get_array_length(env, object, &count)); | ||
| 461 | + NAPI_THROW_RETURN_ERR(env, count == 0, GENERAL_PARAMETER_ERROR, "PermissionQuery cannot be empty"); | ||
| 462 | + NAPI_THROW_RETURN_ERR(env, count > MAX_REMOTE_QUERY_ARRAY_SIZE, GENERAL_PARAMETER_ERROR, | ||
| 463 | + "PermissionQuery count exceeds limit"); | ||
| 464 | + for (uint32_t i = 0; i < count; ++i) { | ||
| 465 | + napi_value item = nullptr; | ||
| 466 | + NAPI_CALL_RETURN_ERR(env, napi_get_element(env, object, i, &item)); | ||
| 467 | + SAF::PermissionQuery query; | ||
| 468 | + NAPI_CALL_RETURN_ERR(env, NapiGetProperty(env, item, query)); | ||
| 469 | + permissionQueryVector.emplace_back(query); | ||
| 470 | + } | ||
| 471 | + return napi_ok; | ||
| 472 | +} | ||
| 473 | + | ||
| 474 | +napi_status NapiGetProperty(const napi_env env, napi_value object, | ||
| 475 | + std::vector<SAF::RemoteAuthPackage> &remoteAuthPackageVector) | ||
| 476 | +{ | ||
| 477 | + NAPI_RETURN_IF_VALUE_UNDEFINED(env, object); | ||
| 478 | + uint32_t count = 0; | ||
| 479 | + NAPI_CALL_RETURN_ERR(env, napi_get_array_length(env, object, &count)); | ||
| 480 | + NAPI_THROW_RETURN_ERR(env, count == 0, GENERAL_PARAMETER_ERROR, "RemoteAuthPackage cannot be empty"); | ||
| 481 | + for (uint32_t i = 0; i < count; ++i) { | ||
| 482 | + napi_value item = nullptr; | ||
| 483 | + NAPI_CALL_RETURN_ERR(env, napi_get_element(env, object, i, &item)); | ||
| 484 | + SAF::RemoteAuthPackage pkg; | ||
| 485 | + NAPI_CALL_RETURN_ERR(env, NapiGetProperty(env, item, pkg)); | ||
| 486 | + remoteAuthPackageVector.emplace_back(pkg); | ||
| 487 | + } | ||
| 488 | + return napi_ok; | ||
| 489 | +} | ||
| 490 | + | ||
| 491 | +napi_status NapiGetProperty(const napi_env env, napi_value object, | ||
| 492 | + SAF::RemoteAuthPackage &remoteAuthPackage) | ||
| 493 | +{ | ||
| 494 | + NAPI_RETURN_IF_VALUE_UNDEFINED(env, object); | ||
| 495 | + napi_value propValue; | ||
| 496 | + | ||
| 497 | + napi_status status = napi_get_named_property(env, object, "remoteMessage", &propValue); | ||
| 498 | + NAPI_THROW_RETURN_ERR(env, status != napi_ok, INVALID_PARAMETER, "remoteMessage is required"); | ||
| 499 | + NAPI_CALL_RETURN_ERR(env, NapiGetProperty(env, propValue, remoteAuthPackage.remoteMessage)); | ||
| 500 | + | ||
| 501 | + status = napi_get_named_property(env, object, "challenge", &propValue); | ||
| 502 | + NAPI_THROW_RETURN_ERR(env, status != napi_ok, INVALID_PARAMETER, "challenge is required"); | ||
| 503 | + NAPI_CALL_RETURN_ERR(env, NapiGetProperty(env, propValue, remoteAuthPackage.challenge)); | ||
| 504 | + | ||
| 505 | + status = napi_get_named_property(env, object, "ticket", &propValue); | ||
| 506 | + NAPI_THROW_RETURN_ERR(env, status != napi_ok, INVALID_PARAMETER, "ticket is required"); | ||
| 507 | + NAPI_CALL_RETURN_ERR(env, NapiGetProperty(env, propValue, remoteAuthPackage.ticket)); | ||
| 508 | + | ||
| 509 | + return napi_ok; | ||
| 510 | +} | ||
| 511 | + | ||
| 512 | +napi_status NapiGetProperty(const napi_env env, napi_value object, | ||
| 513 | + SAF::RemoteUserAuthItem &remoteUserAuthItem) | ||
| 514 | +{ | ||
| 515 | + NAPI_RETURN_IF_VALUE_UNDEFINED(env, object); | ||
| 516 | + napi_value propValue; | ||
| 517 | + | ||
| 518 | + napi_status status = napi_get_named_property(env, object, "permission", &propValue); | ||
| 519 | + NAPI_THROW_RETURN_ERR(env, status != napi_ok, INVALID_PARAMETER, "permission is required"); | ||
| 520 | + NAPI_CALL_RETURN_ERR(env, NapiGetProperty(env, propValue, remoteUserAuthItem.permission)); | ||
| 521 | + | ||
| 522 | + status = napi_get_named_property(env, object, "authResult", &propValue); | ||
| 523 | + NAPI_THROW_RETURN_ERR(env, status != napi_ok, INVALID_PARAMETER, "authResult is required"); | ||
| 524 | + NAPI_CALL_RETURN_ERR(env, NapiGetProperty(env, propValue, remoteUserAuthItem.authResult)); | ||
| 525 | + | ||
| 526 | + return napi_ok; | ||
| 527 | +} | ||
| 528 | + | ||
| 529 | +napi_status NapiGetProperty(const napi_env env, napi_value object, | ||
| 530 | + std::vector<SAF::RemoteUserAuthItem> &remoteUserAuthItemVector) | ||
| 531 | +{ | ||
| 532 | + NAPI_RETURN_IF_VALUE_UNDEFINED(env, object); | ||
| 533 | + uint32_t count = 0; | ||
| 534 | + NAPI_CALL_RETURN_ERR(env, napi_get_array_length(env, object, &count)); | ||
| 535 | + NAPI_THROW_RETURN_ERR(env, count == 0, GENERAL_PARAMETER_ERROR, "RemoteUserAuthItem cannot be empty"); | ||
| 536 | + for (uint32_t i = 0; i < count; ++i) { | ||
| 537 | + napi_value item = nullptr; | ||
| 538 | + NAPI_CALL_RETURN_ERR(env, napi_get_element(env, object, i, &item)); | ||
| 539 | + SAF::RemoteUserAuthItem remoteUserAuthItem; | ||
| 540 | + NAPI_CALL_RETURN_ERR(env, NapiGetProperty(env, item, remoteUserAuthItem)); | ||
| 541 | + remoteUserAuthItemVector.emplace_back(remoteUserAuthItem); | ||
| 542 | + } | ||
| 543 | + return napi_ok; | ||
| 544 | +} | ||
| 545 | + | ||
| 546 | +napi_status NapiGetProperty(const napi_env env, napi_value object, | ||
| 547 | + SAF::RemoteUserAuthResults &remoteUserAuthResults) | ||
| 548 | +{ | ||
| 549 | + NAPI_RETURN_IF_VALUE_UNDEFINED(env, object); | ||
| 550 | + napi_value propValue; | ||
| 551 | + | ||
| 552 | + napi_status status = napi_get_named_property(env, object, "results", &propValue); | ||
| 553 | + NAPI_THROW_RETURN_ERR(env, status != napi_ok, INVALID_PARAMETER, "results is required"); | ||
| 554 | + NAPI_CALL_RETURN_ERR(env, NapiGetProperty(env, propValue, remoteUserAuthResults.results)); | ||
| 555 | + | ||
| 556 | + status = napi_get_named_property(env, object, "permissionQuery", &propValue); | ||
| 557 | + NAPI_THROW_RETURN_ERR(env, status != napi_ok, INVALID_PARAMETER, "permissionQuery is required"); | ||
| 558 | + NAPI_CALL_RETURN_ERR(env, NapiGetProperty(env, propValue, remoteUserAuthResults.permissionQuery)); | ||
| 559 | + | ||
| 560 | + return napi_ok; | ||
| 561 | +} | ||
| 562 | + | ||
| 563 | +napi_status NapiGetProperty(const napi_env env, napi_value object, | ||
| 564 | + std::vector<SAF::RemoteUserAuthResults> &remoteUserAuthResultsVector) | ||
| 565 | +{ | ||
| 566 | + NAPI_RETURN_IF_VALUE_UNDEFINED(env, object); | ||
| 567 | + uint32_t count = 0; | ||
| 568 | + NAPI_CALL_RETURN_ERR(env, napi_get_array_length(env, object, &count)); | ||
| 569 | + NAPI_THROW_RETURN_ERR(env, count == 0, GENERAL_PARAMETER_ERROR, "RemoteUserAuthResults cannot be empty"); | ||
| 570 | + NAPI_THROW_RETURN_ERR(env, count > MAX_REMOTE_QUERY_ARRAY_SIZE, GENERAL_PARAMETER_ERROR, | ||
| 571 | + "RemoteUserAuthResults count exceeds limit 10"); | ||
| 572 | + for (uint32_t i = 0; i < count; ++i) { | ||
| 573 | + napi_value item = nullptr; | ||
| 574 | + NAPI_CALL_RETURN_ERR(env, napi_get_element(env, object, i, &item)); | ||
| 575 | + SAF::RemoteUserAuthResults remoteUserAuthResults; | ||
| 576 | + NAPI_CALL_RETURN_ERR(env, NapiGetProperty(env, item, remoteUserAuthResults)); | ||
| 577 | + remoteUserAuthResultsVector.emplace_back(remoteUserAuthResults); | ||
| 578 | + } | ||
| 579 | + return napi_ok; | ||
| 580 | +} | ||
| 581 | + | ||
| 582 | +napi_status NapiSetProperty(const napi_env env, napi_value object, const char *propertyName, | ||
| 583 | + const SAF::RemoteAuthPackage &remoteAuthPackage) | ||
| 584 | +{ | ||
| 585 | + napi_value jsResult = nullptr; | ||
| 586 | + NAPI_CALL_RETURN_ERR(env, napi_create_object(env, &jsResult)); | ||
| 587 | + NAPI_CALL_RETURN_ERR(env, NapiSetProperty(env, jsResult, "remoteMessage", remoteAuthPackage.remoteMessage)); | ||
| 588 | + NAPI_CALL_RETURN_ERR(env, NapiSetProperty(env, jsResult, "challenge", remoteAuthPackage.challenge)); | ||
| 589 | + NAPI_CALL_RETURN_ERR(env, NapiSetProperty(env, jsResult, "ticket", remoteAuthPackage.ticket)); | ||
| 590 | + NAPI_CALL_RETURN_ERR(env, napi_set_named_property(env, object, propertyName, jsResult)); | ||
| 591 | + return napi_ok; | ||
| 592 | +} | ||
| 593 | + | ||
| 442 | napi_value CreateAsyncWork(napi_env env, napi_callback_info info, std::unique_ptr<SAF::AgentFenceAsyncContext> context, | 594 | napi_value CreateAsyncWork(napi_env env, napi_callback_info info, std::unique_ptr<SAF::AgentFenceAsyncContext> context, |
| 443 | const char *resourceName) | 595 | const char *resourceName) |
| 444 | { | 596 | { |
| @@ -173,6 +173,12 @@ impl_enum_trait! { | |||
| 173 | /// The error code indicates that invalid plugin. | 173 | /// The error code indicates that invalid plugin. |
| 174 | InvalidPlugin = 0x3001B, | 174 | InvalidPlugin = 0x3001B, |
| 175 | 175 | ||
| 176 | + /// The error code indicates that invalid argument. | ||
| 177 | + InvalidArgument = 0x3001C, | ||
| 178 | + | ||
| 179 | + /// The error code indicates that invalid argument size. | ||
| 180 | + InvalidArgSize = 0x3001D, | ||
| 181 | + | ||
| 176 | // ==================== PERMISSION (0x32000) ==================== | 182 | // ==================== PERMISSION (0x32000) ==================== |
| 177 | /// The error code indicates that the caller doesn't have the permission. | 183 | /// The error code indicates that the caller doesn't have the permission. |
| 178 | PermissionDenied = 0x32001, | 184 | PermissionDenied = 0x32001, |
| @@ -190,6 +196,9 @@ impl_enum_trait! { | |||
| 190 | /// The error code indicates that base64 invalid character. | 196 | /// The error code indicates that base64 invalid character. |
| 191 | Base64InvalidChar = 0x33004, | 197 | Base64InvalidChar = 0x33004, |
| 192 | 198 | ||
| 199 | + /// The error code indicates that json parse failed. | ||
| 200 | + JsonParseError = 0x33005, | ||
| 201 | + | ||
| 193 | // ==================== CLI_TOOL (0x20000) ==================== | 202 | // ==================== CLI_TOOL (0x20000) ==================== |
| 194 | /// The error code indicates that the operation of calling Tool Service is failed. | 203 | /// The error code indicates that the operation of calling Tool Service is failed. |
| 195 | ToolError = 0x20001, | 204 | ToolError = 0x20001, |
| @@ -252,6 +261,14 @@ impl_enum_trait! { | |||
| 252 | 261 | ||
| 253 | /// The error code indicates that screen is locked. | 262 | /// The error code indicates that screen is locked. |
| 254 | ScreenIsLocked = 0x22002, | 263 | ScreenIsLocked = 0x22002, |
| 264 | + | ||
| 265 | + // ==================== DEVICE_MANAGER (0x23000) ==================== | ||
| 266 | + /// The error code indicates that get udid failed | ||
| 267 | + GetUdidFailed = 0x23001, | ||
| 268 | + | ||
| 269 | + // ==================== FILE_OPERATION (0x24000) ==================== | ||
| 270 | + /// The error code indicates that file operation failed. | ||
| 271 | + FileOperationError = 0x24001, | ||
| 255 | } | 272 | } |
| 256 | } | 273 | } |
| 257 | 274 | ||
| @@ -302,3 +319,183 @@ pub trait Verify { | |||
| 302 | /// Verify is correct enum. | 319 | /// Verify is correct enum. |
| 303 | fn is_correct_enum(val: u32) -> bool; | 320 | fn is_correct_enum(val: u32) -> bool; |
| 304 | } | 321 | } |
| 322 | + | ||
| 323 | +// ============================================================================ | ||
| 324 | +// Enums | ||
| 325 | +// ============================================================================ | ||
| 326 | + | ||
| 327 | +/// Operation type enum. | ||
| 328 | + | ||
| 329 | +pub enum OperationType { | ||
| 330 | + /// CLI operation type. | ||
| 331 | + Cli = 1, | ||
| 332 | + /// API operation type. | ||
| 333 | + Api = 2, | ||
| 334 | +} | ||
| 335 | + | ||
| 336 | +/// Role enum. | ||
| 337 | + | ||
| 338 | +pub enum Role { | ||
| 339 | + /// Controller role. | ||
| 340 | + Controller = 1, | ||
| 341 | + /// Controlled role. | ||
| 342 | + Controlled = 2, | ||
| 343 | +} | ||
| 344 | + | ||
| 345 | +/// Grant type enum. | ||
| 346 | + | ||
| 347 | +pub enum GrantType { | ||
| 348 | + /// Local grant. | ||
| 349 | + LocalGrant = 0x01, | ||
| 350 | + /// Remote grant. | ||
| 351 | + RemoteGrant = 0x02, | ||
| 352 | +} | ||
| 353 | +// ============================================================================ | ||
| 354 | +// Basic Structures | ||
| 355 | +// ============================================================================ | ||
| 356 | + | ||
| 357 | +/// Command info structure. | ||
| 358 | + | ||
| 359 | +pub struct CommandInfo { | ||
| 360 | + /// Command name. | ||
| 361 | + pub cmd_name: String, | ||
| 362 | + /// Sub command. | ||
| 363 | + pub sub_cmd: String, | ||
| 364 | +} | ||
| 365 | + | ||
| 366 | +/// Operation info structure. | ||
| 367 | + | ||
| 368 | +pub struct OperationInfo { | ||
| 369 | + /// Operation type. | ||
| 370 | + pub operation_type: OperationType, | ||
| 371 | + /// CLI command info. | ||
| 372 | + pub cli_cmd_info: CommandInfo, | ||
| 373 | + /// Permission string. | ||
| 374 | + pub permission: String, | ||
| 375 | +} | ||
| 376 | + | ||
| 377 | +/// Verify ticket info structure (matching IDL definition). | ||
| 378 | + | ||
| 379 | +pub struct VerifyTicketInfo { | ||
| 380 | + /// Message for ticket. | ||
| 381 | + pub message: String, | ||
| 382 | + /// Challenge string. | ||
| 383 | + pub challenge: String, | ||
| 384 | + /// Ticket string. | ||
| 385 | + pub ticket: String, | ||
| 386 | +} | ||
| 387 | + | ||
| 388 | +/// CLI info structure (matching IDL definition). | ||
| 389 | + | ||
| 390 | +pub struct CliInfo { | ||
| 391 | + /// Caller token ID. | ||
| 392 | + pub caller_token_id: String, | ||
| 393 | + /// CLI command name. | ||
| 394 | + pub cli_cmd_name: String, | ||
| 395 | + /// Sub CLI command name. | ||
| 396 | + pub sub_cli_cmd_name: String, | ||
| 397 | + /// Permission list for the CLI command. | ||
| 398 | + pub permission_list: Vec<String>, | ||
| 399 | +} | ||
| 400 | + | ||
| 401 | +/// Remote control params structure. | ||
| 402 | + | ||
| 403 | +pub struct RemoteControlParams { | ||
| 404 | + /// Challenge string. | ||
| 405 | + pub challenge: String, | ||
| 406 | + /// Remote control ticket. | ||
| 407 | + pub remote_control_ticket: String, | ||
| 408 | + /// Controlled device name. | ||
| 409 | + pub controlled_device_name: String, | ||
| 410 | + /// Controller device name. | ||
| 411 | + pub controller_device_name: String, | ||
| 412 | + /// Sign verify message. | ||
| 413 | + pub sign_verify_msg: String, | ||
| 414 | +} | ||
| 415 | + | ||
| 416 | +/// Remote info structure. | ||
| 417 | + | ||
| 418 | +pub struct RemoteInfo { | ||
| 419 | + /// Role. | ||
| 420 | + pub role: Role, | ||
| 421 | + /// Remote ID. | ||
| 422 | + pub remote_id: String, | ||
| 423 | + /// Domain ID. | ||
| 424 | + pub domain_id: String, | ||
| 425 | + /// Remote control params. | ||
| 426 | + pub remote_control_params: RemoteControlParams, | ||
| 427 | +} | ||
| 428 | + | ||
| 429 | +/// Permission query structure. | ||
| 430 | + | ||
| 431 | +pub struct PermissionQuery { | ||
| 432 | + /// Operation info list. | ||
| 433 | + pub operation_info: Vec<OperationInfo>, | ||
| 434 | + /// Need ticket flag. | ||
| 435 | + pub need_ticket: bool, | ||
| 436 | + /// Ticket expire time in milliseconds. | ||
| 437 | + pub ticket_expire_time_ms: i32, | ||
| 438 | + /// Caller token ID. | ||
| 439 | + pub caller_token_id: i32, | ||
| 440 | + /// Domain ID. | ||
| 441 | + pub domain_id: String, | ||
| 442 | + /// Remote info. | ||
| 443 | + pub remote_info: RemoteInfo, | ||
| 444 | +} | ||
| 445 | + | ||
| 446 | +// ============================================================================ | ||
| 447 | +// Remote Message Structures | ||
| 448 | +// ============================================================================ | ||
| 449 | + | ||
| 450 | +/// Device ID header structure. | ||
| 451 | + | ||
| 452 | +pub struct DeviceIdHeader { | ||
| 453 | + /// Controller device ID. | ||
| 454 | + pub controller_device_id: String, | ||
| 455 | + /// Controlled device ID. | ||
| 456 | + pub controlled_device_id: String, | ||
| 457 | +} | ||
| 458 | + | ||
| 459 | +/// Remote message structure. | ||
| 460 | + | ||
| 461 | +pub struct RemoteMessage { | ||
| 462 | + /// Device info header. | ||
| 463 | + pub device_info: DeviceIdHeader, | ||
| 464 | + /// Remote auth message. | ||
| 465 | + pub remote_auth_message: String, | ||
| 466 | + /// Caller bundle name resolved from caller_token_id | ||
| 467 | + pub caller_bundle_name: String, | ||
| 468 | +} | ||
| 469 | + | ||
| 470 | +/// Remote auth package structure. | ||
| 471 | + | ||
| 472 | +pub struct RemoteAuthPackage { | ||
| 473 | + /// Remote message (JSON string in IDL, struct in Rust for convenience). | ||
| 474 | + pub remote_message: RemoteMessage, | ||
| 475 | + /// Challenge string. | ||
| 476 | + pub challenge: String, | ||
| 477 | + /// Ticket string. | ||
| 478 | + pub ticket: String, | ||
| 479 | +} | ||
| 480 | + | ||
| 481 | +// ============================================================================ | ||
| 482 | +// Controller Device Structures | ||
| 483 | +// ============================================================================ | ||
| 484 | + | ||
| 485 | +/// Remote user auth item structure. | ||
| 486 | + | ||
| 487 | +pub struct RemoteUserAuthItem { | ||
| 488 | + /// Permission string. | ||
| 489 | + pub permission: String, | ||
| 490 | + /// Authentication result string. | ||
| 491 | + pub auth_result: String, | ||
| 492 | +} | ||
| 493 | + | ||
| 494 | +/// Remote user auth results structure. | ||
| 495 | + | ||
| 496 | +pub struct RemoteUserAuthResults { | ||
| 497 | + /// List of authentication result items. | ||
| 498 | + pub results: Vec<RemoteUserAuthItem>, | ||
| 499 | + /// Permission query associated with the authentication. | ||
| 500 | + pub permission_query: PermissionQuery, | ||
| 501 | +} | ||
| @@ -174,6 +174,83 @@ int32_t SafAgentFence::GrantToolPermissionsByUser( | |||
| 174 | return resultCode; | 174 | return resultCode; |
| 175 | } | 175 | } |
| 176 | 176 | ||
| 177 | +int32_t SafAgentFence::GenerateControlledDevicePackage( | ||
| 178 | + const std::vector<PermissionQuery> &permissionQuery, | ||
| 179 | + std::vector<RemoteAuthPackage> &remoteAuthPackage) | ||
| 180 | +{ | ||
| 181 | + LOGI("SafAgentFence::GenerateControlledDevicePackage enter"); | ||
| 182 | + | ||
| 183 | + auto proxy = GetProxy(g_mutex); | ||
| 184 | + IF_TRUE_LOGE_RETURN_ERR(proxy == nullptr, SAF_ERR_SERVICE_UNAVAILABLE, "load sa fail."); | ||
| 185 | + | ||
| 186 | + int32_t resultCode = SAF_SUCCESS; | ||
| 187 | + int32_t ret = HandleIpcError(proxy, [&]() { | ||
| 188 | + return proxy->GenerateControlledDevicePackage(permissionQuery, remoteAuthPackage, resultCode); | ||
| 189 | + }); | ||
| 190 | + IF_ERROR_LOGE_RETURN_ERR(ret, SAF_ERR_IPC_PROXY_FAIL, "IPC call failed, ret=%{public}d", ret); | ||
| 191 | + | ||
| 192 | + LOGI("SafAgentFence::GenerateControlledDevicePackage finished, resultCode = 0x%{public}x", resultCode); | ||
| 193 | + return resultCode; | ||
| 194 | +} | ||
| 195 | + | ||
| 196 | +int32_t SafAgentFence::VerifyControlledDevicePackage( | ||
| 197 | + const std::vector<RemoteAuthPackage> &ticketInfo, | ||
| 198 | + std::vector<bool> &verifyRes) | ||
| 199 | +{ | ||
| 200 | + LOGI("SafAgentFence::VerifyControlledDevicePackage enter"); | ||
| 201 | + | ||
| 202 | + auto proxy = GetProxy(g_mutex); | ||
| 203 | + IF_TRUE_LOGE_RETURN_ERR(proxy == nullptr, SAF_ERR_SERVICE_UNAVAILABLE, "load sa fail."); | ||
| 204 | + | ||
| 205 | + int32_t resultCode = SAF_SUCCESS; | ||
| 206 | + int32_t ret = HandleIpcError(proxy, [&]() { | ||
| 207 | + return proxy->VerifyControlledDevicePackage(ticketInfo, verifyRes, resultCode); | ||
| 208 | + }); | ||
| 209 | + IF_ERROR_LOGE_RETURN_ERR(ret, SAF_ERR_IPC_PROXY_FAIL, "IPC call failed, ret=%{public}d", ret); | ||
| 210 | + | ||
| 211 | + LOGI("SafAgentFence::VerifyControlledDevicePackage finished, resultCode = 0x%{public}x", resultCode); | ||
| 212 | + return resultCode; | ||
| 213 | +} | ||
| 214 | + | ||
| 215 | +int32_t SafAgentFence::GenerateControllerDevicePackage( | ||
| 216 | + const std::vector<RemoteUserAuthResults> &remoteUserAuthResults, | ||
| 217 | + std::vector<RemoteAuthPackage> &remoteAuthPackage) | ||
| 218 | +{ | ||
| 219 | + LOGI("SafAgentFence::GenerateControllerDevicePackage enter"); | ||
| 220 | + | ||
| 221 | + auto proxy = GetProxy(g_mutex); | ||
| 222 | + IF_TRUE_LOGE_RETURN_ERR(proxy == nullptr, SAF_ERR_SERVICE_UNAVAILABLE, "load sa fail."); | ||
| 223 | + | ||
| 224 | + int32_t resultCode = SAF_SUCCESS; | ||
| 225 | + int32_t ret = HandleIpcError(proxy, [&]() { | ||
| 226 | + return proxy->GenerateControllerDevicePackage(remoteUserAuthResults, remoteAuthPackage, resultCode); | ||
| 227 | + }); | ||
| 228 | + IF_ERROR_LOGE_RETURN_ERR(ret, SAF_ERR_IPC_PROXY_FAIL, "IPC call failed, ret=%{public}d", ret); | ||
| 229 | + | ||
| 230 | + LOGI("SafAgentFence::GenerateControllerDevicePackage finished, resultCode = 0x%{public}x", resultCode); | ||
| 231 | + return resultCode; | ||
| 232 | +} | ||
| 233 | + | ||
| 234 | +int32_t SafAgentFence::VerifyControllerDevicePackage( | ||
| 235 | + const std::vector<RemoteAuthPackage> &ticketInfo, | ||
| 236 | + const RemoteInfo &remoteInfo, | ||
| 237 | + std::vector<bool> &verifyRes) | ||
| 238 | +{ | ||
| 239 | + LOGI("SafAgentFence::VerifyControllerDevicePackage enter"); | ||
| 240 | + | ||
| 241 | + auto proxy = GetProxy(g_mutex); | ||
| 242 | + IF_TRUE_LOGE_RETURN_ERR(proxy == nullptr, SAF_ERR_SERVICE_UNAVAILABLE, "load sa fail."); | ||
| 243 | + | ||
| 244 | + int32_t resultCode = SAF_SUCCESS; | ||
| 245 | + int32_t ret = HandleIpcError(proxy, [&]() { | ||
| 246 | + return proxy->VerifyControllerDevicePackage(ticketInfo, remoteInfo, verifyRes, resultCode); | ||
| 247 | + }); | ||
| 248 | + IF_ERROR_LOGE_RETURN_ERR(ret, SAF_ERR_IPC_PROXY_FAIL, "IPC call failed, ret=%{public}d", ret); | ||
| 249 | + | ||
| 250 | + LOGI("SafAgentFence::VerifyControllerDevicePackage finished, resultCode = 0x%{public}x", resultCode); | ||
| 251 | + return resultCode; | ||
| 252 | +} | ||
| 253 | + | ||
| 177 | int32_t SafAgentFence::VerifyTicket( | 254 | int32_t SafAgentFence::VerifyTicket( |
| 178 | int32_t osAccountId, | 255 | int32_t osAccountId, |
| 179 | const std::string &callerId, | 256 | const std::string &callerId, |
| @@ -92,6 +92,7 @@ ohos_rust_static_library("saf_ipc") { | |||
| 92 | deps = [ | 92 | deps = [ |
| 93 | "../../definition:saf_definition", | 93 | "../../definition:saf_definition", |
| 94 | "../../os_dependency/log:saf_log", | 94 | "../../os_dependency/log:saf_log", |
| 95 | + "../../utils:saf_utils", | ||
| 95 | ] | 96 | ] |
| 96 | external_deps = [ "ipc:ipc_rust" ] | 97 | external_deps = [ "ipc:ipc_rust" ] |
| 97 | 98 | ||
| @@ -21,4 +21,5 @@ edition = "2021" | |||
| 21 | [dependencies] | 21 | [dependencies] |
| 22 | saf_definition = { path = "../../definition" } | 22 | saf_definition = { path = "../../definition" } |
| 23 | saf_log = { path = "../../os_dependency/log" } | 23 | saf_log = { path = "../../os_dependency/log" } |
| 24 | +saf_utils = { path = "../../utils" } | ||
| 24 | ipc = { git = "https://gitcode.com/openharmony/communication_ipc.git" } | 25 | ipc = { git = "https://gitcode.com/openharmony/communication_ipc.git" } |
| @@ -56,6 +56,55 @@ interface OHOS.Security.SAF.ISecureAccessFence { | |||
| 56 | [out] int resultCode | 56 | [out] int resultCode |
| 57 | ); | 57 | ); |
| 58 | 58 | ||
| 59 | + /** | ||
| 60 | + * @brief Generate controlled device packages for remote control scenario. | ||
| 61 | + * @param permissionQuery List of permission queries. | ||
| 62 | + * @param remoteAuthPackage List of generated remote auth packages (output parameter). | ||
| 63 | + */ | ||
| 64 | + [ipccode 4] void GenerateControlledDevicePackage( | ||
| 65 | + [in] List<PermissionQuery> permissionQuery, | ||
| 66 | + [out] List<RemoteAuthPackage> remoteAuthPackage, | ||
| 67 | + [out] int resultCode | ||
| 68 | + ); | ||
| 69 | + | ||
| 70 | + /** | ||
| 71 | + * @brief Verify controlled device packages for remote control scenario. | ||
| 72 | + * @param ticketInfo List of remote auth packages to verify. | ||
| 73 | + * @param verifyRes List of verification results (output parameter). | ||
| 74 | + * @param resultCode Error code output (0 for success). | ||
| 75 | + */ | ||
| 76 | + [ipccode 5] void VerifyControlledDevicePackage( | ||
| 77 | + [in] List<RemoteAuthPackage> ticketInfo, | ||
| 78 | + [out] List<boolean> verifyRes, | ||
| 79 | + [out] int resultCode | ||
| 80 | + ); | ||
| 81 | + | ||
| 82 | + /** | ||
| 83 | + * @brief Generate controller device packages for remote control scenario. | ||
| 84 | + * @param remoteUserAuthResults List of remote user authentication results. | ||
| 85 | + * @param remoteAuthPackage List of generated remote auth packages (output parameter). | ||
| 86 | + * @param resultCode Error code output (0 for success). | ||
| 87 | + */ | ||
| 88 | + [ipccode 6] void GenerateControllerDevicePackage( | ||
| 89 | + [in] List<RemoteUserAuthResults> remoteUserAuthResults, | ||
| 90 | + [out] List<RemoteAuthPackage> remoteAuthPackage, | ||
| 91 | + [out] int resultCode | ||
| 92 | + ); | ||
| 93 | + | ||
| 94 | + /** | ||
| 95 | + * @brief Verify controller device packages for remote control scenario. | ||
| 96 | + * @param ticketInfo List of remote auth packages to verify. | ||
| 97 | + * @param remoteInfo Remote information for verification. | ||
| 98 | + * @param verifyRes List of verification results (output parameter). | ||
| 99 | + * @param resultCode Error code output (0 for success). | ||
| 100 | + */ | ||
| 101 | + [ipccode 7] void VerifyControllerDevicePackage( | ||
| 102 | + [in] List<RemoteAuthPackage> ticketInfo, | ||
| 103 | + [in] RemoteInfo remoteInfo, | ||
| 104 | + [out] List<boolean> verifyRes, | ||
| 105 | + [out] int resultCode | ||
| 106 | + ); | ||
| 107 | + | ||
| 59 | /** | 108 | /** |
| 60 | * @brief Batch query command permissions. | 109 | * @brief Batch query command permissions. |
| 61 | * @param cmds List of commands to query (input parameter). | 110 | * @param cmds List of commands to query (input parameter). |
| @@ -149,4 +149,30 @@ struct PermissionQuery { | |||
| 149 | struct UserAuthResult { | 149 | struct UserAuthResult { |
| 150 | List<PermissionInfo> permissionInfo; | 150 | List<PermissionInfo> permissionInfo; |
| 151 | PermissionQuery permissionQuery; | 151 | PermissionQuery permissionQuery; |
| 152 | -}; | 152 | +}; |
| 153 | + | ||
| 154 | +struct RemoteAuthPackage { | ||
| 155 | + String remoteMessage; | ||
| 156 | + String challenge; | ||
| 157 | + String ticket; | ||
| 158 | +}; | ||
| 159 | + | ||
| 160 | +/** | ||
| 161 | + * @brief Remote user authentication item for controller device. | ||
| 162 | + */ | ||
| 163 | +struct RemoteUserAuthItem { | ||
| 164 | + /** Permission string. */ | ||
| 165 | + String permission; | ||
| 166 | + /** Authentication result string. */ | ||
| 167 | + String authResult; | ||
| 168 | +}; | ||
| 169 | + | ||
| 170 | +/** | ||
| 171 | + * @brief Remote user authentication results for controller device. | ||
| 172 | + */ | ||
| 173 | +struct RemoteUserAuthResults { | ||
| 174 | + /** List of authentication result items. */ | ||
| 175 | + List<RemoteUserAuthItem> results; | ||
| 176 | + /** Permission query associated with the authentication. */ | ||
| 177 | + PermissionQuery permissionQuery; | ||
| 178 | +}; | ||
| @@ -15,12 +15,33 @@ | |||
| 15 | 15 | ||
| 16 | //! This module defines IPC interfaces and constants. | 16 | //! This module defines IPC interfaces and constants. |
| 17 | 17 | ||
| 18 | +pub mod remote_message_wrapper; | ||
| 19 | +pub mod sub_modules; | ||
| 20 | + | ||
| 18 | use ipc::{ | 21 | use ipc::{ |
| 19 | parcel::{Deserialize, MsgParcel}, | 22 | parcel::{Deserialize, MsgParcel}, |
| 20 | IpcStatusCode, | 23 | IpcStatusCode, |
| 21 | }; | 24 | }; |
| 22 | 25 | ||
| 23 | -use saf_definition::{macros_lib, Conversion, DataType, ErrCode, Result, SAFError, SAFMap, Tag, Value}; | 26 | +use saf_definition::{ |
| 27 | + macros_lib, Conversion, DataType, ErrCode, Result, SAFError, SAFMap, Tag, Value, VerifyTicketInfo | ||
| 28 | +}; | ||
| 29 | + | ||
| 30 | +pub use sub_modules::{ | ||
| 31 | + deserialize_permission_queries, | ||
| 32 | + deserialize_remote_auth_packages, | ||
| 33 | + deserialize_remote_info, | ||
| 34 | + deserialize_remote_user_auth_results_vec, | ||
| 35 | + deserialize_verify_ticket_infos, | ||
| 36 | + serialize_bool_vec, | ||
| 37 | + serialize_cli_infos, | ||
| 38 | + serialize_remote_auth_packages, | ||
| 39 | + serialize_verify_ticket_infos, | ||
| 40 | +}; | ||
| 41 | + | ||
| 42 | +// ============================================================================ | ||
| 43 | +// Constants | ||
| 44 | +// ============================================================================ | ||
| 24 | 45 | ||
| 25 | /// SA id for SAF service. | 46 | /// SA id for SAF service. |
| 26 | pub const SA_ID: i32 = 66532; | 47 | pub const SA_ID: i32 = 66532; |
| @@ -28,19 +49,24 @@ pub const SA_ID: i32 = 66532; | |||
| 28 | pub const SA_NAME: &str = "OHOS.Security.SAF.ISecureAccessFence"; | 49 | pub const SA_NAME: &str = "OHOS.Security.SAF.ISecureAccessFence"; |
| 29 | /// IPC result code. | 50 | /// IPC result code. |
| 30 | pub const IPC_SUCCESS: u32 = 0; | 51 | pub const IPC_SUCCESS: u32 = 0; |
| 31 | - | ||
| 32 | /// IPC code for BatchGenerateTicket. | 52 | /// IPC code for BatchGenerateTicket. |
| 33 | -pub const CMD_BATCH_GENERATE_TICKET: u32 = 1; | 53 | +pub const CMD_BATCH_GENERATE_TICKET: u32 = 1; |
| 34 | -/// IPC code for BatchVerifyTicket. | 54 | +/// IPC code for BatchVerifyTicket. |
| 35 | -pub const CMD_BATCH_VERIFY_TICKET: u32 = 2; | 55 | +pub const CMD_BATCH_VERIFY_TICKET: u32 = 2; |
| 36 | -/// IPC code for VerifyTicket. | 56 | +/// IPC code for VerifyTicket. |
| 37 | -pub const CMD_VERIFY_TICKET: u32 = 3; | 57 | +pub const CMD_VERIFY_TICKET: u32 = 3; |
| 38 | -/// IPC code for BatchQueryCommandPermission. | 58 | +/// IPC code for GenerateControlledDevicePackage. |
| 39 | -pub const CMD_BATCH_QUERY_COMMAND_PERMISSION: u32 = 500; | 59 | +pub const CMD_GENERATE_CONTROLLED_DEVICE_PACKAGE: u32 = 4; |
| 60 | +/// IPC code for VerifyControlledDevicePackage. | ||
| 61 | +pub const CMD_VERIFY_CONTROLLED_DEVICE_PACKAGE: u32 = 5; | ||
| 62 | +/// IPC code for GenerateControllerDevicePackage. | ||
| 63 | +pub const CMD_GENERATE_CONTROLLER_DEVICE_PACKAGE: u32 = 6; | ||
| 64 | +/// IPC code for VerifyControllerDevicePackage. | ||
| 65 | +pub const CMD_VERIFY_CONTROLLER_DEVICE_PACKAGE: u32 = 7; | ||
| 40 | 66 | ||
| 41 | const MAX_MAP_CAPACITY: u32 = 64; | 67 | const MAX_MAP_CAPACITY: u32 = 64; |
| 42 | -const MAX_VEC_CAPACITY: u32 = 0x10000; | 68 | +pub(crate) const MAX_VEC_CAPACITY: u32 = 0x10000; |
| 43 | -const MAX_TICKET_CAPACITY: u32 = 99; | 69 | +pub(crate) const MAX_TICKET_CAPACITY: u32 = 99; |
| 44 | 70 | ||
| 45 | macros_lib::impl_enum_trait! { | 71 | macros_lib::impl_enum_trait! { |
| 46 | /// Code used to identify the function to be called. | 72 | /// Code used to identify the function to be called. |
| @@ -52,28 +78,18 @@ macros_lib::impl_enum_trait! { | |||
| 52 | } | 78 | } |
| 53 | } | 79 | } |
| 54 | 80 | ||
| 55 | -/// Verify ticket info structure (matching IDL definition). | 81 | +// ============================================================================ |
| 56 | -#[derive(Debug, Clone)] | 82 | +// Error Handling |
| 57 | -pub struct VerifyTicketInfo { | 83 | +// ============================================================================ |
| 58 | - /// Message for ticket. | ||
| 59 | - pub message: String, | ||
| 60 | - /// Challenge string. | ||
| 61 | - pub challenge: String, | ||
| 62 | - /// Ticket string. | ||
| 63 | - pub ticket: String, | ||
| 64 | -} | ||
| 65 | 84 | ||
| 66 | -/// CLI info structure (matching IDL definition). | 85 | +/// Convert ipc error into SAF error. |
| 67 | -#[derive(Debug, Clone)] | 86 | +pub fn ipc_err_handle(e: IpcStatusCode) -> SAFError { |
| 68 | -pub struct CliInfo { | 87 | + match e { |
| 69 | - /// Caller token ID. | 88 | + IpcStatusCode::ServiceDied => { |
| 70 | - pub caller_token_id: String, | 89 | + SAFError::new(ErrCode::ServiceUnavailable, format!("[FATAL][IPC]Ipc status code = {}", e as i32)) |
| 71 | - /// CLI command name. | 90 | + }, |
| 72 | - pub cli_cmd_name: String, | 91 | + _ => SAFError::new(ErrCode::IpcError, format!("[FATAL][IPC]Ipc status code = {}", e)), |
| 73 | - /// Sub CLI command name. | 92 | + } |
| 74 | - pub sub_cli_cmd_name: String, | ||
| 75 | - /// Permission list for the CLI command. | ||
| 76 | - pub permission_list: Vec<String>, | ||
| 77 | } | 93 | } |
| 78 | 94 | ||
| 79 | /// deserialize T from parcel | 95 | /// deserialize T from parcel |
| @@ -87,9 +103,13 @@ pub fn deserialize<T: Deserialize>(parcel: &mut MsgParcel) -> Result<T> { | |||
| 87 | Ok(value) | 103 | Ok(value) |
| 88 | } | 104 | } |
| 89 | 105 | ||
| 106 | +// ============================================================================ | ||
| 107 | +// Map Serialization | ||
| 108 | +// ============================================================================ | ||
| 109 | + | ||
| 90 | /// serialize the map to parcel | 110 | /// serialize the map to parcel |
| 91 | pub fn serialize_map(map: &SAFMap, parcel: &mut MsgParcel) -> Result<()> { | 111 | pub fn serialize_map(map: &SAFMap, parcel: &mut MsgParcel) -> Result<()> { |
| 92 | - if map.len() as u32 > MAX_MAP_CAPACITY { | 112 | + if map.len() > MAX_MAP_CAPACITY as usize { |
| 93 | return macros_lib::log_throw_error!( | 113 | return macros_lib::log_throw_error!( |
| 94 | ErrCode::IpcWriteDataFail, | 114 | ErrCode::IpcWriteDataFail, |
| 95 | "[FATAL][IPC]The map size exceeds the limit." | 115 | "[FATAL][IPC]The map size exceeds the limit." |
| @@ -120,15 +140,15 @@ pub fn serialize_map(map: &SAFMap, parcel: &mut MsgParcel) -> Result<()> { | |||
| 120 | 140 | ||
| 121 | /// deserialize the map from parcel | 141 | /// deserialize the map from parcel |
| 122 | pub fn deserialize_map(parcel: &mut MsgParcel) -> Result<SAFMap> { | 142 | pub fn deserialize_map(parcel: &mut MsgParcel) -> Result<SAFMap> { |
| 123 | - let len = parcel.read::<u32>().map_err(ipc_err_handle)?; | 143 | + let count = parcel.read::<u32>().map_err(ipc_err_handle)?; |
| 124 | - if len > MAX_MAP_CAPACITY { | 144 | + if count > MAX_MAP_CAPACITY { |
| 125 | return macros_lib::log_throw_error!( | 145 | return macros_lib::log_throw_error!( |
| 126 | ErrCode::IpcReadDataFail, | 146 | ErrCode::IpcReadDataFail, |
| 127 | "[FATAL][IPC]The map size exceeds the limit." | 147 | "[FATAL][IPC]The map size exceeds the limit." |
| 128 | ); | 148 | ); |
| 129 | } | 149 | } |
| 130 | - let mut map = SAFMap::with_capacity(len as usize); | 150 | + let mut map = SAFMap::with_capacity(count as usize); |
| 131 | - for _ in 0..len { | 151 | + for _ in 0..count { |
| 132 | let tag = parcel.read::<u32>().map_err(ipc_err_handle)?; | 152 | let tag = parcel.read::<u32>().map_err(ipc_err_handle)?; |
| 133 | let tag = Tag::try_from(tag)?; | 153 | let tag = Tag::try_from(tag)?; |
| 134 | match tag.data_type() { | 154 | match tag.data_type() { |
| @@ -178,29 +198,23 @@ pub fn serialize_maps(vec: &Vec<SAFMap>, parcel: &mut MsgParcel) -> Result<()> { | |||
| 178 | 198 | ||
| 179 | /// Deserialize the collection of map from parcel. | 199 | /// Deserialize the collection of map from parcel. |
| 180 | pub fn deserialize_maps(parcel: &mut MsgParcel) -> Result<Vec<SAFMap>> { | 200 | pub fn deserialize_maps(parcel: &mut MsgParcel) -> Result<Vec<SAFMap>> { |
| 181 | - let len = parcel.read::<u32>().map_err(ipc_err_handle)?; | 201 | + let count = parcel.read::<u32>().map_err(ipc_err_handle)?; |
| 182 | - if len > MAX_VEC_CAPACITY { | 202 | + if count > MAX_VEC_CAPACITY { |
| 183 | return macros_lib::log_throw_error!( | 203 | return macros_lib::log_throw_error!( |
| 184 | ErrCode::IpcReadDataFail, | 204 | ErrCode::IpcReadDataFail, |
| 185 | "[FATAL][IPC]The vector size exceeds the limit." | 205 | "[FATAL][IPC]The vector size exceeds the limit." |
| 186 | ); | 206 | ); |
| 187 | } | 207 | } |
| 188 | - let mut res_vec = Vec::with_capacity(len as usize); | 208 | + let mut res_vec = Vec::with_capacity(count as usize); |
| 189 | - for _i in 0..len { | 209 | + for _i in 0..count { |
| 190 | res_vec.push(deserialize_map(parcel)?); | 210 | res_vec.push(deserialize_map(parcel)?); |
| 191 | } | 211 | } |
| 192 | Ok(res_vec) | 212 | Ok(res_vec) |
| 193 | } | 213 | } |
| 194 | 214 | ||
| 195 | -/// Convert ipc error into SAF error. | 215 | +// ============================================================================ |
| 196 | -pub fn ipc_err_handle(e: IpcStatusCode) -> SAFError { | 216 | +// Batch Request Deserialization (for stub) |
| 197 | - match e { | 217 | +// ============================================================================ |
| 198 | - IpcStatusCode::ServiceDied => { | ||
| 199 | - SAFError::new(ErrCode::ServiceUnavailable, format!("[FATAL][IPC]Ipc status code = {}", e as i32)) | ||
| 200 | - }, | ||
| 201 | - _ => SAFError::new(ErrCode::IpcError, format!("[FATAL][IPC]Ipc status code = {}", e)), | ||
| 202 | - } | ||
| 203 | -} | ||
| 204 | 218 | ||
| 205 | /// Deserialize BatchGenerateTicket request parameters from MsgParcel. | 219 | /// Deserialize BatchGenerateTicket request parameters from MsgParcel. |
| 206 | pub fn deserialize_batch_generate_ticket_request(parcel: &mut MsgParcel) -> Result<(i32, String, Vec<String>)> { | 220 | pub fn deserialize_batch_generate_ticket_request(parcel: &mut MsgParcel) -> Result<(i32, String, Vec<String>)> { |
| @@ -218,53 +232,14 @@ pub fn deserialize_batch_verify_ticket_request(parcel: &mut MsgParcel) -> Result | |||
| 218 | Ok((os_account_id, caller_id, verify_infos)) | 232 | Ok((os_account_id, caller_id, verify_infos)) |
| 219 | } | 233 | } |
| 220 | 234 | ||
| 221 | -/// Deserialize VerifyTicketInfo from MsgParcel. | 235 | +/// Deserialize VerifyTicket request parameters from MsgParcel. |
| 222 | -pub fn deserialize_verify_ticket_info(parcel: &mut MsgParcel) -> Result<VerifyTicketInfo> { | 236 | +pub fn deserialize_verify_ticket_request( |
| 223 | - let message = parcel.read_string16().map_err(ipc_err_handle)?; | 237 | + parcel: &mut MsgParcel, |
| 224 | - let challenge = parcel.read_string16().map_err(ipc_err_handle)?; | 238 | +) -> Result<(i32, String, String)> { |
| 225 | - let ticket = parcel.read_string16().map_err(ipc_err_handle)?; | 239 | + let os_account_id = parcel.read::<i32>().map_err(ipc_err_handle)?; |
| 226 | - Ok(VerifyTicketInfo { message, challenge, ticket }) | 240 | + let caller_id = parcel.read_string16().map_err(ipc_err_handle)?; |
| 227 | -} | 241 | + let verify_info = parcel.read_string16().map_err(ipc_err_handle)?; |
| 228 | - | 242 | + Ok((os_account_id, caller_id, verify_info)) |
| 229 | -/// Deserialize vector of VerifyTicketInfo from MsgParcel. | ||
| 230 | -pub fn deserialize_verify_ticket_infos(parcel: &mut MsgParcel) -> Result<Vec<VerifyTicketInfo>> { | ||
| 231 | - let len = parcel.read::<i32>().map_err(ipc_err_handle)?; | ||
| 232 | - if len < 0 || len as u32 > MAX_TICKET_CAPACITY { | ||
| 233 | - return macros_lib::log_throw_error!( | ||
| 234 | - ErrCode::InvalidArrayLen, | ||
| 235 | - "[FATAL][IPC]VerifyTicketInfo vector size invalid: {}", | ||
| 236 | - len | ||
| 237 | - ); | ||
| 238 | - } | ||
| 239 | - let mut vec = Vec::with_capacity(len as usize); | ||
| 240 | - for _ in 0..len { | ||
| 241 | - vec.push(deserialize_verify_ticket_info(parcel)?); | ||
| 242 | - } | ||
| 243 | - Ok(vec) | ||
| 244 | -} | ||
| 245 | - | ||
| 246 | -/// Serialize VerifyTicketInfo to MsgParcel (for reply). | ||
| 247 | -pub fn serialize_verify_ticket_info(info: &VerifyTicketInfo, parcel: &mut MsgParcel) -> Result<()> { | ||
| 248 | - parcel.write_string16(&info.message).map_err(ipc_err_handle)?; | ||
| 249 | - parcel.write_string16(&info.challenge).map_err(ipc_err_handle)?; | ||
| 250 | - parcel.write_string16(&info.ticket).map_err(ipc_err_handle)?; | ||
| 251 | - Ok(()) | ||
| 252 | -} | ||
| 253 | - | ||
| 254 | -/// Serialize vector of VerifyTicketInfo to MsgParcel (for reply). | ||
| 255 | -pub fn serialize_verify_ticket_infos(infos: &Vec<VerifyTicketInfo>, parcel: &mut MsgParcel) -> Result<()> { | ||
| 256 | - if infos.len() as u32 > MAX_TICKET_CAPACITY { | ||
| 257 | - return macros_lib::log_throw_error!( | ||
| 258 | - ErrCode::InvalidArrayLen, | ||
| 259 | - "[FATAL][IPC]VerifyTicketInfo vector size exceeds limit: {}", | ||
| 260 | - infos.len() | ||
| 261 | - ); | ||
| 262 | - } | ||
| 263 | - parcel.write::<i32>(&(infos.len() as i32)).map_err(ipc_err_handle)?; | ||
| 264 | - for info in infos { | ||
| 265 | - serialize_verify_ticket_info(info, parcel)?; | ||
| 266 | - } | ||
| 267 | - Ok(()) | ||
| 268 | } | 243 | } |
| 269 | 244 | ||
| 270 | /// Serialize vector of i32 to MsgParcel (for reply). | 245 | /// Serialize vector of i32 to MsgParcel (for reply). |
| @@ -276,63 +251,13 @@ pub fn serialize_i32_vec(vec: &Vec<i32>, parcel: &mut MsgParcel) -> Result<()> { | |||
| 276 | vec.len() | 251 | vec.len() |
| 277 | ); | 252 | ); |
| 278 | } | 253 | } |
| 279 | - parcel.write::<i32>(&(vec.len() as i32)).map_err(ipc_err_handle)?; | 254 | + let count = i32::try_from(vec.len()).map_err(|_| { |
| 255 | + macros_lib::log_and_into_saf_error!(ErrCode::InvalidArrayLen, | ||
| 256 | + "[FATAL][IPC]i32 vector length overflows i32") | ||
| 257 | + })?; | ||
| 258 | + parcel.write::<i32>(&count).map_err(ipc_err_handle)?; | ||
| 280 | for val in vec { | 259 | for val in vec { |
| 281 | parcel.write::<i32>(val).map_err(ipc_err_handle)?; | 260 | parcel.write::<i32>(val).map_err(ipc_err_handle)?; |
| 282 | } | 261 | } |
| 283 | Ok(()) | 262 | Ok(()) |
| 284 | -} | 263 | +} |
| 285 | - | ||
| 286 | -/// Deserialize VerifyTicket request parameters from MsgParcel. | ||
| 287 | -pub fn deserialize_verify_ticket_request( | ||
| 288 | - parcel: &mut MsgParcel, | ||
| 289 | -) -> Result<(i32, String, String)> { | ||
| 290 | - let os_account_id = parcel.read::<i32>().map_err(ipc_err_handle)?; | ||
| 291 | - let caller_id = parcel.read_string16().map_err(ipc_err_handle)?; | ||
| 292 | - let verify_info = parcel.read_string16().map_err(ipc_err_handle)?; | ||
| 293 | - Ok((os_account_id, caller_id, verify_info)) | ||
| 294 | -} | ||
| 295 | - | ||
| 296 | -/// Serialize CliInfo to MsgParcel (for reply). | ||
| 297 | -pub fn serialize_cli_info(info: &CliInfo, parcel: &mut MsgParcel) -> Result<()> { | ||
| 298 | - parcel.write_string16(&info.caller_token_id).map_err(ipc_err_handle)?; | ||
| 299 | - parcel.write_string16(&info.cli_cmd_name).map_err(ipc_err_handle)?; | ||
| 300 | - parcel.write_string16(&info.sub_cli_cmd_name).map_err(ipc_err_handle)?; | ||
| 301 | - serialize_string_vec(&info.permission_list, parcel)?; | ||
| 302 | - Ok(()) | ||
| 303 | -} | ||
| 304 | - | ||
| 305 | -/// Write vector length to parcel with capacity check. | ||
| 306 | -pub fn write_vec_len(vec_len: usize, max_capacity: i32, err_code: ErrCode, label: &str, | ||
| 307 | - parcel: &mut MsgParcel) -> Result<()> { | ||
| 308 | - if vec_len as i32 > max_capacity { | ||
| 309 | - return macros_lib::log_throw_error!( | ||
| 310 | - err_code, | ||
| 311 | - "[FATAL][IPC]{} vector size exceeds limit: {}", | ||
| 312 | - label, | ||
| 313 | - vec_len | ||
| 314 | - ); | ||
| 315 | - } | ||
| 316 | - parcel.write::<i32>(&(vec_len as i32)).map_err(ipc_err_handle)?; | ||
| 317 | - Ok(()) | ||
| 318 | -} | ||
| 319 | - | ||
| 320 | -/// Serialize vector of CliInfo to MsgParcel (for reply). | ||
| 321 | -pub fn serialize_cli_infos(infos: &Vec<CliInfo>, parcel: &mut MsgParcel) -> Result<()> { | ||
| 322 | - write_vec_len(infos.len(), MAX_TICKET_CAPACITY as i32, ErrCode::InvalidArrayLen, | ||
| 323 | - "CliInfo", parcel)?; | ||
| 324 | - for info in infos { | ||
| 325 | - serialize_cli_info(info, parcel)?; | ||
| 326 | - } | ||
| 327 | - Ok(()) | ||
| 328 | -} | ||
| 329 | - | ||
| 330 | -/// Serialize string vector to MsgParcel (for reply). | ||
| 331 | -pub fn serialize_string_vec(vec: &Vec<String>, parcel: &mut MsgParcel) -> Result<()> { | ||
| 332 | - write_vec_len(vec.len(), MAX_TICKET_CAPACITY as i32, ErrCode::InvalidArrayLen, | ||
| 333 | - "string", parcel)?; | ||
| 334 | - for s in vec { | ||
| 335 | - parcel.write_string16(s).map_err(ipc_err_handle)?; | ||
| 336 | - } | ||
| 337 | - Ok(()) | ||
| 338 | -} | ||
| @@ -0,0 +1,98 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | +//! RemoteMessage JSON serialization and deserialization utilities. | ||
| 17 | + | ||
| 18 | +use saf_utils::{JsonBuilder, JsonValue, Object, new_object, object_add_string, take_optional_string, | ||
| 19 | + take_required_string, take_required_value, json_into_object}; | ||
| 20 | +use saf_definition::{macros_lib, ErrCode, Result, DeviceIdHeader, RemoteMessage}; | ||
| 21 | + | ||
| 22 | +/// Remote message JSON field keys for serialization/deserialization. | ||
| 23 | +pub struct RemoteMessageKeys { | ||
| 24 | + /// The device info object key | ||
| 25 | + pub device_info: &'static str, | ||
| 26 | + /// The controller device ID key (inside deviceInfo) | ||
| 27 | + pub controller_device_id: &'static str, | ||
| 28 | + /// The controlled device ID key (inside deviceInfo) | ||
| 29 | + pub controlled_device_id: &'static str, | ||
| 30 | + /// The remote auth message key | ||
| 31 | + pub remote_auth_message: &'static str, | ||
| 32 | + /// The caller bundle name key | ||
| 33 | + pub caller_bundle_name: &'static str, | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | +/// Global constant instance for remote message JSON field keys. | ||
| 37 | +pub const REMOTE_MESSAGE_KEYS: RemoteMessageKeys = RemoteMessageKeys { | ||
| 38 | + device_info: "deviceInfo", | ||
| 39 | + controller_device_id: "controllerDeviceId", | ||
| 40 | + controlled_device_id: "controlledDeviceId", | ||
| 41 | + remote_auth_message: "remoteAuthMessage", | ||
| 42 | + caller_bundle_name: "callerBundleName", | ||
| 43 | +}; | ||
| 44 | + | ||
| 45 | +/// Serialize DeviceIdHeader to JSON string | ||
| 46 | +pub fn serialize_device_id_header_to_json(header: &DeviceIdHeader) -> Result<String> { | ||
| 47 | + let mut builder = JsonBuilder::new(); | ||
| 48 | + builder.add_string(REMOTE_MESSAGE_KEYS.controller_device_id, &header.controller_device_id); | ||
| 49 | + builder.add_string(REMOTE_MESSAGE_KEYS.controlled_device_id, &header.controlled_device_id); | ||
| 50 | + builder.build() | ||
| 51 | +} | ||
| 52 | + | ||
| 53 | +/// Deserialize DeviceIdHeader from an Object (the "deviceInfo" sub-object), consuming the fields | ||
| 54 | +pub fn parse_device_id_header_from_json(device_info_obj: &mut Object) -> Result<DeviceIdHeader> { | ||
| 55 | + let controller_device_id = take_optional_string(device_info_obj, REMOTE_MESSAGE_KEYS.controller_device_id)?; | ||
| 56 | + let controlled_device_id = take_optional_string(device_info_obj, REMOTE_MESSAGE_KEYS.controlled_device_id)?; | ||
| 57 | + | ||
| 58 | + Ok(DeviceIdHeader { | ||
| 59 | + controller_device_id, | ||
| 60 | + controlled_device_id | ||
| 61 | + }) | ||
| 62 | +} | ||
| 63 | + | ||
| 64 | +/// Serialize RemoteMessage to JSON string. | ||
| 65 | +pub fn serialize_remote_message_to_json(message: &RemoteMessage) -> Result<String> { | ||
| 66 | + let mut device_info_obj = new_object(); | ||
| 67 | + object_add_string(&mut device_info_obj, REMOTE_MESSAGE_KEYS.controller_device_id, | ||
| 68 | + &message.device_info.controller_device_id); | ||
| 69 | + object_add_string(&mut device_info_obj, REMOTE_MESSAGE_KEYS.controlled_device_id, | ||
| 70 | + &message.device_info.controlled_device_id); | ||
| 71 | + | ||
| 72 | + let mut builder = JsonBuilder::new(); | ||
| 73 | + builder.add_object(REMOTE_MESSAGE_KEYS.device_info, device_info_obj); | ||
| 74 | + builder.add_string(REMOTE_MESSAGE_KEYS.remote_auth_message, &message.remote_auth_message); | ||
| 75 | + builder.add_string(REMOTE_MESSAGE_KEYS.caller_bundle_name, &message.caller_bundle_name); | ||
| 76 | + builder.build() | ||
| 77 | +} | ||
| 78 | + | ||
| 79 | +/// Deserialize RemoteMessage from JSON string. | ||
| 80 | +pub fn deserialize_remote_message_from_json(json_str: &str) -> Result<RemoteMessage> { | ||
| 81 | + let json = JsonValue::from_text(json_str).map_err(|e| { | ||
| 82 | + macros_lib::log_and_into_saf_error!( | ||
| 83 | + ErrCode::JsonParseError, | ||
| 84 | + "[FATAL][IPC]Failed to parse remoteMessage JSON: {}", e | ||
| 85 | + ) | ||
| 86 | + })?; | ||
| 87 | + let mut obj = json_into_object(json)?; | ||
| 88 | + let mut device_info_obj = json_into_object(take_required_value(&mut obj, REMOTE_MESSAGE_KEYS.device_info)?)?; | ||
| 89 | + let device_info = parse_device_id_header_from_json(&mut device_info_obj)?; | ||
| 90 | + let remote_auth_message = take_required_string(&mut obj, REMOTE_MESSAGE_KEYS.remote_auth_message)?; | ||
| 91 | + let caller_bundle_name = take_optional_string(&mut obj, REMOTE_MESSAGE_KEYS.caller_bundle_name)?; | ||
| 92 | + | ||
| 93 | + Ok(RemoteMessage { | ||
| 94 | + device_info, | ||
| 95 | + remote_auth_message, | ||
| 96 | + caller_bundle_name | ||
| 97 | + }) | ||
| 98 | +} | ||
| @@ -0,0 +1,34 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | +//! Sub-modules for IPC serialization. | ||
| 17 | + | ||
| 18 | +pub mod ticket; | ||
| 19 | +pub mod remote_control; | ||
| 20 | + | ||
| 21 | +pub use ticket::{ | ||
| 22 | + deserialize_verify_ticket_infos, | ||
| 23 | + serialize_verify_ticket_infos, | ||
| 24 | + serialize_cli_infos, | ||
| 25 | +}; | ||
| 26 | + | ||
| 27 | +pub use remote_control::{ | ||
| 28 | + deserialize_permission_queries, | ||
| 29 | + deserialize_remote_info, | ||
| 30 | + deserialize_remote_auth_packages, | ||
| 31 | + deserialize_remote_user_auth_results_vec, | ||
| 32 | + serialize_bool_vec, | ||
| 33 | + serialize_remote_auth_packages, | ||
| 34 | +}; | ||
| @@ -0,0 +1,342 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | +//! Remote control related serialization functions. | ||
| 17 | + | ||
| 18 | +use ipc::parcel::MsgParcel; | ||
| 19 | +use saf_definition::{ | ||
| 20 | + macros_lib, ErrCode, Result, | ||
| 21 | + CommandInfo, OperationInfo, OperationType, | ||
| 22 | + RemoteControlParams, RemoteInfo, Role, | ||
| 23 | + PermissionQuery, RemoteAuthPackage, | ||
| 24 | + RemoteUserAuthItem, RemoteUserAuthResults, | ||
| 25 | +}; | ||
| 26 | +use crate::{ipc_err_handle, MAX_TICKET_CAPACITY, MAX_VEC_CAPACITY}; | ||
| 27 | +use crate::remote_message_wrapper; | ||
| 28 | + | ||
| 29 | +/// Deserialize CommandInfo from MsgParcel. | ||
| 30 | +pub fn deserialize_command_info(parcel: &mut MsgParcel) -> Result<CommandInfo> { | ||
| 31 | + let cmd_name = parcel.read_string16().map_err(ipc_err_handle)?; | ||
| 32 | + let sub_cmd = parcel.read_string16().map_err(ipc_err_handle)?; | ||
| 33 | + Ok(CommandInfo { cmd_name, sub_cmd }) | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | +/// Serialize CommandInfo to MsgParcel. | ||
| 37 | +pub fn serialize_command_info(info: &CommandInfo, parcel: &mut MsgParcel) -> Result<()> { | ||
| 38 | + parcel.write_string16(&info.cmd_name).map_err(ipc_err_handle)?; | ||
| 39 | + parcel.write_string16(&info.sub_cmd).map_err(ipc_err_handle)?; | ||
| 40 | + Ok(()) | ||
| 41 | +} | ||
| 42 | + | ||
| 43 | +/// Deserialize OperationInfo from MsgParcel. | ||
| 44 | +pub fn deserialize_operation_info(parcel: &mut MsgParcel) -> Result<OperationInfo> { | ||
| 45 | + let operation_type_val = parcel.read::<u32>().map_err(ipc_err_handle)?; | ||
| 46 | + let operation_type = match operation_type_val { | ||
| 47 | + 1 => OperationType::Cli, | ||
| 48 | + 2 => OperationType::Api, | ||
| 49 | + _ => return macros_lib::log_throw_error!( | ||
| 50 | + ErrCode::InvalidArgument, | ||
| 51 | + "[FATAL][IPC]Invalid operation type: {}", | ||
| 52 | + operation_type_val | ||
| 53 | + ), | ||
| 54 | + }; | ||
| 55 | + let cli_cmd_info = deserialize_command_info(parcel)?; | ||
| 56 | + let permission = parcel.read_string16().map_err(ipc_err_handle)?; | ||
| 57 | + Ok(OperationInfo { | ||
| 58 | + operation_type, | ||
| 59 | + cli_cmd_info, | ||
| 60 | + permission, | ||
| 61 | + }) | ||
| 62 | +} | ||
| 63 | + | ||
| 64 | +/// Deserialize OperationInfo vector from MsgParcel. | ||
| 65 | +pub fn deserialize_operation_infos(parcel: &mut MsgParcel) -> Result<Vec<OperationInfo>> { | ||
| 66 | + let count = parcel.read::<i32>().map_err(ipc_err_handle)?; | ||
| 67 | + if count < 0 || count as u32 > MAX_TICKET_CAPACITY { | ||
| 68 | + return macros_lib::log_throw_error!( | ||
| 69 | + ErrCode::InvalidArrayLen, | ||
| 70 | + "[FATAL][IPC]OperationInfo vector size invalid: {}", | ||
| 71 | + count | ||
| 72 | + ); | ||
| 73 | + } | ||
| 74 | + let mut vec = Vec::with_capacity(count as usize); | ||
| 75 | + for _ in 0..count { | ||
| 76 | + vec.push(deserialize_operation_info(parcel)?); | ||
| 77 | + } | ||
| 78 | + Ok(vec) | ||
| 79 | +} | ||
| 80 | + | ||
| 81 | +/// Serialize OperationInfo to MsgParcel. | ||
| 82 | +pub fn serialize_operation_info(info: &OperationInfo, parcel: &mut MsgParcel) -> Result<()> { | ||
| 83 | + parcel.write::<u32>(&(info.operation_type as u32)).map_err(ipc_err_handle)?; | ||
| 84 | + serialize_command_info(&info.cli_cmd_info, parcel)?; | ||
| 85 | + parcel.write_string16(&info.permission).map_err(ipc_err_handle)?; | ||
| 86 | + Ok(()) | ||
| 87 | +} | ||
| 88 | + | ||
| 89 | +/// Serialize OperationInfo vector to MsgParcel. | ||
| 90 | +pub fn serialize_operation_infos(infos: &Vec<OperationInfo>, parcel: &mut MsgParcel) -> Result<()> { | ||
| 91 | + if infos.len() as u32 > MAX_TICKET_CAPACITY { | ||
| 92 | + return macros_lib::log_throw_error!( | ||
| 93 | + ErrCode::InvalidArrayLen, | ||
| 94 | + "[FATAL][IPC]OperationInfo vector size exceeds limit: {}", | ||
| 95 | + infos.len() | ||
| 96 | + ); | ||
| 97 | + } | ||
| 98 | + let count = i32::try_from(infos.len()).map_err(|_| { | ||
| 99 | + macros_lib::log_and_into_saf_error!(ErrCode::InvalidArrayLen, | ||
| 100 | + "[FATAL][IPC]OperationInfo vector length overflows i32") | ||
| 101 | + })?; | ||
| 102 | + parcel.write::<i32>(&count).map_err(ipc_err_handle)?; | ||
| 103 | + for info in infos { | ||
| 104 | + serialize_operation_info(info, parcel)?; | ||
| 105 | + } | ||
| 106 | + Ok(()) | ||
| 107 | +} | ||
| 108 | + | ||
| 109 | +/// Deserialize RemoteControlParams from MsgParcel. | ||
| 110 | +pub fn deserialize_remote_control_params(parcel: &mut MsgParcel) -> Result<RemoteControlParams> { | ||
| 111 | + let challenge = parcel.read_string16().map_err(ipc_err_handle)?; | ||
| 112 | + let remote_control_ticket = parcel.read_string16().map_err(ipc_err_handle)?; | ||
| 113 | + let controlled_device_name = parcel.read_string16().map_err(ipc_err_handle)?; | ||
| 114 | + let controller_device_name = parcel.read_string16().map_err(ipc_err_handle)?; | ||
| 115 | + let sign_verify_msg = parcel.read_string16().map_err(ipc_err_handle)?; | ||
| 116 | + Ok(RemoteControlParams { | ||
| 117 | + challenge, | ||
| 118 | + remote_control_ticket, | ||
| 119 | + controlled_device_name, | ||
| 120 | + controller_device_name, | ||
| 121 | + sign_verify_msg, | ||
| 122 | + }) | ||
| 123 | +} | ||
| 124 | + | ||
| 125 | +/// Serialize RemoteControlParams to MsgParcel. | ||
| 126 | +pub fn serialize_remote_control_params( | ||
| 127 | + params: &RemoteControlParams, | ||
| 128 | + parcel: &mut MsgParcel, | ||
| 129 | +) -> Result<()> { | ||
| 130 | + parcel.write_string16(¶ms.challenge).map_err(ipc_err_handle)?; | ||
| 131 | + parcel.write_string16(¶ms.remote_control_ticket).map_err(ipc_err_handle)?; | ||
| 132 | + parcel.write_string16(¶ms.controlled_device_name).map_err(ipc_err_handle)?; | ||
| 133 | + parcel.write_string16(¶ms.controller_device_name).map_err(ipc_err_handle)?; | ||
| 134 | + parcel.write_string16(¶ms.sign_verify_msg).map_err(ipc_err_handle)?; | ||
| 135 | + Ok(()) | ||
| 136 | +} | ||
| 137 | + | ||
| 138 | +/// Deserialize RemoteInfo from MsgParcel. | ||
| 139 | +pub fn deserialize_remote_info(parcel: &mut MsgParcel) -> Result<RemoteInfo> { | ||
| 140 | + let role_val = parcel.read::<u32>().map_err(ipc_err_handle)?; | ||
| 141 | + let role = match role_val { | ||
| 142 | + 1 => Role::Controller, | ||
| 143 | + 2 => Role::Controlled, | ||
| 144 | + _ => return macros_lib::log_throw_error!( | ||
| 145 | + ErrCode::InvalidArgument, | ||
| 146 | + "[FATAL][IPC]Invalid role: {}", | ||
| 147 | + role_val | ||
| 148 | + ), | ||
| 149 | + }; | ||
| 150 | + let remote_id = parcel.read_string16().map_err(ipc_err_handle)?; | ||
| 151 | + let domain_id = parcel.read_string16().map_err(ipc_err_handle)?; | ||
| 152 | + let remote_control_params = deserialize_remote_control_params(parcel)?; | ||
| 153 | + Ok(RemoteInfo { | ||
| 154 | + role, | ||
| 155 | + remote_id, | ||
| 156 | + domain_id, | ||
| 157 | + remote_control_params, | ||
| 158 | + }) | ||
| 159 | +} | ||
| 160 | + | ||
| 161 | +/// Serialize RemoteInfo to MsgParcel. | ||
| 162 | +pub fn serialize_remote_info(info: &RemoteInfo, parcel: &mut MsgParcel) -> Result<()> { | ||
| 163 | + parcel.write::<u32>(&(info.role as u32)).map_err(ipc_err_handle)?; | ||
| 164 | + parcel.write_string16(&info.remote_id).map_err(ipc_err_handle)?; | ||
| 165 | + parcel.write_string16(&info.domain_id).map_err(ipc_err_handle)?; | ||
| 166 | + serialize_remote_control_params(&info.remote_control_params, parcel)?; | ||
| 167 | + Ok(()) | ||
| 168 | +} | ||
| 169 | + | ||
| 170 | +/// Deserialize PermissionQuery from MsgParcel. | ||
| 171 | +pub fn deserialize_permission_query(parcel: &mut MsgParcel) -> Result<PermissionQuery> { | ||
| 172 | + let operation_info = deserialize_operation_infos(parcel)?; | ||
| 173 | + let need_ticket = parcel.read::<bool>().map_err(ipc_err_handle)?; | ||
| 174 | + let ticket_expire_time_ms = parcel.read::<i32>().map_err(ipc_err_handle)?; | ||
| 175 | + let caller_token_id = parcel.read::<i32>().map_err(ipc_err_handle)?; | ||
| 176 | + let domain_id = parcel.read_string16().map_err(ipc_err_handle)?; | ||
| 177 | + let remote_info = deserialize_remote_info(parcel)?; | ||
| 178 | + Ok(PermissionQuery { | ||
| 179 | + operation_info, | ||
| 180 | + need_ticket, | ||
| 181 | + ticket_expire_time_ms, | ||
| 182 | + caller_token_id, | ||
| 183 | + domain_id, | ||
| 184 | + remote_info, | ||
| 185 | + }) | ||
| 186 | +} | ||
| 187 | + | ||
| 188 | +/// Deserialize PermissionQuery vector from MsgParcel. | ||
| 189 | +pub fn deserialize_permission_queries(parcel: &mut MsgParcel) -> Result<Vec<PermissionQuery>> { | ||
| 190 | + let count = parcel.read::<i32>().map_err(ipc_err_handle)?; | ||
| 191 | + if count < 0 || count as u32 > MAX_TICKET_CAPACITY { | ||
| 192 | + return macros_lib::log_throw_error!( | ||
| 193 | + ErrCode::InvalidArrayLen, | ||
| 194 | + "[FATAL][IPC]PermissionQuery vector size invalid: {}", | ||
| 195 | + count | ||
| 196 | + ); | ||
| 197 | + } | ||
| 198 | + let mut vec = Vec::with_capacity(count as usize); | ||
| 199 | + for _ in 0..count { | ||
| 200 | + vec.push(deserialize_permission_query(parcel)?); | ||
| 201 | + } | ||
| 202 | + Ok(vec) | ||
| 203 | +} | ||
| 204 | + | ||
| 205 | +/// Deserialize RemoteAuthPackage from MsgParcel. | ||
| 206 | +pub fn deserialize_remote_auth_package(parcel: &mut MsgParcel) -> Result<RemoteAuthPackage> { | ||
| 207 | + let remote_message_str = parcel.read_string16().map_err(ipc_err_handle)?; | ||
| 208 | + let remote_message = remote_message_wrapper::deserialize_remote_message_from_json(&remote_message_str)?; | ||
| 209 | + let challenge = parcel.read_string16().map_err(ipc_err_handle)?; | ||
| 210 | + let ticket = parcel.read_string16().map_err(ipc_err_handle)?; | ||
| 211 | + Ok(RemoteAuthPackage { remote_message, challenge, ticket }) | ||
| 212 | +} | ||
| 213 | + | ||
| 214 | +/// Serialize RemoteAuthPackage to MsgParcel. | ||
| 215 | +pub fn serialize_remote_auth_package(package: &RemoteAuthPackage, parcel: &mut MsgParcel) -> Result<()> { | ||
| 216 | + let remote_message_str = remote_message_wrapper::serialize_remote_message_to_json(&package.remote_message)?; | ||
| 217 | + parcel.write_string16(&remote_message_str).map_err(ipc_err_handle)?; | ||
| 218 | + parcel.write_string16(&package.challenge).map_err(ipc_err_handle)?; | ||
| 219 | + parcel.write_string16(&package.ticket).map_err(ipc_err_handle)?; | ||
| 220 | + Ok(()) | ||
| 221 | +} | ||
| 222 | + | ||
| 223 | +/// Deserialize RemoteAuthPackage vector from MsgParcel. | ||
| 224 | +pub fn deserialize_remote_auth_packages(parcel: &mut MsgParcel) -> Result<Vec<RemoteAuthPackage>> { | ||
| 225 | + let count = parcel.read::<i32>().map_err(ipc_err_handle)?; | ||
| 226 | + if count < 0 || count as u32 > MAX_TICKET_CAPACITY { | ||
| 227 | + return macros_lib::log_throw_error!( | ||
| 228 | + ErrCode::InvalidArrayLen, | ||
| 229 | + "[FATAL][IPC]RemoteAuthPackage vector size invalid: {}", | ||
| 230 | + count | ||
| 231 | + ); | ||
| 232 | + } | ||
| 233 | + let mut vec = Vec::with_capacity(count as usize); | ||
| 234 | + for _ in 0..count { | ||
| 235 | + vec.push(deserialize_remote_auth_package(parcel)?); | ||
| 236 | + } | ||
| 237 | + Ok(vec) | ||
| 238 | +} | ||
| 239 | + | ||
| 240 | +/// Serialize RemoteAuthPackage vector to MsgParcel. | ||
| 241 | +pub fn serialize_remote_auth_packages(packages: &Vec<RemoteAuthPackage>, parcel: &mut MsgParcel) -> Result<()> { | ||
| 242 | + if packages.len() as u32 > MAX_TICKET_CAPACITY { | ||
| 243 | + return macros_lib::log_throw_error!( | ||
| 244 | + ErrCode::InvalidArrayLen, | ||
| 245 | + "[FATAL][IPC]RemoteAuthPackage vector size exceeds limit: {}", | ||
| 246 | + packages.len() | ||
| 247 | + ); | ||
| 248 | + } | ||
| 249 | + let count = i32::try_from(packages.len()).map_err(|_| { | ||
| 250 | + macros_lib::log_and_into_saf_error!(ErrCode::InvalidArrayLen, | ||
| 251 | + "[FATAL][IPC]RemoteAuthPackage vector length overflows i32") | ||
| 252 | + })?; | ||
| 253 | + parcel.write::<i32>(&count).map_err(ipc_err_handle)?; | ||
| 254 | + for package in packages { | ||
| 255 | + serialize_remote_auth_package(package, parcel)?; | ||
| 256 | + } | ||
| 257 | + Ok(()) | ||
| 258 | +} | ||
| 259 | + | ||
| 260 | +/// Serialize bool vector to MsgParcel (for reply). | ||
| 261 | +pub fn serialize_bool_vec(vec: &Vec<bool>, parcel: &mut MsgParcel) -> Result<()> { | ||
| 262 | + if vec.len() as u32 > MAX_VEC_CAPACITY { | ||
| 263 | + return macros_lib::log_throw_error!( | ||
| 264 | + ErrCode::InvalidArrayLen, | ||
| 265 | + "[FATAL][IPC]bool vector size exceeds limit: {}", | ||
| 266 | + vec.len() | ||
| 267 | + ); | ||
| 268 | + } | ||
| 269 | + let count = i32::try_from(vec.len()).map_err(|_| { | ||
| 270 | + macros_lib::log_and_into_saf_error!(ErrCode::InvalidArrayLen, | ||
| 271 | + "[FATAL][IPC]bool vector length overflows i32") | ||
| 272 | + })?; | ||
| 273 | + parcel.write::<i32>(&count).map_err(ipc_err_handle)?; | ||
| 274 | + for val in vec { | ||
| 275 | + parcel.write::<bool>(val).map_err(ipc_err_handle)?; | ||
| 276 | + } | ||
| 277 | + Ok(()) | ||
| 278 | +} | ||
| 279 | + | ||
| 280 | +/// Deserialize RemoteUserAuthItem from MsgParcel. | ||
| 281 | +pub fn deserialize_remote_user_auth_item(parcel: &mut MsgParcel) -> Result<RemoteUserAuthItem> { | ||
| 282 | + let permission = parcel.read_string16().map_err(ipc_err_handle)?; | ||
| 283 | + let auth_result = parcel.read_string16().map_err(ipc_err_handle)?; | ||
| 284 | + Ok(RemoteUserAuthItem { | ||
| 285 | + permission, | ||
| 286 | + auth_result, | ||
| 287 | + }) | ||
| 288 | +} | ||
| 289 | + | ||
| 290 | +/// Serialize RemoteUserAuthItem to MsgParcel. | ||
| 291 | +pub fn serialize_remote_user_auth_item( | ||
| 292 | + item: &RemoteUserAuthItem, | ||
| 293 | + parcel: &mut MsgParcel, | ||
| 294 | +) -> Result<()> { | ||
| 295 | + parcel.write_string16(&item.permission).map_err(ipc_err_handle)?; | ||
| 296 | + parcel.write_string16(&item.auth_result).map_err(ipc_err_handle)?; | ||
| 297 | + Ok(()) | ||
| 298 | +} | ||
| 299 | + | ||
| 300 | +/// Deserialize RemoteUserAuthResults from MsgParcel. | ||
| 301 | +pub fn deserialize_remote_user_auth_results( | ||
| 302 | + parcel: &mut MsgParcel, | ||
| 303 | +) -> Result<RemoteUserAuthResults> { | ||
| 304 | + let count = parcel.read::<i32>().map_err(ipc_err_handle)?; | ||
| 305 | + if count < 0 || count as u32 > MAX_TICKET_CAPACITY { | ||
| 306 | + return macros_lib::log_throw_error!( | ||
| 307 | + ErrCode::InvalidArrayLen, | ||
| 308 | + "[FATAL][IPC]RemoteUserAuthItem vector size invalid: {}", | ||
| 309 | + count | ||
| 310 | + ); | ||
| 311 | + } | ||
| 312 | + let mut results = Vec::with_capacity(count as usize); | ||
| 313 | + for _ in 0..count { | ||
| 314 | + results.push(deserialize_remote_user_auth_item(parcel)?); | ||
| 315 | + } | ||
| 316 | + | ||
| 317 | + let permission_query = deserialize_permission_query(parcel)?; | ||
| 318 | + | ||
| 319 | + Ok(RemoteUserAuthResults { | ||
| 320 | + results, | ||
| 321 | + permission_query, | ||
| 322 | + }) | ||
| 323 | +} | ||
| 324 | + | ||
| 325 | +/// Deserialize RemoteUserAuthResults vector from MsgParcel. | ||
| 326 | +pub fn deserialize_remote_user_auth_results_vec( | ||
| 327 | + parcel: &mut MsgParcel, | ||
| 328 | +) -> Result<Vec<RemoteUserAuthResults>> { | ||
| 329 | + let count = parcel.read::<i32>().map_err(ipc_err_handle)?; | ||
| 330 | + if count < 0 || count as u32 > MAX_TICKET_CAPACITY { | ||
| 331 | + return macros_lib::log_throw_error!( | ||
| 332 | + ErrCode::InvalidArrayLen, | ||
| 333 | + "[FATAL][IPC]RemoteUserAuthResults vector size invalid: {}", | ||
| 334 | + count | ||
| 335 | + ); | ||
| 336 | + } | ||
| 337 | + let mut vec = Vec::with_capacity(count as usize); | ||
| 338 | + for _ in 0..count { | ||
| 339 | + vec.push(deserialize_remote_user_auth_results(parcel)?); | ||
| 340 | + } | ||
| 341 | + Ok(vec) | ||
| 342 | +} | ||
| @@ -0,0 +1,119 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | +//! Ticket related serialization functions. | ||
| 17 | + | ||
| 18 | +use ipc::parcel::MsgParcel; | ||
| 19 | +use saf_definition::{macros_lib, ErrCode, Result}; | ||
| 20 | +use saf_definition::{VerifyTicketInfo, CliInfo}; | ||
| 21 | +use crate::{ipc_err_handle, MAX_TICKET_CAPACITY}; | ||
| 22 | + | ||
| 23 | +/// Deserialize VerifyTicketInfo from MsgParcel. | ||
| 24 | +pub fn deserialize_verify_ticket_info(parcel: &mut MsgParcel) -> Result<VerifyTicketInfo> { | ||
| 25 | + let message = parcel.read_string16().map_err(ipc_err_handle)?; | ||
| 26 | + let challenge = parcel.read_string16().map_err(ipc_err_handle)?; | ||
| 27 | + let ticket = parcel.read_string16().map_err(ipc_err_handle)?; | ||
| 28 | + Ok(VerifyTicketInfo { message, challenge, ticket }) | ||
| 29 | +} | ||
| 30 | + | ||
| 31 | +/// Deserialize vector of VerifyTicketInfo from MsgParcel. | ||
| 32 | +pub fn deserialize_verify_ticket_infos(parcel: &mut MsgParcel) -> Result<Vec<VerifyTicketInfo>> { | ||
| 33 | + let count = parcel.read::<i32>().map_err(ipc_err_handle)?; | ||
| 34 | + if count < 0 || count as u32 > MAX_TICKET_CAPACITY { | ||
| 35 | + return macros_lib::log_throw_error!( | ||
| 36 | + ErrCode::InvalidArrayLen, | ||
| 37 | + "[FATAL][IPC]VerifyTicketInfo vector size invalid: {}", | ||
| 38 | + count | ||
| 39 | + ); | ||
| 40 | + } | ||
| 41 | + let mut vec = Vec::with_capacity(count as usize); | ||
| 42 | + for _ in 0..count { | ||
| 43 | + vec.push(deserialize_verify_ticket_info(parcel)?); | ||
| 44 | + } | ||
| 45 | + Ok(vec) | ||
| 46 | +} | ||
| 47 | + | ||
| 48 | +/// Serialize VerifyTicketInfo to MsgParcel (for reply). | ||
| 49 | +pub fn serialize_verify_ticket_info(info: &VerifyTicketInfo, parcel: &mut MsgParcel) -> Result<()> { | ||
| 50 | + parcel.write_string16(&info.message).map_err(ipc_err_handle)?; | ||
| 51 | + parcel.write_string16(&info.challenge).map_err(ipc_err_handle)?; | ||
| 52 | + parcel.write_string16(&info.ticket).map_err(ipc_err_handle)?; | ||
| 53 | + Ok(()) | ||
| 54 | +} | ||
| 55 | + | ||
| 56 | +/// Serialize vector of VerifyTicketInfo to MsgParcel (for reply). | ||
| 57 | +pub fn serialize_verify_ticket_infos(infos: &Vec<VerifyTicketInfo>, parcel: &mut MsgParcel) -> Result<()> { | ||
| 58 | + if infos.len() as u32 > MAX_TICKET_CAPACITY { | ||
| 59 | + return macros_lib::log_throw_error!( | ||
| 60 | + ErrCode::InvalidArrayLen, | ||
| 61 | + "[FATAL][IPC]VerifyTicketInfo vector size exceeds limit: {}", | ||
| 62 | + infos.len() | ||
| 63 | + ); | ||
| 64 | + } | ||
| 65 | + let count = i32::try_from(infos.len()).map_err(|_| { | ||
| 66 | + macros_lib::log_and_into_saf_error!(ErrCode::InvalidArrayLen, | ||
| 67 | + "[FATAL][IPC]VerifyTicketInfo vector length overflows i32") | ||
| 68 | + })?; | ||
| 69 | + parcel.write::<i32>(&count).map_err(ipc_err_handle)?; | ||
| 70 | + for info in infos { | ||
| 71 | + serialize_verify_ticket_info(info, parcel)?; | ||
| 72 | + } | ||
| 73 | + Ok(()) | ||
| 74 | +} | ||
| 75 | + | ||
| 76 | +/// Serialize CliInfo to MsgParcel (for reply). | ||
| 77 | +pub fn serialize_cli_info(info: &CliInfo, parcel: &mut MsgParcel) -> Result<()> { | ||
| 78 | + parcel.write_string16(&info.caller_token_id).map_err(ipc_err_handle)?; | ||
| 79 | + parcel.write_string16(&info.cli_cmd_name).map_err(ipc_err_handle)?; | ||
| 80 | + parcel.write_string16(&info.sub_cli_cmd_name).map_err(ipc_err_handle)?; | ||
| 81 | + serialize_string_vec(&info.permission_list, parcel)?; | ||
| 82 | + Ok(()) | ||
| 83 | +} | ||
| 84 | + | ||
| 85 | +/// Serialize vector of CliInfo to MsgParcel (for reply). | ||
| 86 | +pub fn serialize_cli_infos(infos: &Vec<CliInfo>, parcel: &mut MsgParcel) -> Result<()> { | ||
| 87 | + write_vec_len(infos.len(), MAX_TICKET_CAPACITY as i32, ErrCode::InvalidArrayLen, | ||
| 88 | + "CliInfo", parcel)?; | ||
| 89 | + for info in infos { | ||
| 90 | + serialize_cli_info(info, parcel)?; | ||
| 91 | + } | ||
| 92 | + Ok(()) | ||
| 93 | +} | ||
| 94 | + | ||
| 95 | +/// Serialize string vector to MsgParcel (for reply). | ||
| 96 | +pub fn serialize_string_vec(vec: &Vec<String>, parcel: &mut MsgParcel) -> Result<()> { | ||
| 97 | + write_vec_len(vec.len(), MAX_TICKET_CAPACITY as i32, ErrCode::InvalidArrayLen, | ||
| 98 | + "string", parcel)?; | ||
| 99 | + for s in vec { | ||
| 100 | + parcel.write_string16(s).map_err(ipc_err_handle)?; | ||
| 101 | + } | ||
| 102 | + Ok(()) | ||
| 103 | +} | ||
| 104 | + | ||
| 105 | +/// Write vector length to parcel with capacity check. | ||
| 106 | +pub fn write_vec_len(vec_len: usize, max_capacity: i32, err_code: ErrCode, label: &str, | ||
| 107 | + parcel: &mut MsgParcel) -> Result<()> { | ||
| 108 | + if vec_len > max_capacity as usize { | ||
| 109 | + return macros_lib::log_throw_error!( | ||
| 110 | + err_code, | ||
| 111 | + "[FATAL][IPC]{} vector size exceeds limit: {}", | ||
| 112 | + label, | ||
| 113 | + vec_len | ||
| 114 | + ); | ||
| 115 | + } | ||
| 116 | + let count = vec_len as i32; | ||
| 117 | + parcel.write::<i32>(&count).map_err(ipc_err_handle)?; | ||
| 118 | + Ok(()) | ||
| 119 | +} | ||
| @@ -54,6 +54,31 @@ class GrantPermissionsContext : public AgentFenceAsyncContext { | |||
| 54 | std::vector<UserAuthResult> userAuthResult {}; | 54 | std::vector<UserAuthResult> userAuthResult {}; |
| 55 | std::vector<VerifyTicketInfo> ticketInfo {}; | 55 | std::vector<VerifyTicketInfo> ticketInfo {}; |
| 56 | }; | 56 | }; |
| 57 | + | ||
| 58 | +class GenerateControlledDevicePackageContext : public AgentFenceAsyncContext { | ||
| 59 | +public: | ||
| 60 | + std::vector<PermissionQuery> permissionQuery {}; | ||
| 61 | + std::vector<RemoteAuthPackage> remoteAuthPackage {}; | ||
| 62 | +}; | ||
| 63 | + | ||
| 64 | +class VerifyControlledDevicePackageContext : public AgentFenceAsyncContext { | ||
| 65 | +public: | ||
| 66 | + std::vector<RemoteAuthPackage> ticketInfo {}; | ||
| 67 | + std::vector<bool> verifyRes {}; | ||
| 68 | +}; | ||
| 69 | + | ||
| 70 | +class GenerateControllerDevicePackageContext : public AgentFenceAsyncContext { | ||
| 71 | +public: | ||
| 72 | + std::vector<RemoteUserAuthResults> remoteUserAuthResults {}; | ||
| 73 | + std::vector<RemoteAuthPackage> remoteAuthPackage {}; | ||
| 74 | +}; | ||
| 75 | + | ||
| 76 | +class VerifyControllerDevicePackageContext : public AgentFenceAsyncContext { | ||
| 77 | +public: | ||
| 78 | + std::vector<RemoteAuthPackage> ticketInfo {}; | ||
| 79 | + RemoteInfo remoteInfo {}; | ||
| 80 | + std::vector<bool> verifyRes {}; | ||
| 81 | +}; | ||
| 57 | } // namespace SAF | 82 | } // namespace SAF |
| 58 | } // namespace Security | 83 | } // namespace Security |
| 59 | } // namespace OHOS | 84 | } // namespace OHOS |
| @@ -28,6 +28,7 @@ using namespace OHOS::Security::SAF; | |||
| 28 | using namespace OHOS::Security::SAF_ASSET_COMMON; | 28 | using namespace OHOS::Security::SAF_ASSET_COMMON; |
| 29 | 29 | ||
| 30 | namespace { | 30 | namespace { |
| 31 | +constexpr size_t VERIFY_CONTROLLER_PACKAGE_ARG_COUNT = 2; | ||
| 31 | napi_value NapiRequestToolPermissions(const napi_env env, napi_callback_info info) | 32 | napi_value NapiRequestToolPermissions(const napi_env env, napi_callback_info info) |
| 32 | { | 33 | { |
| 33 | auto asyncContext = std::unique_ptr<RequestToolPermissionContext>(new (std::nothrow)RequestToolPermissionContext()); | 34 | auto asyncContext = std::unique_ptr<RequestToolPermissionContext>(new (std::nothrow)RequestToolPermissionContext()); |
| @@ -104,6 +105,188 @@ napi_value NapiGrantToolPermissionsByUser(const napi_env env, napi_callback_info | |||
| 104 | return CreateAsyncWork(env, info, std::move(asyncContext), __func__); | 105 | return CreateAsyncWork(env, info, std::move(asyncContext), __func__); |
| 105 | } | 106 | } |
| 106 | 107 | ||
| 108 | +napi_value NapiGenerateControlledDevicePackage(const napi_env env, napi_callback_info info) | ||
| 109 | +{ | ||
| 110 | + auto asyncContext = std::unique_ptr<GenerateControlledDevicePackageContext>( | ||
| 111 | + new (std::nothrow) GenerateControlledDevicePackageContext()); | ||
| 112 | + NAPI_THROW(env, asyncContext == nullptr, COMMON_INTERNAL_ERROR, | ||
| 113 | + "Failed to create GenerateControlledDevicePackageContext"); | ||
| 114 | + | ||
| 115 | + asyncContext->parse = [](napi_env env, napi_callback_info info, AgentFenceAsyncContext *context) | ||
| 116 | + -> napi_status { | ||
| 117 | + GenerateControlledDevicePackageContext *asyncContext = | ||
| 118 | + static_cast<GenerateControlledDevicePackageContext *>(context); | ||
| 119 | + size_t argc = 1; | ||
| 120 | + napi_value argv[1] = { nullptr }; | ||
| 121 | + NAPI_CALL_RETURN_ERR(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr)); | ||
| 122 | + NAPI_THROW_RETURN_ERR(env, argc < 1, GENERAL_PARAMETER_ERROR, "Invalid number of arguments"); | ||
| 123 | + NAPI_CALL_RETURN_ERR(env, NapiGetProperty(env, argv[0], asyncContext->permissionQuery)); | ||
| 124 | + return napi_ok; | ||
| 125 | + }; | ||
| 126 | + | ||
| 127 | + asyncContext->execute = [](napi_env env, void* data) { | ||
| 128 | + GenerateControlledDevicePackageContext *asyncContext = | ||
| 129 | + static_cast<GenerateControlledDevicePackageContext *>(data); | ||
| 130 | + asyncContext->result = SafAgentFence::GenerateControlledDevicePackage( | ||
| 131 | + asyncContext->permissionQuery, asyncContext->remoteAuthPackage); | ||
| 132 | + }; | ||
| 133 | + | ||
| 134 | + asyncContext->resolve = [](napi_env env, AgentFenceAsyncContext *context) -> napi_value { | ||
| 135 | + GenerateControlledDevicePackageContext *asyncContext = | ||
| 136 | + static_cast<GenerateControlledDevicePackageContext *>(context); | ||
| 137 | + napi_value jsResult = nullptr; | ||
| 138 | + NAPI_CALL(env, napi_create_array(env, &jsResult)); | ||
| 139 | + for (uint32_t i = 0; i < asyncContext->remoteAuthPackage.size(); ++i) { | ||
| 140 | + napi_value jsResultItem = nullptr; | ||
| 141 | + NAPI_CALL(env, napi_create_object(env, &jsResultItem)); | ||
| 142 | + NAPI_CALL(env, NapiSetProperty(env, jsResultItem, "remoteMessage", | ||
| 143 | + asyncContext->remoteAuthPackage[i].remoteMessage)); | ||
| 144 | + NAPI_CALL(env, NapiSetProperty(env, jsResultItem, "challenge", | ||
| 145 | + asyncContext->remoteAuthPackage[i].challenge)); | ||
| 146 | + NAPI_CALL(env, NapiSetProperty(env, jsResultItem, "ticket", | ||
| 147 | + asyncContext->remoteAuthPackage[i].ticket)); | ||
| 148 | + NAPI_CALL(env, napi_set_element(env, jsResult, i, jsResultItem)); | ||
| 149 | + } | ||
| 150 | + return jsResult; | ||
| 151 | + }; | ||
| 152 | + | ||
| 153 | + return CreateAsyncWork(env, info, std::move(asyncContext), __func__); | ||
| 154 | +} | ||
| 155 | + | ||
| 156 | +napi_value NapiVerifyControlledDevicePackage(const napi_env env, napi_callback_info info) | ||
| 157 | +{ | ||
| 158 | + auto asyncContext = std::unique_ptr<VerifyControlledDevicePackageContext>( | ||
| 159 | + new (std::nothrow) VerifyControlledDevicePackageContext()); | ||
| 160 | + NAPI_THROW(env, asyncContext == nullptr, COMMON_INTERNAL_ERROR, | ||
| 161 | + "Failed to create VerifyControlledDevicePackageContext"); | ||
| 162 | + | ||
| 163 | + asyncContext->parse = [](napi_env env, napi_callback_info info, AgentFenceAsyncContext *context) | ||
| 164 | + -> napi_status { | ||
| 165 | + VerifyControlledDevicePackageContext *asyncContext = | ||
| 166 | + static_cast<VerifyControlledDevicePackageContext *>(context); | ||
| 167 | + size_t argc = 1; | ||
| 168 | + napi_value argv[1] = { nullptr }; | ||
| 169 | + NAPI_CALL_RETURN_ERR(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr)); | ||
| 170 | + NAPI_THROW_RETURN_ERR(env, argc < 1, GENERAL_PARAMETER_ERROR, "Invalid number of arguments"); | ||
| 171 | + NAPI_CALL_RETURN_ERR(env, NapiGetProperty(env, argv[0], asyncContext->ticketInfo)); | ||
| 172 | + return napi_ok; | ||
| 173 | + }; | ||
| 174 | + | ||
| 175 | + asyncContext->execute = [](napi_env env, void* data) { | ||
| 176 | + VerifyControlledDevicePackageContext *asyncContext = | ||
| 177 | + static_cast<VerifyControlledDevicePackageContext *>(data); | ||
| 178 | + asyncContext->result = SafAgentFence::VerifyControlledDevicePackage( | ||
| 179 | + asyncContext->ticketInfo, asyncContext->verifyRes); | ||
| 180 | + }; | ||
| 181 | + | ||
| 182 | + asyncContext->resolve = [](napi_env env, AgentFenceAsyncContext *context) -> napi_value { | ||
| 183 | + VerifyControlledDevicePackageContext *asyncContext = | ||
| 184 | + static_cast<VerifyControlledDevicePackageContext *>(context); | ||
| 185 | + napi_value jsResult = nullptr; | ||
| 186 | + NAPI_CALL(env, napi_create_array(env, &jsResult)); | ||
| 187 | + for (uint32_t i = 0; i < asyncContext->verifyRes.size(); ++i) { | ||
| 188 | + napi_value jsResultItem = nullptr; | ||
| 189 | + NAPI_CALL(env, napi_get_boolean(env, asyncContext->verifyRes[i], &jsResultItem)); | ||
| 190 | + NAPI_CALL(env, napi_set_element(env, jsResult, i, jsResultItem)); | ||
| 191 | + } | ||
| 192 | + return jsResult; | ||
| 193 | + }; | ||
| 194 | + | ||
| 195 | + return CreateAsyncWork(env, info, std::move(asyncContext), __func__); | ||
| 196 | +} | ||
| 197 | + | ||
| 198 | +napi_value NapiGenerateControllerDevicePackage(const napi_env env, napi_callback_info info) | ||
| 199 | +{ | ||
| 200 | + auto asyncContext = std::unique_ptr<GenerateControllerDevicePackageContext>( | ||
| 201 | + new (std::nothrow) GenerateControllerDevicePackageContext()); | ||
| 202 | + NAPI_THROW(env, asyncContext == nullptr, COMMON_INTERNAL_ERROR, | ||
| 203 | + "Failed to create GenerateControllerDevicePackageContext"); | ||
| 204 | + | ||
| 205 | + asyncContext->parse = [](napi_env env, napi_callback_info info, AgentFenceAsyncContext *context) | ||
| 206 | + -> napi_status { | ||
| 207 | + GenerateControllerDevicePackageContext *asyncContext = | ||
| 208 | + static_cast<GenerateControllerDevicePackageContext *>(context); | ||
| 209 | + size_t argc = 1; | ||
| 210 | + napi_value argv[1] = { nullptr }; | ||
| 211 | + NAPI_CALL_RETURN_ERR(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr)); | ||
| 212 | + NAPI_THROW_RETURN_ERR(env, argc < 1, GENERAL_PARAMETER_ERROR, "Invalid number of arguments"); | ||
| 213 | + NAPI_CALL_RETURN_ERR(env, NapiGetProperty(env, argv[0], asyncContext->remoteUserAuthResults)); | ||
| 214 | + return napi_ok; | ||
| 215 | + }; | ||
| 216 | + | ||
| 217 | + asyncContext->execute = [](napi_env env, void* data) { | ||
| 218 | + GenerateControllerDevicePackageContext *asyncContext = | ||
| 219 | + static_cast<GenerateControllerDevicePackageContext *>(data); | ||
| 220 | + asyncContext->result = SafAgentFence::GenerateControllerDevicePackage( | ||
| 221 | + asyncContext->remoteUserAuthResults, asyncContext->remoteAuthPackage); | ||
| 222 | + }; | ||
| 223 | + | ||
| 224 | + asyncContext->resolve = [](napi_env env, AgentFenceAsyncContext *context) -> napi_value { | ||
| 225 | + GenerateControllerDevicePackageContext *asyncContext = | ||
| 226 | + static_cast<GenerateControllerDevicePackageContext *>(context); | ||
| 227 | + napi_value jsResult = nullptr; | ||
| 228 | + NAPI_CALL(env, napi_create_array(env, &jsResult)); | ||
| 229 | + for (uint32_t i = 0; i < asyncContext->remoteAuthPackage.size(); ++i) { | ||
| 230 | + napi_value jsResultItem = nullptr; | ||
| 231 | + NAPI_CALL(env, napi_create_object(env, &jsResultItem)); | ||
| 232 | + NAPI_CALL(env, NapiSetProperty(env, jsResultItem, "remoteMessage", | ||
| 233 | + asyncContext->remoteAuthPackage[i].remoteMessage)); | ||
| 234 | + NAPI_CALL(env, NapiSetProperty(env, jsResultItem, "challenge", | ||
| 235 | + asyncContext->remoteAuthPackage[i].challenge)); | ||
| 236 | + NAPI_CALL(env, NapiSetProperty(env, jsResultItem, "ticket", | ||
| 237 | + asyncContext->remoteAuthPackage[i].ticket)); | ||
| 238 | + NAPI_CALL(env, napi_set_element(env, jsResult, i, jsResultItem)); | ||
| 239 | + } | ||
| 240 | + return jsResult; | ||
| 241 | + }; | ||
| 242 | + | ||
| 243 | + return CreateAsyncWork(env, info, std::move(asyncContext), __func__); | ||
| 244 | +} | ||
| 245 | + | ||
| 246 | +napi_value NapiVerifyControllerDevicePackage(const napi_env env, napi_callback_info info) | ||
| 247 | +{ | ||
| 248 | + auto asyncContext = std::unique_ptr<VerifyControllerDevicePackageContext>( | ||
| 249 | + new (std::nothrow) VerifyControllerDevicePackageContext()); | ||
| 250 | + NAPI_THROW(env, asyncContext == nullptr, COMMON_INTERNAL_ERROR, | ||
| 251 | + "Failed to create VerifyControllerDevicePackageContext"); | ||
| 252 | + | ||
| 253 | + asyncContext->parse = [](napi_env env, napi_callback_info info, AgentFenceAsyncContext *context) | ||
| 254 | + -> napi_status { | ||
| 255 | + VerifyControllerDevicePackageContext *asyncContext = | ||
| 256 | + static_cast<VerifyControllerDevicePackageContext *>(context); | ||
| 257 | + size_t argc = VERIFY_CONTROLLER_PACKAGE_ARG_COUNT; | ||
| 258 | + napi_value argv[VERIFY_CONTROLLER_PACKAGE_ARG_COUNT] = { nullptr, nullptr }; | ||
| 259 | + NAPI_CALL_RETURN_ERR(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr)); | ||
| 260 | + NAPI_THROW_RETURN_ERR(env, argc < VERIFY_CONTROLLER_PACKAGE_ARG_COUNT, GENERAL_PARAMETER_ERROR, | ||
| 261 | + "Invalid number of arguments"); | ||
| 262 | + NAPI_CALL_RETURN_ERR(env, NapiGetProperty(env, argv[0], asyncContext->ticketInfo)); | ||
| 263 | + NAPI_CALL_RETURN_ERR(env, NapiGetProperty(env, argv[1], asyncContext->remoteInfo)); | ||
| 264 | + return napi_ok; | ||
| 265 | + }; | ||
| 266 | + | ||
| 267 | + asyncContext->execute = [](napi_env env, void* data) { | ||
| 268 | + VerifyControllerDevicePackageContext *asyncContext = | ||
| 269 | + static_cast<VerifyControllerDevicePackageContext *>(data); | ||
| 270 | + asyncContext->result = SafAgentFence::VerifyControllerDevicePackage( | ||
| 271 | + asyncContext->ticketInfo, asyncContext->remoteInfo, asyncContext->verifyRes); | ||
| 272 | + }; | ||
| 273 | + | ||
| 274 | + asyncContext->resolve = [](napi_env env, AgentFenceAsyncContext *context) -> napi_value { | ||
| 275 | + VerifyControllerDevicePackageContext *asyncContext = | ||
| 276 | + static_cast<VerifyControllerDevicePackageContext *>(context); | ||
| 277 | + napi_value jsResult = nullptr; | ||
| 278 | + NAPI_CALL(env, napi_create_array(env, &jsResult)); | ||
| 279 | + for (uint32_t i = 0; i < asyncContext->verifyRes.size(); ++i) { | ||
| 280 | + napi_value jsResultItem = nullptr; | ||
| 281 | + NAPI_CALL(env, napi_get_boolean(env, asyncContext->verifyRes[i], &jsResultItem)); | ||
| 282 | + NAPI_CALL(env, napi_set_element(env, jsResult, i, jsResultItem)); | ||
| 283 | + } | ||
| 284 | + return jsResult; | ||
| 285 | + }; | ||
| 286 | + | ||
| 287 | + return CreateAsyncWork(env, info, std::move(asyncContext), __func__); | ||
| 288 | +} | ||
| 289 | + | ||
| 107 | napi_value DeclareOperationType(const napi_env env) | 290 | napi_value DeclareOperationType(const napi_env env) |
| 108 | { | 291 | { |
| 109 | napi_value status = nullptr; | 292 | napi_value status = nullptr; |
| @@ -137,11 +320,13 @@ napi_value DeclareRole(const napi_env env) | |||
| 137 | napi_value Register(const napi_env env, napi_value exports) | 320 | napi_value Register(const napi_env env, napi_value exports) |
| 138 | { | 321 | { |
| 139 | napi_property_descriptor desc[] = { | 322 | napi_property_descriptor desc[] = { |
| 140 | - // register function | ||
| 141 | DECLARE_NAPI_FUNCTION("requestToolPermissions", NapiRequestToolPermissions), | 323 | DECLARE_NAPI_FUNCTION("requestToolPermissions", NapiRequestToolPermissions), |
| 142 | DECLARE_NAPI_FUNCTION("grantToolPermissionsByUser", NapiGrantToolPermissionsByUser), | 324 | DECLARE_NAPI_FUNCTION("grantToolPermissionsByUser", NapiGrantToolPermissionsByUser), |
| 325 | + DECLARE_NAPI_FUNCTION("generateControlledDevicePackage", NapiGenerateControlledDevicePackage), | ||
| 326 | + DECLARE_NAPI_FUNCTION("verifyControlledDevicePackage", NapiVerifyControlledDevicePackage), | ||
| 327 | + DECLARE_NAPI_FUNCTION("generateControllerDevicePackage", NapiGenerateControllerDevicePackage), | ||
| 328 | + DECLARE_NAPI_FUNCTION("verifyControllerDevicePackage", NapiVerifyControllerDevicePackage), | ||
| 143 | 329 | ||
| 144 | - // register enum | ||
| 145 | DECLARE_NAPI_PROPERTY("OperationType", DeclareOperationType(env)), | 330 | DECLARE_NAPI_PROPERTY("OperationType", DeclareOperationType(env)), |
| 146 | DECLARE_NAPI_PROPERTY("AuthStatus", DeclareAuthStatus(env)), | 331 | DECLARE_NAPI_PROPERTY("AuthStatus", DeclareAuthStatus(env)), |
| 147 | DECLARE_NAPI_PROPERTY("Role", DeclareRole(env)), | 332 | DECLARE_NAPI_PROPERTY("Role", DeclareRole(env)), |
| @@ -0,0 +1,47 @@ | |||
| 1 | +# Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 2 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 3 | +# you may not use this file except in compliance with the License. | ||
| 4 | +# You may obtain a copy of the License at | ||
| 5 | +# | ||
| 6 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
| 7 | +# | ||
| 8 | +# Unless required by applicable law or agreed to in writing, software | ||
| 9 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
| 10 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 11 | +# See the License for the specific language governing permissions and | ||
| 12 | +# limitations under the License. | ||
| 13 | + | ||
| 14 | +import("//build/ohos.gni") | ||
| 15 | + | ||
| 16 | +ohos_rust_static_library("saf_utils") { | ||
| 17 | + sources = [ "src/lib.rs", "src/json_builder.rs" ] | ||
| 18 | + | ||
| 19 | + deps = [ | ||
| 20 | + "../definition:saf_definition", | ||
| 21 | + "../os_dependency/log:saf_log", | ||
| 22 | + ] | ||
| 23 | + | ||
| 24 | + external_deps = [ "ylong_json:lib" ] | ||
| 25 | + | ||
| 26 | +rustflags = [ "-C", "codegen-units=1", "-C", "opt-level=z", "-C", "debuginfo=0" ] | ||
| 27 | + | ||
| 28 | +sanitize = { | ||
| 29 | + integer_overflow = true | ||
| 30 | + cfi = true | ||
| 31 | + debug = false | ||
| 32 | + cfi_cross_dso = true | ||
| 33 | + boundary_sanitize = true | ||
| 34 | + ubsan = true | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | +cflags_cc = [ | ||
| 38 | + "-Wall", | ||
| 39 | + "-Werror", | ||
| 40 | + "-fPIC", | ||
| 41 | +] | ||
| 42 | + | ||
| 43 | +crate_name = "saf_utils" | ||
| 44 | + crate_type = "rlib" | ||
| 45 | + subsystem_name = "security" | ||
| 46 | + part_name = "asset" | ||
| 47 | +} | ||
| @@ -0,0 +1,24 @@ | |||
| 1 | +# Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 2 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 3 | +# you may not use this file except in compliance with the License. | ||
| 4 | +# You may obtain a copy of the License at | ||
| 5 | +# | ||
| 6 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
| 7 | +# | ||
| 8 | +# Unless required by applicable law or agreed to in writing, software | ||
| 9 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
| 10 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 11 | +# See the License for the specific language governing permissions and | ||
| 12 | +# limitations under the License. | ||
| 13 | + | ||
| 14 | +[package] | ||
| 15 | +name = "saf_utils" | ||
| 16 | +version = "0.1.0" | ||
| 17 | +edition = "2021" | ||
| 18 | + | ||
| 19 | +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
| 20 | + | ||
| 21 | +[dependencies] | ||
| 22 | +ylong_json = { git = "https://gitcode.com/openharmony/commonlibrary_rust_ylong_json.git" } | ||
| 23 | +saf_definition = { path = "../../definition" } | ||
| 24 | +saf_log = { path = "../../os_dependency/log" } | ||
| @@ -0,0 +1,219 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | +//! JSON builder module for message construction | ||
| 17 | + | ||
| 18 | +use std::str::FromStr; | ||
| 19 | +use ylong_json::{JsonValue, Object, Array, Number}; | ||
| 20 | +use saf_definition::{macros_lib, ErrCode, Result}; | ||
| 21 | + | ||
| 22 | +/// JSON builder for constructing JSON objects with chainable API. | ||
| 23 | +pub struct JsonBuilder { | ||
| 24 | + root: Object, | ||
| 25 | +} | ||
| 26 | + | ||
| 27 | +impl JsonBuilder { | ||
| 28 | + /// Creates a new empty JSON builder. | ||
| 29 | + pub fn new() -> Self { | ||
| 30 | + Self { root: Object::new() } | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + /// Adds a string field to the JSON object. | ||
| 34 | + pub fn add_string(&mut self, key: &str, value: &str) { | ||
| 35 | + self.root.insert(key.to_string(), JsonValue::String(value.to_string())); | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + /// Adds a signed number field to the JSON object. | ||
| 39 | + pub fn add_number(&mut self, key: &str, value: i64) { | ||
| 40 | + self.root.insert(key.to_string(), JsonValue::Number(Number::Signed(value))); | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + /// Adds an unsigned 64-bit number field to the JSON object. | ||
| 44 | + pub fn add_u64(&mut self, key: &str, value: u64) { | ||
| 45 | + self.root.insert(key.to_string(), JsonValue::Number(Number::Unsigned(value))); | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + /// Adds a boolean field to the JSON object. | ||
| 49 | + pub fn add_bool(&mut self, key: &str, value: bool) { | ||
| 50 | + self.root.insert(key.to_string(), JsonValue::Boolean(value)); | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + /// Adds a string array field to the JSON object. | ||
| 54 | + pub fn add_string_array(&mut self, key: &str, values: Vec<String>) { | ||
| 55 | + let mut arr = Array::new(); | ||
| 56 | + for v in values { | ||
| 57 | + arr.push(JsonValue::String(v)); | ||
| 58 | + } | ||
| 59 | + self.root.insert(key.to_string(), JsonValue::Array(arr)); | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + /// Adds an object array field to the JSON object. | ||
| 63 | + pub fn add_object_array(&mut self, key: &str, objects: Vec<Object>) { | ||
| 64 | + let mut arr = Array::new(); | ||
| 65 | + for obj in objects { | ||
| 66 | + arr.push(JsonValue::Object(obj)); | ||
| 67 | + } | ||
| 68 | + self.root.insert(key.to_string(), JsonValue::Array(arr)); | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + /// Adds a nested object field to the JSON object. | ||
| 72 | + pub fn add_object(&mut self, key: &str, obj: Object) { | ||
| 73 | + self.root.insert(key.to_string(), JsonValue::Object(obj)); | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + /// Builds and serializes the JSON object to a compact string. | ||
| 77 | + pub fn build(self) -> Result<String> { | ||
| 78 | + let json = JsonValue::Object(self.root); | ||
| 79 | + json.to_compact_string().map_err(|e| { | ||
| 80 | + macros_lib::log_and_into_saf_error!(ErrCode::JsonParseError, "JSON serialization failed: {}", e) | ||
| 81 | + }) | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + /// Returns a mutable reference to the root object for advanced operations. | ||
| 85 | + pub fn root(&mut self) -> &mut Object { | ||
| 86 | + &mut self.root | ||
| 87 | + } | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | +impl Default for JsonBuilder { | ||
| 91 | + fn default() -> Self { | ||
| 92 | + Self::new() | ||
| 93 | + } | ||
| 94 | +} | ||
| 95 | + | ||
| 96 | +/// Creates a new empty JSON object. | ||
| 97 | +pub fn new_object() -> Object { | ||
| 98 | + Object::new() | ||
| 99 | +} | ||
| 100 | + | ||
| 101 | +/// Adds a string field to a JSON object. | ||
| 102 | +pub fn object_add_string(obj: &mut Object, key: &str, value: &str) { | ||
| 103 | + obj.insert(key.to_string(), JsonValue::String(value.to_string())); | ||
| 104 | +} | ||
| 105 | + | ||
| 106 | +/// Adds a number field to a JSON object. | ||
| 107 | +pub fn object_add_number(obj: &mut Object, key: &str, value: i64) { | ||
| 108 | + obj.insert(key.to_string(), JsonValue::Number(Number::Signed(value))); | ||
| 109 | +} | ||
| 110 | + | ||
| 111 | +/// Gets an optional string from a JSON object by key, removing the key from the object. | ||
| 112 | +/// Returns empty string if the key doesn't exist or the value is Null. | ||
| 113 | +/// Returns error if the key exists but type is not String. | ||
| 114 | +pub fn take_optional_string(obj: &mut Object, key: &str) -> Result<String> { | ||
| 115 | + match obj.remove(key) { | ||
| 116 | + Some(JsonValue::String(s)) => Ok(s), | ||
| 117 | + Some(JsonValue::Null) | None => Ok(String::new()), | ||
| 118 | + Some(_) => macros_lib::log_throw_error!( | ||
| 119 | + ErrCode::DataTypeMismatch, | ||
| 120 | + "[FATAL][JSON]{} type mismatch, expected String", key | ||
| 121 | + ) | ||
| 122 | + } | ||
| 123 | +} | ||
| 124 | + | ||
| 125 | +/// Gets a required string from a JSON object by key, removing the key from the object. | ||
| 126 | +/// Returns error if the key doesn't exist, is Null, or type mismatch. | ||
| 127 | +pub fn take_required_string(obj: &mut Object, key: &str) -> Result<String> { | ||
| 128 | + match obj.remove(key) { | ||
| 129 | + Some(JsonValue::String(s)) => Ok(s), | ||
| 130 | + Some(JsonValue::Null) | None => macros_lib::log_throw_error!( | ||
| 131 | + ErrCode::ArgEmpty, | ||
| 132 | + "[FATAL][JSON]{} not found or is null", key | ||
| 133 | + ), | ||
| 134 | + Some(_) => macros_lib::log_throw_error!( | ||
| 135 | + ErrCode::DataTypeMismatch, | ||
| 136 | + "[FATAL][JSON]{} type mismatch, expected String", key | ||
| 137 | + ), | ||
| 138 | + } | ||
| 139 | +} | ||
| 140 | + | ||
| 141 | +/// Gets a required value from a JSON object by key, removing the key from the object. | ||
| 142 | +/// Returns error if the key doesn't exist or is Null. | ||
| 143 | +pub fn take_required_value(obj: &mut Object, key: &str) -> Result<JsonValue> { | ||
| 144 | + match obj.remove(key) { | ||
| 145 | + None | Some(JsonValue::Null) => macros_lib::log_throw_error!( | ||
| 146 | + ErrCode::ArgEmpty, | ||
| 147 | + "[FATAL][JSON]{} is null", key | ||
| 148 | + ), | ||
| 149 | + Some(v) => Ok(v), | ||
| 150 | + } | ||
| 151 | +} | ||
| 152 | + | ||
| 153 | +/// Converts a JsonValue to Object, returning error if not an object. | ||
| 154 | +pub fn json_into_object(value: JsonValue) -> Result<Object> { | ||
| 155 | + match value { | ||
| 156 | + JsonValue::Object(o) => Ok(o), | ||
| 157 | + _ => macros_lib::log_throw_error!( | ||
| 158 | + ErrCode::DataTypeMismatch, | ||
| 159 | + "[FATAL][JSON]value is not an object" | ||
| 160 | + ), | ||
| 161 | + } | ||
| 162 | +} | ||
| 163 | + | ||
| 164 | +/// Gets a required JSON value and converts it to compact string representation. | ||
| 165 | +/// Returns error if the key doesn't exist or is Null. | ||
| 166 | +/// For String values, returns the string directly. | ||
| 167 | +/// For other types (Number, Boolean, etc.), converts to compact string representation. | ||
| 168 | +pub fn get_compact_json_value(json: &JsonValue, key: &str) -> Result<String> { | ||
| 169 | + match &json[key] { | ||
| 170 | + JsonValue::Null => { | ||
| 171 | + macros_lib::log_throw_error!( | ||
| 172 | + ErrCode::ArgEmpty, | ||
| 173 | + "[FATAL][JSON]{} not found or is null", key | ||
| 174 | + ) | ||
| 175 | + } | ||
| 176 | + JsonValue::String(s) => Ok(s.clone()), | ||
| 177 | + value => value.to_compact_string().map_err(|e| { | ||
| 178 | + macros_lib::log_and_into_saf_error!( | ||
| 179 | + ErrCode::JsonParseError, | ||
| 180 | + "[FATAL][JSON]{} to_compact_string failed: {}", key, e | ||
| 181 | + ) | ||
| 182 | + }), | ||
| 183 | + } | ||
| 184 | +} | ||
| 185 | + | ||
| 186 | +/// template function for get array from json | ||
| 187 | +pub fn get_array_from_json<T, F>(json_str: &str, key: &str, convert: F) -> Result<Vec<T>> | ||
| 188 | +where | ||
| 189 | + F: Fn(&JsonValue) -> Result<T>, | ||
| 190 | +{ | ||
| 191 | + let json: JsonValue = JsonValue::from_str(json_str).map_err(|e| { | ||
| 192 | + macros_lib::log_and_into_saf_error!(ErrCode::JsonParseError, "JSON parse failed: {}", e) | ||
| 193 | + })?; | ||
| 194 | + | ||
| 195 | + match &json[key] { | ||
| 196 | + JsonValue::Null => { | ||
| 197 | + macros_lib::log_throw_error!( | ||
| 198 | + ErrCode::ArgEmpty, | ||
| 199 | + "[FATAL][JSON]{} not found or is null", key | ||
| 200 | + ) | ||
| 201 | + } | ||
| 202 | + JsonValue::Array(arr) => arr.iter().map(convert).collect(), | ||
| 203 | + _ => macros_lib::log_throw_error!( | ||
| 204 | + ErrCode::DataTypeMismatch, | ||
| 205 | + "[FATAL][JSON]{} type mismatch, expected Array", key | ||
| 206 | + ), | ||
| 207 | + } | ||
| 208 | +} | ||
| 209 | + | ||
| 210 | +/// get string array from json | ||
| 211 | +pub fn get_string_array_from_json(json_str: &str, key: &str) -> Result<Vec<String>> { | ||
| 212 | + get_array_from_json(json_str, key, |v| match v { | ||
| 213 | + JsonValue::String(s) => Ok(s.clone()), | ||
| 214 | + _ => macros_lib::log_throw_error!( | ||
| 215 | + ErrCode::DataTypeMismatch, | ||
| 216 | + "[FATAL][JSON]array contains non-string element" | ||
| 217 | + ), | ||
| 218 | + }) | ||
| 219 | +} | ||
| @@ -0,0 +1,28 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | +//! This module provides common utilities for SAF. | ||
| 17 | + | ||
| 18 | +mod json_builder; | ||
| 19 | +mod time; | ||
| 20 | + | ||
| 21 | +pub use json_builder::{JsonBuilder, new_object, object_add_string, object_add_number, | ||
| 22 | + get_compact_json_value, take_optional_string, take_required_string, take_required_value, json_into_object, | ||
| 23 | + get_string_array_from_json, get_array_from_json}; | ||
| 24 | + | ||
| 25 | +pub use time::{system_time_in_millis, system_time_in_seconds}; | ||
| 26 | + | ||
| 27 | +// Re-export ylong_json types for convenience | ||
| 28 | +pub use ylong_json::{JsonValue, Object}; | ||
| @@ -0,0 +1,76 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | +//! This module is used to get the system time. | ||
| 17 | + | ||
| 18 | +use std::os::raw::{c_int, c_long}; | ||
| 19 | + | ||
| 20 | +use saf_definition::{macros_lib, ErrCode, Result}; | ||
| 21 | + | ||
| 22 | +const CLOCK_REALTIME: c_int = 0; | ||
| 23 | +const SECS_TO_MILLIS: i64 = 1000; | ||
| 24 | +const NANOS_TO_MILLIS: i64 = 1_000_000; | ||
| 25 | + | ||
| 26 | + | ||
| 27 | +struct Timespec { | ||
| 28 | + tv_sec: c_long, | ||
| 29 | + tv_nsec: c_long, | ||
| 30 | +} | ||
| 31 | + | ||
| 32 | +extern "C" { | ||
| 33 | + fn clock_gettime(clock_id: c_int, tp: *mut Timespec) -> c_int; | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | +fn get_timespec() -> Result<Timespec> { | ||
| 37 | + let mut ts = Timespec { tv_sec: 0, tv_nsec: 0 }; | ||
| 38 | + if unsafe { clock_gettime(CLOCK_REALTIME, &mut ts) } != 0 { | ||
| 39 | + return macros_lib::log_throw_error!(ErrCode::GeneralError, "clock_gettime failed"); | ||
| 40 | + } | ||
| 41 | + if ts.tv_sec < 0 { | ||
| 42 | + return macros_lib::log_throw_error!(ErrCode::GeneralError, "32-bit time_t overflow (Y2038)"); | ||
| 43 | + } | ||
| 44 | + Ok(ts) | ||
| 45 | +} | ||
| 46 | + | ||
| 47 | +/// Get the current time from the system, in milliseconds. | ||
| 48 | +pub fn system_time_in_millis() -> Result<u64> { | ||
| 49 | + let ts = get_timespec()?; | ||
| 50 | + let sec_millis = (ts.tv_sec as i64) | ||
| 51 | + .checked_mul(SECS_TO_MILLIS) | ||
| 52 | + .ok_or_else(|| macros_lib::log_and_into_saf_error!( | ||
| 53 | + ErrCode::GeneralError, | ||
| 54 | + "seconds to milliseconds overflow" | ||
| 55 | + ))?; | ||
| 56 | + let nano_millis = ts.tv_nsec as i64 / NANOS_TO_MILLIS; | ||
| 57 | + let millis = sec_millis | ||
| 58 | + .checked_add(nano_millis) | ||
| 59 | + .ok_or_else(|| macros_lib::log_and_into_saf_error!( | ||
| 60 | + ErrCode::GeneralError, | ||
| 61 | + "milliseconds overflow" | ||
| 62 | + ))?; | ||
| 63 | + if millis < 0 { | ||
| 64 | + return macros_lib::log_throw_error!( | ||
| 65 | + ErrCode::GeneralError, | ||
| 66 | + "milliseconds is negative: {}", millis | ||
| 67 | + ); | ||
| 68 | + } | ||
| 69 | + Ok(millis as u64) | ||
| 70 | +} | ||
| 71 | + | ||
| 72 | +/// Get the current time from the system, in seconds. | ||
| 73 | +pub fn system_time_in_seconds() -> Result<u64> { | ||
| 74 | + let ts = get_timespec()?; | ||
| 75 | + Ok(ts.tv_sec as u64) | ||
| 76 | +} | ||
| @@ -106,8 +106,53 @@ public: | |||
| 106 | static int32_t GrantToolPermissionsByUser( | 106 | static int32_t GrantToolPermissionsByUser( |
| 107 | const std::vector<UserAuthResult>& userAuthResult, | 107 | const std::vector<UserAuthResult>& userAuthResult, |
| 108 | std::vector<VerifyTicketInfo>& ticketInfo); | 108 | std::vector<VerifyTicketInfo>& ticketInfo); |
| 109 | + | ||
| 110 | + /** | ||
| 111 | + * @brief Generate controlled device packages for remote control scenario. | ||
| 112 | + * | ||
| 113 | + * @param permissionQuery The vector of permission queries. | ||
| 114 | + * @param remoteAuthPackage Output vector of generated remote auth packages. | ||
| 115 | + * @return Returns 0 on success, or error code on failure. | ||
| 116 | + */ | ||
| 117 | + static int32_t GenerateControlledDevicePackage( | ||
| 118 | + const std::vector<PermissionQuery> &permissionQuery, | ||
| 119 | + std::vector<RemoteAuthPackage> &remoteAuthPackage); | ||
| 120 | + | ||
| 121 | + /** | ||
| 122 | + * @brief Verify controlled device packages for remote control scenario. | ||
| 123 | + * | ||
| 124 | + * @param ticketInfo The vector of remote auth packages to verify. | ||
| 125 | + * @param verifyRes Output vector of verification results (boolean). | ||
| 126 | + * @return Returns 0 on success, or error code on failure. | ||
| 127 | + */ | ||
| 128 | + static int32_t VerifyControlledDevicePackage( | ||
| 129 | + const std::vector<RemoteAuthPackage> &ticketInfo, | ||
| 130 | + std::vector<bool> &verifyRes); | ||
| 131 | + | ||
| 132 | + /** | ||
| 133 | + * @brief Generate controller device packages for remote control scenario. | ||
| 134 | + * | ||
| 135 | + * @param remoteUserAuthResults The vector of remote user authentication results. | ||
| 136 | + * @param remoteAuthPackage Output vector of generated remote auth packages. | ||
| 137 | + * @return Returns 0 on success, or error code on failure. | ||
| 138 | + */ | ||
| 139 | + static int32_t GenerateControllerDevicePackage( | ||
| 140 | + const std::vector<RemoteUserAuthResults> &remoteUserAuthResults, | ||
| 141 | + std::vector<RemoteAuthPackage> &remoteAuthPackage); | ||
| 142 | + | ||
| 143 | + /** | ||
| 144 | + * @brief Verify controller device packages for remote control scenario. | ||
| 145 | + * | ||
| 146 | + * @param ticketInfo The vector of remote auth packages to verify. | ||
| 147 | + * @param remoteInfo Remote information for verification. | ||
| 148 | + * @param verifyRes Output vector of verification results (boolean). | ||
| 149 | + * @return Returns 0 on success, or error code on failure. | ||
| 150 | + */ | ||
| 151 | + static int32_t VerifyControllerDevicePackage( | ||
| 152 | + const std::vector<RemoteAuthPackage> &ticketInfo, | ||
| 153 | + const RemoteInfo &remoteInfo, | ||
| 154 | + std::vector<bool> &verifyRes); | ||
| 109 | }; | 155 | }; |
| 110 | 156 | ||
| 111 | } | 157 | } |
| 112 | - | ||
| 113 | 158 | ||
| @@ -97,7 +97,8 @@ | |||
| 97 | SAF_ASSIGN_ENUM_VALUE(SAF_ERR_TRUSTED_RING_REMOTE_DEVICE_UNTRUSTED, TRUSTED_RING | 8) \ | 97 | SAF_ASSIGN_ENUM_VALUE(SAF_ERR_TRUSTED_RING_REMOTE_DEVICE_UNTRUSTED, TRUSTED_RING | 8) \ |
| 98 | SAF_ASSIGN_ENUM_VALUE(SAF_ERR_TRUSTED_RING_LACK_OF_AUTH_TOKEN, TRUSTED_RING | 9) \ | 98 | SAF_ASSIGN_ENUM_VALUE(SAF_ERR_TRUSTED_RING_LACK_OF_AUTH_TOKEN, TRUSTED_RING | 9) \ |
| 99 | SAF_ASSIGN_ENUM_VALUE(SAF_ERR_TRUSTED_RING_AUTH_TOKEN_IS_EXPIRED, TRUSTED_RING | 10) \ | 99 | SAF_ASSIGN_ENUM_VALUE(SAF_ERR_TRUSTED_RING_AUTH_TOKEN_IS_EXPIRED, TRUSTED_RING | 10) \ |
| 100 | - | 100 | + /* BMS */ \ |
| 101 | + SAF_ASSIGN_ENUM_VALUE(SAF_ERR_GET_BUNDLE_NAME_BY_TOKEN_FAILED, BMS | 9) \ | ||
| 101 | 102 | ||
| 102 | 103 | ||
| 103 | 104 | ||
| @@ -45,6 +45,14 @@ pub enum EventType { | |||
| 45 | 45 | ||
| 46 | /// VerifyRemoteTicket | 46 | /// VerifyRemoteTicket |
| 47 | VerifyRemoteTicket = 5, | 47 | VerifyRemoteTicket = 5, |
| 48 | + /// Sign remote auth package for remote control scenario | ||
| 49 | + SignRemoteAuthPackage = 6, | ||
| 50 | + /// Verify remote auth package for remote control scenario | ||
| 51 | + VerifyRemoteAuthPackage = 7, | ||
| 52 | + /// Store grant record for remote control scenario | ||
| 53 | + StoreGrantRecord = 8, | ||
| 54 | + /// Get device udid for device identification | ||
| 55 | + GetDeviceUdid = 9, | ||
| 48 | } | 56 | } |
| 49 | 57 | ||
| 50 | /// Performance metrics parameter keys for StatisticsMetrics EventType. | 58 | /// Performance metrics parameter keys for StatisticsMetrics EventType. |
| @@ -108,6 +116,113 @@ pub const VERIFY_REMOTE_TICKET_KEYS: VerifyRemoteTicketKeys = | |||
| 108 | os_account_id: "osAccountId" | 116 | os_account_id: "osAccountId" |
| 109 | }; | 117 | }; |
| 110 | 118 | ||
| 119 | +/// Sign remote auth package parameter keys for SignRemoteAuthPackage EventType. | ||
| 120 | +pub struct SignRemoteAuthPackageKeys { | ||
| 121 | + /// The OS account ID | ||
| 122 | + pub os_account_id: &'static str, | ||
| 123 | + /// The user ID (domainId) | ||
| 124 | + pub uid: &'static str, | ||
| 125 | + /// The remote auth package message | ||
| 126 | + pub remote_auth_package: &'static str, | ||
| 127 | + /// The remote control token | ||
| 128 | + pub remote_control_token: &'static str, | ||
| 129 | + /// The device ID header (output) | ||
| 130 | + pub device_id_header: &'static str, | ||
| 131 | + /// The result remote auth package (output) | ||
| 132 | + pub result_remote_auth_package: &'static str, | ||
| 133 | + /// The sign info (output) | ||
| 134 | + pub sign_info: &'static str, | ||
| 135 | +} | ||
| 136 | + | ||
| 137 | +/// SignRemoteAuthPackageKeys constant for SignRemoteAuthPackage EventType. | ||
| 138 | +pub const SIGN_REMOTE_AUTH_PACKAGE_KEYS: SignRemoteAuthPackageKeys = | ||
| 139 | + SignRemoteAuthPackageKeys { | ||
| 140 | + os_account_id: "OsAccountId", | ||
| 141 | + uid: "Uid", | ||
| 142 | + remote_auth_package: "RemoteAuthPackage", | ||
| 143 | + remote_control_token: "RemoteControlToken", | ||
| 144 | + device_id_header: "DeviceIdHeader", | ||
| 145 | + result_remote_auth_package: "ResultRemoteAuthPackage", | ||
| 146 | + sign_info: "SignInfo", | ||
| 147 | + }; | ||
| 148 | + | ||
| 149 | +/// Verify remote auth package parameter keys for VerifyRemoteAuthPackage EventType. | ||
| 150 | +pub struct VerifyRemoteAuthPackageKeys { | ||
| 151 | + /// The OS account ID | ||
| 152 | + pub os_account_id: &'static str, | ||
| 153 | + /// The UID string (domainId) | ||
| 154 | + pub uid: &'static str, | ||
| 155 | + /// The device ID header | ||
| 156 | + pub device_id_header: &'static str, | ||
| 157 | + /// The remote auth message | ||
| 158 | + pub remote_auth_message: &'static str, | ||
| 159 | + /// The sign info | ||
| 160 | + pub sign_info: &'static str, | ||
| 161 | + /// The verify result (output) | ||
| 162 | + pub verify_result: &'static str, | ||
| 163 | +} | ||
| 164 | + | ||
| 165 | +/// VerifyRemoteAuthPackageKeys constant for VerifyRemoteAuthPackage EventType. | ||
| 166 | +pub const VERIFY_REMOTE_AUTH_PACKAGE_KEYS: VerifyRemoteAuthPackageKeys = | ||
| 167 | + VerifyRemoteAuthPackageKeys { | ||
| 168 | + os_account_id: "OsAccountId", | ||
| 169 | + uid: "Uid", | ||
| 170 | + device_id_header: "DeviceIdHeader", | ||
| 171 | + remote_auth_message: "RemoteAuthMessage", | ||
| 172 | + sign_info: "SignInfo", | ||
| 173 | + verify_result: "VerifyResult", | ||
| 174 | + }; | ||
| 175 | + | ||
| 176 | +/// Store grant record parameter keys for StoreGrantRecord EventType. | ||
| 177 | +pub struct StoreGrantRecordKeys { | ||
| 178 | + /// The OS account ID | ||
| 179 | + pub os_account_id: &'static str, | ||
| 180 | + /// The controlled device name | ||
| 181 | + pub controlled_device_name: &'static str, | ||
| 182 | + /// The controller device name | ||
| 183 | + pub controller_device_name: &'static str, | ||
| 184 | + /// The is self grant flag | ||
| 185 | + pub is_self_grant: &'static str, | ||
| 186 | + /// The permission names | ||
| 187 | + pub permission_names: &'static str, | ||
| 188 | + /// The device role | ||
| 189 | + pub device_role: &'static str, | ||
| 190 | + /// The calling bundle name | ||
| 191 | + pub calling_bundle_name: &'static str, | ||
| 192 | + /// The grant type | ||
| 193 | + pub grant_type: &'static str, | ||
| 194 | + /// The timestamp | ||
| 195 | + pub timestamp: &'static str, | ||
| 196 | +} | ||
| 197 | + | ||
| 198 | +/// StoreGrantRecordKeys constant for StoreGrantRecord EventType. | ||
| 199 | +pub const STORE_GRANT_RECORD_KEYS: StoreGrantRecordKeys = | ||
| 200 | + StoreGrantRecordKeys { | ||
| 201 | + os_account_id: "OsAccountId", | ||
| 202 | + controlled_device_name: "ControlledDeviceName", | ||
| 203 | + controller_device_name: "ControllerDeviceName", | ||
| 204 | + is_self_grant: "IsSelfGrant", | ||
| 205 | + permission_names: "PermissionNames", | ||
| 206 | + device_role: "DeviceRole", | ||
| 207 | + calling_bundle_name: "CallingBundleName", | ||
| 208 | + grant_type: "GrantType", | ||
| 209 | + timestamp: "Timestamp", | ||
| 210 | + }; | ||
| 211 | + | ||
| 212 | +/// GetDeviceUdid parameter keys for GetDeviceUdid EventType. | ||
| 213 | +pub struct GetDeviceUdidKeys { | ||
| 214 | + /// The os account ID (input) | ||
| 215 | + pub os_account_id: &'static str, | ||
| 216 | + /// The device UDID (output) | ||
| 217 | + pub udid: &'static str, | ||
| 218 | +} | ||
| 219 | + | ||
| 220 | +/// GetDeviceUdidKeys constant for GetDeviceUdid EventType | ||
| 221 | +pub const GET_DEVICE_UDID_KEYS: GetDeviceUdidKeys = GetDeviceUdidKeys { | ||
| 222 | + os_account_id: "osAccountId", | ||
| 223 | + udid: "Udid", | ||
| 224 | +}; | ||
| 225 | + | ||
| 111 | /// Global constant instance for error metrics parameter keys. | 226 | /// Global constant instance for error metrics parameter keys. |
| 112 | /// Use this to access standardized parameter names for StatisticsError events. | 227 | /// Use this to access standardized parameter names for StatisticsError events. |
| 113 | pub const ERROR_METRICS_KEYS: ErrorMetricsKeys = | 228 | pub const ERROR_METRICS_KEYS: ErrorMetricsKeys = |
| @@ -15,6 +15,7 @@ | |||
| 15 | 15 | ||
| 16 | //! This module defines the interface of the SAF Rust SDK. | 16 | //! This module defines the interface of the SAF Rust SDK. |
| 17 | 17 | ||
| 18 | + | ||
| 18 | pub use saf_definition::*; | 19 | pub use saf_definition::*; |
| 19 | 20 | ||
| 20 | use std::sync::{Arc, Mutex, OnceLock}; | 21 | use std::sync::{Arc, Mutex, OnceLock}; |
| @@ -54,11 +55,11 @@ impl Manager { | |||
| 54 | Ok(Arc::new(Mutex::new(Manager { remote }))) | 55 | Ok(Arc::new(Mutex::new(Manager { remote }))) |
| 55 | }).cloned() | 56 | }).cloned() |
| 56 | } | 57 | } |
| 57 | - | 58 | + |
| 58 | /// Check access for certain application. | 59 | /// Check access for certain application. |
| 59 | - pub fn check_access(&mut self, attributes: &SAFMap) -> Result<()> { | 60 | + pub fn check_access(&mut self, attributes: &SAFMap) -> Result<()> { |
| 60 | - self.process_one_agr_request(attributes, IpcCode::CheckAccess)?; | 61 | + self.process_one_agr_request(attributes, IpcCode::CheckAccess)?; |
| 61 | - Ok(()) | 62 | + Ok(()) |
| 62 | } | 63 | } |
| 63 | 64 | ||
| 64 | fn rebuild(&mut self) -> Result<()> { | 65 | fn rebuild(&mut self) -> Result<()> { |
| @@ -630,5 +630,5 @@ impl AssetService { | |||
| 630 | 630 | ||
| 631 | /// stub for test | 631 | /// stub for test |
| 632 | pub mod ut_core_service_lib_stub { | 632 | pub mod ut_core_service_lib_stub { |
| 633 | - include!{"../../../test/unittest/ut_test/services/core_service/test_stub/ut_core_service_lib_stub.rs"} | 633 | + include!{"../../../../test/asset/unittest/ut_test/services/core_service/test_stub/ut_core_service_lib_stub.rs"} |
| 634 | } | 634 | } |
| @@ -184,5 +184,5 @@ pub(crate) fn add(calling_info: &CallingInfo, attributes: &AssetMap) -> Result<( | |||
| 184 | 184 | ||
| 185 | /// stub for test | 185 | /// stub for test |
| 186 | pub mod ut_operation_add_stub { | 186 | pub mod ut_operation_add_stub { |
| 187 | - include!{"../../../../test/unittest/ut_test/services/core_service/test_stub/operations/ut_operation_add_stub.rs"} | 187 | + include!{"../../../../../test/asset/unittest/ut_test/services/core_service/test_stub/operations/ut_operation_add_stub.rs"} |
| 188 | } | 188 | } |
| @@ -11,6 +11,7 @@ | |||
| 11 | # See the License for the specific language governing permissions and | 11 | # See the License for the specific language governing permissions and |
| 12 | # limitations under the License. | 12 | # limitations under the License. |
| 13 | 13 | ||
| 14 | +import("//base/security/asset/config.gni") | ||
| 14 | import("//build/ohos.gni") | 15 | import("//build/ohos.gni") |
| 15 | import("//build/test.gni") | 16 | import("//build/test.gni") |
| 16 | 17 | ||
| @@ -19,13 +20,35 @@ ohos_rust_static_library("saf_common") { | |||
| 19 | deps = [ | 20 | deps = [ |
| 20 | "../../../frameworks/secure_access_fence/os_dependency/log:saf_log", | 21 | "../../../frameworks/secure_access_fence/os_dependency/log:saf_log", |
| 21 | "../../../frameworks/secure_access_fence/definition:saf_definition", | 22 | "../../../frameworks/secure_access_fence/definition:saf_definition", |
| 23 | + "../../../frameworks/secure_access_fence/utils:saf_utils", | ||
| 22 | "../os_dependency:saf_os_dependency", | 24 | "../os_dependency:saf_os_dependency", |
| 23 | ] | 25 | ] |
| 24 | external_deps = [ | 26 | external_deps = [ |
| 25 | "ipc:ipc_rust", | 27 | "ipc:ipc_rust", |
| 26 | "ylong_runtime:ylong_runtime", | 28 | "ylong_runtime:ylong_runtime", |
| 29 | + "ylong_json:lib", | ||
| 27 | ] | 30 | ] |
| 28 | rustflags = [ "-C", "codegen-units=1", "-C", "opt-level=z", "-C", "debuginfo=0" ] | 31 | rustflags = [ "-C", "codegen-units=1", "-C", "opt-level=z", "-C", "debuginfo=0" ] |
| 32 | + if (enable_local_test) { | ||
| 33 | + rustflags += [ "--cfg", "feature=\"SAFTest\"" ] | ||
| 34 | + deps += [ "//third_party/rust/crates/lazy-static.rs:lib" ] | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + sanitize = { | ||
| 38 | + integer_overflow = true | ||
| 39 | + cfi = true | ||
| 40 | + debug = false | ||
| 41 | + cfi_cross_dso = true | ||
| 42 | + boundary_sanitize = true | ||
| 43 | + ubsan = true | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + cflags_cc = [ | ||
| 47 | + "-Wall", | ||
| 48 | + "-Werror", | ||
| 49 | + "-fPIC", | ||
| 50 | + ] | ||
| 51 | + | ||
| 29 | crate_name = "saf_common" | 52 | crate_name = "saf_common" |
| 30 | crate_type = "rlib" | 53 | crate_type = "rlib" |
| 31 | subsystem_name = "security" | 54 | subsystem_name = "security" |
| @@ -18,9 +18,15 @@ edition = "2021" | |||
| 18 | 18 | ||
| 19 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | 19 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
| 20 | 20 | ||
| 21 | +[features] | ||
| 22 | +SAFTest = ["lazy_static"] | ||
| 23 | + | ||
| 21 | [dependencies] | 24 | [dependencies] |
| 22 | ipc = { git = "https://gitcode.com/openharmony/communication_ipc" } | 25 | ipc = { git = "https://gitcode.com/openharmony/communication_ipc" } |
| 23 | ylong_runtime = { git = "https://gitcode.com/openharmony/commonlibrary_rust_ylong_runtime.git", features = ["full"] } | 26 | ylong_runtime = { git = "https://gitcode.com/openharmony/commonlibrary_rust_ylong_runtime.git", features = ["full"] } |
| 27 | +ylong_json = { git = "https://gitcode.com/openharmony/commonlibrary_rust_ylong_json.git" } | ||
| 28 | +lazy_static = { version = "1.0.0", optional = true } | ||
| 24 | 29 | ||
| 25 | saf_log = { path = "../../../frameworks/secure_access_fence/os_dependency/log" } | 30 | saf_log = { path = "../../../frameworks/secure_access_fence/os_dependency/log" } |
| 26 | saf_definition = { path = "../../../frameworks/secure_access_fence/definition" } | 31 | saf_definition = { path = "../../../frameworks/secure_access_fence/definition" } |
| 32 | +saf_utils = { path = "../../../frameworks/secure_access_fence/utils" } | ||
| @@ -22,9 +22,34 @@ pub use counter::{AutoCounter, Counter}; | |||
| 22 | pub use task_manager::TaskManager; | 22 | pub use task_manager::TaskManager; |
| 23 | pub use calling_info::CallingInfo; | 23 | pub use calling_info::CallingInfo; |
| 24 | 24 | ||
| 25 | -use saf_definition::macros_lib; | 25 | +// Re-export JsonBuilder from saf_utils |
| 26 | +pub use saf_utils::{JsonBuilder, new_object, object_add_string, object_add_number}; | ||
| 27 | + | ||
| 28 | + | ||
| 29 | +use saf_definition::{macros_lib, ErrCode, Result}; | ||
| 26 | use std::convert::TryFrom; | 30 | use std::convert::TryFrom; |
| 27 | 31 | ||
| 32 | + | ||
| 33 | +extern "C" { | ||
| 34 | + fn GetOsAccountIdFromUid(uid: i32, userId: &mut i32) -> bool; | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | +/// Calculate user id from uid. | ||
| 38 | + | ||
| 39 | +pub fn get_user_id(uid: u64) -> Result<i32> { | ||
| 40 | + let uid_i32 = i32::try_from(uid) | ||
| 41 | + .map_err(|_| macros_lib::log_and_into_saf_error!(ErrCode::InvalidOsAccountId, | ||
| 42 | + "[FATAL]Uid overflow i32 range"))?; | ||
| 43 | + unsafe { | ||
| 44 | + let mut user_id: i32 = 0; | ||
| 45 | + if GetOsAccountIdFromUid(uid_i32, &mut user_id) { | ||
| 46 | + Ok(user_id) | ||
| 47 | + } else { | ||
| 48 | + macros_lib::log_throw_error!(ErrCode::InvalidOsAccountId, "[FATAL]Get user id failed.") | ||
| 49 | + } | ||
| 50 | + } | ||
| 51 | +} | ||
| 52 | + | ||
| 28 | /// The type of the common event. | 53 | /// The type of the common event. |
| 29 | 54 | ||
| 30 | 55 | ||
| @@ -59,3 +84,44 @@ impl TryFrom<&str> for CommonEventType { | |||
| 59 | Ok(CommonEventType::Unknown) | 84 | Ok(CommonEventType::Unknown) |
| 60 | } | 85 | } |
| 61 | } | 86 | } |
| 87 | + | ||
| 88 | +// ======================== SAFTest mock implementations ======================== | ||
| 89 | + | ||
| 90 | + | ||
| 91 | +use saf_definition::{macros_lib, Result}; | ||
| 92 | + | ||
| 93 | +use lazy_static::lazy_static; | ||
| 94 | + | ||
| 95 | +use std::sync::RwLock; | ||
| 96 | + | ||
| 97 | + | ||
| 98 | +lazy_static! { | ||
| 99 | + static ref MOCK_LOCAL_UDID: RwLock<String> = RwLock::new(String::from("mock_local_udid")); | ||
| 100 | + static ref MOCK_USER_ID: RwLock<i32> = RwLock::new(100); | ||
| 101 | +} | ||
| 102 | + | ||
| 103 | + | ||
| 104 | +pub fn get_user_id(_uid: u64) -> Result<i32> { | ||
| 105 | + Ok(*MOCK_USER_ID.read().unwrap()) | ||
| 106 | +} | ||
| 107 | + | ||
| 108 | + | ||
| 109 | +pub fn set_mock_user_id(user_id: i32) { | ||
| 110 | + *MOCK_USER_ID.write().unwrap() = user_id; | ||
| 111 | +} | ||
| 112 | + | ||
| 113 | + | ||
| 114 | +pub fn get_local_udid() -> Result<String> { | ||
| 115 | + Ok(MOCK_LOCAL_UDID.read().unwrap().clone()) | ||
| 116 | +} | ||
| 117 | + | ||
| 118 | + | ||
| 119 | +pub fn set_mock_local_udid(udid: &str) { | ||
| 120 | + *MOCK_LOCAL_UDID.write().unwrap() = udid.to_string(); | ||
| 121 | +} | ||
| 122 | + | ||
| 123 | + | ||
| 124 | +pub fn reset_mock_local_udid() { | ||
| 125 | + *MOCK_LOCAL_UDID.write().unwrap() = "mock_local_udid".to_string(); | ||
| 126 | + *MOCK_USER_ID.write().unwrap() = 100; | ||
| 127 | +} | ||
| @@ -28,6 +28,7 @@ ohos_static_library("secure_access_fence_wrapper_static") { | |||
| 28 | "${target_gen_dir}", | 28 | "${target_gen_dir}", |
| 29 | "${target_gen_dir}/src", | 29 | "${target_gen_dir}/src", |
| 30 | "../os_dependency/inc", | 30 | "../os_dependency/inc", |
| 31 | + "../../../frameworks/secure_access_fence/base/inc", | ||
| 31 | "../../../frameworks/secure_access_fence/inner_api/agent_fence/inc", | 32 | "../../../frameworks/secure_access_fence/inner_api/agent_fence/inc", |
| 32 | "../../../frameworks/secure_access_fence/inner_api/ipc", | 33 | "../../../frameworks/secure_access_fence/inner_api/ipc", |
| 33 | "../../../frameworks/secure_access_fence/os_dependency/log/inc", | 34 | "../../../frameworks/secure_access_fence/os_dependency/log/inc", |
| @@ -90,6 +91,7 @@ ohos_rust_shared_library("secure_access_fence_service") { | |||
| 90 | "../../../frameworks/secure_access_fence/inner_api/ipc:secure_access_fence_interface", | 91 | "../../../frameworks/secure_access_fence/inner_api/ipc:secure_access_fence_interface", |
| 91 | "../../../frameworks/secure_access_fence/definition:saf_definition", | 92 | "../../../frameworks/secure_access_fence/definition:saf_definition", |
| 92 | "../../../frameworks/secure_access_fence/os_dependency/log:saf_log", | 93 | "../../../frameworks/secure_access_fence/os_dependency/log:saf_log", |
| 94 | + "../../../frameworks/secure_access_fence/utils:saf_utils", | ||
| 93 | "../../../interfaces/inner_kits/plugin_interface/secure_access_fence:saf_plugin_interface_rust", | 95 | "../../../interfaces/inner_kits/plugin_interface/secure_access_fence:saf_plugin_interface_rust", |
| 94 | "../../../interfaces/inner_kits/rs/secure_access_fence:saf_sdk_rust", | 96 | "../../../interfaces/inner_kits/rs/secure_access_fence:saf_sdk_rust", |
| 95 | "../../../interfaces/inner_kits/c/secure_access_fence/agent_fence:saf_agent_fence", | 97 | "../../../interfaces/inner_kits/c/secure_access_fence/agent_fence:saf_agent_fence", |
| @@ -112,6 +114,10 @@ ohos_rust_shared_library("secure_access_fence_service") { | |||
| 112 | ] | 114 | ] |
| 113 | 115 | ||
| 114 | rustflags = [ "-C", "codegen-units=1", "-C", "opt-level=z", "-C", "debuginfo=0" ] | 116 | rustflags = [ "-C", "codegen-units=1", "-C", "opt-level=z", "-C", "debuginfo=0" ] |
| 117 | + if (enable_local_test) { | ||
| 118 | + rustflags += [ "--cfg", "feature=\"SAFTest\"", ] | ||
| 119 | + deps += [ "//third_party/rust/crates/lazy-static.rs:lib" ] | ||
| 120 | + } | ||
| 115 | 121 | ||
| 116 | sanitize = { | 122 | sanitize = { |
| 117 | integer_overflow = true | 123 | integer_overflow = true |
| @@ -18,6 +18,9 @@ edition = "2021" | |||
| 18 | 18 | ||
| 19 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | 19 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
| 20 | 20 | ||
| 21 | +[features] | ||
| 22 | +SAFTest = ["lazy_static", "saf_common/SAFTest"] | ||
| 23 | + | ||
| 21 | [dependencies] | 24 | [dependencies] |
| 22 | hilog_rust = { git = "https://gitcode.com/openharmony/hiviewdfx_hilog.git" } | 25 | hilog_rust = { git = "https://gitcode.com/openharmony/hiviewdfx_hilog.git" } |
| 23 | samgr = { git = "https://gitcode.com/openharmony/systemabilitymgr_samgr.git" } | 26 | samgr = { git = "https://gitcode.com/openharmony/systemabilitymgr_samgr.git" } |
| @@ -31,7 +34,8 @@ saf_common = { path = "../common" } | |||
| 31 | saf_definition = { path = "../../../frameworks/secure_access_fence/definition" } | 34 | saf_definition = { path = "../../../frameworks/secure_access_fence/definition" } |
| 32 | saf_ipc = { path = "../../../frameworks/secure_access_fence/inner_api/ipc" } | 35 | saf_ipc = { path = "../../../frameworks/secure_access_fence/inner_api/ipc" } |
| 33 | saf_log = { path = "../../../frameworks/secure_access_fence/os_dependency/log" } | 36 | saf_log = { path = "../../../frameworks/secure_access_fence/os_dependency/log" } |
| 37 | +saf_utils = { path = "../../../frameworks/secure_access_fence/utils" } | ||
| 34 | saf_plugin = { path = "../plugin" } | 38 | saf_plugin = { path = "../plugin" } |
| 35 | saf_plugin_interface = { path = "../../../interfaces/inner_kits/plugin_interface/secure_access_fence" } | 39 | saf_plugin_interface = { path = "../../../interfaces/inner_kits/plugin_interface/secure_access_fence" } |
| 36 | saf_sdk = { path = "../../../interfaces/inner_kits/rs/secure_access_fence" } | 40 | saf_sdk = { path = "../../../interfaces/inner_kits/rs/secure_access_fence" } |
| 37 | -lazy_static = "1.0.0" | 41 | +lazy_static = { version = "1.0.0", optional = true } |
| @@ -842,4 +842,4 @@ int32_t PermissionManager::GetVerifyTicketInfo(const UserAuthResult &userAuthRes | |||
| 842 | "GetVerifyTicketInfo :: GenerateTicketInfoWithTimeStamp failed, ret=%{public}d", ret); | 842 | "GetVerifyTicketInfo :: GenerateTicketInfoWithTimeStamp failed, ret=%{public}d", ret); |
| 843 | return SAF_SUCCESS; | 843 | return SAF_SUCCESS; |
| 844 | } | 844 | } |
| 845 | -} | 845 | +} |
| @@ -174,6 +174,6 @@ int64_t GetBootTimeMs() | |||
| 174 | return TimeWrapper::GetBootTimeMs(); | 174 | return TimeWrapper::GetBootTimeMs(); |
| 175 | } | 175 | } |
| 176 | 176 | ||
| 177 | -} | 177 | +} // namespace SAF |
| 178 | -} | 178 | +} // namespace Security |
| 179 | -} | 179 | +} // namespace OHOS |
| @@ -28,14 +28,17 @@ use system_ability_fwk::{ | |||
| 28 | use ylong_runtime::builder::RuntimeBuilder; | 28 | use ylong_runtime::builder::RuntimeBuilder; |
| 29 | 29 | ||
| 30 | use saf_common::{Counter, TaskManager}; | 30 | use saf_common::{Counter, TaskManager}; |
| 31 | -use saf_definition::{macros_lib, ErrCode, Result}; | 31 | +use saf_definition::{macros_lib, ErrCode, Result, CliInfo, VerifyTicketInfo}; |
| 32 | -use saf_ipc::{CliInfo, VerifyTicketInfo, SA_ID}; | 32 | +use saf_ipc::SA_ID; |
| 33 | use saf_log::{logd, loge, logi}; | 33 | use saf_log::{logd, loge, logi}; |
| 34 | use saf_plugin::saf_plugin::{SAFContext, SAFPlugin}; | 34 | use saf_plugin::saf_plugin::{SAFContext, SAFPlugin}; |
| 35 | 35 | ||
| 36 | +use saf_utils::get_compact_json_value; | ||
| 37 | + | ||
| 36 | use crate::wrapper::{cxx_is_screen_locked, notify_error, notify_performance_metrics}; | 38 | use crate::wrapper::{cxx_is_screen_locked, notify_error, notify_performance_metrics}; |
| 37 | 39 | ||
| 38 | mod common_event; | 40 | mod common_event; |
| 41 | +mod remote_control; | ||
| 39 | mod stub; | 42 | mod stub; |
| 40 | mod ticket_operation; | 43 | mod ticket_operation; |
| 41 | mod wrapper; | 44 | mod wrapper; |
| @@ -397,9 +400,9 @@ fn parse_verify_info_json_full(info_str: &str) -> Result<(VerifyTicketInfo, ylon | |||
| 397 | return Err(macros_lib::log_and_into_saf_error!(ErrCode::ArgEmpty, "VerifyTicket: json object is empty")); | 400 | return Err(macros_lib::log_and_into_saf_error!(ErrCode::ArgEmpty, "VerifyTicket: json object is empty")); |
| 398 | } | 401 | } |
| 399 | 402 | ||
| 400 | - let raw_message = extract_json_string(&json, JSON_KEY_MESSAGE)?; | 403 | + let raw_message = get_compact_json_value(&json, JSON_KEY_MESSAGE)?; |
| 401 | - let raw_challenge = extract_json_string(&json, JSON_KEY_CHALLENGE)?; | 404 | + let raw_challenge = get_compact_json_value(&json, JSON_KEY_CHALLENGE)?; |
| 402 | - let raw_ticket = extract_json_string(&json, JSON_KEY_TICKET)?; | 405 | + let raw_ticket = get_compact_json_value(&json, JSON_KEY_TICKET)?; |
| 403 | 406 | ||
| 404 | let message_json = ylong_json::JsonValue::from_text(&raw_message).map_err(|e| { | 407 | let message_json = ylong_json::JsonValue::from_text(&raw_message).map_err(|e| { |
| 405 | loge!("VerifyTicket: message json parse failed: {}", e); | 408 | loge!("VerifyTicket: message json parse failed: {}", e); |
| @@ -409,19 +412,6 @@ fn parse_verify_info_json_full(info_str: &str) -> Result<(VerifyTicketInfo, ylon | |||
| 409 | Ok((VerifyTicketInfo { message: raw_message, challenge: raw_challenge, ticket: raw_ticket }, message_json)) | 412 | Ok((VerifyTicketInfo { message: raw_message, challenge: raw_challenge, ticket: raw_ticket }, message_json)) |
| 410 | } | 413 | } |
| 411 | 414 | ||
| 412 | -fn extract_json_string(json: &ylong_json::JsonValue, key: &str) -> Result<String> { | ||
| 413 | - let value = &json[key]; | ||
| 414 | - if value == &ylong_json::JsonValue::Null { | ||
| 415 | - return macros_lib::log_throw_error!(ErrCode::ArgEmpty, "{} field missing", key); | ||
| 416 | - } | ||
| 417 | - match value { | ||
| 418 | - ylong_json::JsonValue::String(s) => Ok(s.clone()), | ||
| 419 | - _ => value.to_compact_string().map_err(|e| { | ||
| 420 | - macros_lib::SAFError::new(ErrCode::ArgEmpty, format!("VerifyTicket: {} extract failed: {}", key, e)) | ||
| 421 | - }), | ||
| 422 | - } | ||
| 423 | -} | ||
| 424 | - | ||
| 425 | fn extract_cli_infos_with_json(message_json: &ylong_json::JsonValue) -> Result<Vec<CliInfo>> { | 415 | fn extract_cli_infos_with_json(message_json: &ylong_json::JsonValue) -> Result<Vec<CliInfo>> { |
| 426 | if message_json == &ylong_json::JsonValue::Null { | 416 | if message_json == &ylong_json::JsonValue::Null { |
| 427 | return macros_lib::log_throw_error!( | 417 | return macros_lib::log_throw_error!( |
| @@ -430,7 +420,7 @@ fn extract_cli_infos_with_json(message_json: &ylong_json::JsonValue) -> Result<V | |||
| 430 | ); | 420 | ); |
| 431 | } | 421 | } |
| 432 | 422 | ||
| 433 | - let caller_token_id = extract_json_string(message_json, JSON_KEY_CALLER_TOKEN_ID)?; | 423 | + let caller_token_id = get_compact_json_value(message_json, JSON_KEY_CALLER_TOKEN_ID)?; |
| 434 | 424 | ||
| 435 | let cli_infos_array = &message_json[JSON_KEY_CLI_INFOS]; | 425 | let cli_infos_array = &message_json[JSON_KEY_CLI_INFOS]; |
| 436 | if cli_infos_array == &ylong_json::JsonValue::Null { | 426 | if cli_infos_array == &ylong_json::JsonValue::Null { |
| @@ -444,9 +434,9 @@ fn extract_cli_infos_with_json(message_json: &ylong_json::JsonValue) -> Result<V | |||
| 444 | let mut result = Vec::with_capacity(arr.len()); | 434 | let mut result = Vec::with_capacity(arr.len()); |
| 445 | for item in arr.iter() { | 435 | for item in arr.iter() { |
| 446 | // ATM不解析这个json, 且ATM需要不带双引号的字符串,在此处帮助ATM删除双引号 | 436 | // ATM不解析这个json, 且ATM需要不带双引号的字符串,在此处帮助ATM删除双引号 |
| 447 | - let cli_cmd_name = extract_json_string(item, JSON_KEY_CLI_CMD_NAME)?.trim_matches(STRING_QUOTE).to_string(); | 437 | + let cli_cmd_name = get_compact_json_value(item, JSON_KEY_CLI_CMD_NAME)?.trim_matches(STRING_QUOTE).to_string(); |
| 448 | let sub_cli_cmd_name = | 438 | let sub_cli_cmd_name = |
| 449 | - extract_json_string(item, JSON_KEY_SUB_CLI_CMD_NAME)?.trim_matches(STRING_QUOTE).to_string(); | 439 | + get_compact_json_value(item, JSON_KEY_SUB_CLI_CMD_NAME)?.trim_matches(STRING_QUOTE).to_string(); |
| 450 | let permission_list = extract_permission_list(item)?; | 440 | let permission_list = extract_permission_list(item)?; |
| 451 | result.push(CliInfo { | 441 | result.push(CliInfo { |
| 452 | caller_token_id: caller_token_id.clone(), | 442 | caller_token_id: caller_token_id.clone(), |
| @@ -481,5 +471,5 @@ fn extract_permission_list(json: &ylong_json::JsonValue) -> Result<Vec<String>> | |||
| 481 | 471 | ||
| 482 | /// stub for test | 472 | /// stub for test |
| 483 | pub mod ut_core_service_lib_stub { | 473 | pub mod ut_core_service_lib_stub { |
| 484 | - include! {"../../../test/unittest/ut_test/services/core_service/test_stub/ut_core_service_lib_stub.rs"} | 474 | + include! {"../../../../test/secure_access_fence/unittest/ut_test/services/core_service/test_stub/ut_core_service_lib_stub.rs"} |
| 485 | } | 475 | } |
| @@ -0,0 +1,284 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | +//! Trusted ring adapter for remote auth package operations | ||
| 17 | + | ||
| 18 | +use saf_definition::{macros_lib, ErrCode, Result}; | ||
| 19 | + | ||
| 20 | +use saf_log::logi; | ||
| 21 | +use saf_definition::{DeviceIdHeader, RemoteAuthPackage}; | ||
| 22 | + | ||
| 23 | + | ||
| 24 | +use std::collections::HashMap; | ||
| 25 | + | ||
| 26 | +use saf_definition::Value; | ||
| 27 | + | ||
| 28 | +use saf_plugin::saf_plugin::SAFPlugin; | ||
| 29 | + | ||
| 30 | +use saf_plugin_interface::plugin_interface::{ | ||
| 31 | + EventType, ExtMap, | ||
| 32 | + SIGN_REMOTE_AUTH_PACKAGE_KEYS, VERIFY_REMOTE_AUTH_PACKAGE_KEYS, | ||
| 33 | +}; | ||
| 34 | + | ||
| 35 | +use saf_ipc::remote_message_wrapper; | ||
| 36 | + | ||
| 37 | +use saf_utils::{JsonValue, json_into_object, get_compact_json_value}; | ||
| 38 | + | ||
| 39 | + | ||
| 40 | +fn log_sign_params(params: &SignParams) { | ||
| 41 | + logi!( | ||
| 42 | + "SignParams: os_account_id={}, uid_len={}, remote_auth_package_len={}, remote_control_token_len={}", | ||
| 43 | + params.os_account_id, | ||
| 44 | + params.uid.len(), | ||
| 45 | + params.remote_auth_package.len(), | ||
| 46 | + params.remote_control_token.len() | ||
| 47 | + ); | ||
| 48 | +} | ||
| 49 | + | ||
| 50 | + | ||
| 51 | +fn log_sign_result(result: &SignResult) { | ||
| 52 | + logi!( | ||
| 53 | + "SignResult: controller_device_id_len={}, controlled_device_id_len={}, \ | ||
| 54 | + remote_auth_package_len={}, sign_info_len={}", | ||
| 55 | + result.device_id_header.controller_device_id.len(), | ||
| 56 | + result.device_id_header.controlled_device_id.len(), | ||
| 57 | + result.remote_auth_package.len(), | ||
| 58 | + result.sign_info.len() | ||
| 59 | + ); | ||
| 60 | +} | ||
| 61 | + | ||
| 62 | + | ||
| 63 | +fn log_verify_package(package: &RemoteAuthPackage) { | ||
| 64 | + let remote_message = &package.remote_message; | ||
| 65 | + let device_info = &remote_message.device_info; | ||
| 66 | + logi!( | ||
| 67 | + "RemoteAuthPackage: ticket_len={}, \ | ||
| 68 | + controller_device_id_len={}, controlled_device_id_len={}, \ | ||
| 69 | + remote_auth_message_len={}", | ||
| 70 | + package.ticket.len(), | ||
| 71 | + device_info.controller_device_id.len(), | ||
| 72 | + device_info.controlled_device_id.len(), | ||
| 73 | + remote_message.remote_auth_message.len() | ||
| 74 | + ); | ||
| 75 | +} | ||
| 76 | + | ||
| 77 | +/// Sign request parameters. | ||
| 78 | +pub struct SignParams { | ||
| 79 | + /// OS account ID. | ||
| 80 | + pub os_account_id: i32, | ||
| 81 | + /// UID string. | ||
| 82 | + pub uid: String, | ||
| 83 | + /// Remote auth package. | ||
| 84 | + pub remote_auth_package: String, | ||
| 85 | + /// Remote control token. | ||
| 86 | + pub remote_control_token: String, | ||
| 87 | +} | ||
| 88 | + | ||
| 89 | +/// Sign result. | ||
| 90 | +pub struct SignResult { | ||
| 91 | + /// Device ID header. | ||
| 92 | + pub device_id_header: DeviceIdHeader, | ||
| 93 | + /// Remote auth package. | ||
| 94 | + pub remote_auth_package: String, | ||
| 95 | + /// Sign info. | ||
| 96 | + pub sign_info: String, | ||
| 97 | +} | ||
| 98 | + | ||
| 99 | + | ||
| 100 | +fn parse_sign_result(mut ret: ExtMap) -> Result<SignResult> { | ||
| 101 | + let device_id_header_str = match ret.remove(SIGN_REMOTE_AUTH_PACKAGE_KEYS.device_id_header) { | ||
| 102 | + Some(Value::String(ref s)) => s.clone(), | ||
| 103 | + _ => return macros_lib::log_throw_error!(ErrCode::HashMapKeyNotFound, "device_id_header not found"), | ||
| 104 | + }; | ||
| 105 | + | ||
| 106 | + let mut device_id_header_obj = json_into_object(JsonValue::from_text(device_id_header_str) | ||
| 107 | + .map_err(|e| macros_lib::log_and_into_saf_error!( | ||
| 108 | + ErrCode::JsonParseError, "parse device_id_header failed: {}", e))?)?; | ||
| 109 | + | ||
| 110 | + let device_id_header = remote_message_wrapper::parse_device_id_header_from_json(&mut device_id_header_obj)?; | ||
| 111 | + | ||
| 112 | + let remote_auth_package = match ret.remove(SIGN_REMOTE_AUTH_PACKAGE_KEYS.result_remote_auth_package) { | ||
| 113 | + Some(Value::String(ref s)) => s.clone(), | ||
| 114 | + None => return macros_lib::log_throw_error!(ErrCode::HashMapKeyNotFound, | ||
| 115 | + "result_remote_auth_package not found"), | ||
| 116 | + _ => return macros_lib::log_throw_error!(ErrCode::DataTypeMismatch, | ||
| 117 | + "result_remote_auth_package type mismatch, expected String"), | ||
| 118 | + }; | ||
| 119 | + | ||
| 120 | + let sign_info = match ret.remove(SIGN_REMOTE_AUTH_PACKAGE_KEYS.sign_info) { | ||
| 121 | + Some(Value::String(ref s)) => s.clone(), | ||
| 122 | + None => return macros_lib::log_throw_error!(ErrCode::HashMapKeyNotFound, | ||
| 123 | + "sign_info not found"), | ||
| 124 | + _ => return macros_lib::log_throw_error!(ErrCode::DataTypeMismatch, | ||
| 125 | + "sign_info type mismatch, expected String"), | ||
| 126 | + }; | ||
| 127 | + | ||
| 128 | + Ok(SignResult { | ||
| 129 | + device_id_header, | ||
| 130 | + remote_auth_package, | ||
| 131 | + sign_info, | ||
| 132 | + }) | ||
| 133 | +} | ||
| 134 | + | ||
| 135 | +/// Signs remote auth package through trusted ring plugin. | ||
| 136 | + | ||
| 137 | +pub fn sign_remote_auth_package(params: SignParams) -> Result<SignResult> { | ||
| 138 | + log_sign_params(¶ms); | ||
| 139 | + | ||
| 140 | + let loader = SAFPlugin::get_instance().load_plugin().map_err(|e| { | ||
| 141 | + macros_lib::log_and_into_saf_error!(ErrCode::PluginNotSupport, "load plugin failed: {}", e) | ||
| 142 | + })?; | ||
| 143 | + | ||
| 144 | + let mut ext_map: ExtMap = HashMap::new(); | ||
| 145 | + let os_account_id_u32 = u32::try_from(params.os_account_id).map_err(|_| { | ||
| 146 | + macros_lib::log_and_into_saf_error!(ErrCode::InvalidOsAccountId, | ||
| 147 | + "os_account_id is negative: {}", params.os_account_id) | ||
| 148 | + })?; | ||
| 149 | + ext_map.insert(SIGN_REMOTE_AUTH_PACKAGE_KEYS.os_account_id, | ||
| 150 | + Value::Number(os_account_id_u32)); | ||
| 151 | + ext_map.insert(SIGN_REMOTE_AUTH_PACKAGE_KEYS.uid, | ||
| 152 | + Value::String(params.uid)); | ||
| 153 | + ext_map.insert(SIGN_REMOTE_AUTH_PACKAGE_KEYS.remote_auth_package, | ||
| 154 | + Value::String(params.remote_auth_package)); | ||
| 155 | + ext_map.insert(SIGN_REMOTE_AUTH_PACKAGE_KEYS.remote_control_token, | ||
| 156 | + Value::String(params.remote_control_token)); | ||
| 157 | + | ||
| 158 | + let ret = loader.process_event(EventType::SignRemoteAuthPackage, &mut ext_map) | ||
| 159 | + .map_err(|e| { | ||
| 160 | + macros_lib::log_and_into_saf_error!( | ||
| 161 | + ErrCode::try_from(e).unwrap_or(ErrCode::GeneralError), | ||
| 162 | + "sign_remote_auth_package process_event failed") | ||
| 163 | + })?; | ||
| 164 | + | ||
| 165 | + let result = parse_sign_result(ret)?; | ||
| 166 | + log_sign_result(&result); | ||
| 167 | + Ok(result) | ||
| 168 | +} | ||
| 169 | + | ||
| 170 | +/// Verifies remote auth package through trusted ring plugin. | ||
| 171 | + | ||
| 172 | +pub fn verify_remote_auth_package(os_account_id: i32, package: &RemoteAuthPackage) -> Result<bool> { | ||
| 173 | + log_verify_package(package); | ||
| 174 | + | ||
| 175 | + let loader = SAFPlugin::get_instance().load_plugin().map_err(|e| { | ||
| 176 | + macros_lib::log_and_into_saf_error!(ErrCode::PluginNotSupport, "load plugin failed: {}", e) | ||
| 177 | + })?; | ||
| 178 | + | ||
| 179 | + let device_id_header_str = remote_message_wrapper::serialize_device_id_header_to_json( | ||
| 180 | + &package.remote_message.device_info | ||
| 181 | + )?; | ||
| 182 | + | ||
| 183 | + let uid = parse_uid_from_remote_auth_message(&package.remote_message.remote_auth_message)?; | ||
| 184 | + | ||
| 185 | + let mut ext_map: ExtMap = HashMap::new(); | ||
| 186 | + let os_account_id_u32 = u32::try_from(os_account_id).map_err(|_| { | ||
| 187 | + macros_lib::log_and_into_saf_error!(ErrCode::InvalidOsAccountId, | ||
| 188 | + "os_account_id is negative: {}", os_account_id) | ||
| 189 | + })?; | ||
| 190 | + ext_map.insert(VERIFY_REMOTE_AUTH_PACKAGE_KEYS.os_account_id, | ||
| 191 | + Value::Number(os_account_id_u32)); | ||
| 192 | + ext_map.insert(VERIFY_REMOTE_AUTH_PACKAGE_KEYS.uid, | ||
| 193 | + Value::String(uid)); | ||
| 194 | + ext_map.insert(VERIFY_REMOTE_AUTH_PACKAGE_KEYS.device_id_header, | ||
| 195 | + Value::String(device_id_header_str)); | ||
| 196 | + ext_map.insert(VERIFY_REMOTE_AUTH_PACKAGE_KEYS.remote_auth_message, | ||
| 197 | + Value::String(package.remote_message.remote_auth_message.clone())); | ||
| 198 | + ext_map.insert(VERIFY_REMOTE_AUTH_PACKAGE_KEYS.sign_info, | ||
| 199 | + Value::String(package.ticket.clone())); | ||
| 200 | + | ||
| 201 | + let ret = loader.process_event(EventType::VerifyRemoteAuthPackage, &mut ext_map) | ||
| 202 | + .map_err(|e| { | ||
| 203 | + macros_lib::log_and_into_saf_error!( | ||
| 204 | + ErrCode::try_from(e).unwrap_or(ErrCode::GeneralError), | ||
| 205 | + "verify_remote_auth_package process_event failed") | ||
| 206 | + })?; | ||
| 207 | + | ||
| 208 | + match ret.get(VERIFY_REMOTE_AUTH_PACKAGE_KEYS.verify_result) { | ||
| 209 | + Some(Value::Bool(b)) => { | ||
| 210 | + logi!("[verify_remote_auth_package] verify_result={}", b); | ||
| 211 | + Ok(*b) | ||
| 212 | + }, | ||
| 213 | + _ => macros_lib::log_throw_error!(ErrCode::HashMapKeyNotFound, "verify_result not found"), | ||
| 214 | + } | ||
| 215 | +} | ||
| 216 | + | ||
| 217 | + | ||
| 218 | +fn parse_uid_from_remote_auth_message(remote_auth_message: &str) -> Result<String> { | ||
| 219 | + let json = JsonValue::from_text(remote_auth_message).map_err(|e| { | ||
| 220 | + macros_lib::log_and_into_saf_error!(ErrCode::JsonParseError, | ||
| 221 | + "parse remote_auth_message failed: {}", e) | ||
| 222 | + })?; | ||
| 223 | + | ||
| 224 | + let uid = get_compact_json_value(&json, "domainId") | ||
| 225 | + .map(|s| s.trim_matches('"').to_string()) | ||
| 226 | + .unwrap_or_default(); | ||
| 227 | + | ||
| 228 | + if uid.is_empty() { | ||
| 229 | + return macros_lib::log_throw_error!(ErrCode::ArgEmpty, "uid is empty in remote_auth_message"); | ||
| 230 | + } | ||
| 231 | + | ||
| 232 | + Ok(uid) | ||
| 233 | +} | ||
| 234 | + | ||
| 235 | +// ======================== SAFTest mock implementations ======================== | ||
| 236 | + | ||
| 237 | + | ||
| 238 | +use std::sync::atomic::{AtomicBool, Ordering}; | ||
| 239 | + | ||
| 240 | +use lazy_static::lazy_static; | ||
| 241 | + | ||
| 242 | + | ||
| 243 | +lazy_static! { | ||
| 244 | + static ref MOCK_SIGN_SUCCESS: AtomicBool = AtomicBool::new(true); | ||
| 245 | + static ref MOCK_VERIFY_SUCCESS: AtomicBool = AtomicBool::new(true); | ||
| 246 | +} | ||
| 247 | + | ||
| 248 | + | ||
| 249 | +pub fn sign_remote_auth_package(params: SignParams) -> Result<SignResult> { | ||
| 250 | + if MOCK_SIGN_SUCCESS.load(Ordering::SeqCst) { | ||
| 251 | + Ok(SignResult { | ||
| 252 | + device_id_header: DeviceIdHeader { | ||
| 253 | + controller_device_id: "mock_controller_udid".to_string(), | ||
| 254 | + controlled_device_id: "mock_controlled_udid".to_string(), | ||
| 255 | + }, | ||
| 256 | + remote_auth_package: params.remote_auth_package, | ||
| 257 | + sign_info: "mock_sign_info".to_string(), | ||
| 258 | + }) | ||
| 259 | + } else { | ||
| 260 | + macros_lib::log_throw_error!(ErrCode::GeneralError, "Mock sign failed") | ||
| 261 | + } | ||
| 262 | +} | ||
| 263 | + | ||
| 264 | + | ||
| 265 | +pub fn set_mock_sign_success(success: bool) { | ||
| 266 | + MOCK_SIGN_SUCCESS.store(success, Ordering::SeqCst); | ||
| 267 | +} | ||
| 268 | + | ||
| 269 | + | ||
| 270 | +pub fn verify_remote_auth_package(_os_account_id: i32, _package: &RemoteAuthPackage) -> Result<bool> { | ||
| 271 | + Ok(MOCK_VERIFY_SUCCESS.load(Ordering::SeqCst)) | ||
| 272 | +} | ||
| 273 | + | ||
| 274 | + | ||
| 275 | +pub fn set_mock_verify_success(success: bool) { | ||
| 276 | + MOCK_VERIFY_SUCCESS.store(success, Ordering::SeqCst); | ||
| 277 | +} | ||
| 278 | + | ||
| 279 | + | ||
| 280 | +pub fn reset_mock_state() { | ||
| 281 | + MOCK_VERIFY_SUCCESS.store(true, Ordering::SeqCst); | ||
| 282 | + MOCK_SIGN_SUCCESS.store(true, Ordering::SeqCst); | ||
| 283 | + crate::remote_control::remote_challenge_manager::reset_mock_challenge_cache(); | ||
| 284 | +} | ||
| @@ -0,0 +1,145 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | +//! CLI tool permission manager adapter | ||
| 17 | + | ||
| 18 | +use saf_definition::{macros_lib, CommandInfo, ErrCode, Result}; | ||
| 19 | +use saf_log::loge; | ||
| 20 | + | ||
| 21 | +const MAX_PERMISSION_BUF_SIZE: usize = 4096; | ||
| 22 | + | ||
| 23 | + | ||
| 24 | +struct CxxStr { | ||
| 25 | + ptr: *const std::ffi::c_char, | ||
| 26 | + len: i32, | ||
| 27 | +} | ||
| 28 | + | ||
| 29 | + | ||
| 30 | +struct CxxCmdInfo { | ||
| 31 | + cmd_name: CxxStr, | ||
| 32 | + sub_cmd: CxxStr, | ||
| 33 | +} | ||
| 34 | + | ||
| 35 | + | ||
| 36 | +struct CxxQueryResult { | ||
| 37 | + ret_code: i32, | ||
| 38 | + result_code: i32, | ||
| 39 | + perm_count: i32, | ||
| 40 | +} | ||
| 41 | + | ||
| 42 | +extern "C" { | ||
| 43 | + fn CxxBatchQueryCliPermissions( | ||
| 44 | + cmds: *const CxxCmdInfo, | ||
| 45 | + cmd_count: i32, | ||
| 46 | + out_buf: *mut std::ffi::c_char, | ||
| 47 | + buf_size: i32, | ||
| 48 | + out_result: *mut CxxQueryResult, | ||
| 49 | + ) -> i32; | ||
| 50 | +} | ||
| 51 | + | ||
| 52 | +/// Batch query CLI tool permissions | ||
| 53 | +pub fn batch_query_cli_permission( | ||
| 54 | + cli_infos: &[CommandInfo], | ||
| 55 | + permissions: &mut Vec<String> | ||
| 56 | +) -> Result<()> { | ||
| 57 | + if cli_infos.is_empty() { | ||
| 58 | + return Ok(()); | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + let cxx_cmds = prepare_cxx_cmd_infos(cli_infos); | ||
| 62 | + let mut out_buf = vec![0u8; MAX_PERMISSION_BUF_SIZE]; | ||
| 63 | + let mut result = CxxQueryResult { | ||
| 64 | + ret_code: 0, | ||
| 65 | + result_code: 0, | ||
| 66 | + perm_count: 0, | ||
| 67 | + }; | ||
| 68 | + | ||
| 69 | + call_cxx_batch_query(&cxx_cmds, &mut out_buf, &mut result)?; | ||
| 70 | + parse_permissions_from_buffer(&out_buf, result.perm_count, permissions)?; | ||
| 71 | + | ||
| 72 | + Ok(()) | ||
| 73 | +} | ||
| 74 | + | ||
| 75 | +fn prepare_cxx_cmd_infos(cli_infos: &[CommandInfo]) -> Vec<CxxCmdInfo> { | ||
| 76 | + cli_infos.iter().map(|cmd| { | ||
| 77 | + CxxCmdInfo { | ||
| 78 | + cmd_name: CxxStr { | ||
| 79 | + ptr: cmd.cmd_name.as_ptr() as *const std::ffi::c_char, | ||
| 80 | + len: cmd.cmd_name.len() as i32, | ||
| 81 | + }, | ||
| 82 | + sub_cmd: CxxStr { | ||
| 83 | + ptr: cmd.sub_cmd.as_ptr() as *const std::ffi::c_char, | ||
| 84 | + len: cmd.sub_cmd.len() as i32, | ||
| 85 | + }, | ||
| 86 | + } | ||
| 87 | + }).collect() | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | +fn call_cxx_batch_query( | ||
| 91 | + cxx_cmds: &[CxxCmdInfo], | ||
| 92 | + out_buf: &mut [u8], | ||
| 93 | + result: &mut CxxQueryResult, | ||
| 94 | +) -> Result<()> { | ||
| 95 | + let ret = unsafe { | ||
| 96 | + CxxBatchQueryCliPermissions( | ||
| 97 | + cxx_cmds.as_ptr(), | ||
| 98 | + cxx_cmds.len() as i32, | ||
| 99 | + out_buf.as_mut_ptr() as *mut std::ffi::c_char, | ||
| 100 | + out_buf.len() as i32, | ||
| 101 | + result, | ||
| 102 | + ) | ||
| 103 | + }; | ||
| 104 | + | ||
| 105 | + if ret != 0 { | ||
| 106 | + loge!("CxxBatchQueryCliPermissions failed, ret={}, result_code={}", ret, result.result_code); | ||
| 107 | + return macros_lib::log_throw_error!(ErrCode::GeneralError, | ||
| 108 | + "CxxBatchQueryCliPermissions failed"); | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + Ok(()) | ||
| 112 | +} | ||
| 113 | + | ||
| 114 | +fn parse_permissions_from_buffer( | ||
| 115 | + out_buf: &[u8], | ||
| 116 | + perm_count: i32, | ||
| 117 | + permissions: &mut Vec<String>, | ||
| 118 | +) -> Result<()> { | ||
| 119 | + const MAX_PERMISSION_COUNT: i32 = 200; | ||
| 120 | + | ||
| 121 | + if !(0..=MAX_PERMISSION_COUNT).contains(&perm_count) { | ||
| 122 | + return Err(macros_lib::log_and_into_saf_error!(ErrCode::GeneralError, | ||
| 123 | + "Invalid permission count: {}", perm_count)); | ||
| 124 | + } | ||
| 125 | + | ||
| 126 | + let mut pos = 0; | ||
| 127 | + for _ in 0..perm_count { | ||
| 128 | + if pos >= out_buf.len() { | ||
| 129 | + return Err(macros_lib::log_and_into_saf_error!(ErrCode::GeneralError, | ||
| 130 | + "Permission buffer overflow: pos {} >= buf len {}", pos, out_buf.len())); | ||
| 131 | + } | ||
| 132 | + let end = out_buf[pos..].iter().position(|&b| b == 0).unwrap_or(out_buf.len() - pos); | ||
| 133 | + if pos + end > out_buf.len() { | ||
| 134 | + return Err(macros_lib::log_and_into_saf_error!(ErrCode::GeneralError, | ||
| 135 | + "Permission string exceeds buffer boundary")); | ||
| 136 | + } | ||
| 137 | + let s = std::str::from_utf8(&out_buf[pos..pos + end]) | ||
| 138 | + .map_err(|_| macros_lib::log_and_into_saf_error!(ErrCode::GeneralError, | ||
| 139 | + "Invalid UTF-8 in permission"))?; | ||
| 140 | + permissions.push(s.to_string()); | ||
| 141 | + pos += end + 1; | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | + Ok(()) | ||
| 145 | +} | ||
| @@ -0,0 +1,315 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | +//! This module implements controlled device package generation and verification. | ||
| 17 | + | ||
| 18 | +use ipc::Skeleton; | ||
| 19 | +use saf_common::{get_user_id, JsonBuilder}; | ||
| 20 | +use saf_utils::system_time_in_millis; | ||
| 21 | +use saf_definition::{macros_lib, ErrCode, Result, | ||
| 22 | + PermissionQuery, RemoteAuthPackage, | ||
| 23 | + Role, RemoteMessage, | ||
| 24 | +}; | ||
| 25 | +use saf_log::{loge, logi}; | ||
| 26 | +use std::ffi::CString; | ||
| 27 | +use std::os::raw::c_char as raw_c_char; | ||
| 28 | + | ||
| 29 | +extern "C" { | ||
| 30 | + fn CheckPermission(permission: *const raw_c_char) -> bool; | ||
| 31 | +} | ||
| 32 | + | ||
| 33 | +const QUERY_TOOL_PERMISSIONS: &str = "ohos.permission.QUERY_TOOL_PERMISSIONS"; | ||
| 34 | + | ||
| 35 | +use crate::remote_control::{create_empty_package, log_remote_auth_package, parse_ticket_expire_time, parse_timestamp, | ||
| 36 | + validate_ticket_expiration, generate_crypto_random_challenge, serialize_permission_query_to_message, | ||
| 37 | + validate_remote_auth_message_fields, BatchGenerateResult, BatchVerifyResult}; | ||
| 38 | +use crate::remote_control::remote_challenge_manager::cache_challenge; | ||
| 39 | +use crate::remote_control::account_based_auth_manager::{sign_remote_auth_package, verify_remote_auth_package, SignParams}; | ||
| 40 | + | ||
| 41 | +/// Generates controlled device packages for permission queries. | ||
| 42 | +pub fn generate_controlled_device_package( | ||
| 43 | + queries: Vec<PermissionQuery> | ||
| 44 | +) -> BatchGenerateResult { | ||
| 45 | + let permission = CString::new(QUERY_TOOL_PERMISSIONS).unwrap(); | ||
| 46 | + if unsafe { !CheckPermission(permission.as_ptr()) } { | ||
| 47 | + loge!("Permission denied! Need {}", QUERY_TOOL_PERMISSIONS); | ||
| 48 | + return BatchGenerateResult { | ||
| 49 | + packages: vec![create_empty_package(); queries.len() | ||
| 50 | + ], | ||
| 51 | + error_code: ErrCode::PermissionDenied as i32, | ||
| 52 | + }; | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + if let Err(e) = validate_controlled_batch_params(&queries) { | ||
| 56 | + loge!("Invalid batch params: {:?}", e); | ||
| 57 | + return BatchGenerateResult { | ||
| 58 | + packages: vec![create_empty_package(); queries.len()], | ||
| 59 | + error_code: e.code as i32, | ||
| 60 | + }; | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + let mut packages = Vec::with_capacity(queries.len()); | ||
| 64 | + let mut has_error = false; | ||
| 65 | + | ||
| 66 | + for (idx, query) in queries.into_iter().enumerate() { | ||
| 67 | + match generate_single_controlled_package(query) { | ||
| 68 | + Ok(pkg) => packages.push(pkg), | ||
| 69 | + Err(e) => { | ||
| 70 | + loge!("Generate package failed at idx[{}], err={:?}",idx, e); | ||
| 71 | + packages.push(create_empty_package()); | ||
| 72 | + has_error = true; | ||
| 73 | + } | ||
| 74 | + } | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + BatchGenerateResult { | ||
| 78 | + packages, | ||
| 79 | + error_code: if has_error { ErrCode::GeneralError as i32 } else { ErrCode::Success as i32 }, | ||
| 80 | + } | ||
| 81 | +} | ||
| 82 | + | ||
| 83 | +fn check_controller_device_id_match(local_udid: &str, package: &RemoteAuthPackage) -> bool { | ||
| 84 | + local_udid == package.remote_message.device_info.controller_device_id | ||
| 85 | +} | ||
| 86 | + | ||
| 87 | +/// Verifies controlled device packages. | ||
| 88 | +pub fn verify_controlled_device_package( | ||
| 89 | + os_account_id: i32, | ||
| 90 | + packages: Vec<RemoteAuthPackage> | ||
| 91 | +) -> BatchVerifyResult { | ||
| 92 | + let permission = CString::new(QUERY_TOOL_PERMISSIONS).unwrap(); | ||
| 93 | + if unsafe { !CheckPermission(permission.as_ptr()) } { | ||
| 94 | + loge!("Permission denied! Need {}", QUERY_TOOL_PERMISSIONS); | ||
| 95 | + return BatchVerifyResult { | ||
| 96 | + results: Vec::new(), | ||
| 97 | + error_code: ErrCode::PermissionDenied as i32, | ||
| 98 | + }; | ||
| 99 | + } | ||
| 100 | + | ||
| 101 | + logi!("[verify_controlled_device_package] os_account_id={}, package_count={}", os_account_id, packages.len()); | ||
| 102 | + | ||
| 103 | + if packages.is_empty() || packages.len() > super::MAX_REMOTE_BATCH_COUNT { | ||
| 104 | + loge!("Invalid packages count: {}, max allowed: {}", packages.len(), super::MAX_REMOTE_BATCH_COUNT); | ||
| 105 | + return BatchVerifyResult { | ||
| 106 | + results: Vec::new(), | ||
| 107 | + error_code: ErrCode::InvalidArrayLen as i32, | ||
| 108 | + }; | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + for (_idx, package) in packages.iter().enumerate() { | ||
| 112 | + log_remote_auth_package(package); | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + let local_udid = match crate::wrapper::get_device_udid(os_account_id) { | ||
| 116 | + Ok(udid) => udid, | ||
| 117 | + Err(e) => { | ||
| 118 | + loge!("Failed to get local udid: {:?}", e); | ||
| 119 | + return BatchVerifyResult { | ||
| 120 | + results: Vec::new(), | ||
| 121 | + error_code: e.code as i32, | ||
| 122 | + }; | ||
| 123 | + } | ||
| 124 | + }; | ||
| 125 | + | ||
| 126 | + let current_time = match system_time_in_millis() { | ||
| 127 | + Ok(t) => t, | ||
| 128 | + Err(e) => { | ||
| 129 | + loge!("Failed to get system time: {:?}", e); | ||
| 130 | + return BatchVerifyResult { | ||
| 131 | + results: Vec::new(), | ||
| 132 | + error_code: e.code as i32, | ||
| 133 | + }; | ||
| 134 | + } | ||
| 135 | + }; | ||
| 136 | + | ||
| 137 | + let mut results = Vec::with_capacity(packages.len()); | ||
| 138 | + let mut system_error_code = ErrCode::Success as i32; | ||
| 139 | + | ||
| 140 | + for package in packages.iter() { | ||
| 141 | + match validate_single_controlled_package(package, &local_udid, current_time, os_account_id) { | ||
| 142 | + Ok(result) => results.push(result), | ||
| 143 | + Err(e) => { | ||
| 144 | + loge!("System error during validation: {:?}", e); | ||
| 145 | + results.push(false); | ||
| 146 | + if system_error_code == ErrCode::Success as i32 { | ||
| 147 | + system_error_code = e.code as i32; | ||
| 148 | + } | ||
| 149 | + } | ||
| 150 | + } | ||
| 151 | + } | ||
| 152 | + | ||
| 153 | + BatchVerifyResult { | ||
| 154 | + results, | ||
| 155 | + error_code: system_error_code, | ||
| 156 | + } | ||
| 157 | +} | ||
| 158 | + | ||
| 159 | +fn validate_single_controlled_package( | ||
| 160 | + package: &RemoteAuthPackage, | ||
| 161 | + local_udid: &str, | ||
| 162 | + current_time: u64, | ||
| 163 | + os_account_id: i32, | ||
| 164 | +) -> Result<bool> { | ||
| 165 | + if !check_controller_device_id_match(local_udid, package) { | ||
| 166 | + loge!("Local udid mismatch"); | ||
| 167 | + return Ok(false); | ||
| 168 | + } | ||
| 169 | + | ||
| 170 | + if let Err(e) = validate_remote_auth_message_fields(&package.remote_message.remote_auth_message) { | ||
| 171 | + loge!("Invalid remote_auth_message fields: {:?}", e); | ||
| 172 | + return Ok(false); | ||
| 173 | + } | ||
| 174 | + | ||
| 175 | + // Verify cross-layer consistency: out challenge must match inner (signed) challenge | ||
| 176 | + if let Err(e) = super::validate_challenge_consistency(package) { | ||
| 177 | + loge!("Challenge consistency check failed: {:?}", e); | ||
| 178 | + return Ok(false); | ||
| 179 | + } | ||
| 180 | + | ||
| 181 | + let timestamp = match parse_timestamp(&package.remote_message.remote_auth_message) { | ||
| 182 | + Ok(t) => t, | ||
| 183 | + Err(_) => { | ||
| 184 | + loge!("Failed to parse timestamp"); | ||
| 185 | + return Ok(false); | ||
| 186 | + } | ||
| 187 | + }; | ||
| 188 | + | ||
| 189 | + let expire_time_ms = match parse_ticket_expire_time(&package.remote_message.remote_auth_message) { | ||
| 190 | + Ok(t) => t, | ||
| 191 | + Err(_) => { | ||
| 192 | + loge!("Failed to parse ticket expire time"); | ||
| 193 | + return Ok(false); | ||
| 194 | + } | ||
| 195 | + }; | ||
| 196 | + | ||
| 197 | + if !validate_ticket_expiration(timestamp, expire_time_ms, current_time) { | ||
| 198 | + loge!("Ticket expired"); | ||
| 199 | + return Ok(false); | ||
| 200 | + } | ||
| 201 | + | ||
| 202 | + match verify_remote_auth_package(os_account_id, package) { | ||
| 203 | + Ok(result) => Ok(result), | ||
| 204 | + Err(e) => { | ||
| 205 | + if e.code == ErrCode::ArgEmpty { | ||
| 206 | + loge!("Business failure - missing field: {:?}", e); | ||
| 207 | + Ok(false) | ||
| 208 | + } else { | ||
| 209 | + loge!("System error during verification: {:?}", e); | ||
| 210 | + Err(e) | ||
| 211 | + } | ||
| 212 | + } | ||
| 213 | + } | ||
| 214 | +} | ||
| 215 | + | ||
| 216 | +fn generate_single_controlled_package(query: PermissionQuery) -> Result<RemoteAuthPackage> { | ||
| 217 | + validate_controlled_permission_query(&query)?; | ||
| 218 | + | ||
| 219 | + let (cli_infos, mut api_permissions) = super::parse_cli_and_permission(&query.operation_info)?; | ||
| 220 | + | ||
| 221 | + if !cli_infos.is_empty() { | ||
| 222 | + super::batch_query_cli_permission(&cli_infos, &mut api_permissions)?; | ||
| 223 | + } | ||
| 224 | + | ||
| 225 | + let challenge = generate_crypto_random_challenge()?; | ||
| 226 | + let timestamp = system_time_in_millis()?; | ||
| 227 | + | ||
| 228 | + let remote_auth_message = build_remote_auth_message(&query, api_permissions, &challenge, timestamp)?; | ||
| 229 | + | ||
| 230 | + let uid = Skeleton::calling_uid(); | ||
| 231 | + let user_id = get_user_id(uid)?; | ||
| 232 | + | ||
| 233 | + logi!("Generate package: user_id={}", user_id); | ||
| 234 | + | ||
| 235 | + let sign_params = SignParams { | ||
| 236 | + os_account_id: user_id, | ||
| 237 | + uid: query.remote_info.domain_id.clone(), | ||
| 238 | + remote_auth_package: remote_auth_message, | ||
| 239 | + remote_control_token: query.remote_info.remote_control_params.remote_control_ticket.clone(), | ||
| 240 | + }; | ||
| 241 | + | ||
| 242 | + let sign_result = sign_remote_auth_package(sign_params)?; | ||
| 243 | + | ||
| 244 | + cache_challenge(user_id, &challenge, timestamp, &sign_result.device_id_header)?; | ||
| 245 | + | ||
| 246 | + let package = RemoteAuthPackage { | ||
| 247 | + remote_message: RemoteMessage { | ||
| 248 | + device_info: sign_result.device_id_header, | ||
| 249 | + remote_auth_message: sign_result.remote_auth_package, | ||
| 250 | + caller_bundle_name: String::new(), | ||
| 251 | + }, | ||
| 252 | + challenge, | ||
| 253 | + ticket: sign_result.sign_info, | ||
| 254 | + }; | ||
| 255 | + | ||
| 256 | + log_remote_auth_package(&package); | ||
| 257 | + | ||
| 258 | + Ok(package) | ||
| 259 | +} | ||
| 260 | + | ||
| 261 | +fn validate_controlled_permission_query(query: &PermissionQuery) -> Result<()> { | ||
| 262 | + if query.remote_info.role != Role::Controlled { | ||
| 263 | + return macros_lib::log_throw_error!(ErrCode::DataTypeMismatch, "Invalid role: expected CONTROLLED"); | ||
| 264 | + } | ||
| 265 | + if query.domain_id.is_empty() { | ||
| 266 | + return macros_lib::log_throw_error!(ErrCode::ArgEmpty, "domain_id is empty"); | ||
| 267 | + } | ||
| 268 | + if query.remote_info.remote_control_params.remote_control_ticket.is_empty() { | ||
| 269 | + return macros_lib::log_throw_error!(ErrCode::ArgEmpty, "remote_control_ticket is empty"); | ||
| 270 | + } | ||
| 271 | + if query.ticket_expire_time_ms <= 0 || query.ticket_expire_time_ms > super::MAX_REMOTE_TICKET_EXPIRE_TIME_MS { | ||
| 272 | + return macros_lib::log_throw_error!(ErrCode::InvalidArgument, "ticket_expire_time_ms out of range {}", | ||
| 273 | + query.ticket_expire_time_ms); | ||
| 274 | + } | ||
| 275 | + Ok(()) | ||
| 276 | +} | ||
| 277 | + | ||
| 278 | +fn build_remote_auth_message( | ||
| 279 | + query: &PermissionQuery, | ||
| 280 | + api_permissions: Vec<String>, | ||
| 281 | + challenge: &str, | ||
| 282 | + timestamp: u64, | ||
| 283 | +) -> Result<String> { | ||
| 284 | + let mut builder = JsonBuilder::new(); | ||
| 285 | + serialize_permission_query_to_message(&mut builder, query); | ||
| 286 | + builder.add_string("challenge", challenge); | ||
| 287 | + builder.add_u64("timestamp", timestamp); | ||
| 288 | + builder.add_string_array("permissions", api_permissions); | ||
| 289 | + builder.add_number("version", 1 as i64); | ||
| 290 | + | ||
| 291 | + builder.build() | ||
| 292 | +} | ||
| 293 | + | ||
| 294 | +fn validate_controlled_batch_params(queries: &[PermissionQuery]) -> Result<()> { | ||
| 295 | + if queries.is_empty() || queries.len() > super::MAX_REMOTE_BATCH_COUNT { | ||
| 296 | + return macros_lib::log_throw_error!(ErrCode::InvalidArrayLen, | ||
| 297 | + "Invalid queries count: {}, max allowed: {}", | ||
| 298 | + queries.len(), super::MAX_REMOTE_BATCH_COUNT); | ||
| 299 | + } | ||
| 300 | + | ||
| 301 | + for (idx, query) in queries.iter().enumerate() { | ||
| 302 | + if query.operation_info.len() > super::MAX_REMOTE_PERMISSION_COUNT { | ||
| 303 | + return macros_lib::log_throw_error!(ErrCode::InvalidArrayLen, | ||
| 304 | + "Invalid operation_info count at idx[{}]: {}, max allowed: {}", | ||
| 305 | + idx, query.operation_info.len(), super::MAX_REMOTE_PERMISSION_COUNT); | ||
| 306 | + } | ||
| 307 | + } | ||
| 308 | + | ||
| 309 | + Ok(()) | ||
| 310 | +} | ||
| 311 | + | ||
| 312 | + | ||
| 313 | +pub mod ut_controlled_device_stub { | ||
| 314 | + include! {"../../../../../test/secure_access_fence/unittest/ut_test/services/core_service/test_stub/remote_control/ut_controlled_device_stub.rs"} | ||
| 315 | +} | ||
| @@ -0,0 +1,517 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | +//! This module implements controller device package generation and verification. | ||
| 17 | + | ||
| 18 | +use ipc::Skeleton; | ||
| 19 | +use saf_common::{get_user_id, JsonBuilder, new_object, object_add_string}; | ||
| 20 | +use saf_utils::system_time_in_millis; | ||
| 21 | +use saf_definition::{macros_lib, ErrCode, Result, | ||
| 22 | + PermissionQuery, RemoteAuthPackage, Role, DeviceIdHeader, RemoteUserAuthItem, | ||
| 23 | + RemoteUserAuthResults, RemoteInfo, RemoteMessage, GrantType, | ||
| 24 | +}; | ||
| 25 | +use crate::remote_control::{create_empty_package, log_remote_auth_package, parse_ticket_expire_time, | ||
| 26 | + parse_timestamp, serialize_permission_query_to_message, validate_remote_auth_message_fields, BatchGenerateResult, BatchVerifyResult}; | ||
| 27 | +use crate::remote_control::grant_record::{store_grant_record, StoreGrantRecordParams}; | ||
| 28 | +use crate::remote_control::remote_challenge_manager::verify_and_remove_challenge; | ||
| 29 | +use crate::remote_control::account_based_auth_manager::{sign_remote_auth_package, verify_remote_auth_package, SignParams}; | ||
| 30 | +use crate::remote_control::grant_record::get_bundle_name_from_token; | ||
| 31 | +use saf_log::{loge, logi}; | ||
| 32 | +use std::ffi::CString; | ||
| 33 | +use std::os::raw::c_char as raw_c_char; | ||
| 34 | + | ||
| 35 | +extern "C" { | ||
| 36 | + fn CheckPermission(permission: *const raw_c_char) -> bool; | ||
| 37 | +} | ||
| 38 | + | ||
| 39 | +const QUERY_TOOL_PERMISSIONS: &str = "ohos.permission.QUERY_TOOL_PERMISSIONS"; | ||
| 40 | + | ||
| 41 | +const MAX_PERMISSION_LEN: usize = 128; | ||
| 42 | + | ||
| 43 | +/// Generates controller device packages for remote user auth results. | ||
| 44 | +pub fn generate_controller_device_package( | ||
| 45 | + remote_user_auth_results: Vec<RemoteUserAuthResults> | ||
| 46 | +) -> BatchGenerateResult { | ||
| 47 | + let permission = CString::new(QUERY_TOOL_PERMISSIONS).unwrap(); | ||
| 48 | + if unsafe { !CheckPermission(permission.as_ptr()) } { | ||
| 49 | + loge!("Permission denied! Need {}", QUERY_TOOL_PERMISSIONS); | ||
| 50 | + return BatchGenerateResult { | ||
| 51 | + packages: vec![create_empty_package(); remote_user_auth_results.len().max(1)], | ||
| 52 | + error_code: ErrCode::PermissionDenied as i32, | ||
| 53 | + }; | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + if let Err(e) = validate_controller_batch_params(&remote_user_auth_results) { | ||
| 57 | + loge!("Invalid batch params: {:?}", e); | ||
| 58 | + return BatchGenerateResult { | ||
| 59 | + packages: vec![create_empty_package(); remote_user_auth_results.len()], | ||
| 60 | + error_code: e.code as i32, | ||
| 61 | + }; | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + let uid = Skeleton::calling_uid(); | ||
| 65 | + let user_id = match get_user_id(uid) { | ||
| 66 | + Ok(id) => id, | ||
| 67 | + Err(e) => { | ||
| 68 | + loge!("Failed to get user_id: {:?}", e); | ||
| 69 | + return BatchGenerateResult { | ||
| 70 | + packages: vec![create_empty_package(); remote_user_auth_results.len()], | ||
| 71 | + error_code: ErrCode::InvalidOsAccountId as i32, | ||
| 72 | + }; | ||
| 73 | + } | ||
| 74 | + }; | ||
| 75 | + | ||
| 76 | + let mut packages = Vec::with_capacity(remote_user_auth_results.len()); | ||
| 77 | + let mut has_error = false; | ||
| 78 | + | ||
| 79 | + for (idx, auth_result) in remote_user_auth_results.iter().enumerate() { | ||
| 80 | + match generate_single_controller_package(user_id, auth_result) { | ||
| 81 | + Ok(pkg) => { | ||
| 82 | + store_grant_record_if_success(user_id, &pkg, Role::Controller); | ||
| 83 | + packages.push(pkg); | ||
| 84 | + }, | ||
| 85 | + Err(e) => { | ||
| 86 | + loge!("Generate controller package failed at idx[{}], err={:?}", idx, e); | ||
| 87 | + packages.push(create_empty_package()); | ||
| 88 | + has_error = true; | ||
| 89 | + } | ||
| 90 | + } | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + BatchGenerateResult { | ||
| 94 | + packages, | ||
| 95 | + error_code: if has_error { ErrCode::GeneralError as i32 } else { ErrCode::Success as i32 }, | ||
| 96 | + } | ||
| 97 | +} | ||
| 98 | + | ||
| 99 | +fn store_grant_record_if_success(user_id: i32, pkg: &RemoteAuthPackage, role: Role) { | ||
| 100 | + let caller_bundle_name = super::parse_caller_bundle_name_from_remote_auth_message( | ||
| 101 | + &pkg.remote_message.remote_auth_message | ||
| 102 | + ).unwrap_or_default(); | ||
| 103 | + | ||
| 104 | + if let Ok(params) = StoreGrantRecordParams::from_remote_auth_package( | ||
| 105 | + user_id, | ||
| 106 | + &pkg.remote_message.remote_auth_message, | ||
| 107 | + role, | ||
| 108 | + GrantType::RemoteGrant, | ||
| 109 | + caller_bundle_name | ||
| 110 | + ) { | ||
| 111 | + if params.permission_names.is_empty() { | ||
| 112 | + logi!("No GRANTED permissions found, skip storing grant record"); | ||
| 113 | + return; | ||
| 114 | + } | ||
| 115 | + if let Err(e) = store_grant_record(params) { | ||
| 116 | + loge!("Failed to store grant record: {:?}", e); | ||
| 117 | + } | ||
| 118 | + } | ||
| 119 | +} | ||
| 120 | + | ||
| 121 | +fn check_role_is_controller(role: Role) -> bool { | ||
| 122 | + role == Role::Controller | ||
| 123 | +} | ||
| 124 | + | ||
| 125 | +/// Verifies controller device packages with remote info. | ||
| 126 | +pub fn verify_controller_device_package( | ||
| 127 | + os_account_id: i32, | ||
| 128 | + packages: Vec<RemoteAuthPackage>, | ||
| 129 | + remote_info: &RemoteInfo | ||
| 130 | +) -> BatchVerifyResult { | ||
| 131 | + let permission = CString::new(QUERY_TOOL_PERMISSIONS).unwrap(); | ||
| 132 | + if unsafe { !CheckPermission(permission.as_ptr()) } { | ||
| 133 | + loge!("Permission denied! Need {}", QUERY_TOOL_PERMISSIONS); | ||
| 134 | + return BatchVerifyResult { | ||
| 135 | + results: Vec::new(), | ||
| 136 | + error_code: ErrCode::PermissionDenied as i32, | ||
| 137 | + }; | ||
| 138 | + } | ||
| 139 | + | ||
| 140 | + logi!("[verify_controller_device_package] os_account_id={}, package_count={}, domain_id={}", | ||
| 141 | + os_account_id, packages.len(), remote_info.domain_id); | ||
| 142 | + | ||
| 143 | + if packages.is_empty() || packages.len() > super::MAX_REMOTE_BATCH_COUNT { | ||
| 144 | + loge!("Invalid packages count: {}, max allowed: {}", packages.len(), super::MAX_REMOTE_BATCH_COUNT); | ||
| 145 | + return BatchVerifyResult { | ||
| 146 | + results: Vec::new(), | ||
| 147 | + error_code: ErrCode::InvalidArrayLen as i32, | ||
| 148 | + }; | ||
| 149 | + } | ||
| 150 | + | ||
| 151 | + for (_idx, package) in packages.iter().enumerate() { | ||
| 152 | + log_remote_auth_package(package); | ||
| 153 | + } | ||
| 154 | + | ||
| 155 | + if !check_role_is_controller(remote_info.role) { | ||
| 156 | + loge!("Invalid role for verification: expected CONTROLLER"); | ||
| 157 | + return BatchVerifyResult { | ||
| 158 | + results: Vec::new(), | ||
| 159 | + error_code: ErrCode::DataTypeMismatch as i32, | ||
| 160 | + }; | ||
| 161 | + } | ||
| 162 | + | ||
| 163 | + let local_udid = match crate::wrapper::get_device_udid(os_account_id) { | ||
| 164 | + Ok(udid) => udid, | ||
| 165 | + Err(e) => { | ||
| 166 | + loge!("Failed to get local udid: {:?}", e); | ||
| 167 | + return BatchVerifyResult { | ||
| 168 | + results: Vec::new(), | ||
| 169 | + error_code: e.code as i32, | ||
| 170 | + }; | ||
| 171 | + } | ||
| 172 | + }; | ||
| 173 | + | ||
| 174 | + let current_time = match system_time_in_millis() { | ||
| 175 | + Ok(t) => t, | ||
| 176 | + Err(e) => { | ||
| 177 | + loge!("Failed to get system time: {:?}", e); | ||
| 178 | + return BatchVerifyResult { | ||
| 179 | + results: Vec::new(), | ||
| 180 | + error_code: e.code as i32, | ||
| 181 | + }; | ||
| 182 | + } | ||
| 183 | + }; | ||
| 184 | + | ||
| 185 | + let mut results = Vec::with_capacity(packages.len()); | ||
| 186 | + let mut system_error_code = ErrCode::Success as i32; | ||
| 187 | + | ||
| 188 | + for package in packages.iter() { | ||
| 189 | + match validate_and_verify_single_controller_package( | ||
| 190 | + package, &local_udid, current_time, os_account_id | ||
| 191 | + ) { | ||
| 192 | + Ok(result) => results.push(result), | ||
| 193 | + Err(e) => { | ||
| 194 | + loge!("System error during validation: {:?}", e); | ||
| 195 | + results.push(false); | ||
| 196 | + if system_error_code == ErrCode::Success as i32 { | ||
| 197 | + system_error_code = e.code as i32; | ||
| 198 | + } | ||
| 199 | + } | ||
| 200 | + } | ||
| 201 | + } | ||
| 202 | + | ||
| 203 | + BatchVerifyResult { | ||
| 204 | + results, | ||
| 205 | + error_code: system_error_code, | ||
| 206 | + } | ||
| 207 | +} | ||
| 208 | + | ||
| 209 | +fn validate_and_verify_single_controller_package( | ||
| 210 | + package: &RemoteAuthPackage, | ||
| 211 | + local_udid: &str, | ||
| 212 | + current_time: u64, | ||
| 213 | + os_account_id: i32, | ||
| 214 | +) -> Result<bool> { | ||
| 215 | + let verify_result = match verify_single_controller_package( | ||
| 216 | + os_account_id, package, local_udid, current_time | ||
| 217 | + ) { | ||
| 218 | + Ok(result) => result, | ||
| 219 | + Err(e) => { | ||
| 220 | + if e.code == ErrCode::ArgEmpty || e.code == ErrCode::ReplayAttackDetected { | ||
| 221 | + loge!("Business failure: {:?}", e); | ||
| 222 | + return Ok(false); | ||
| 223 | + } else { | ||
| 224 | + loge!("System error during verification: {:?}", e); | ||
| 225 | + return Err(e); | ||
| 226 | + } | ||
| 227 | + } | ||
| 228 | + }; | ||
| 229 | + | ||
| 230 | + if verify_result { | ||
| 231 | + store_grant_record_if_success(os_account_id, package, Role::Controlled); | ||
| 232 | + } | ||
| 233 | + | ||
| 234 | + Ok(verify_result) | ||
| 235 | +} | ||
| 236 | + | ||
| 237 | +fn generate_single_controller_package( | ||
| 238 | + os_account_id: i32, | ||
| 239 | + auth_result: &RemoteUserAuthResults | ||
| 240 | +) -> Result<RemoteAuthPackage> { | ||
| 241 | + let (local_udid, api_permissions, challenge, timestamp) = | ||
| 242 | + prepare_controller_package_data(os_account_id, auth_result)?; | ||
| 243 | + | ||
| 244 | + let uid = Skeleton::calling_uid(); | ||
| 245 | + let user_id = get_user_id(uid)?; | ||
| 246 | + | ||
| 247 | + logi!("Generate controller package: user_id={}", user_id); | ||
| 248 | + | ||
| 249 | + let package = build_and_sign_controller_package( | ||
| 250 | + &auth_result.permission_query, | ||
| 251 | + &auth_result.results, | ||
| 252 | + challenge, | ||
| 253 | + &local_udid, | ||
| 254 | + &api_permissions, | ||
| 255 | + user_id, | ||
| 256 | + timestamp, | ||
| 257 | + )?; | ||
| 258 | + | ||
| 259 | + log_remote_auth_package(&package); | ||
| 260 | + | ||
| 261 | + Ok(package) | ||
| 262 | +} | ||
| 263 | + | ||
| 264 | +fn prepare_controller_package_data( | ||
| 265 | + os_account_id: i32, | ||
| 266 | + auth_result: &RemoteUserAuthResults | ||
| 267 | +) -> Result<(String, Vec<String>, String, u64)> { | ||
| 268 | + validate_controller_permission_query(&auth_result.permission_query)?; | ||
| 269 | + validate_auth_results_permissions(&auth_result.results)?; | ||
| 270 | + | ||
| 271 | + let local_udid = crate::wrapper::get_device_udid(os_account_id)?; | ||
| 272 | + | ||
| 273 | + let (cli_infos, mut api_permissions) = | ||
| 274 | + super::parse_cli_and_permission(&auth_result.permission_query.operation_info)?; | ||
| 275 | + | ||
| 276 | + if !cli_infos.is_empty() { | ||
| 277 | + super::batch_query_cli_permission(&cli_infos, &mut api_permissions)?; | ||
| 278 | + } | ||
| 279 | + | ||
| 280 | + validate_permissions_match(&auth_result.results, &api_permissions)?; | ||
| 281 | + | ||
| 282 | + let challenge = auth_result.permission_query.remote_info.remote_control_params.challenge.clone(); | ||
| 283 | + if challenge.is_empty() { | ||
| 284 | + return macros_lib::log_throw_error!(ErrCode::ArgEmpty, "challenge is empty"); | ||
| 285 | + } | ||
| 286 | + | ||
| 287 | + let timestamp = system_time_in_millis()?; | ||
| 288 | + | ||
| 289 | + Ok((local_udid, api_permissions, challenge, timestamp)) | ||
| 290 | +} | ||
| 291 | + | ||
| 292 | +fn build_and_sign_controller_package( | ||
| 293 | + query: &PermissionQuery, | ||
| 294 | + auth_results: &[RemoteUserAuthItem], | ||
| 295 | + challenge: String, | ||
| 296 | + local_udid: &str, | ||
| 297 | + api_permissions: &[String], | ||
| 298 | + user_id: i32, | ||
| 299 | + timestamp: u64, | ||
| 300 | +) -> Result<RemoteAuthPackage> { | ||
| 301 | + let caller_bundle_name = get_bundle_name_from_token(query.caller_token_id) | ||
| 302 | + .unwrap_or_default(); | ||
| 303 | + | ||
| 304 | + let remote_auth_message = build_controller_remote_auth_message( | ||
| 305 | + query, auth_results, &challenge, timestamp, local_udid, api_permissions, &caller_bundle_name | ||
| 306 | + )?; | ||
| 307 | + | ||
| 308 | + let sign_params = SignParams { | ||
| 309 | + os_account_id: user_id, | ||
| 310 | + uid: query.remote_info.domain_id.clone(), | ||
| 311 | + remote_auth_package: remote_auth_message, | ||
| 312 | + remote_control_token: String::new(), | ||
| 313 | + }; | ||
| 314 | + | ||
| 315 | + let sign_result = sign_remote_auth_package(sign_params)?; | ||
| 316 | + | ||
| 317 | + Ok(RemoteAuthPackage { | ||
| 318 | + remote_message: RemoteMessage { | ||
| 319 | + device_info: sign_result.device_id_header, | ||
| 320 | + remote_auth_message: sign_result.remote_auth_package, | ||
| 321 | + caller_bundle_name, | ||
| 322 | + }, | ||
| 323 | + challenge, | ||
| 324 | + ticket: sign_result.sign_info, | ||
| 325 | + }) | ||
| 326 | +} | ||
| 327 | + | ||
| 328 | +fn verify_single_controller_package( | ||
| 329 | + os_account_id: i32, | ||
| 330 | + package: &RemoteAuthPackage, | ||
| 331 | + local_udid: &str, | ||
| 332 | + current_time: u64 | ||
| 333 | +) -> Result<bool> { | ||
| 334 | + // Validate cross-layer consistency first | ||
| 335 | + let _inner_challenge = match super::validate_challenge_consistency(package) { | ||
| 336 | + Ok(c) => c, | ||
| 337 | + Err(e) => { | ||
| 338 | + loge!("Challenge consistency check failed: {:?}", e); | ||
| 339 | + return Ok(false); | ||
| 340 | + } | ||
| 341 | + }; | ||
| 342 | + | ||
| 343 | + if let Err(e) = validate_remote_auth_message_fields(&package.remote_message.remote_auth_message) { | ||
| 344 | + loge!("Invalid remote_auth_message fields: {:?}", e); | ||
| 345 | + return Ok(false); | ||
| 346 | + } | ||
| 347 | + | ||
| 348 | + let timestamp = match parse_timestamp(&package.remote_message.remote_auth_message) { | ||
| 349 | + Ok(t) => t, | ||
| 350 | + Err(_) => { | ||
| 351 | + loge!("Failed to parse timestamp"); | ||
| 352 | + return Ok(false); | ||
| 353 | + } | ||
| 354 | + }; | ||
| 355 | + | ||
| 356 | + let expire_time_ms = match parse_ticket_expire_time(&package.remote_message.remote_auth_message) { | ||
| 357 | + Ok(t) => t, | ||
| 358 | + Err(_) => { | ||
| 359 | + loge!("Failed to parse ticket expire time"); | ||
| 360 | + return Ok(false); | ||
| 361 | + } | ||
| 362 | + }; | ||
| 363 | + | ||
| 364 | + if !crate::remote_control::validate_ticket_expiration(timestamp, expire_time_ms, current_time) { | ||
| 365 | + loge!("Ticket expired"); | ||
| 366 | + return Ok(false); | ||
| 367 | + } | ||
| 368 | + | ||
| 369 | + let verify_result = verify_remote_auth_package(os_account_id, package)?; | ||
| 370 | + if !verify_result { | ||
| 371 | + return Ok(false); | ||
| 372 | + } | ||
| 373 | + | ||
| 374 | + let controller_device_id = match super::parse_local_device_id_from_remote_auth_message( | ||
| 375 | + &package.remote_message.remote_auth_message | ||
| 376 | + ) { | ||
| 377 | + Ok(id) => id, | ||
| 378 | + Err(e) => { | ||
| 379 | + loge!("Failed to parse localDeviceId from remote_auth_message: {:?}", e); | ||
| 380 | + return Ok(false); | ||
| 381 | + } | ||
| 382 | + }; | ||
| 383 | + | ||
| 384 | + let device_id_header = DeviceIdHeader { | ||
| 385 | + controlled_device_id: local_udid.to_string(), | ||
| 386 | + controller_device_id, | ||
| 387 | + }; | ||
| 388 | + | ||
| 389 | + verify_and_remove_challenge(os_account_id, &package.challenge, &device_id_header)?; | ||
| 390 | + | ||
| 391 | + Ok(true) | ||
| 392 | +} | ||
| 393 | + | ||
| 394 | +fn validate_controller_permission_query(query: &PermissionQuery) -> Result<()> { | ||
| 395 | + if query.remote_info.role != Role::Controller { | ||
| 396 | + return macros_lib::log_throw_error!(ErrCode::DataTypeMismatch, | ||
| 397 | + "Invalid role: expected CONTROLLER"); | ||
| 398 | + } | ||
| 399 | + | ||
| 400 | + if query.domain_id.is_empty() { | ||
| 401 | + return macros_lib::log_throw_error!(ErrCode::ArgEmpty, "domain_id is empty"); | ||
| 402 | + } | ||
| 403 | + | ||
| 404 | + if query.remote_info.remote_control_params.challenge.is_empty() { | ||
| 405 | + return macros_lib::log_throw_error!(ErrCode::ArgEmpty, "challenge is empty"); | ||
| 406 | + } | ||
| 407 | + | ||
| 408 | + if query.operation_info.is_empty() { | ||
| 409 | + return macros_lib::log_throw_error!(ErrCode::ArgEmpty, "operation_info is empty"); | ||
| 410 | + } | ||
| 411 | + if query.ticket_expire_time_ms <= 0 || query.ticket_expire_time_ms > super::MAX_REMOTE_TICKET_EXPIRE_TIME_MS { | ||
| 412 | + return macros_lib::log_throw_error!(ErrCode::InvalidArgument, "ticket_expire_time_ms out of range {}", | ||
| 413 | + query.ticket_expire_time_ms); | ||
| 414 | + } | ||
| 415 | + Ok(()) | ||
| 416 | +} | ||
| 417 | + | ||
| 418 | +fn validate_auth_results_permissions(results: &[RemoteUserAuthItem]) -> Result<()> { | ||
| 419 | + if results.is_empty() { | ||
| 420 | + return macros_lib::log_throw_error!(ErrCode::ArgEmpty, "authResults is empty"); | ||
| 421 | + } | ||
| 422 | + | ||
| 423 | + for (idx, item) in results.iter().enumerate() { | ||
| 424 | + if item.permission.is_empty() { | ||
| 425 | + return macros_lib::log_throw_error!(ErrCode::ArgEmpty, | ||
| 426 | + "Permission at idx[{}] is empty", idx); | ||
| 427 | + } | ||
| 428 | + if item.permission.len() >= MAX_PERMISSION_LEN { | ||
| 429 | + return macros_lib::log_throw_error!(ErrCode::InvalidArgSize, | ||
| 430 | + "Permission at idx[{}] exceeds max length {}, actual len={}", | ||
| 431 | + idx, MAX_PERMISSION_LEN, item.permission.len()); | ||
| 432 | + } | ||
| 433 | + } | ||
| 434 | + Ok(()) | ||
| 435 | +} | ||
| 436 | + | ||
| 437 | +fn validate_permissions_match(results: &[RemoteUserAuthItem], api_permissions: &[String]) -> Result<()> { | ||
| 438 | + let results_permissions: std::collections::HashSet<&str> = results | ||
| 439 | + .iter() | ||
| 440 | + .map(|r| r.permission.as_str()) | ||
| 441 | + .collect(); | ||
| 442 | + | ||
| 443 | + let api_permissions_set: std::collections::HashSet<&str> = api_permissions | ||
| 444 | + .iter() | ||
| 445 | + .map(|p| p.as_str()) | ||
| 446 | + .collect(); | ||
| 447 | + | ||
| 448 | + if results_permissions != api_permissions_set { | ||
| 449 | + loge!("Permissions mismatch: results_count={}, api_permissions_count={}", | ||
| 450 | + results_permissions.len(), api_permissions_set.len()); | ||
| 451 | + return macros_lib::log_throw_error!(ErrCode::DataTypeMismatch, | ||
| 452 | + "Permissions in results do not match permissions from operationInfo"); | ||
| 453 | + } | ||
| 454 | + | ||
| 455 | + Ok(()) | ||
| 456 | +} | ||
| 457 | + | ||
| 458 | +fn build_controller_remote_auth_message( | ||
| 459 | + query: &PermissionQuery, | ||
| 460 | + auth_results: &[RemoteUserAuthItem], | ||
| 461 | + challenge: &str, | ||
| 462 | + timestamp: u64, | ||
| 463 | + local_device_id: &str, | ||
| 464 | + permissions: &[String], | ||
| 465 | + caller_bundle_name: &str | ||
| 466 | +) -> Result<String> { | ||
| 467 | + let mut builder = JsonBuilder::new(); | ||
| 468 | + serialize_permission_query_to_message(&mut builder, query); | ||
| 469 | + | ||
| 470 | + builder.add_string("challenge", challenge); | ||
| 471 | + builder.add_u64("timestamp", timestamp); | ||
| 472 | + | ||
| 473 | + let mut auth_results_array = Vec::with_capacity(auth_results.len()); | ||
| 474 | + for item in auth_results { | ||
| 475 | + let mut item_obj = new_object(); | ||
| 476 | + object_add_string(&mut item_obj, "permission", &item.permission); | ||
| 477 | + object_add_string(&mut item_obj, "authResult", &item.auth_result); | ||
| 478 | + auth_results_array.push(item_obj); | ||
| 479 | + } | ||
| 480 | + builder.add_object_array("authResults", auth_results_array); | ||
| 481 | + | ||
| 482 | + builder.add_string_array("permissions", permissions.to_vec()); | ||
| 483 | + builder.add_string("localDeviceId", local_device_id); | ||
| 484 | + builder.add_string("callerBundleName", caller_bundle_name); | ||
| 485 | + builder.add_number("version", 1 as i64); | ||
| 486 | + | ||
| 487 | + builder.build() | ||
| 488 | +} | ||
| 489 | + | ||
| 490 | +fn validate_controller_batch_params(auth_results: &[RemoteUserAuthResults]) -> Result<()> { | ||
| 491 | + if auth_results.is_empty() || auth_results.len() > super::MAX_REMOTE_BATCH_COUNT { | ||
| 492 | + return macros_lib::log_throw_error!(ErrCode::InvalidArrayLen, | ||
| 493 | + "Invalid auth_results count: {}, max allowed: {}", | ||
| 494 | + auth_results.len(), super::MAX_REMOTE_BATCH_COUNT); | ||
| 495 | + } | ||
| 496 | + | ||
| 497 | + for (idx, auth_result) in auth_results.iter().enumerate() { | ||
| 498 | + if auth_result.permission_query.operation_info.len() > super::MAX_REMOTE_PERMISSION_COUNT { | ||
| 499 | + return macros_lib::log_throw_error!(ErrCode::InvalidArrayLen, | ||
| 500 | + "Invalid operation_info count at idx[{}]: {}, max allowed: {}", | ||
| 501 | + idx, auth_result.permission_query.operation_info.len(), super::MAX_REMOTE_PERMISSION_COUNT); | ||
| 502 | + } | ||
| 503 | + | ||
| 504 | + if auth_result.results.len() > super::MAX_REMOTE_PERMISSION_COUNT { | ||
| 505 | + return macros_lib::log_throw_error!(ErrCode::InvalidArrayLen, | ||
| 506 | + "Invalid results count at idx[{}]: {}, max allowed: {}", | ||
| 507 | + idx, auth_result.results.len(), super::MAX_REMOTE_PERMISSION_COUNT); | ||
| 508 | + } | ||
| 509 | + } | ||
| 510 | + | ||
| 511 | + Ok(()) | ||
| 512 | +} | ||
| 513 | + | ||
| 514 | + | ||
| 515 | +pub mod ut_controller_device_stub { | ||
| 516 | + include! {"../../../../../test/secure_access_fence/unittest/ut_test/services/core_service/test_stub/remote_control/ut_controller_device_stub.rs"} | ||
| 517 | +} | ||
| @@ -0,0 +1,238 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | +//! This module implements grant record storage functionality. | ||
| 17 | + | ||
| 18 | +use ylong_json::JsonValue; | ||
| 19 | +use saf_definition::{macros_lib, ErrCode, GrantType, Result, Role}; | ||
| 20 | +use saf_log::{loge, logi}; | ||
| 21 | +use saf_utils::{get_compact_json_value, system_time_in_millis}; | ||
| 22 | + | ||
| 23 | + | ||
| 24 | +use std::collections::HashMap; | ||
| 25 | + | ||
| 26 | +use saf_plugin::saf_plugin::SAFPlugin; | ||
| 27 | + | ||
| 28 | +use saf_plugin_interface::plugin_interface::{ | ||
| 29 | + EventType, ExtMap, STORE_GRANT_RECORD_KEYS, | ||
| 30 | +}; | ||
| 31 | + | ||
| 32 | +use saf_definition::Value; | ||
| 33 | + | ||
| 34 | +const MAX_BUNDLE_NAME_LEN: usize = 256; | ||
| 35 | + | ||
| 36 | + | ||
| 37 | +extern "C" { | ||
| 38 | + fn GetBundleNameFromTokenId( | ||
| 39 | + tokenId: i32, | ||
| 40 | + bundleName: *mut std::ffi::c_char, | ||
| 41 | + len: *mut i32 | ||
| 42 | + ) -> i32; | ||
| 43 | +} | ||
| 44 | + | ||
| 45 | + | ||
| 46 | +pub fn get_bundle_name_from_token(caller_token_id: i32) -> Result<String> { | ||
| 47 | + let mut bundle_name_buf = vec![0u8; MAX_BUNDLE_NAME_LEN]; | ||
| 48 | + let mut len = MAX_BUNDLE_NAME_LEN as i32; | ||
| 49 | + | ||
| 50 | + let ret = unsafe { | ||
| 51 | + GetBundleNameFromTokenId( | ||
| 52 | + caller_token_id, | ||
| 53 | + bundle_name_buf.as_mut_ptr() as *mut std::ffi::c_char, | ||
| 54 | + &mut len, | ||
| 55 | + ) | ||
| 56 | + }; | ||
| 57 | + | ||
| 58 | + if ret != 0 { | ||
| 59 | + loge!("GetBundleNameFromTokenId failed, ret={}", ret); | ||
| 60 | + return Err(macros_lib::log_and_into_saf_error!(ErrCode::GeneralError, | ||
| 61 | + "GetBundleNameFromTokenId failed")); | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + unsafe { | ||
| 65 | + Ok(std::ffi::CStr::from_ptr(bundle_name_buf.as_ptr() as *const std::ffi::c_char) | ||
| 66 | + .to_str() | ||
| 67 | + .map_err(|e| { | ||
| 68 | + loge!("Invalid UTF-8 in bundle name: {}", e); | ||
| 69 | + macros_lib::log_and_into_saf_error!(ErrCode::GeneralError, | ||
| 70 | + "Invalid UTF-8 in bundle name") | ||
| 71 | + })? | ||
| 72 | + .to_string()) | ||
| 73 | + } | ||
| 74 | +} | ||
| 75 | + | ||
| 76 | +/// Store grant record params structure. | ||
| 77 | + | ||
| 78 | +pub struct StoreGrantRecordParams { | ||
| 79 | + /// OS account ID. | ||
| 80 | + pub os_account_id: i32, | ||
| 81 | + /// Controlled device name. | ||
| 82 | + pub controlled_device_name: String, | ||
| 83 | + /// Controller device name. | ||
| 84 | + pub controller_device_name: String, | ||
| 85 | + /// Is self grant flag. | ||
| 86 | + pub is_self_grant: bool, | ||
| 87 | + /// Permission names. | ||
| 88 | + pub permission_names: Vec<String>, | ||
| 89 | + /// Device role. | ||
| 90 | + pub device_role: Role, | ||
| 91 | + /// Calling bundle name. | ||
| 92 | + pub calling_bundle_name: String, | ||
| 93 | + /// Grant type. | ||
| 94 | + pub grant_type: GrantType, | ||
| 95 | + /// Timestamp. | ||
| 96 | + pub timestamp: u64, | ||
| 97 | +} | ||
| 98 | + | ||
| 99 | +impl StoreGrantRecordParams { | ||
| 100 | +/// Creates a new StoreGrantRecordParams from remote auth package. | ||
| 101 | + pub fn from_remote_auth_package( | ||
| 102 | + os_account_id: i32, | ||
| 103 | + remote_auth_message: &str, | ||
| 104 | + device_role: Role, | ||
| 105 | + grant_type: GrantType, | ||
| 106 | + calling_bundle_name: String, | ||
| 107 | + ) -> Result<Self> { | ||
| 108 | + let json = JsonValue::from_text(remote_auth_message).map_err(|e| { | ||
| 109 | + macros_lib::log_and_into_saf_error!(ErrCode::JsonParseError, | ||
| 110 | + "parse remote_auth_message failed: {}", e) | ||
| 111 | + })?; | ||
| 112 | + | ||
| 113 | + let (controlled_device_name, controller_device_name) = Self::parse_device_names(&json)?; | ||
| 114 | + let permission_names = Self::parse_permission_names(remote_auth_message)?; | ||
| 115 | + let timestamp = system_time_in_millis()?; | ||
| 116 | + | ||
| 117 | + Ok(Self { | ||
| 118 | + os_account_id, | ||
| 119 | + controlled_device_name, | ||
| 120 | + controller_device_name, | ||
| 121 | + is_self_grant: false, | ||
| 122 | + permission_names, | ||
| 123 | + device_role, | ||
| 124 | + calling_bundle_name, | ||
| 125 | + grant_type, | ||
| 126 | + timestamp, | ||
| 127 | + }) | ||
| 128 | + } | ||
| 129 | + | ||
| 130 | + fn parse_device_names(json: &JsonValue) -> Result<(String, String)> { | ||
| 131 | + let controlled_device_name = get_compact_json_value(json, "controlledDeviceName") | ||
| 132 | + .map_err(|e| { | ||
| 133 | + loge!("Failed to get controlledDeviceName: {:?}", e); | ||
| 134 | + macros_lib::log_and_into_saf_error!(ErrCode::ArgEmpty, | ||
| 135 | + "controlledDeviceName not found in remote_auth_message") | ||
| 136 | + })?; | ||
| 137 | + | ||
| 138 | + let controller_device_name = get_compact_json_value(json, "controllerDeviceName") | ||
| 139 | + .map_err(|e| { | ||
| 140 | + loge!("Failed to get controllerDeviceName: {:?}", e); | ||
| 141 | + macros_lib::log_and_into_saf_error!(ErrCode::ArgEmpty, | ||
| 142 | + "controllerDeviceName not found in remote_auth_message") | ||
| 143 | + })?; | ||
| 144 | + | ||
| 145 | + Ok((controlled_device_name, controller_device_name)) | ||
| 146 | + } | ||
| 147 | + | ||
| 148 | + fn parse_permission_names(remote_auth_message: &str) -> Result<Vec<String>> { | ||
| 149 | + let json = JsonValue::from_text(remote_auth_message).map_err(|e| { | ||
| 150 | + loge!("Failed to parse JSON: {:?}", e); | ||
| 151 | + macros_lib::log_and_into_saf_error!(ErrCode::JsonParseError, | ||
| 152 | + "parse remote_auth_message failed: {}", e) | ||
| 153 | + })?; | ||
| 154 | + | ||
| 155 | + match &json["authResults"] { | ||
| 156 | + JsonValue::Null => { | ||
| 157 | + loge!("authResults not found or is null"); | ||
| 158 | + macros_lib::log_throw_error!(ErrCode::ArgEmpty, | ||
| 159 | + "authResults not found in remote_auth_message") | ||
| 160 | + } | ||
| 161 | + JsonValue::Array(arr) => { | ||
| 162 | + let permissions: Vec<String> = arr.iter() | ||
| 163 | + .filter_map(|item| { | ||
| 164 | + match (&item["permission"], &item["authResult"]) { | ||
| 165 | + (JsonValue::String(perm), JsonValue::String(result)) | ||
| 166 | + if result.to_uppercase() == "GRANTED" => Some(perm.clone()), | ||
| 167 | + _ => None, | ||
| 168 | + } | ||
| 169 | + }) | ||
| 170 | + .collect(); | ||
| 171 | + Ok(permissions) | ||
| 172 | + } | ||
| 173 | + _ => { | ||
| 174 | + loge!("authResults is not an array"); | ||
| 175 | + macros_lib::log_throw_error!(ErrCode::DataTypeMismatch, | ||
| 176 | + "authResults is not an array") | ||
| 177 | + } | ||
| 178 | + } | ||
| 179 | + } | ||
| 180 | +} | ||
| 181 | + | ||
| 182 | + | ||
| 183 | +pub fn store_grant_record(params: StoreGrantRecordParams) -> Result<()> { | ||
| 184 | + logi!("[store_grant_record] os_account_id={}, device_role={:?}, grant_type={:?}", | ||
| 185 | + params.os_account_id, params.device_role, params.grant_type); | ||
| 186 | + | ||
| 187 | + let loader = SAFPlugin::get_instance().load_plugin().map_err(|e| { | ||
| 188 | + macros_lib::log_and_into_saf_error!(ErrCode::PluginNotSupport, "load plugin failed: {}", e) | ||
| 189 | + })?; | ||
| 190 | + | ||
| 191 | + let mut ext_map: ExtMap = HashMap::new(); | ||
| 192 | + let os_account_id_u32 = u32::try_from(params.os_account_id).map_err(|_| { | ||
| 193 | + macros_lib::log_and_into_saf_error!(ErrCode::InvalidOsAccountId, | ||
| 194 | + "os_account_id is negative: {}", params.os_account_id) | ||
| 195 | + })?; | ||
| 196 | + | ||
| 197 | + ext_map.insert(STORE_GRANT_RECORD_KEYS.os_account_id, Value::Number(os_account_id_u32)); | ||
| 198 | + ext_map.insert(STORE_GRANT_RECORD_KEYS.controlled_device_name, Value::String(params.controlled_device_name)); | ||
| 199 | + ext_map.insert(STORE_GRANT_RECORD_KEYS.controller_device_name, Value::String(params.controller_device_name)); | ||
| 200 | + ext_map.insert(STORE_GRANT_RECORD_KEYS.is_self_grant, Value::Bool(params.is_self_grant)); | ||
| 201 | + ext_map.insert(STORE_GRANT_RECORD_KEYS.permission_names, Value::StringList(params.permission_names)); | ||
| 202 | + ext_map.insert(STORE_GRANT_RECORD_KEYS.device_role, Value::Number(params.device_role as u32)); | ||
| 203 | + ext_map.insert(STORE_GRANT_RECORD_KEYS.calling_bundle_name, Value::String(params.calling_bundle_name)); | ||
| 204 | + ext_map.insert(STORE_GRANT_RECORD_KEYS.grant_type, Value::Number(params.grant_type as u32)); | ||
| 205 | + ext_map.insert(STORE_GRANT_RECORD_KEYS.timestamp, Value::String(params.timestamp.to_string())); | ||
| 206 | + | ||
| 207 | + let _ = loader.process_event(EventType::StoreGrantRecord, &mut ext_map).map_err(|e| { | ||
| 208 | + macros_lib::log_and_into_saf_error!( | ||
| 209 | + ErrCode::try_from(e).unwrap_or(ErrCode::GeneralError), | ||
| 210 | + "store_grant_record process_event failed") | ||
| 211 | + })?; | ||
| 212 | + | ||
| 213 | + logi!("[store_grant_record] success"); | ||
| 214 | + Ok(()) | ||
| 215 | +} | ||
| 216 | + | ||
| 217 | +// ======================== SAFTest mock implementations ======================== | ||
| 218 | + | ||
| 219 | + | ||
| 220 | +use std::sync::atomic::{AtomicBool, Ordering}; | ||
| 221 | + | ||
| 222 | +use lazy_static::lazy_static; | ||
| 223 | + | ||
| 224 | + | ||
| 225 | +lazy_static! { | ||
| 226 | + static ref MOCK_BUNDLE_NAME: String = "mock_bundle_name".to_string(); | ||
| 227 | +} | ||
| 228 | + | ||
| 229 | + | ||
| 230 | +fn get_bundle_name_from_token(_caller_token_id: i32) -> Result<String> { | ||
| 231 | + Ok(MOCK_BUNDLE_NAME.clone()) | ||
| 232 | +} | ||
| 233 | + | ||
| 234 | + | ||
| 235 | +pub fn store_grant_record(_params: StoreGrantRecordParams) -> Result<()> { | ||
| 236 | + logi!("[store_grant_record] mock implementation for test"); | ||
| 237 | + Ok(()) | ||
| 238 | +} | ||
| @@ -0,0 +1,305 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | +//! This module implements remote control functionality for GenerateControlledDevicePackage. | ||
| 17 | + | ||
| 18 | +mod cli_manager; | ||
| 19 | +mod grant_record; | ||
| 20 | +mod remote_challenge_manager; | ||
| 21 | +mod account_based_auth_manager; | ||
| 22 | + | ||
| 23 | + | ||
| 24 | +mod controlled_device; | ||
| 25 | + | ||
| 26 | + | ||
| 27 | +mod controller_device; | ||
| 28 | + | ||
| 29 | +use saf_common::{JsonBuilder, new_object, object_add_number, object_add_string}; | ||
| 30 | +use saf_definition::{macros_lib, ErrCode, Result, RemoteAuthPackage, RemoteMessage, DeviceIdHeader, OperationType, PermissionQuery}; | ||
| 31 | +use saf_log::logi; | ||
| 32 | +use saf_utils::{JsonValue, get_compact_json_value}; | ||
| 33 | + | ||
| 34 | +/// Batch generate result for device packages. | ||
| 35 | +pub struct BatchGenerateResult { | ||
| 36 | + /// Generated packages (same count as input queries, failures use empty objects). | ||
| 37 | + pub packages: Vec<RemoteAuthPackage>, | ||
| 38 | + /// Error code (0 for full success, non-zero for partial or full failure). | ||
| 39 | + pub error_code: i32, | ||
| 40 | +} | ||
| 41 | + | ||
| 42 | +/// Batch verify result for device packages. | ||
| 43 | +pub struct BatchVerifyResult { | ||
| 44 | + /// Verification results (same count as input packages). | ||
| 45 | + pub results: Vec<bool>, | ||
| 46 | + /// System error code (0 for no system error, non-zero for system error encountered). | ||
| 47 | + pub error_code: i32, | ||
| 48 | +} | ||
| 49 | + | ||
| 50 | +const MAX_REMOTE_BATCH_COUNT: usize = 10; | ||
| 51 | +const MAX_REMOTE_PERMISSION_COUNT: usize = 40; | ||
| 52 | + | ||
| 53 | +fn log_remote_auth_package(package: &RemoteAuthPackage) { | ||
| 54 | + let remote_message = &package.remote_message; | ||
| 55 | + let device_info = &remote_message.device_info; | ||
| 56 | + logi!( | ||
| 57 | + "RemoteAuthPackage: ticket_len={}, \ | ||
| 58 | + controller_device_id_len={}, controlled_device_id_len={}, \ | ||
| 59 | + remote_auth_message_len={}", | ||
| 60 | + package.ticket.len(), | ||
| 61 | + device_info.controller_device_id.len(), | ||
| 62 | + device_info.controlled_device_id.len(), | ||
| 63 | + remote_message.remote_auth_message.len() | ||
| 64 | + ); | ||
| 65 | +} | ||
| 66 | + | ||
| 67 | +fn create_empty_package() -> RemoteAuthPackage { | ||
| 68 | + RemoteAuthPackage { | ||
| 69 | + remote_message: RemoteMessage { | ||
| 70 | + device_info: DeviceIdHeader { | ||
| 71 | + controller_device_id: String::new(), | ||
| 72 | + controlled_device_id: String::new(), | ||
| 73 | + }, | ||
| 74 | + remote_auth_message: String::new(), | ||
| 75 | + caller_bundle_name: String::new(), | ||
| 76 | + }, | ||
| 77 | + challenge: String::new(), | ||
| 78 | + ticket: String::new(), | ||
| 79 | + } | ||
| 80 | +} | ||
| 81 | + | ||
| 82 | +fn parse_ticket_expire_time(remote_auth_message: &str) -> Result<i32> { | ||
| 83 | + let json = JsonValue::from_text(remote_auth_message).map_err(|e| { | ||
| 84 | + macros_lib::log_and_into_saf_error!(ErrCode::JsonParseError, | ||
| 85 | + "parse remote_auth_message failed: {}", e) | ||
| 86 | + })?; | ||
| 87 | + | ||
| 88 | + let expire_time_str = get_compact_json_value(&json, "ticketExpireTimeMs") | ||
| 89 | + .map(|s| s.trim_matches('"').to_string()) | ||
| 90 | + .unwrap_or_default(); | ||
| 91 | + | ||
| 92 | + expire_time_str.parse::<i32>().map_err(|e| { | ||
| 93 | + macros_lib::log_and_into_saf_error!(ErrCode::JsonParseError, | ||
| 94 | + "parse ticketExpireTimeMs failed: {}", e) | ||
| 95 | + }) | ||
| 96 | +} | ||
| 97 | + | ||
| 98 | +fn parse_challenge_from_remote_auth_message(remote_auth_message: &str) -> Result<String> { | ||
| 99 | + let json = JsonValue::from_text(remote_auth_message).map_err(|e| { | ||
| 100 | + macros_lib::log_and_into_saf_error!(ErrCode::JsonParseError, | ||
| 101 | + "parse remote_auth_message failed: {}", e) | ||
| 102 | + })?; | ||
| 103 | + | ||
| 104 | + let challenge = get_compact_json_value(&json, "challenge") | ||
| 105 | + .map(|s| s.trim_matches('"').to_string()) | ||
| 106 | + .unwrap_or_default(); | ||
| 107 | + | ||
| 108 | + if challenge.is_empty() { | ||
| 109 | + return macros_lib::log_throw_error!(ErrCode::ArgEmpty, "challenge is empty"); | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + Ok(challenge) | ||
| 113 | +} | ||
| 114 | + | ||
| 115 | +fn validate_challenge_consistency(package: &RemoteAuthPackage) -> Result<String> { | ||
| 116 | + let inner_challenge = parse_challenge_from_remote_auth_message(&package.remote_message.remote_auth_message)?; | ||
| 117 | + | ||
| 118 | + if inner_challenge != package.challenge { | ||
| 119 | + return macros_lib::log_throw_error!(ErrCode::InvalidArgument, "challenge mismatch!"); | ||
| 120 | + } | ||
| 121 | + Ok(inner_challenge) | ||
| 122 | +} | ||
| 123 | + | ||
| 124 | +fn parse_caller_bundle_name_from_remote_auth_message(remote_auth_message: &str) -> Result<String> { | ||
| 125 | + let json = JsonValue::from_text(remote_auth_message).map_err(|e| { | ||
| 126 | + macros_lib::log_and_into_saf_error!(ErrCode::JsonParseError, | ||
| 127 | + "parse remote_auth_message failed: {}", e) | ||
| 128 | + })?; | ||
| 129 | + | ||
| 130 | + let bundle_name = get_compact_json_value(&json, "callerBundleName") | ||
| 131 | + .map(|s| s.trim_matches('"').to_string()) | ||
| 132 | + .unwrap_or_default(); | ||
| 133 | + | ||
| 134 | + Ok(bundle_name) | ||
| 135 | +} | ||
| 136 | + | ||
| 137 | +fn parse_local_device_id_from_remote_auth_message(remote_auth_message: &str) -> Result<String> { | ||
| 138 | + let json = JsonValue::from_text(remote_auth_message).map_err(|e| { | ||
| 139 | + macros_lib::log_and_into_saf_error!(ErrCode::JsonParseError, | ||
| 140 | + "parse remote_auth_message failed: {}", e) | ||
| 141 | + })?; | ||
| 142 | + | ||
| 143 | + let local_device_id = get_compact_json_value(&json, "localDeviceId") | ||
| 144 | + .map(|s| s.trim_matches('"').to_string()) | ||
| 145 | + .unwrap_or_default(); | ||
| 146 | + | ||
| 147 | + Ok(local_device_id) | ||
| 148 | +} | ||
| 149 | + | ||
| 150 | +const ALLOWED_REMOTE_AUTH_MESSAGE_FIELDS: &[&str] = &[ | ||
| 151 | + "operationInfo", | ||
| 152 | + "role", | ||
| 153 | + "ticketExpireTimeMs", | ||
| 154 | + "domainId", | ||
| 155 | + "controlledDeviceName", | ||
| 156 | + "controllerDeviceName", | ||
| 157 | + "signVerifyMsg", | ||
| 158 | + "callerTokenId", | ||
| 159 | + "challenge", | ||
| 160 | + "timestamp", | ||
| 161 | + "permissions", | ||
| 162 | + "authResults", | ||
| 163 | + "localDeviceId", | ||
| 164 | + "callerBundleName", | ||
| 165 | + "version" | ||
| 166 | +]; | ||
| 167 | + | ||
| 168 | +fn validate_remote_auth_message_fields(remote_auth_message: &str) -> Result<()> { | ||
| 169 | + let json = JsonValue::from_text(remote_auth_message).map_err(|e| { | ||
| 170 | + macros_lib::log_and_into_saf_error!(ErrCode::JsonParseError, | ||
| 171 | + "parse remote_auth_message failed: {}", e) | ||
| 172 | + })?; | ||
| 173 | + | ||
| 174 | + let obj = match json { | ||
| 175 | + JsonValue::Object(ref obj) => obj, | ||
| 176 | + _ => return macros_lib::log_throw_error!(ErrCode::DataTypeMismatch, | ||
| 177 | + "remote_auth_message is not a JSON object"), | ||
| 178 | + }; | ||
| 179 | + | ||
| 180 | + let allowed_set: std::collections::HashSet<&str> = ALLOWED_REMOTE_AUTH_MESSAGE_FIELDS.iter().copied().collect(); | ||
| 181 | + | ||
| 182 | + for (key, _) in obj.iter() { | ||
| 183 | + if !allowed_set.contains(key.as_str()) { | ||
| 184 | + return macros_lib::log_throw_error!(ErrCode::InvalidArgument, | ||
| 185 | + "Unexpected field '{}' in remote_auth_message - potential MITM attack detected", key); | ||
| 186 | + } | ||
| 187 | + } | ||
| 188 | + | ||
| 189 | + Ok(()) | ||
| 190 | +} | ||
| 191 | + | ||
| 192 | +fn parse_timestamp(remote_auth_message: &str) -> Result<u64> { | ||
| 193 | + let json = JsonValue::from_text(remote_auth_message).map_err(|e| { | ||
| 194 | + macros_lib::log_and_into_saf_error!(ErrCode::JsonParseError, | ||
| 195 | + "parse remote_auth_message failed: {}", e) | ||
| 196 | + })?; | ||
| 197 | + | ||
| 198 | + let ts_str = get_compact_json_value(&json, "timestamp") | ||
| 199 | + .map(|s| s.trim_matches('"').to_string()) | ||
| 200 | + .unwrap_or_default(); | ||
| 201 | + | ||
| 202 | + ts_str.parse::<u64>().map_err(|e| { | ||
| 203 | + macros_lib::log_and_into_saf_error!(ErrCode::JsonParseError, | ||
| 204 | + "parse timestamp failed: {}", e) | ||
| 205 | + }) | ||
| 206 | +} | ||
| 207 | + | ||
| 208 | +/// Maximum ticket expire time for remote control scenario: 1 day | ||
| 209 | +pub const MAX_REMOTE_TICKET_EXPIRE_TIME_MS: i32 = 24 * 60 * 60 * 1000; | ||
| 210 | +fn validate_ticket_expiration(timestamp: u64, expire_time_ms: i32, current_time: u64) -> bool { | ||
| 211 | + if expire_time_ms <= 0 || expire_time_ms > MAX_REMOTE_TICKET_EXPIRE_TIME_MS { | ||
| 212 | + return false; | ||
| 213 | + } | ||
| 214 | + match timestamp.checked_add(expire_time_ms as u64) { | ||
| 215 | + Some(expire_time) => expire_time >= current_time, | ||
| 216 | + None => false, | ||
| 217 | + } | ||
| 218 | +} | ||
| 219 | + | ||
| 220 | +const REMOTE_CHALLENGE_SIZE: u32 = 16; | ||
| 221 | + | ||
| 222 | +fn generate_crypto_random_challenge() -> Result<String> { | ||
| 223 | + let mut buf = vec![0u8; REMOTE_CHALLENGE_SIZE as usize]; | ||
| 224 | + crate::ticket_operation::generate_random_bytes(&mut buf, REMOTE_CHALLENGE_SIZE)?; | ||
| 225 | + let encoded = crate::ticket_operation::base64_encode(&buf)?; | ||
| 226 | + Ok(String::from_utf8_lossy(&encoded).to_string()) | ||
| 227 | +} | ||
| 228 | +fn parse_cli_and_permission( | ||
| 229 | + operation_info: &[saf_definition::OperationInfo] | ||
| 230 | +) -> Result<(Vec<saf_definition::CommandInfo>, Vec<String>)> { | ||
| 231 | + let mut cli_infos = Vec::with_capacity(operation_info.len()); | ||
| 232 | + let mut api_permissions = Vec::with_capacity(operation_info.len()); | ||
| 233 | + | ||
| 234 | + for op in operation_info { | ||
| 235 | + match op.operation_type { | ||
| 236 | + saf_definition::OperationType::Cli => { | ||
| 237 | + if op.cli_cmd_info.cmd_name.is_empty() { | ||
| 238 | + return macros_lib::log_throw_error!(ErrCode::ArgEmpty, "CLI command name is empty"); | ||
| 239 | + } | ||
| 240 | + cli_infos.push(op.cli_cmd_info.clone()); | ||
| 241 | + }, | ||
| 242 | + saf_definition::OperationType::Api => { | ||
| 243 | + if op.permission.is_empty() { | ||
| 244 | + return macros_lib::log_throw_error!(ErrCode::ArgEmpty, "API permission is empty"); | ||
| 245 | + } | ||
| 246 | + api_permissions.push(op.permission.clone()); | ||
| 247 | + }, | ||
| 248 | + } | ||
| 249 | + } | ||
| 250 | + Ok((cli_infos, api_permissions)) | ||
| 251 | +} | ||
| 252 | + | ||
| 253 | +fn serialize_permission_query_to_message(builder: &mut JsonBuilder, query: &PermissionQuery) { | ||
| 254 | + let mut op_array = Vec::with_capacity(query.operation_info.len()); | ||
| 255 | + for op in &query.operation_info { | ||
| 256 | + let mut op_obj = new_object(); | ||
| 257 | + object_add_number(&mut op_obj, "operationType", op.operation_type as i64); | ||
| 258 | + if op.operation_type == OperationType::Cli { | ||
| 259 | + let mut cli_obj = new_object(); | ||
| 260 | + object_add_string(&mut cli_obj, "cliCmdName", &op.cli_cmd_info.cmd_name); | ||
| 261 | + object_add_string(&mut cli_obj, "subCliCmdName", &op.cli_cmd_info.sub_cmd); | ||
| 262 | + op_obj.insert("info".to_string(), JsonValue::Object(cli_obj)); | ||
| 263 | + } else { | ||
| 264 | + op_obj.insert("info".to_string(), JsonValue::String(op.permission.clone())); | ||
| 265 | + } | ||
| 266 | + op_array.push(op_obj); | ||
| 267 | + } | ||
| 268 | + builder.add_object_array("operationInfo", op_array); | ||
| 269 | + | ||
| 270 | + builder.add_number("role", query.remote_info.role as i64); | ||
| 271 | + builder.add_number("ticketExpireTimeMs", query.ticket_expire_time_ms as i64); | ||
| 272 | + builder.add_string("domainId", &query.remote_info.domain_id); | ||
| 273 | + builder.add_string("controlledDeviceName", &query.remote_info.remote_control_params.controlled_device_name); | ||
| 274 | + builder.add_string("controllerDeviceName", &query.remote_info.remote_control_params.controller_device_name); | ||
| 275 | + builder.add_string("signVerifyMsg", &query.remote_info.remote_control_params.sign_verify_msg); | ||
| 276 | + builder.add_number("callerTokenId", query.caller_token_id as i64); | ||
| 277 | +} | ||
| 278 | + | ||
| 279 | +pub use controlled_device::{ | ||
| 280 | + generate_controlled_device_package, | ||
| 281 | + verify_controlled_device_package, | ||
| 282 | +}; | ||
| 283 | + | ||
| 284 | +pub use controller_device::{ | ||
| 285 | + generate_controller_device_package, | ||
| 286 | + verify_controller_device_package, | ||
| 287 | +}; | ||
| 288 | + | ||
| 289 | +pub use grant_record::{store_grant_record, StoreGrantRecordParams}; | ||
| 290 | + | ||
| 291 | +pub use cli_manager::batch_query_cli_permission; | ||
| 292 | + | ||
| 293 | +// ======================== SAFTest module declarations ======================== | ||
| 294 | + | ||
| 295 | + | ||
| 296 | +pub mod controlled_device; | ||
| 297 | + | ||
| 298 | + | ||
| 299 | +pub mod controller_device; | ||
| 300 | + | ||
| 301 | + | ||
| 302 | +pub use controlled_device::ut_controlled_device_stub; | ||
| 303 | + | ||
| 304 | + | ||
| 305 | +pub use controller_device::ut_controller_device_stub; | ||
| @@ -0,0 +1,329 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | +//! Challenge cache for remote control verification. | ||
| 17 | + | ||
| 18 | + | ||
| 19 | +use std::fs::{self, File, OpenOptions}; | ||
| 20 | + | ||
| 21 | +use std::io::{BufRead, BufReader, Write}; | ||
| 22 | + | ||
| 23 | +use std::os::unix::fs::{OpenOptionsExt, PermissionsExt}; | ||
| 24 | + | ||
| 25 | +use std::path::Path; | ||
| 26 | + | ||
| 27 | +use std::sync::Mutex; | ||
| 28 | + | ||
| 29 | +use saf_definition::{macros_lib, ErrCode, Result, DeviceIdHeader}; | ||
| 30 | + | ||
| 31 | +use saf_log::{loge, logi}; | ||
| 32 | + | ||
| 33 | +use saf_utils::{JsonValue, get_compact_json_value}; | ||
| 34 | + | ||
| 35 | +use saf_common::JsonBuilder; | ||
| 36 | + | ||
| 37 | + | ||
| 38 | +static CHALLENGE_CACHE_LOCK: Mutex<()> = Mutex::new(()); | ||
| 39 | + | ||
| 40 | + | ||
| 41 | +const CACHE_DIR_PREFIX: &str = "/data/service/el2"; | ||
| 42 | + | ||
| 43 | +const CACHE_DIR_SUFFIX: &str = "secure_access_fence/agent_plugin"; | ||
| 44 | + | ||
| 45 | +const CACHE_FILE_NAME: &str = "challenge_cache_list.txt"; | ||
| 46 | + | ||
| 47 | +const FILE_MODE: u32 = 0o640; | ||
| 48 | + | ||
| 49 | + | ||
| 50 | + | ||
| 51 | +struct ChallengeCacheEntry { | ||
| 52 | + timestamp: u64, | ||
| 53 | + controller_device_id: String, | ||
| 54 | + controlled_device_id: String, | ||
| 55 | +} | ||
| 56 | + | ||
| 57 | + | ||
| 58 | +fn serialize_cache_entry(entry: &ChallengeCacheEntry) -> Result<String> { | ||
| 59 | + let mut builder = JsonBuilder::new(); | ||
| 60 | + builder.add_u64("timestamp", entry.timestamp); | ||
| 61 | + builder.add_string("controllerDeviceId", &entry.controller_device_id); | ||
| 62 | + builder.add_string("controlledDeviceId", &entry.controlled_device_id); | ||
| 63 | + builder.build() | ||
| 64 | +} | ||
| 65 | + | ||
| 66 | + | ||
| 67 | +fn deserialize_cache_entry(json: &str) -> Result<ChallengeCacheEntry> { | ||
| 68 | + let parsed = JsonValue::from_text(json).map_err(|e| { | ||
| 69 | + macros_lib::log_and_into_saf_error!(ErrCode::JsonParseError, | ||
| 70 | + "Failed to parse cache entry JSON: {}", e) | ||
| 71 | + })?; | ||
| 72 | + let timestamp_str = get_compact_json_value(&parsed, "timestamp") | ||
| 73 | + .map(|s| s.trim_matches('"').to_string()) | ||
| 74 | + .unwrap_or_default(); | ||
| 75 | + let timestamp = timestamp_str.parse::<u64>().map_err(|e| { | ||
| 76 | + macros_lib::log_and_into_saf_error!(ErrCode::JsonParseError, | ||
| 77 | + "Failed to parse timestamp: {}", e) | ||
| 78 | + })?; | ||
| 79 | + let controller_device_id = get_compact_json_value(&parsed, "controllerDeviceId") | ||
| 80 | + .map(|s| s.trim_matches('"').to_string()) | ||
| 81 | + .unwrap_or_default(); | ||
| 82 | + let controlled_device_id = get_compact_json_value(&parsed, "controlledDeviceId") | ||
| 83 | + .map(|s| s.trim_matches('"').to_string()) | ||
| 84 | + .unwrap_or_default(); | ||
| 85 | + Ok(ChallengeCacheEntry { timestamp, controller_device_id, controlled_device_id }) | ||
| 86 | +} | ||
| 87 | + | ||
| 88 | + | ||
| 89 | +fn get_cache_file_path(os_account_id: i32) -> String { | ||
| 90 | + format!("{}/{}/{}", CACHE_DIR_PREFIX, os_account_id, CACHE_DIR_SUFFIX) | ||
| 91 | +} | ||
| 92 | + | ||
| 93 | + | ||
| 94 | +fn get_cache_file_full_path(os_account_id: i32) -> String { | ||
| 95 | + format!("{}/{}", get_cache_file_path(os_account_id), CACHE_FILE_NAME) | ||
| 96 | +} | ||
| 97 | + | ||
| 98 | + | ||
| 99 | +fn ensure_cache_dir_exists(os_account_id: i32) -> Result<()> { | ||
| 100 | + let dir_path = get_cache_file_path(os_account_id); | ||
| 101 | + let path = Path::new(&dir_path); | ||
| 102 | + | ||
| 103 | + if !path.exists() { | ||
| 104 | + fs::create_dir_all(path).map_err(|e| { | ||
| 105 | + macros_lib::log_and_into_saf_error!(ErrCode::FileOperationError, | ||
| 106 | + "Failed to create cache dir {}: {}", dir_path, e) | ||
| 107 | + })?; | ||
| 108 | + | ||
| 109 | + fs::set_permissions(path, fs::Permissions::from_mode(0o750)).map_err(|e| { | ||
| 110 | + macros_lib::log_and_into_saf_error!(ErrCode::FileOperationError, | ||
| 111 | + "Failed to set dir permissions {}: {}", dir_path, e) | ||
| 112 | + })?; | ||
| 113 | + } | ||
| 114 | + Ok(()) | ||
| 115 | +} | ||
| 116 | + | ||
| 117 | + | ||
| 118 | +pub fn cache_challenge(os_account_id: i32, challenge: &str, timestamp: u64, device_id_header: &DeviceIdHeader) | ||
| 119 | + -> Result<()> { | ||
| 120 | + let _lock = CHALLENGE_CACHE_LOCK.lock().unwrap_or_else(|e| e.into_inner()); | ||
| 121 | + | ||
| 122 | + ensure_cache_dir_exists(os_account_id)?; | ||
| 123 | + | ||
| 124 | + let entry = ChallengeCacheEntry { | ||
| 125 | + timestamp, | ||
| 126 | + controller_device_id: device_id_header.controller_device_id.clone(), | ||
| 127 | + controlled_device_id: device_id_header.controlled_device_id.clone(), | ||
| 128 | + }; | ||
| 129 | + let entry_json = serialize_cache_entry(&entry)?; | ||
| 130 | + let line = format!("{}|{}\n", challenge, entry_json); | ||
| 131 | + | ||
| 132 | + let file_path = get_cache_file_full_path(os_account_id); | ||
| 133 | + let path = Path::new(&file_path); | ||
| 134 | + | ||
| 135 | + let mut file = OpenOptions::new() | ||
| 136 | + .write(true) | ||
| 137 | + .create(true) | ||
| 138 | + .append(true) | ||
| 139 | + .mode(FILE_MODE) | ||
| 140 | + .open(path) | ||
| 141 | + .map_err(|e| { | ||
| 142 | + macros_lib::log_and_into_saf_error!(ErrCode::FileOperationError, | ||
| 143 | + "Failed to open cache file {}: {}", file_path, e) | ||
| 144 | + })?; | ||
| 145 | + | ||
| 146 | + file.write_all(line.as_bytes()).map_err(|e| { | ||
| 147 | + macros_lib::log_and_into_saf_error!(ErrCode::FileOperationError, | ||
| 148 | + "Failed to write cache file {}: {}", file_path, e) | ||
| 149 | + })?; | ||
| 150 | + | ||
| 151 | + logi!("[challenge_cache] Cached challenge for os_account_id={}", os_account_id); | ||
| 152 | + Ok(()) | ||
| 153 | +} | ||
| 154 | + | ||
| 155 | + | ||
| 156 | +pub fn verify_and_remove_challenge( | ||
| 157 | + os_account_id: i32, | ||
| 158 | + challenge: &str, | ||
| 159 | + device_id_header: &DeviceIdHeader | ||
| 160 | +) -> Result<bool> { | ||
| 161 | + let _lock = CHALLENGE_CACHE_LOCK.lock().unwrap_or_else(|e| e.into_inner()); | ||
| 162 | + | ||
| 163 | + let file_path = get_cache_file_full_path(os_account_id); | ||
| 164 | + let path = Path::new(&file_path); | ||
| 165 | + | ||
| 166 | + if !path.exists() { | ||
| 167 | + loge!("[challenge_cache] Cache file not found"); | ||
| 168 | + return macros_lib::log_throw_error!(ErrCode::ReplayAttackDetected, | ||
| 169 | + "Cache file not found"); | ||
| 170 | + } | ||
| 171 | + | ||
| 172 | + let (found, remaining_lines) = find_and_remove_challenge_in_file( | ||
| 173 | + &file_path, challenge, | ||
| 174 | + &device_id_header.controller_device_id, | ||
| 175 | + &device_id_header.controlled_device_id, | ||
| 176 | + )?; | ||
| 177 | + | ||
| 178 | + if found { | ||
| 179 | + rewrite_cache_file(&file_path, &remaining_lines)?; | ||
| 180 | + logi!("[challenge_cache] Verified and removed challenge for os_account_id={}", os_account_id); | ||
| 181 | + } else { | ||
| 182 | + loge!("[challenge_cache] Challenge not found: {}", challenge); | ||
| 183 | + return macros_lib::log_throw_error!(ErrCode::ReplayAttackDetected, | ||
| 184 | + "Challenge not found: {}", challenge); | ||
| 185 | + } | ||
| 186 | + | ||
| 187 | + Ok(found) | ||
| 188 | +} | ||
| 189 | + | ||
| 190 | + | ||
| 191 | +fn find_and_remove_challenge_in_file( | ||
| 192 | + file_path: &str, | ||
| 193 | + challenge: &str, | ||
| 194 | + expected_controller_id: &str, | ||
| 195 | + expected_controlled_id: &str, | ||
| 196 | +) -> Result<(bool, Vec<String>)> { | ||
| 197 | + let file = File::open(file_path).map_err(|e| { | ||
| 198 | + macros_lib::log_and_into_saf_error!(ErrCode::FileOperationError, | ||
| 199 | + "Failed to open cache file {}: {}", file_path, e) | ||
| 200 | + })?; | ||
| 201 | + | ||
| 202 | + let reader = BufReader::new(file); | ||
| 203 | + let mut found = false; | ||
| 204 | + let mut remaining_lines = Vec::new(); | ||
| 205 | + | ||
| 206 | + for line in reader.lines() { | ||
| 207 | + let line = line.map_err(|e| { | ||
| 208 | + macros_lib::log_and_into_saf_error!(ErrCode::FileOperationError, | ||
| 209 | + "Failed to read cache file: {}", e) | ||
| 210 | + })?; | ||
| 211 | + | ||
| 212 | + if line.is_empty() { | ||
| 213 | + continue; | ||
| 214 | + } | ||
| 215 | + | ||
| 216 | + if let Some((cached_challenge, cached_entry_json)) = line.split_once('|') { | ||
| 217 | + if cached_challenge == challenge { | ||
| 218 | + if let Ok(entry) = deserialize_cache_entry(cached_entry_json) { | ||
| 219 | + if entry.controller_device_id == expected_controller_id | ||
| 220 | + && entry.controlled_device_id == expected_controlled_id { | ||
| 221 | + found = true; | ||
| 222 | + continue; | ||
| 223 | + } else { | ||
| 224 | + loge!("[challenge_cache] DeviceIdHeader mismatch for challenge"); | ||
| 225 | + return macros_lib::log_throw_error!(ErrCode::ReplayAttackDetected, | ||
| 226 | + "DeviceIdHeader mismatch for challenge"); | ||
| 227 | + } | ||
| 228 | + } else { | ||
| 229 | + loge!("[challenge_cache] DeviceIdHeader mismatch for challenge"); | ||
| 230 | + return macros_lib::log_throw_error!(ErrCode::ReplayAttackDetected, | ||
| 231 | + "DeviceIdHeader mismatch for challenge"); | ||
| 232 | + } | ||
| 233 | + } | ||
| 234 | + } | ||
| 235 | + remaining_lines.push(line); | ||
| 236 | + } | ||
| 237 | + | ||
| 238 | + Ok((found, remaining_lines)) | ||
| 239 | +} | ||
| 240 | + | ||
| 241 | + | ||
| 242 | +fn rewrite_cache_file(file_path: &str, lines: &[String]) -> Result<()> { | ||
| 243 | + let path = Path::new(file_path); | ||
| 244 | + let tmp_path = format!("{}.tmp", file_path); | ||
| 245 | + let tmp_path_ref = Path::new(&tmp_path); | ||
| 246 | + let mut file = OpenOptions::new() | ||
| 247 | + .write(true) | ||
| 248 | + .create(true) | ||
| 249 | + .truncate(true) | ||
| 250 | + .mode(FILE_MODE) | ||
| 251 | + .open(tmp_path_ref) | ||
| 252 | + .map_err(|e| { | ||
| 253 | + macros_lib::log_and_into_saf_error!(ErrCode::FileOperationError, | ||
| 254 | + "Failed to open tmp cache file for writing {}: {}", file_path, e) | ||
| 255 | + })?; | ||
| 256 | + | ||
| 257 | + for line in lines { | ||
| 258 | + writeln!(file, "{}", line).map_err(|e| { | ||
| 259 | + let _ = fs::remove_file(tmp_path_ref); | ||
| 260 | + macros_lib::log_and_into_saf_error!(ErrCode::FileOperationError, | ||
| 261 | + "Failed to write tmp cache file: {}", e) | ||
| 262 | + })?; | ||
| 263 | + } | ||
| 264 | + | ||
| 265 | + file.sync_all().map_err(|e| { | ||
| 266 | + let _ = fs::remove_file(tmp_path_ref); | ||
| 267 | + macros_lib::log_and_into_saf_error!(ErrCode::FileOperationError, | ||
| 268 | + "Failed to sync tmp cache file: {}", e) | ||
| 269 | + })?; | ||
| 270 | + | ||
| 271 | + fs::rename(tmp_path_ref, path).map_err(|e| { | ||
| 272 | + let _ = fs::remove_file(tmp_path_ref); | ||
| 273 | + macros_lib::log_and_into_saf_error!(ErrCode::FileOperationError, | ||
| 274 | + "Failed to rename tmp cache file: {}", e) | ||
| 275 | + })?; | ||
| 276 | + | ||
| 277 | + Ok(()) | ||
| 278 | +} | ||
| 279 | + | ||
| 280 | +// ======================== SAFTest mock implementations ======================== | ||
| 281 | + | ||
| 282 | + | ||
| 283 | +use lazy_static::lazy_static; | ||
| 284 | + | ||
| 285 | +use std::collections::HashMap; | ||
| 286 | + | ||
| 287 | +use std::sync::Mutex; | ||
| 288 | + | ||
| 289 | + | ||
| 290 | +lazy_static! { | ||
| 291 | + static ref MOCK_CHALLENGE_CACHE: Mutex<HashMap<String, (u64, String, String)>> = Mutex::new(HashMap::new()); | ||
| 292 | +} | ||
| 293 | + | ||
| 294 | + | ||
| 295 | +pub fn cache_challenge(os_account_id: i32, challenge: &str, timestamp: u64, device_id_header: &DeviceIdHeader) | ||
| 296 | + -> Result<()> { | ||
| 297 | + let key = format!("{}:{}", os_account_id, challenge); | ||
| 298 | + let value = (timestamp, device_id_header.controller_device_id.clone(), | ||
| 299 | + device_id_header.controlled_device_id.clone()); | ||
| 300 | + MOCK_CHALLENGE_CACHE.lock().unwrap().insert(key, value); | ||
| 301 | + Ok(()) | ||
| 302 | +} | ||
| 303 | + | ||
| 304 | + | ||
| 305 | +pub fn verify_and_remove_challenge( | ||
| 306 | + os_account_id: i32, | ||
| 307 | + challenge: &str, | ||
| 308 | + device_id_header: &DeviceIdHeader, | ||
| 309 | +) -> Result<bool> { | ||
| 310 | + let key = format!("{}:{}", os_account_id, challenge); | ||
| 311 | + let mut cache = MOCK_CHALLENGE_CACHE.lock().unwrap(); | ||
| 312 | + match cache.remove(&key) { | ||
| 313 | + Some((_, cached_controller, cached_controlled)) => { | ||
| 314 | + if cached_controller == device_id_header.controller_device_id | ||
| 315 | + && cached_controlled == device_id_header.controlled_device_id { | ||
| 316 | + Ok(true) | ||
| 317 | + } else { | ||
| 318 | + return macros_lib::log_throw_error!(ErrCode::ReplayAttackDetected, | ||
| 319 | + "DeviceIdHeader mismatch for challenge"); | ||
| 320 | + } | ||
| 321 | + } | ||
| 322 | + None => Ok(true), | ||
| 323 | + } | ||
| 324 | +} | ||
| 325 | + | ||
| 326 | + | ||
| 327 | +pub fn reset_mock_challenge_cache() { | ||
| 328 | + MOCK_CHALLENGE_CACHE.lock().unwrap().clear(); | ||
| 329 | +} | ||
| @@ -17,20 +17,26 @@ | |||
| 17 | 17 | ||
| 18 | const DEFAULT_DOMAIN_ID: &str = ""; | 18 | const DEFAULT_DOMAIN_ID: &str = ""; |
| 19 | 19 | ||
| 20 | -use ipc::{parcel::MsgParcel, remote::RemoteStub, IpcResult, IpcStatusCode}; | 20 | +use ipc::{parcel::MsgParcel, remote::RemoteStub, IpcResult, IpcStatusCode, Skeleton}; |
| 21 | -use saf_common::{AutoCounter, Counter}; | 21 | +use saf_common::{AutoCounter, Counter, get_user_id}; |
| 22 | 22 | ||
| 23 | use saf_ipc::{ | 23 | use saf_ipc::{ |
| 24 | deserialize_batch_generate_ticket_request, deserialize_batch_verify_ticket_request, | 24 | deserialize_batch_generate_ticket_request, deserialize_batch_verify_ticket_request, |
| 25 | + deserialize_permission_queries, deserialize_remote_auth_packages, | ||
| 26 | + deserialize_remote_user_auth_results_vec, deserialize_remote_info, | ||
| 25 | deserialize_verify_ticket_request, | 27 | deserialize_verify_ticket_request, |
| 26 | - serialize_cli_infos, serialize_i32_vec, serialize_verify_ticket_infos, | 28 | + serialize_bool_vec, serialize_cli_infos, serialize_i32_vec, |
| 29 | + serialize_remote_auth_packages, serialize_verify_ticket_infos, | ||
| 27 | CMD_BATCH_GENERATE_TICKET, CMD_BATCH_VERIFY_TICKET, CMD_VERIFY_TICKET, | 30 | CMD_BATCH_GENERATE_TICKET, CMD_BATCH_VERIFY_TICKET, CMD_VERIFY_TICKET, |
| 31 | + CMD_GENERATE_CONTROLLED_DEVICE_PACKAGE, CMD_VERIFY_CONTROLLED_DEVICE_PACKAGE, | ||
| 32 | + CMD_GENERATE_CONTROLLER_DEVICE_PACKAGE, CMD_VERIFY_CONTROLLER_DEVICE_PACKAGE, | ||
| 28 | IPC_SUCCESS, SA_NAME, | 33 | IPC_SUCCESS, SA_NAME, |
| 29 | }; | 34 | }; |
| 30 | use saf_log::{loge, logi}; | 35 | use saf_log::{loge, logi}; |
| 31 | use saf_plugin::saf_plugin::SAFPlugin; | 36 | use saf_plugin::saf_plugin::SAFPlugin; |
| 32 | use saf_sdk::{ErrCode, Result, SAFError}; | 37 | use saf_sdk::{ErrCode, Result, SAFError}; |
| 33 | 38 | ||
| 39 | +use crate::remote_control; | ||
| 34 | use crate::wrapper; | 40 | use crate::wrapper; |
| 35 | use crate::SAFService; | 41 | use crate::SAFService; |
| 36 | 42 | ||
| @@ -87,16 +93,28 @@ fn on_remote_request(stub: &SAFService, code: u32, data: &mut MsgParcel, reply: | |||
| 87 | }, | 93 | }, |
| 88 | } | 94 | } |
| 89 | 95 | ||
| 90 | - match code { | 96 | + match code { |
| 91 | - CMD_BATCH_GENERATE_TICKET => { | 97 | + CMD_BATCH_GENERATE_TICKET => { |
| 92 | - handle_batch_generate_ticket(stub, data, reply) | 98 | + handle_batch_generate_ticket(stub, data, reply) |
| 93 | - }, | 99 | + }, |
| 94 | - CMD_BATCH_VERIFY_TICKET => { | 100 | + CMD_BATCH_VERIFY_TICKET => { |
| 95 | - handle_batch_verify_ticket(stub, data, reply) | 101 | + handle_batch_verify_ticket(stub, data, reply) |
| 96 | - }, | 102 | + }, |
| 97 | - CMD_VERIFY_TICKET => { | 103 | + CMD_VERIFY_TICKET => { |
| 98 | - handle_verify_ticket(stub, data, reply) | 104 | + handle_verify_ticket(stub, data, reply) |
| 99 | - }, | 105 | + }, |
| 106 | + CMD_GENERATE_CONTROLLED_DEVICE_PACKAGE => { | ||
| 107 | + handle_generate_controlled_device_package(data, reply) | ||
| 108 | + }, | ||
| 109 | + CMD_VERIFY_CONTROLLED_DEVICE_PACKAGE => { | ||
| 110 | + handle_verify_controlled_device_package(data, reply) | ||
| 111 | + }, | ||
| 112 | + CMD_GENERATE_CONTROLLER_DEVICE_PACKAGE => { | ||
| 113 | + handle_generate_controller_device_package(data, reply) | ||
| 114 | + }, | ||
| 115 | + CMD_VERIFY_CONTROLLER_DEVICE_PACKAGE => { | ||
| 116 | + handle_verify_controller_device_package(data, reply) | ||
| 117 | + }, | ||
| 100 | _ => { | 118 | _ => { |
| 101 | if code >= C_REDIRECT_START_CODE { | 119 | if code >= C_REDIRECT_START_CODE { |
| 102 | let res = wrapper::on_remote_request(code, data, reply); | 120 | let res = wrapper::on_remote_request(code, data, reply); |
| @@ -139,7 +157,7 @@ fn handle_batch_generate_ticket(stub: &SAFService, data: &mut MsgParcel, reply: | |||
| 139 | Err(e) => { | 157 | Err(e) => { |
| 140 | loge!("[FATAL]BatchGenerateTicket failed: {}", e.msg); | 158 | loge!("[FATAL]BatchGenerateTicket failed: {}", e.msg); |
| 141 | reply.write::<i32>(&(IPC_SUCCESS as i32))?; | 159 | reply.write::<i32>(&(IPC_SUCCESS as i32))?; |
| 142 | - let empty_infos: Vec<saf_ipc::VerifyTicketInfo> = vec![]; | 160 | + let empty_infos: Vec<saf_definition::VerifyTicketInfo> = vec![]; |
| 143 | serialize_verify_ticket_infos(&empty_infos, reply).map_err(|e| { | 161 | serialize_verify_ticket_infos(&empty_infos, reply).map_err(|e| { |
| 144 | loge!("[FATAL]Serialize empty ticket infos failed: {}", e.msg); | 162 | loge!("[FATAL]Serialize empty ticket infos failed: {}", e.msg); |
| 145 | IpcStatusCode::Failed | 163 | IpcStatusCode::Failed |
| @@ -217,7 +235,7 @@ fn handle_verify_ticket( | |||
| 217 | Err(e) => { | 235 | Err(e) => { |
| 218 | loge!("[FATAL]VerifyTicket failed: {}", e.msg); | 236 | loge!("[FATAL]VerifyTicket failed: {}", e.msg); |
| 219 | reply.write::<i32>(&(IPC_SUCCESS as i32))?; | 237 | reply.write::<i32>(&(IPC_SUCCESS as i32))?; |
| 220 | - let empty_infos: Vec<saf_ipc::CliInfo> = vec![]; | 238 | + let empty_infos: Vec<saf_definition::CliInfo> = vec![]; |
| 221 | serialize_cli_infos(&empty_infos, reply).map_err(|e| { | 239 | serialize_cli_infos(&empty_infos, reply).map_err(|e| { |
| 222 | loge!("[FATAL]Serialize empty cli infos failed: {}", e.msg); | 240 | loge!("[FATAL]Serialize empty cli infos failed: {}", e.msg); |
| 223 | IpcStatusCode::Failed | 241 | IpcStatusCode::Failed |
| @@ -228,6 +246,138 @@ fn handle_verify_ticket( | |||
| 228 | Ok(()) | 246 | Ok(()) |
| 229 | } | 247 | } |
| 230 | 248 | ||
| 249 | +fn handle_generate_controlled_device_package( | ||
| 250 | + data: &mut MsgParcel, reply: &mut MsgParcel | ||
| 251 | +) -> IpcResult<()> { | ||
| 252 | + let queries = deserialize_permission_queries(data).map_err(|e| { | ||
| 253 | + loge!("[FATAL]Deserialize GenerateControlledDevicePackage request failed: {}", e.msg); | ||
| 254 | + IpcStatusCode::Failed | ||
| 255 | + })?; | ||
| 256 | + | ||
| 257 | + logi!("GenerateControlledDevicePackage received, queryCount: {}", queries.len()); | ||
| 258 | + | ||
| 259 | + let result = remote_control::generate_controlled_device_package(queries); | ||
| 260 | + | ||
| 261 | + reply.write::<i32>(&(IPC_SUCCESS as i32))?; | ||
| 262 | + serialize_remote_auth_packages(&result.packages, reply).map_err(|e| { | ||
| 263 | + loge!("[FATAL]Serialize packages failed: {}", e.msg); | ||
| 264 | + IpcStatusCode::Failed | ||
| 265 | + })?; | ||
| 266 | + reply.write::<i32>(&result.error_code)?; | ||
| 267 | + | ||
| 268 | + if result.error_code != 0 { | ||
| 269 | + loge!("GenerateControlledDevicePackage partial failure, error_code={}", result.error_code); | ||
| 270 | + } else { | ||
| 271 | + logi!("GenerateControlledDevicePackage success, packageCount={}", result.packages.len()); | ||
| 272 | + } | ||
| 273 | + | ||
| 274 | + Ok(()) | ||
| 275 | +} | ||
| 276 | + | ||
| 277 | +fn handle_verify_controlled_device_package( | ||
| 278 | + data: &mut MsgParcel, reply: &mut MsgParcel | ||
| 279 | +) -> IpcResult<()> { | ||
| 280 | + let packages = deserialize_remote_auth_packages(data).map_err(|e| { | ||
| 281 | + loge!("[FATAL]Deserialize VerifyControlledDevicePackage request failed: {}", e.msg); | ||
| 282 | + IpcStatusCode::Failed | ||
| 283 | + })?; | ||
| 284 | + | ||
| 285 | + let uid = Skeleton::calling_uid(); | ||
| 286 | + let os_account_id = get_user_id(uid).map_err(|e| { | ||
| 287 | + loge!("[FATAL]Get user id failed: {}", e.msg); | ||
| 288 | + IpcStatusCode::Failed | ||
| 289 | + })?; | ||
| 290 | + | ||
| 291 | + logi!("VerifyControlledDevicePackage received, os_account_id: {}, packageCount: {}", os_account_id, packages.len()); | ||
| 292 | + | ||
| 293 | + let result = remote_control::verify_controlled_device_package(os_account_id, packages); | ||
| 294 | + | ||
| 295 | + reply.write::<i32>(&(IPC_SUCCESS as i32))?; | ||
| 296 | + serialize_bool_vec(&result.results, reply).map_err(|e| { | ||
| 297 | + loge!("[FATAL]Serialize verify results failed: {}", e.msg); | ||
| 298 | + IpcStatusCode::Failed | ||
| 299 | + })?; | ||
| 300 | + reply.write::<i32>(&result.error_code)?; | ||
| 301 | + | ||
| 302 | + if result.error_code != 0 { | ||
| 303 | + loge!("VerifyControlledDevicePackage system error, error_code={}", result.error_code); | ||
| 304 | + } else { | ||
| 305 | + logi!("VerifyControlledDevicePackage success, resultCount: {}", result.results.len()); | ||
| 306 | + } | ||
| 307 | + | ||
| 308 | + Ok(()) | ||
| 309 | +} | ||
| 310 | + | ||
| 311 | +fn handle_generate_controller_device_package( | ||
| 312 | + data: &mut MsgParcel, reply: &mut MsgParcel | ||
| 313 | +) -> IpcResult<()> { | ||
| 314 | + let remote_user_auth_results = deserialize_remote_user_auth_results_vec(data).map_err(|e| { | ||
| 315 | + loge!("[FATAL]Deserialize GenerateControllerDevicePackage request failed: {}", e.msg); | ||
| 316 | + IpcStatusCode::Failed | ||
| 317 | + })?; | ||
| 318 | + | ||
| 319 | + logi!("GenerateControllerDevicePackage received, resultCount: {}", | ||
| 320 | + remote_user_auth_results.len()); | ||
| 321 | + | ||
| 322 | + let result = remote_control::generate_controller_device_package(remote_user_auth_results); | ||
| 323 | + | ||
| 324 | + reply.write::<i32>(&(IPC_SUCCESS as i32))?; | ||
| 325 | + serialize_remote_auth_packages(&result.packages, reply).map_err(|e| { | ||
| 326 | + loge!("[FATAL]Serialize packages failed: {}", e.msg); | ||
| 327 | + IpcStatusCode::Failed | ||
| 328 | + })?; | ||
| 329 | + reply.write::<i32>(&result.error_code)?; | ||
| 330 | + | ||
| 331 | + if result.error_code != 0 { | ||
| 332 | + loge!("GenerateControllerDevicePackage partial failure, error_code={}", | ||
| 333 | + result.error_code); | ||
| 334 | + } else { | ||
| 335 | + logi!("GenerateControllerDevicePackage success, packageCount={}", | ||
| 336 | + result.packages.len()); | ||
| 337 | + } | ||
| 338 | + | ||
| 339 | + Ok(()) | ||
| 340 | +} | ||
| 341 | + | ||
| 342 | +fn handle_verify_controller_device_package( | ||
| 343 | + data: &mut MsgParcel, reply: &mut MsgParcel | ||
| 344 | +) -> IpcResult<()> { | ||
| 345 | + let packages = deserialize_remote_auth_packages(data).map_err(|e| { | ||
| 346 | + loge!("[FATAL]Deserialize VerifyControllerDevicePackage packages failed: {}", e.msg); | ||
| 347 | + IpcStatusCode::Failed | ||
| 348 | + })?; | ||
| 349 | + | ||
| 350 | + let remote_info = deserialize_remote_info(data).map_err(|e| { | ||
| 351 | + loge!("[FATAL]Deserialize VerifyControllerDevicePackage remote_info failed: {}", e.msg); | ||
| 352 | + IpcStatusCode::Failed | ||
| 353 | + })?; | ||
| 354 | + | ||
| 355 | + let uid = Skeleton::calling_uid(); | ||
| 356 | + let os_account_id = get_user_id(uid).map_err(|e| { | ||
| 357 | + loge!("[FATAL]Get user id failed: {}", e.msg); | ||
| 358 | + IpcStatusCode::Failed | ||
| 359 | + })?; | ||
| 360 | + | ||
| 361 | + logi!("VerifyControllerDevicePackage received, os_account_id: {}, packageCount: {}", os_account_id, packages.len()); | ||
| 362 | + | ||
| 363 | + let result = remote_control::verify_controller_device_package(os_account_id, packages, &remote_info); | ||
| 364 | + | ||
| 365 | + reply.write::<i32>(&(IPC_SUCCESS as i32))?; | ||
| 366 | + serialize_bool_vec(&result.results, reply).map_err(|e| { | ||
| 367 | + loge!("[FATAL]Serialize verify results failed: {}", e.msg); | ||
| 368 | + IpcStatusCode::Failed | ||
| 369 | + })?; | ||
| 370 | + reply.write::<i32>(&result.error_code)?; | ||
| 371 | + | ||
| 372 | + if result.error_code != 0 { | ||
| 373 | + loge!("VerifyControllerDevicePackage system error, error_code={}", result.error_code); | ||
| 374 | + } else { | ||
| 375 | + logi!("VerifyControllerDevicePackage success, resultCount: {}", result.results.len()); | ||
| 376 | + } | ||
| 377 | + | ||
| 378 | + Ok(()) | ||
| 379 | +} | ||
| 380 | + | ||
| 231 | fn on_extension_request(_stub: &SAFService, code: u32, data: &mut MsgParcel, reply: &mut MsgParcel) -> i32 { | 381 | fn on_extension_request(_stub: &SAFService, code: u32, data: &mut MsgParcel, reply: &mut MsgParcel) -> i32 { |
| 232 | if let Ok(load) = SAFPlugin::get_instance().load_plugin() { | 382 | if let Ok(load) = SAFPlugin::get_instance().load_plugin() { |
| 233 | match load.on_remote_request(code, data, reply) { | 383 | match load.on_remote_request(code, data, reply) { |
| @@ -26,8 +26,7 @@ pub use account_based_ticket_key_manager::AccountBasedTicketKeyManager; | |||
| 26 | 26 | ||
| 27 | use std::ffi::CString; | 27 | use std::ffi::CString; |
| 28 | use std::os::raw::c_char; | 28 | use std::os::raw::c_char; |
| 29 | -use saf_definition::{ErrCode, Result, macros_lib}; | 29 | +use saf_definition::{ErrCode, Result, macros_lib, VerifyTicketInfo}; |
| 30 | -use saf_ipc::VerifyTicketInfo; | ||
| 31 | use crate::notify_error; | 30 | use crate::notify_error; |
| 32 | 31 | ||
| 33 | const CHALLENGE_SIZE: usize = 32; | 32 | const CHALLENGE_SIZE: usize = 32; |
| @@ -35,15 +34,15 @@ const HMAC_SHA256_SIZE: usize = 32; | |||
| 35 | const SAF_SUCCESS: i32 = 0; | 34 | const SAF_SUCCESS: i32 = 0; |
| 36 | 35 | ||
| 37 | 36 | ||
| 38 | -struct Uint8Buff { | 37 | +pub(crate) struct Uint8Buff { |
| 39 | - buf: *mut u8, | 38 | + pub buf: *mut u8, |
| 40 | - size: u32, | 39 | + pub size: u32, |
| 41 | } | 40 | } |
| 42 | 41 | ||
| 43 | 42 | ||
| 44 | -struct Uint8BuffConst { | 43 | +pub(crate) struct Uint8BuffConst { |
| 45 | - buf: *const u8, | 44 | + pub buf: *const u8, |
| 46 | - size: u32, | 45 | + pub size: u32, |
| 47 | } | 46 | } |
| 48 | 47 | ||
| 49 | extern "C" { | 48 | extern "C" { |
| @@ -58,15 +57,20 @@ extern "C" { | |||
| 58 | 57 | ||
| 59 | fn generate_challenge() -> Result<Vec<u8>> { | 58 | fn generate_challenge() -> Result<Vec<u8>> { |
| 60 | let mut buf = vec![0u8; CHALLENGE_SIZE]; | 59 | let mut buf = vec![0u8; CHALLENGE_SIZE]; |
| 60 | + generate_random_bytes(&mut buf, CHALLENGE_SIZE as u32)?; | ||
| 61 | + Ok(buf) | ||
| 62 | +} | ||
| 63 | + | ||
| 64 | +pub(crate) fn generate_random_bytes(buf: &mut [u8], size: u32) -> Result<()> { | ||
| 61 | let mut buff = Uint8Buff { | 65 | let mut buff = Uint8Buff { |
| 62 | buf: buf.as_mut_ptr(), | 66 | buf: buf.as_mut_ptr(), |
| 63 | - size: CHALLENGE_SIZE as u32, | 67 | + size, |
| 64 | }; | 68 | }; |
| 65 | let ret = unsafe { GenerateRandomBytes(&mut buff) }; | 69 | let ret = unsafe { GenerateRandomBytes(&mut buff) }; |
| 66 | if ret != SAF_SUCCESS { | 70 | if ret != SAF_SUCCESS { |
| 67 | - macros_lib::log_throw_error!(ErrCode::try_from(ret as u32)?, "generate challenge failed") | 71 | + macros_lib::log_throw_error!(ErrCode::try_from(ret as u32)?, "generate random bytes failed") |
| 68 | } else { | 72 | } else { |
| 69 | - Ok(buf) | 73 | + Ok(()) |
| 70 | } | 74 | } |
| 71 | } | 75 | } |
| 72 | 76 | ||
| @@ -116,6 +120,7 @@ fn verify_hmac_sha256(key: &[u8], data: &[u8], expected_hmac: &[u8]) -> Result<b | |||
| 116 | } | 120 | } |
| 117 | } | 121 | } |
| 118 | 122 | ||
| 123 | +pub(crate) | ||
| 119 | fn base64_encode(input: &[u8]) -> Result<Vec<u8>> { | 124 | fn base64_encode(input: &[u8]) -> Result<Vec<u8>> { |
| 120 | let expected_len = 4 * ((input.len() + 2) / 3) + 1; | 125 | let expected_len = 4 * ((input.len() + 2) / 3) + 1; |
| 121 | let mut output = vec![0u8; expected_len]; | 126 | let mut output = vec![0u8; expected_len]; |
| @@ -17,27 +17,36 @@ | |||
| 17 | 17 | ||
| 18 | use crate::ticket_operation; | 18 | use crate::ticket_operation; |
| 19 | use ipc::parcel::MsgParcel; | 19 | use ipc::parcel::MsgParcel; |
| 20 | +use saf_definition::{ErrCode, macros_lib}; | ||
| 20 | use saf_log::{loge, logi}; | 21 | use saf_log::{loge, logi}; |
| 21 | use saf_plugin::saf_plugin::SAFPlugin; | 22 | use saf_plugin::saf_plugin::SAFPlugin; |
| 22 | use saf_plugin_interface::plugin_interface::{ | 23 | use saf_plugin_interface::plugin_interface::{ |
| 23 | - EventType, ExtMap, ERROR_METRICS_KEYS, PERFORMANCE_METRICS_KEYS, POLICY_AUTH_STATUS_KEYS, VERIFY_REMOTE_TICKET_KEYS, | 24 | + EventType, ExtMap, ERROR_METRICS_KEYS, PERFORMANCE_METRICS_KEYS, POLICY_AUTH_STATUS_KEYS, |
| 25 | + VERIFY_REMOTE_TICKET_KEYS, GET_DEVICE_UDID_KEYS | ||
| 24 | }; | 26 | }; |
| 25 | use saf_sdk::Value; | 27 | use saf_sdk::Value; |
| 26 | 28 | ||
| 27 | 29 | ||
| 28 | pub mod ffi { | 30 | pub mod ffi { |
| 29 | - // C++ compatible VerifyTicketInfo for bridge | 31 | + /// C++ compatible VerifyTicketInfo for bridge. |
| 30 | pub struct CxxVerifyTicketInfo { | 32 | pub struct CxxVerifyTicketInfo { |
| 33 | + /// Message string. | ||
| 31 | pub message: String, | 34 | pub message: String, |
| 35 | + /// Challenge string. | ||
| 32 | pub challenge: String, | 36 | pub challenge: String, |
| 37 | + /// Ticket string. | ||
| 33 | pub ticket: String, | 38 | pub ticket: String, |
| 34 | } | 39 | } |
| 35 | 40 | ||
| 36 | // C++ callable Rust functions | 41 | // C++ callable Rust functions |
| 37 | extern "Rust" { | 42 | extern "Rust" { |
| 43 | + /// Notify performance metrics. | ||
| 38 | fn notify_performance_metrics(item_count: i32, elapsed_time: i32, os_account_id: i32, function_name: String); | 44 | fn notify_performance_metrics(item_count: i32, elapsed_time: i32, os_account_id: i32, function_name: String); |
| 45 | + /// Notify error. | ||
| 39 | fn notify_error(error_message: String, error_code: i32, os_account_id: i32, function_name: String); | 46 | fn notify_error(error_message: String, error_code: i32, os_account_id: i32, function_name: String); |
| 47 | + /// CXX bridge for batch_generate_ticket. | ||
| 40 | fn cxx_batch_generate_ticket(os_account_id: i32, caller_id: &str, domain_id: &str, messages: &[String], result_code: &mut i32) -> Vec<CxxVerifyTicketInfo>; | 48 | fn cxx_batch_generate_ticket(os_account_id: i32, caller_id: &str, domain_id: &str, messages: &[String], result_code: &mut i32) -> Vec<CxxVerifyTicketInfo>; |
| 49 | + /// Get policy auth status. | ||
| 41 | fn get_policy_auth_status(permissions: &Vec<String>, auth_statuses: &mut Vec<i32>) -> i32; | 50 | fn get_policy_auth_status(permissions: &Vec<String>, auth_statuses: &mut Vec<i32>) -> i32; |
| 42 | fn verify_remote_ticket(domain_id: String, remote_control_ticket: String, os_account_id: i32) -> i32; | 51 | fn verify_remote_ticket(domain_id: String, remote_control_ticket: String, os_account_id: i32) -> i32; |
| 43 | fn cxx_store_challenge(caller_token_id: &str, challenge: &str, expire_time_ms: u64) -> i32; | 52 | fn cxx_store_challenge(caller_token_id: &str, challenge: &str, expire_time_ms: u64) -> i32; |
| @@ -50,19 +59,20 @@ pub mod ffi { | |||
| 50 | include!("secure_access_fence_service.h"); | 59 | include!("secure_access_fence_service.h"); |
| 51 | 60 | ||
| 52 | type MessageParcel = ipc::parcel::MessageParcel; | 61 | type MessageParcel = ipc::parcel::MessageParcel; |
| 62 | + /// On remote request. | ||
| 53 | fn OnRemoteRequest(code: u32, data: Pin<&mut MessageParcel>, reply: Pin<&mut MessageParcel>) -> i32; | 63 | fn OnRemoteRequest(code: u32, data: Pin<&mut MessageParcel>, reply: Pin<&mut MessageParcel>) -> i32; |
| 54 | fn GetBootTimeMs() -> i64; | 64 | fn GetBootTimeMs() -> i64; |
| 55 | } | 65 | } |
| 56 | } | 66 | } |
| 57 | 67 | ||
| 58 | -// Original function: IPC request handler | 68 | +/// IPC request handler for C++ redirect. |
| 59 | pub fn on_remote_request(code: u32, data: &mut MsgParcel, reply: &mut MsgParcel) -> i32 { | 69 | pub fn on_remote_request(code: u32, data: &mut MsgParcel, reply: &mut MsgParcel) -> i32 { |
| 60 | let data_pin = data.as_msg_parcel_mut(); | 70 | let data_pin = data.as_msg_parcel_mut(); |
| 61 | let reply_pin = reply.as_msg_parcel_mut(); | 71 | let reply_pin = reply.as_msg_parcel_mut(); |
| 62 | ffi::OnRemoteRequest(code, data_pin, reply_pin) | 72 | ffi::OnRemoteRequest(code, data_pin, reply_pin) |
| 63 | } | 73 | } |
| 64 | 74 | ||
| 65 | -// Performance metrics notification function | 75 | +/// Performance metrics notification function. |
| 66 | pub fn notify_performance_metrics(item_count: i32, elapsed_time: i32, os_account_id: i32, function_name: String) { | 76 | pub fn notify_performance_metrics(item_count: i32, elapsed_time: i32, os_account_id: i32, function_name: String) { |
| 67 | logi!( | 77 | logi!( |
| 68 | "[INFO] Performance metrics from C++/Rust: function={}, item_count={}, elapsed_time={}ms", | 78 | "[INFO] Performance metrics from C++/Rust: function={}, item_count={}, elapsed_time={}ms", |
| @@ -76,7 +86,7 @@ pub fn notify_performance_metrics(item_count: i32, elapsed_time: i32, os_account | |||
| 76 | } | 86 | } |
| 77 | } | 87 | } |
| 78 | 88 | ||
| 79 | -// Error notification function | 89 | +/// Error notification function. |
| 80 | pub fn notify_error(error_message: String, error_code: i32, os_account_id: i32, function_name: String) { | 90 | pub fn notify_error(error_message: String, error_code: i32, os_account_id: i32, function_name: String) { |
| 81 | logi!( | 91 | logi!( |
| 82 | "[INFO] Error metrics from C++: function={}, error_code={}, os_account_id={}, error_message={}", | 92 | "[INFO] Error metrics from C++: function={}, error_code={}, os_account_id={}, error_message={}", |
| @@ -153,7 +163,7 @@ pub fn verify_remote_ticket(domain_id: String, remote_control_ticket: String, os | |||
| 153 | } | 163 | } |
| 154 | } | 164 | } |
| 155 | 165 | ||
| 156 | -// C++ -> Rust bridge for batch_generate_ticket. Returns empty vector on error and reports via notify_error. | 166 | +/// C++ -> Rust bridge for batch_generate_ticket. |
| 157 | pub fn cxx_batch_generate_ticket(os_account_id: i32, caller_id: &str, domain_id: &str, messages: &[String], result_code: &mut i32) -> Vec<ffi::CxxVerifyTicketInfo> { | 167 | pub fn cxx_batch_generate_ticket(os_account_id: i32, caller_id: &str, domain_id: &str, messages: &[String], result_code: &mut i32) -> Vec<ffi::CxxVerifyTicketInfo> { |
| 158 | logi!("[Wrapper cxx_batch_generate_ticket] os_account_id = {}, caller_id = {}, messages_count = {}", | 168 | logi!("[Wrapper cxx_batch_generate_ticket] os_account_id = {}, caller_id = {}, messages_count = {}", |
| 159 | os_account_id, caller_id, messages.len()); | 169 | os_account_id, caller_id, messages.len()); |
| @@ -190,6 +200,44 @@ pub fn cxx_store_challenge(caller_token_id: &str, challenge: &str, expire_time_m | |||
| 190 | } | 200 | } |
| 191 | } | 201 | } |
| 192 | 202 | ||
| 203 | +/// Get Device UDID via plugin process_event | ||
| 204 | +pub fn get_device_udid(os_account_id: i32) -> saf_definition::Result<String> { | ||
| 205 | + if os_account_id < 0 { | ||
| 206 | + loge!("[Wrapper get_device_udid] os_account_id is negative: {}", os_account_id); | ||
| 207 | + return macros_lib::log_throw_error!(ErrCode::InvalidOsAccountId, | ||
| 208 | + "os_account_id is negative: {}", os_account_id); | ||
| 209 | + } | ||
| 210 | + | ||
| 211 | + let plugin = SAFPlugin::get_instance(); | ||
| 212 | + let loader = match plugin.load_plugin() { | ||
| 213 | + Ok(loader) => loader, | ||
| 214 | + Err(_e) => { | ||
| 215 | + loge!("[Wrapper get_device_udid] load_plugin failed"); | ||
| 216 | + return macros_lib::log_throw_error!(ErrCode::PluginNotSupport, "load plugin failed"); | ||
| 217 | + }, | ||
| 218 | + }; | ||
| 219 | + | ||
| 220 | + let mut params = ExtMap::new(); | ||
| 221 | + params.insert(GET_DEVICE_UDID_KEYS.os_account_id, Value::Number(os_account_id as u32)); | ||
| 222 | + | ||
| 223 | + let result = match loader.process_event(EventType::GetDeviceUdid, &mut params) { | ||
| 224 | + Ok(r) => r, | ||
| 225 | + Err(e) => { | ||
| 226 | + loge!("[Wrapper get_device_udid] process_event failed, e={}", e); | ||
| 227 | + return macros_lib::log_throw_error!(ErrCode::GetUdidFailed, "process_event failed, e={}", e); | ||
| 228 | + }, | ||
| 229 | + }; | ||
| 230 | + | ||
| 231 | + let Some(Value::String(udid)) = result.get(GET_DEVICE_UDID_KEYS.udid) else { | ||
| 232 | + loge!("[Wrapper get_device_udid] udid not found in result params"); | ||
| 233 | + return macros_lib::log_throw_error!(ErrCode::HashMapKeyNotFound, | ||
| 234 | + "udid not found in result params"); | ||
| 235 | + }; | ||
| 236 | + | ||
| 237 | + logi!("[Wrapper get_device_udid] success, udid_len={}", udid.len()); | ||
| 238 | + Ok(udid.clone()) | ||
| 239 | +} | ||
| 240 | + | ||
| 193 | // Plugin performance event helper | 241 | // Plugin performance event helper |
| 194 | fn call_plugin_performance_event( | 242 | fn call_plugin_performance_event( |
| 195 | item_count: i32, | 243 | item_count: i32, |
| @@ -211,7 +259,6 @@ fn call_plugin_performance_event( | |||
| 211 | Ok(()) | 259 | Ok(()) |
| 212 | } | 260 | } |
| 213 | 261 | ||
| 214 | -// Plugin error event helper | ||
| 215 | fn call_plugin_error_event( | 262 | fn call_plugin_error_event( |
| 216 | error_message: String, | 263 | error_message: String, |
| 217 | error_code: i32, | 264 | error_code: i32, |
| @@ -22,6 +22,7 @@ ohos_static_library("saf_os_dependency") { | |||
| 22 | ] | 22 | ] |
| 23 | sources = [ | 23 | sources = [ |
| 24 | "src/access_token_wrapper.cpp", | 24 | "src/access_token_wrapper.cpp", |
| 25 | + "src/cli_tool_wrapper.cpp", | ||
| 25 | "src/os_account_wrapper.cpp", | 26 | "src/os_account_wrapper.cpp", |
| 26 | "src/screen_lock_wrapper.cpp", | 27 | "src/screen_lock_wrapper.cpp", |
| 27 | "src/system_ability_wrapper.cpp", | 28 | "src/system_ability_wrapper.cpp", |
| @@ -31,6 +32,7 @@ ohos_static_library("saf_os_dependency") { | |||
| 31 | deps = [] | 32 | deps = [] |
| 32 | external_deps = [ | 33 | external_deps = [ |
| 33 | "ability_base:want", | 34 | "ability_base:want", |
| 35 | + "ability_runtime:cli_tool_client", | ||
| 34 | "access_token:libaccesstoken_sdk", | 36 | "access_token:libaccesstoken_sdk", |
| 35 | "access_token:libtokenid_sdk", | 37 | "access_token:libtokenid_sdk", |
| 36 | "bounds_checking_function:libsec_shared", | 38 | "bounds_checking_function:libsec_shared", |
| @@ -43,6 +45,7 @@ ohos_static_library("saf_os_dependency") { | |||
| 43 | "os_account:os_account_innerkits", | 45 | "os_account:os_account_innerkits", |
| 44 | "samgr:samgr_proxy", | 46 | "samgr:samgr_proxy", |
| 45 | "openssl:libcrypto_shared", | 47 | "openssl:libcrypto_shared", |
| 48 | + "init:libbegetutil", | ||
| 46 | ] | 49 | ] |
| 47 | 50 | ||
| 48 | cflags_cc = [ | 51 | cflags_cc = [ |
| @@ -17,6 +17,7 @@ | |||
| 17 | 17 | ||
| 18 | 18 | ||
| 19 | 19 | ||
| 20 | + | ||
| 20 | 21 | ||
| 21 | 22 | ||
| 22 | extern "C" { | 23 | extern "C" { |
| @@ -24,6 +25,7 @@ extern "C" { | |||
| 24 | 25 | ||
| 25 | bool CheckPermission(const char *permission); | 26 | bool CheckPermission(const char *permission); |
| 26 | bool CheckIsSystemHap(); | 27 | bool CheckIsSystemHap(); |
| 28 | +int32_t GetBundleNameFromTokenId(int32_t tokenId, char *bundleName, int32_t *len); | ||
| 27 | 29 | ||
| 28 | 30 | ||
| 29 | } | 31 | } |
| @@ -0,0 +1,52 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | +extern "C" { | ||
| 23 | + | ||
| 24 | + | ||
| 25 | +struct CxxStr { | ||
| 26 | + const char* ptr; | ||
| 27 | + int32_t len; | ||
| 28 | +}; | ||
| 29 | + | ||
| 30 | +struct CxxCmdInfo { | ||
| 31 | + CxxStr cmd_name; | ||
| 32 | + CxxStr sub_cmd; | ||
| 33 | +}; | ||
| 34 | + | ||
| 35 | +struct CxxQueryResult { | ||
| 36 | + int32_t ret_code; | ||
| 37 | + int32_t result_code; | ||
| 38 | + int32_t perm_count; | ||
| 39 | +}; | ||
| 40 | + | ||
| 41 | +int32_t CxxBatchQueryCliPermissions( | ||
| 42 | + const CxxCmdInfo* cmds, | ||
| 43 | + int32_t cmd_count, | ||
| 44 | + char* out_buf, | ||
| 45 | + int32_t buf_size, | ||
| 46 | + CxxQueryResult* out_result); | ||
| 47 | + | ||
| 48 | + | ||
| 49 | +} | ||
| 50 | + | ||
| 51 | + | ||
| 52 | + | ||
| @@ -25,6 +25,7 @@ | |||
| 25 | 25 | ||
| 26 | 26 | ||
| 27 | 27 | ||
| 28 | + | ||
| 28 | 29 | ||
| 29 | using namespace OHOS; | 30 | using namespace OHOS; |
| 30 | using namespace Security::AccessToken; | 31 | using namespace Security::AccessToken; |
| @@ -68,3 +69,40 @@ bool CheckIsSystemHap() | |||
| 68 | } | 69 | } |
| 69 | return (tokenType == ATokenTypeEnum::TOKEN_HAP) ? CheckSystemApp() : true; | 70 | return (tokenType == ATokenTypeEnum::TOKEN_HAP) ? CheckSystemApp() : true; |
| 70 | } | 71 | } |
| 72 | + | ||
| 73 | +int32_t GetBundleNameFromTokenId(int32_t tokenId, char *bundleName, int32_t *len) | ||
| 74 | +{ | ||
| 75 | + if (tokenId < 0) { | ||
| 76 | + LOGE("[FATAL]GetBundleNameFromTokenId failed, tokenId is negative"); | ||
| 77 | + return SAF_ERR_GET_BUNDLE_NAME_BY_TOKEN_FAILED; | ||
| 78 | + } | ||
| 79 | + if (bundleName == nullptr || len == nullptr) { | ||
| 80 | + LOGE("[FATAL]GetBundleNameFromTokenId failed, bundleName or len is nullptr"); | ||
| 81 | + return SAF_ERR_GET_BUNDLE_NAME_BY_TOKEN_FAILED; | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + AccessTokenID accessTokenId = static_cast<AccessTokenID>(tokenId); | ||
| 85 | + HapTokenInfo hapTokenInfo; | ||
| 86 | + int result = AccessTokenKit::GetHapTokenInfo(accessTokenId, hapTokenInfo); | ||
| 87 | + if (result != RET_SUCCESS) { | ||
| 88 | + LOGE("[FATAL]GetHapTokenInfo failed, ret=%{public}d", result); | ||
| 89 | + return SAF_ERR_GET_BUNDLE_NAME_BY_TOKEN_FAILED; | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + const std::string &bundleNameStr = hapTokenInfo.bundleName; | ||
| 93 | + size_t bundleNameLen = bundleNameStr.size(); | ||
| 94 | + if (bundleNameLen >= static_cast<size_t>(*len)) { | ||
| 95 | + LOGE("[FATAL]GetBundleNameFromTokenId failed, bundleName too long"); | ||
| 96 | + return SAF_ERR_GET_BUNDLE_NAME_BY_TOKEN_FAILED; | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + if (memcpy_s(bundleName, *len, bundleNameStr.c_str(), bundleNameLen) != EOK) { | ||
| 100 | + LOGE("[FATAL]GetBundleNameFromTokenId failed, memcpy_s failed"); | ||
| 101 | + return SAF_ERR_GET_BUNDLE_NAME_BY_TOKEN_FAILED; | ||
| 102 | + } | ||
| 103 | + bundleName[bundleNameLen] = '\0'; | ||
| 104 | + *len = static_cast<int32_t>(bundleNameLen); | ||
| 105 | + | ||
| 106 | + LOGI("[INFO]GetBundleNameFromTokenId success, bundleName=%{public}s", bundleName); | ||
| 107 | + return SAF_SUCCESS; | ||
| 108 | +} | ||
| @@ -0,0 +1,152 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | +using namespace OHOS::CliTool; | ||
| 30 | + | ||
| 31 | +static bool ValidateCmdInfo(const CxxCmdInfo& cmd) | ||
| 32 | +{ | ||
| 33 | + if (cmd.cmd_name.ptr == nullptr || cmd.cmd_name.len <= 0) { | ||
| 34 | + LOGE("Invalid cmd_name: ptr is nullptr or len <= 0"); | ||
| 35 | + return false; | ||
| 36 | + } | ||
| 37 | + if (cmd.sub_cmd.ptr == nullptr || cmd.sub_cmd.len < 0) { | ||
| 38 | + LOGE("Invalid sub_cmd: ptr is nullptr or len < 0"); | ||
| 39 | + return false; | ||
| 40 | + } | ||
| 41 | + return true; | ||
| 42 | +} | ||
| 43 | + | ||
| 44 | +static int32_t ConvertCxxCmdsToCommands( | ||
| 45 | + const CxxCmdInfo* cmds, | ||
| 46 | + int32_t cmd_count, | ||
| 47 | + std::vector<Command>& cliCmds) | ||
| 48 | +{ | ||
| 49 | + for (int32_t i = 0; i < cmd_count; i++) { | ||
| 50 | + if (!ValidateCmdInfo(cmds[i])) { | ||
| 51 | + LOGE("Invalid cmd info at index %{public}d", i); | ||
| 52 | + return SAF_ERR_ARG_INVALID; | ||
| 53 | + } | ||
| 54 | + cliCmds.push_back(Command{ | ||
| 55 | + std::string(cmds[i].cmd_name.ptr, cmds[i].cmd_name.len), | ||
| 56 | + std::string(cmds[i].sub_cmd.ptr, cmds[i].sub_cmd.len) | ||
| 57 | + }); | ||
| 58 | + } | ||
| 59 | + return 0; | ||
| 60 | +} | ||
| 61 | + | ||
| 62 | +static int32_t QueryCliPermissions( | ||
| 63 | + const std::vector<Command>& cliCmds, | ||
| 64 | + std::vector<CommandPermission>& cliCmdPermissions) | ||
| 65 | +{ | ||
| 66 | + int32_t ret = CliToolMGRClient::GetInstance().BatchQueryPermissionBySubCommand(cliCmds, cliCmdPermissions); | ||
| 67 | + if (ret != 0) { | ||
| 68 | + LOGE("BatchQueryPermissionBySubCommand failed, ret=%{public}d", ret); | ||
| 69 | + return SAF_ERR_TOOL_ERROR; | ||
| 70 | + } | ||
| 71 | + return 0; | ||
| 72 | +} | ||
| 73 | + | ||
| 74 | +static void CollectAllPermissions( | ||
| 75 | + const std::vector<CommandPermission>& cliCmdPermissions, | ||
| 76 | + std::set<std::string>& allPermissions) | ||
| 77 | +{ | ||
| 78 | + for (const auto& perm : cliCmdPermissions) { | ||
| 79 | + for (const auto& p : perm.permissions) { | ||
| 80 | + allPermissions.insert(p); | ||
| 81 | + } | ||
| 82 | + } | ||
| 83 | +} | ||
| 84 | + | ||
| 85 | +static int32_t WritePermissionsToBuffer( | ||
| 86 | + const std::set<std::string>& allPermissions, | ||
| 87 | + char* out_buf, | ||
| 88 | + int32_t buf_size, | ||
| 89 | + int32_t& permCount) | ||
| 90 | +{ | ||
| 91 | + int32_t offset = 0; | ||
| 92 | + permCount = 0; | ||
| 93 | + | ||
| 94 | + for (const auto& perm : allPermissions) { | ||
| 95 | + int32_t needed = static_cast<int32_t>(perm.size()) + 1; | ||
| 96 | + if (offset + needed > buf_size) { | ||
| 97 | + break; | ||
| 98 | + } | ||
| 99 | + if (memcpy_s(out_buf + offset, static_cast<size_t>(buf_size - offset), | ||
| 100 | + perm.c_str(), perm.size()) != 0) { | ||
| 101 | + LOGE("memcpy_s failed"); | ||
| 102 | + return SAF_ERR_TOOL_ERROR; | ||
| 103 | + } | ||
| 104 | + out_buf[offset + perm.size()] = '\0'; | ||
| 105 | + offset += needed; | ||
| 106 | + permCount++; | ||
| 107 | + } | ||
| 108 | + return 0; | ||
| 109 | +} | ||
| 110 | + | ||
| 111 | +extern "C" int32_t CxxBatchQueryCliPermissions( | ||
| 112 | + const CxxCmdInfo* cmds, | ||
| 113 | + int32_t cmd_count, | ||
| 114 | + char* out_buf, | ||
| 115 | + int32_t buf_size, | ||
| 116 | + CxxQueryResult* out_result) | ||
| 117 | +{ | ||
| 118 | + if (cmds == nullptr || out_buf == nullptr || out_result == nullptr) { | ||
| 119 | + return SAF_ERR_ARG_INVALID; | ||
| 120 | + } | ||
| 121 | + | ||
| 122 | + out_result->ret_code = 0; | ||
| 123 | + out_result->result_code = 0; | ||
| 124 | + out_result->perm_count = 0; | ||
| 125 | + | ||
| 126 | + std::vector<Command> cliCmds; | ||
| 127 | + int32_t ret = ConvertCxxCmdsToCommands(cmds, cmd_count, cliCmds); | ||
| 128 | + if (ret != 0) { | ||
| 129 | + return ret; | ||
| 130 | + } | ||
| 131 | + | ||
| 132 | + std::vector<CommandPermission> cliCmdPermissions; | ||
| 133 | + ret = QueryCliPermissions(cliCmds, cliCmdPermissions); | ||
| 134 | + if (ret != 0) { | ||
| 135 | + out_result->ret_code = ret; | ||
| 136 | + out_result->result_code = ret; | ||
| 137 | + return ret; | ||
| 138 | + } | ||
| 139 | + | ||
| 140 | + std::set<std::string> allPermissions; | ||
| 141 | + CollectAllPermissions(cliCmdPermissions, allPermissions); | ||
| 142 | + | ||
| 143 | + int32_t permCount = 0; | ||
| 144 | + ret = WritePermissionsToBuffer(allPermissions, out_buf, buf_size, permCount); | ||
| 145 | + if (ret != 0) { | ||
| 146 | + out_result->ret_code = ret; | ||
| 147 | + return ret; | ||
| 148 | + } | ||
| 149 | + | ||
| 150 | + out_result->perm_count = permCount; | ||
| 151 | + return 0; | ||
| 152 | +} | ||
| @@ -28,6 +28,7 @@ ohos_rust_static_library("saf_plugin") { | |||
| 28 | "ylong_runtime:ylong_runtime", | 28 | "ylong_runtime:ylong_runtime", |
| 29 | ] | 29 | ] |
| 30 | 30 | ||
| 31 | + | ||
| 31 | rustflags = [ "-C", "codegen-units=1", "-C", "opt-level=z", "-C", "debuginfo=0" ] | 32 | rustflags = [ "-C", "codegen-units=1", "-C", "opt-level=z", "-C", "debuginfo=0" ] |
| 32 | 33 | ||
| 33 | crate_name = "saf_plugin" | 34 | crate_name = "saf_plugin" |
| @@ -22,7 +22,7 @@ ohos_rust_unittest("rust_asset_core_service_ut_test") { | |||
| 22 | sources = [ "src/lib.rs" ] | 22 | sources = [ "src/lib.rs" ] |
| 23 | deps = [ | 23 | deps = [ |
| 24 | "../../../../../../frameworks/asset/definition:asset_definition", | 24 | "../../../../../../frameworks/asset/definition:asset_definition", |
| 25 | - "../../../../../../services/asset/core_service/asset:asset_service", | 25 | + "../../../../../../services/asset/core_service:asset_service", |
| 26 | "../../../../../../services/asset/common:asset_common", | 26 | "../../../../../../services/asset/common:asset_common", |
| 27 | "../../../../../../services/asset/db_operator:asset_db_operator", | 27 | "../../../../../../services/asset/db_operator:asset_db_operator", |
| 28 | ] | 28 | ] |
| @@ -37,7 +37,8 @@ ohos_unittest("saf_agent_fence_ndk_test") { | |||
| 37 | "src/saf_agent_params_checker_test.cpp", | 37 | "src/saf_agent_params_checker_test.cpp", |
| 38 | "src/saf_agent_fence_ipc_test.cpp", | 38 | "src/saf_agent_fence_ipc_test.cpp", |
| 39 | "src/saf_agent_fence_grant_tool_permissions_by_user_test.cpp", | 39 | "src/saf_agent_fence_grant_tool_permissions_by_user_test.cpp", |
| 40 | - "src/saf_agent_fence_request_tool_permissions_test.cpp" | 40 | + "src/saf_agent_fence_request_tool_permissions_test.cpp", |
| 41 | + "src/saf_agent_fence_remote_control_params_test.cpp" | ||
| 41 | ] | 42 | ] |
| 42 | 43 | ||
| 43 | deps = [ | 44 | deps = [ |
| @@ -0,0 +1,46 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | +namespace UnitTest::SafAgentFenceRemoteControlParamsTest { | ||
| 20 | +int GenerateControlledDevicePackageParamsTest001(void); | ||
| 21 | +int GenerateControlledDevicePackageParamsTest002(void); | ||
| 22 | +int GenerateControlledDevicePackageParamsTest003(void); | ||
| 23 | +int GenerateControlledDevicePackageParamsTest004(void); | ||
| 24 | +int GenerateControlledDevicePackageParamsTest005(void); | ||
| 25 | +int GenerateControlledDevicePackageParamsTest006(void); | ||
| 26 | +int GenerateControlledDevicePackageParamsTest007(void); | ||
| 27 | +int VerifyControlledDevicePackageParamsTest001(void); | ||
| 28 | +int VerifyControlledDevicePackageParamsTest002(void); | ||
| 29 | +int VerifyControlledDevicePackageParamsTest003(void); | ||
| 30 | +int VerifyControlledDevicePackageParamsTest004(void); | ||
| 31 | +int GenerateControllerDevicePackageParamsTest001(void); | ||
| 32 | +int GenerateControllerDevicePackageParamsTest002(void); | ||
| 33 | +int GenerateControllerDevicePackageParamsTest003(void); | ||
| 34 | +int GenerateControllerDevicePackageParamsTest004(void); | ||
| 35 | +int GenerateControllerDevicePackageParamsTest005(void); | ||
| 36 | +int GenerateControllerDevicePackageParamsTest006(void); | ||
| 37 | +int GenerateControllerDevicePackageParamsTest007(void); | ||
| 38 | +int VerifyControllerDevicePackageParamsTest001(void); | ||
| 39 | +int VerifyControllerDevicePackageParamsTest002(void); | ||
| 40 | +int VerifyControllerDevicePackageParamsTest003(void); | ||
| 41 | +int VerifyControllerDevicePackageParamsTest004(void); | ||
| 42 | +int VerifyControllerDevicePackageParamsTest005(void); | ||
| 43 | +int VerifyControllerDevicePackageParamsTest006(void); | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | + | ||
Atest/secure_access_fence/unittest/kits_ndk/src/saf_agent_fence_remote_control_params_test.cpp+419-0
| @@ -0,0 +1,419 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | +using namespace testing::ext; | ||
| 24 | +namespace UnitTest::SafAgentFenceRemoteControlParamsTest { | ||
| 25 | + | ||
| 26 | +class SafAgentFenceRemoteControlParamsTest : public testing::Test { | ||
| 27 | +public: | ||
| 28 | + static void SetUpTestCase(void); | ||
| 29 | + static void TearDownTestCase(void); | ||
| 30 | + void SetUp(void); | ||
| 31 | + void TearDown(void); | ||
| 32 | +}; | ||
| 33 | + | ||
| 34 | +void SafAgentFenceRemoteControlParamsTest::SetUpTestCase(void) | ||
| 35 | +{ | ||
| 36 | +} | ||
| 37 | + | ||
| 38 | +void SafAgentFenceRemoteControlParamsTest::TearDownTestCase(void) | ||
| 39 | +{ | ||
| 40 | +} | ||
| 41 | + | ||
| 42 | +void SafAgentFenceRemoteControlParamsTest::SetUp(void) | ||
| 43 | +{ | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | +void SafAgentFenceRemoteControlParamsTest::TearDown(void) | ||
| 47 | +{ | ||
| 48 | +} | ||
| 49 | + | ||
| 50 | +HWTEST_F(SafAgentFenceRemoteControlParamsTest, GenerateControlledDevicePackageParamsTest001, TestSize.Level0) | ||
| 51 | +{ | ||
| 52 | + OHOS::Security::SAF::SafAgentFence agentFence; | ||
| 53 | + | ||
| 54 | + std::vector<OHOS::Security::SAF::PermissionQuery> queries; | ||
| 55 | + | ||
| 56 | + std::vector<OHOS::Security::SAF::RemoteAuthPackage> packages; | ||
| 57 | + int32_t result = agentFence.GenerateControlledDevicePackage(queries, packages); | ||
| 58 | + EXPECT_NE(result, SAF_SUCCESS); | ||
| 59 | +} | ||
| 60 | + | ||
| 61 | +HWTEST_F(SafAgentFenceRemoteControlParamsTest, GenerateControlledDevicePackageParamsTest002, TestSize.Level0) | ||
| 62 | +{ | ||
| 63 | + OHOS::Security::SAF::SafAgentFence agentFence; | ||
| 64 | + | ||
| 65 | + std::vector<OHOS::Security::SAF::PermissionQuery> queries; | ||
| 66 | + for (int i = 0; i < 11; i++) { | ||
| 67 | + OHOS::Security::SAF::PermissionQuery query; | ||
| 68 | + queries.push_back(query); | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + std::vector<OHOS::Security::SAF::RemoteAuthPackage> packages; | ||
| 72 | + int32_t result = agentFence.GenerateControlledDevicePackage(queries, packages); | ||
| 73 | + EXPECT_NE(result, SAF_SUCCESS); | ||
| 74 | +} | ||
| 75 | + | ||
| 76 | +HWTEST_F(SafAgentFenceRemoteControlParamsTest, GenerateControlledDevicePackageParamsTest003, TestSize.Level0) | ||
| 77 | +{ | ||
| 78 | + OHOS::Security::SAF::SafAgentFence agentFence; | ||
| 79 | + | ||
| 80 | + std::vector<OHOS::Security::SAF::PermissionQuery> queries; | ||
| 81 | + OHOS::Security::SAF::PermissionQuery query; | ||
| 82 | + query.callerTokenId = 0; | ||
| 83 | + queries.push_back(query); | ||
| 84 | + | ||
| 85 | + std::vector<OHOS::Security::SAF::RemoteAuthPackage> packages; | ||
| 86 | + int32_t result = agentFence.GenerateControlledDevicePackage(queries, packages); | ||
| 87 | + EXPECT_NE(result, SAF_SUCCESS); | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | +HWTEST_F(SafAgentFenceRemoteControlParamsTest, GenerateControlledDevicePackageParamsTest004, TestSize.Level0) | ||
| 91 | +{ | ||
| 92 | + OHOS::Security::SAF::SafAgentFence agentFence; | ||
| 93 | + | ||
| 94 | + std::vector<OHOS::Security::SAF::PermissionQuery> queries; | ||
| 95 | + OHOS::Security::SAF::PermissionQuery query; | ||
| 96 | + query.domainId = ""; | ||
| 97 | + queries.push_back(query); | ||
| 98 | + | ||
| 99 | + std::vector<OHOS::Security::SAF::RemoteAuthPackage> packages; | ||
| 100 | + int32_t result = agentFence.GenerateControlledDevicePackage(queries, packages); | ||
| 101 | + EXPECT_NE(result, SAF_SUCCESS); | ||
| 102 | +} | ||
| 103 | + | ||
| 104 | +HWTEST_F(SafAgentFenceRemoteControlParamsTest, GenerateControlledDevicePackageParamsTest005, TestSize.Level0) | ||
| 105 | +{ | ||
| 106 | + OHOS::Security::SAF::SafAgentFence agentFence; | ||
| 107 | + | ||
| 108 | + std::vector<OHOS::Security::SAF::PermissionQuery> queries; | ||
| 109 | + OHOS::Security::SAF::PermissionQuery query; | ||
| 110 | + OHOS::Security::SAF::RemoteInfo remoteInfo; | ||
| 111 | + remoteInfo.role = OHOS::Security::SAF::Role::CONTROLLED; | ||
| 112 | + query.remoteInfo = remoteInfo; | ||
| 113 | + queries.push_back(query); | ||
| 114 | + | ||
| 115 | + std::vector<OHOS::Security::SAF::RemoteAuthPackage> packages; | ||
| 116 | + int32_t result = agentFence.GenerateControlledDevicePackage(queries, packages); | ||
| 117 | + EXPECT_NE(result, SAF_SUCCESS); | ||
| 118 | +} | ||
| 119 | + | ||
| 120 | +HWTEST_F(SafAgentFenceRemoteControlParamsTest, GenerateControlledDevicePackageParamsTest006, TestSize.Level0) | ||
| 121 | +{ | ||
| 122 | + OHOS::Security::SAF::SafAgentFence agentFence; | ||
| 123 | + | ||
| 124 | + std::vector<OHOS::Security::SAF::PermissionQuery> queries; | ||
| 125 | + OHOS::Security::SAF::PermissionQuery query; | ||
| 126 | + OHOS::Security::SAF::RemoteInfo remoteInfo; | ||
| 127 | + remoteInfo.role = OHOS::Security::SAF::Role::CONTROLLER; | ||
| 128 | + query.remoteInfo = remoteInfo; | ||
| 129 | + queries.push_back(query); | ||
| 130 | + | ||
| 131 | + std::vector<OHOS::Security::SAF::RemoteAuthPackage> packages; | ||
| 132 | + int32_t result = agentFence.GenerateControlledDevicePackage(queries, packages); | ||
| 133 | + EXPECT_NE(result, SAF_SUCCESS); | ||
| 134 | +} | ||
| 135 | + | ||
| 136 | +HWTEST_F(SafAgentFenceRemoteControlParamsTest, GenerateControlledDevicePackageParamsTest007, TestSize.Level0) | ||
| 137 | +{ | ||
| 138 | + OHOS::Security::SAF::SafAgentFence agentFence; | ||
| 139 | + | ||
| 140 | + std::vector<OHOS::Security::SAF::PermissionQuery> queries; | ||
| 141 | + OHOS::Security::SAF::PermissionQuery query; | ||
| 142 | + OHOS::Security::SAF::RemoteInfo remoteInfo; | ||
| 143 | + remoteInfo.role = OHOS::Security::SAF::Role::CONTROLLED; | ||
| 144 | + OHOS::Security::SAF::RemoteControlParams remoteControlParams; | ||
| 145 | + remoteControlParams.challenge = ""; | ||
| 146 | + remoteInfo.remoteControlParams = remoteControlParams; | ||
| 147 | + query.remoteInfo = remoteInfo; | ||
| 148 | + queries.push_back(query); | ||
| 149 | + | ||
| 150 | + std::vector<OHOS::Security::SAF::RemoteAuthPackage> packages; | ||
| 151 | + int32_t result = agentFence.GenerateControlledDevicePackage(queries, packages); | ||
| 152 | + EXPECT_NE(result, SAF_SUCCESS); | ||
| 153 | +} | ||
| 154 | + | ||
| 155 | +HWTEST_F(SafAgentFenceRemoteControlParamsTest, VerifyControlledDevicePackageParamsTest001, TestSize.Level0) | ||
| 156 | +{ | ||
| 157 | + OHOS::Security::SAF::SafAgentFence agentFence; | ||
| 158 | + | ||
| 159 | + std::vector<OHOS::Security::SAF::RemoteAuthPackage> packages; | ||
| 160 | + | ||
| 161 | + std::vector<bool> verifyRes; | ||
| 162 | + int32_t result = agentFence.VerifyControlledDevicePackage(packages, verifyRes); | ||
| 163 | + EXPECT_NE(result, SAF_SUCCESS); | ||
| 164 | +} | ||
| 165 | + | ||
| 166 | +HWTEST_F(SafAgentFenceRemoteControlParamsTest, VerifyControlledDevicePackageParamsTest002, TestSize.Level0) | ||
| 167 | +{ | ||
| 168 | + OHOS::Security::SAF::SafAgentFence agentFence; | ||
| 169 | + | ||
| 170 | + std::vector<OHOS::Security::SAF::RemoteAuthPackage> packages; | ||
| 171 | + for (int i = 0; i < 11; i++) { | ||
| 172 | + OHOS::Security::SAF::RemoteAuthPackage pkg; | ||
| 173 | + packages.push_back(pkg); | ||
| 174 | + } | ||
| 175 | + | ||
| 176 | + std::vector<bool> verifyRes; | ||
| 177 | + int32_t result = agentFence.VerifyControlledDevicePackage(packages, verifyRes); | ||
| 178 | + EXPECT_NE(result, SAF_SUCCESS); | ||
| 179 | +} | ||
| 180 | + | ||
| 181 | +HWTEST_F(SafAgentFenceRemoteControlParamsTest, VerifyControlledDevicePackageParamsTest003, TestSize.Level0) | ||
| 182 | +{ | ||
| 183 | + OHOS::Security::SAF::SafAgentFence agentFence; | ||
| 184 | + | ||
| 185 | + std::vector<OHOS::Security::SAF::RemoteAuthPackage> packages; | ||
| 186 | + OHOS::Security::SAF::RemoteAuthPackage pkg; | ||
| 187 | + pkg.remoteMessage = ""; | ||
| 188 | + packages.push_back(pkg); | ||
| 189 | + | ||
| 190 | + std::vector<bool> verifyRes; | ||
| 191 | + int32_t result = agentFence.VerifyControlledDevicePackage(packages, verifyRes); | ||
| 192 | + EXPECT_NE(result, SAF_SUCCESS); | ||
| 193 | +} | ||
| 194 | + | ||
| 195 | +HWTEST_F(SafAgentFenceRemoteControlParamsTest, VerifyControlledDevicePackageParamsTest004, TestSize.Level0) | ||
| 196 | +{ | ||
| 197 | + OHOS::Security::SAF::SafAgentFence agentFence; | ||
| 198 | + | ||
| 199 | + std::vector<OHOS::Security::SAF::RemoteAuthPackage> packages; | ||
| 200 | + OHOS::Security::SAF::RemoteAuthPackage pkg; | ||
| 201 | + pkg.challenge = "invalid_challenge"; | ||
| 202 | + packages.push_back(pkg); | ||
| 203 | + | ||
| 204 | + std::vector<bool> verifyRes; | ||
| 205 | + int32_t result = agentFence.VerifyControlledDevicePackage(packages, verifyRes); | ||
| 206 | + EXPECT_NE(result, SAF_SUCCESS); | ||
| 207 | +} | ||
| 208 | + | ||
| 209 | +HWTEST_F(SafAgentFenceRemoteControlParamsTest, GenerateControllerDevicePackageParamsTest001, TestSize.Level0) | ||
| 210 | +{ | ||
| 211 | + OHOS::Security::SAF::SafAgentFence agentFence; | ||
| 212 | + | ||
| 213 | + std::vector<OHOS::Security::SAF::RemoteUserAuthResults> authResults; | ||
| 214 | + | ||
| 215 | + std::vector<OHOS::Security::SAF::RemoteAuthPackage> packages; | ||
| 216 | + int32_t result = agentFence.GenerateControllerDevicePackage(authResults, packages); | ||
| 217 | + EXPECT_NE(result, SAF_SUCCESS); | ||
| 218 | +} | ||
| 219 | + | ||
| 220 | +HWTEST_F(SafAgentFenceRemoteControlParamsTest, GenerateControllerDevicePackageParamsTest002, TestSize.Level0) | ||
| 221 | +{ | ||
| 222 | + OHOS::Security::SAF::SafAgentFence agentFence; | ||
| 223 | + | ||
| 224 | + std::vector<OHOS::Security::SAF::RemoteUserAuthResults> authResults; | ||
| 225 | + for (int i = 0; i < 11; i++) { | ||
| 226 | + OHOS::Security::SAF::RemoteUserAuthResults authResult; | ||
| 227 | + authResults.push_back(authResult); | ||
| 228 | + } | ||
| 229 | + | ||
| 230 | + std::vector<OHOS::Security::SAF::RemoteAuthPackage> packages; | ||
| 231 | + int32_t result = agentFence.GenerateControllerDevicePackage(authResults, packages); | ||
| 232 | + EXPECT_NE(result, SAF_SUCCESS); | ||
| 233 | +} | ||
| 234 | + | ||
| 235 | +HWTEST_F(SafAgentFenceRemoteControlParamsTest, GenerateControllerDevicePackageParamsTest003, TestSize.Level0) | ||
| 236 | +{ | ||
| 237 | + OHOS::Security::SAF::SafAgentFence agentFence; | ||
| 238 | + | ||
| 239 | + std::vector<OHOS::Security::SAF::RemoteUserAuthResults> authResults; | ||
| 240 | + OHOS::Security::SAF::RemoteUserAuthResults authResult; | ||
| 241 | + authResult.results.clear(); | ||
| 242 | + authResults.push_back(authResult); | ||
| 243 | + | ||
| 244 | + std::vector<OHOS::Security::SAF::RemoteAuthPackage> packages; | ||
| 245 | + int32_t result = agentFence.GenerateControllerDevicePackage(authResults, packages); | ||
| 246 | + EXPECT_NE(result, SAF_SUCCESS); | ||
| 247 | +} | ||
| 248 | + | ||
| 249 | +HWTEST_F(SafAgentFenceRemoteControlParamsTest, GenerateControllerDevicePackageParamsTest004, TestSize.Level0) | ||
| 250 | +{ | ||
| 251 | + OHOS::Security::SAF::SafAgentFence agentFence; | ||
| 252 | + | ||
| 253 | + std::vector<OHOS::Security::SAF::RemoteUserAuthResults> authResults; | ||
| 254 | + OHOS::Security::SAF::RemoteUserAuthResults authResult; | ||
| 255 | + OHOS::Security::SAF::RemoteUserAuthItem item; | ||
| 256 | + item.permission = ""; | ||
| 257 | + authResult.results.push_back(item); | ||
| 258 | + authResults.push_back(authResult); | ||
| 259 | + | ||
| 260 | + std::vector<OHOS::Security::SAF::RemoteAuthPackage> packages; | ||
| 261 | + int32_t result = agentFence.GenerateControllerDevicePackage(authResults, packages); | ||
| 262 | + EXPECT_NE(result, SAF_SUCCESS); | ||
| 263 | +} | ||
| 264 | + | ||
| 265 | +HWTEST_F(SafAgentFenceRemoteControlParamsTest, GenerateControllerDevicePackageParamsTest005, TestSize.Level0) | ||
| 266 | +{ | ||
| 267 | + OHOS::Security::SAF::SafAgentFence agentFence; | ||
| 268 | + | ||
| 269 | + std::vector<OHOS::Security::SAF::RemoteUserAuthResults> authResults; | ||
| 270 | + OHOS::Security::SAF::RemoteUserAuthResults authResult; | ||
| 271 | + OHOS::Security::SAF::RemoteUserAuthItem item; | ||
| 272 | + item.permission = "ohos.permission.TEST"; | ||
| 273 | + item.authResult = ""; | ||
| 274 | + authResult.results.push_back(item); | ||
| 275 | + authResults.push_back(authResult); | ||
| 276 | + | ||
| 277 | + std::vector<OHOS::Security::SAF::RemoteAuthPackage> packages; | ||
| 278 | + int32_t result = agentFence.GenerateControllerDevicePackage(authResults, packages); | ||
| 279 | + EXPECT_NE(result, SAF_SUCCESS); | ||
| 280 | +} | ||
| 281 | + | ||
| 282 | +HWTEST_F(SafAgentFenceRemoteControlParamsTest, GenerateControllerDevicePackageParamsTest006, TestSize.Level0) | ||
| 283 | +{ | ||
| 284 | + OHOS::Security::SAF::SafAgentFence agentFence; | ||
| 285 | + | ||
| 286 | + std::vector<OHOS::Security::SAF::RemoteUserAuthResults> authResults; | ||
| 287 | + OHOS::Security::SAF::RemoteUserAuthResults authResult; | ||
| 288 | + OHOS::Security::SAF::RemoteUserAuthItem item; | ||
| 289 | + item.permission = "ohos.permission.TEST"; | ||
| 290 | + item.authResult = "granted"; | ||
| 291 | + authResult.results.push_back(item); | ||
| 292 | + authResult.permissionQuery.remoteInfo.role = OHOS::Security::SAF::Role::CONTROLLER; | ||
| 293 | + authResults.push_back(authResult); | ||
| 294 | + | ||
| 295 | + std::vector<OHOS::Security::SAF::RemoteAuthPackage> packages; | ||
| 296 | + int32_t result = agentFence.GenerateControllerDevicePackage(authResults, packages); | ||
| 297 | + EXPECT_NE(result, SAF_SUCCESS); | ||
| 298 | +} | ||
| 299 | + | ||
| 300 | +HWTEST_F(SafAgentFenceRemoteControlParamsTest, GenerateControllerDevicePackageParamsTest007, TestSize.Level0) | ||
| 301 | +{ | ||
| 302 | + OHOS::Security::SAF::SafAgentFence agentFence; | ||
| 303 | + | ||
| 304 | + std::vector<OHOS::Security::SAF::RemoteUserAuthResults> authResults; | ||
| 305 | + OHOS::Security::SAF::RemoteUserAuthResults authResult; | ||
| 306 | + OHOS::Security::SAF::RemoteUserAuthItem item; | ||
| 307 | + item.permission = "ohos.permission.TEST"; | ||
| 308 | + item.authResult = "granted"; | ||
| 309 | + authResult.results.push_back(item); | ||
| 310 | + authResult.permissionQuery.remoteInfo.role = OHOS::Security::SAF::Role::CONTROLLED; | ||
| 311 | + authResults.push_back(authResult); | ||
| 312 | + | ||
| 313 | + std::vector<OHOS::Security::SAF::RemoteAuthPackage> packages; | ||
| 314 | + int32_t result = agentFence.GenerateControllerDevicePackage(authResults, packages); | ||
| 315 | + EXPECT_NE(result, SAF_SUCCESS); | ||
| 316 | +} | ||
| 317 | + | ||
| 318 | +HWTEST_F(SafAgentFenceRemoteControlParamsTest, VerifyControllerDevicePackageParamsTest001, TestSize.Level0) | ||
| 319 | +{ | ||
| 320 | + OHOS::Security::SAF::SafAgentFence agentFence; | ||
| 321 | + | ||
| 322 | + std::vector<OHOS::Security::SAF::RemoteAuthPackage> packages; | ||
| 323 | + OHOS::Security::SAF::RemoteInfo remoteInfo; | ||
| 324 | + remoteInfo.role = OHOS::Security::SAF::Role::CONTROLLER; | ||
| 325 | + | ||
| 326 | + std::vector<bool> verifyRes; | ||
| 327 | + int32_t result = agentFence.VerifyControllerDevicePackage(packages, remoteInfo, verifyRes); | ||
| 328 | + EXPECT_NE(result, SAF_SUCCESS); | ||
| 329 | +} | ||
| 330 | + | ||
| 331 | +HWTEST_F(SafAgentFenceRemoteControlParamsTest, VerifyControllerDevicePackageParamsTest002, TestSize.Level0) | ||
| 332 | +{ | ||
| 333 | + OHOS::Security::SAF::SafAgentFence agentFence; | ||
| 334 | + | ||
| 335 | + std::vector<OHOS::Security::SAF::RemoteAuthPackage> packages; | ||
| 336 | + for (int i = 0; i < 11; i++) { | ||
| 337 | + OHOS::Security::SAF::RemoteAuthPackage pkg; | ||
| 338 | + packages.push_back(pkg); | ||
| 339 | + } | ||
| 340 | + OHOS::Security::SAF::RemoteInfo remoteInfo; | ||
| 341 | + remoteInfo.role = OHOS::Security::SAF::Role::CONTROLLER; | ||
| 342 | + | ||
| 343 | + std::vector<bool> verifyRes; | ||
| 344 | + int32_t result = agentFence.VerifyControllerDevicePackage(packages, remoteInfo, verifyRes); | ||
| 345 | + EXPECT_NE(result, SAF_SUCCESS); | ||
| 346 | +} | ||
| 347 | + | ||
| 348 | +HWTEST_F(SafAgentFenceRemoteControlParamsTest, VerifyControllerDevicePackageParamsTest003, TestSize.Level0) | ||
| 349 | +{ | ||
| 350 | + OHOS::Security::SAF::SafAgentFence agentFence; | ||
| 351 | + | ||
| 352 | + std::vector<OHOS::Security::SAF::RemoteAuthPackage> packages; | ||
| 353 | + OHOS::Security::SAF::RemoteAuthPackage pkg; | ||
| 354 | + pkg.remoteMessage = ""; | ||
| 355 | + packages.push_back(pkg); | ||
| 356 | + | ||
| 357 | + OHOS::Security::SAF::RemoteInfo remoteInfo; | ||
| 358 | + remoteInfo.role = OHOS::Security::SAF::Role::CONTROLLER; | ||
| 359 | + | ||
| 360 | + std::vector<bool> verifyRes; | ||
| 361 | + int32_t result = agentFence.VerifyControllerDevicePackage(packages, remoteInfo, verifyRes); | ||
| 362 | + EXPECT_NE(result, SAF_SUCCESS); | ||
| 363 | +} | ||
| 364 | + | ||
| 365 | +HWTEST_F(SafAgentFenceRemoteControlParamsTest, VerifyControllerDevicePackageParamsTest004, TestSize.Level0) | ||
| 366 | +{ | ||
| 367 | + OHOS::Security::SAF::SafAgentFence agentFence; | ||
| 368 | + | ||
| 369 | + std::vector<OHOS::Security::SAF::RemoteAuthPackage> packages; | ||
| 370 | + OHOS::Security::SAF::RemoteAuthPackage pkg; | ||
| 371 | + pkg.remoteMessage = "valid_message"; | ||
| 372 | + packages.push_back(pkg); | ||
| 373 | + | ||
| 374 | + OHOS::Security::SAF::RemoteInfo remoteInfo; | ||
| 375 | + remoteInfo.role = OHOS::Security::SAF::Role::CONTROLLED; | ||
| 376 | + | ||
| 377 | + std::vector<bool> verifyRes; | ||
| 378 | + int32_t result = agentFence.VerifyControllerDevicePackage(packages, remoteInfo, verifyRes); | ||
| 379 | + EXPECT_NE(result, SAF_SUCCESS); | ||
| 380 | +} | ||
| 381 | + | ||
| 382 | +HWTEST_F(SafAgentFenceRemoteControlParamsTest, VerifyControllerDevicePackageParamsTest005, TestSize.Level0) | ||
| 383 | +{ | ||
| 384 | + OHOS::Security::SAF::SafAgentFence agentFence; | ||
| 385 | + | ||
| 386 | + std::vector<OHOS::Security::SAF::RemoteAuthPackage> packages; | ||
| 387 | + OHOS::Security::SAF::RemoteAuthPackage pkg; | ||
| 388 | + pkg.remoteMessage = "valid_message"; | ||
| 389 | + packages.push_back(pkg); | ||
| 390 | + | ||
| 391 | + OHOS::Security::SAF::RemoteInfo remoteInfo; | ||
| 392 | + remoteInfo.role = OHOS::Security::SAF::Role::CONTROLLER; | ||
| 393 | + remoteInfo.remoteId = ""; | ||
| 394 | + | ||
| 395 | + std::vector<bool> verifyRes; | ||
| 396 | + int32_t result = agentFence.VerifyControllerDevicePackage(packages, remoteInfo, verifyRes); | ||
| 397 | + EXPECT_NE(result, SAF_SUCCESS); | ||
| 398 | +} | ||
| 399 | + | ||
| 400 | +HWTEST_F(SafAgentFenceRemoteControlParamsTest, VerifyControllerDevicePackageParamsTest006, TestSize.Level0) | ||
| 401 | +{ | ||
| 402 | + OHOS::Security::SAF::SafAgentFence agentFence; | ||
| 403 | + | ||
| 404 | + std::vector<OHOS::Security::SAF::RemoteAuthPackage> packages; | ||
| 405 | + OHOS::Security::SAF::RemoteAuthPackage pkg; | ||
| 406 | + pkg.remoteMessage = "valid_message"; | ||
| 407 | + packages.push_back(pkg); | ||
| 408 | + | ||
| 409 | + OHOS::Security::SAF::RemoteInfo remoteInfo; | ||
| 410 | + remoteInfo.role = OHOS::Security::SAF::Role::CONTROLLER; | ||
| 411 | + remoteInfo.remoteId = "test_remote_id"; | ||
| 412 | + remoteInfo.remoteControlParams.challenge = ""; | ||
| 413 | + | ||
| 414 | + std::vector<bool> verifyRes; | ||
| 415 | + int32_t result = agentFence.VerifyControllerDevicePackage(packages, remoteInfo, verifyRes); | ||
| 416 | + EXPECT_NE(result, SAF_SUCCESS); | ||
| 417 | +} | ||
| 418 | + | ||
| 419 | +} | ||
| @@ -0,0 +1,43 @@ | |||
| 1 | +# Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 2 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 3 | +# you may not use this file except in compliance with the License. | ||
| 4 | +# You may obtain a copy of the License at | ||
| 5 | +# | ||
| 6 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
| 7 | +# | ||
| 8 | +# Unless required by applicable law or agreed to in writing, software | ||
| 9 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
| 10 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 11 | +# See the License for the specific language governing permissions and | ||
| 12 | +# limitations under the License. | ||
| 13 | + | ||
| 14 | +import("//base/security/asset/config.gni") | ||
| 15 | +import("//build/ohos.gni") | ||
| 16 | +import("//build/test.gni") | ||
| 17 | + | ||
| 18 | +module_output_path = "asset/asset" | ||
| 19 | + | ||
| 20 | +ohos_rust_unittest("rust_saf_core_service_ut_test") { | ||
| 21 | + module_out_path = module_output_path | ||
| 22 | + sources = [ "src/lib.rs" ] | ||
| 23 | + deps = [ | ||
| 24 | + "../../../../../../services/secure_access_fence/core_service:secure_access_fence_service", | ||
| 25 | + "../../../../../../frameworks/secure_access_fence/definition:saf_definition", | ||
| 26 | + "../../../../../../services/secure_access_fence/common:saf_common", | ||
| 27 | + "../../../../../../frameworks/secure_access_fence/utils:saf_utils", | ||
| 28 | + "../../../../../../interfaces/inner_kits/plugin_interface/secure_access_fence:saf_plugin_interface_rust", | ||
| 29 | + "../../../../../../services/secure_access_fence/plugin:saf_plugin", | ||
| 30 | + "//third_party/rust/crates/lazy-static.rs:lib", | ||
| 31 | + ] | ||
| 32 | + rustflags = [] | ||
| 33 | + if (enable_local_test) { | ||
| 34 | + rustflags += [ "--cfg", "feature=\"SAFTest\"" ] | ||
| 35 | + } | ||
| 36 | + external_deps = [ | ||
| 37 | + "ipc:ipc_rust", | ||
| 38 | + "hilog:libhilog", | ||
| 39 | + "ylong_runtime:ylong_runtime", | ||
| 40 | + ] | ||
| 41 | + subsystem_name = "security" | ||
| 42 | + part_name = "asset" | ||
| 43 | +} | ||
| @@ -0,0 +1,28 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | +//! the module test for secure_access_fence core_service | ||
| 17 | +mod remote_control; | ||
| 18 | + | ||
| 19 | + | ||
| 20 | +macro_rules! function { | ||
| 21 | + () => {{ | ||
| 22 | + fn f() {} | ||
| 23 | + fn type_name_of<T>(_: T) -> &'static str { | ||
| 24 | + std::any::type_name::<T>() | ||
| 25 | + } | ||
| 26 | + type_name_of(f).rsplit("::").find(|&part| part != "f" && part != "{{closure}}").expect("Short function name") | ||
| 27 | + }}; | ||
| 28 | +} | ||
| @@ -0,0 +1,360 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | +//! Test cases for generate_controlled_device_package | ||
| 17 | + | ||
| 18 | +use saf_definition::{ErrCode, Role}; | ||
| 19 | +use secure_access_fence_service::ut_core_service_lib_stub::*; | ||
| 20 | + | ||
| 21 | + | ||
| 22 | +fn test_generate_controlled_device_package_empty() { | ||
| 23 | + let result = generate_controlled_device_package(vec![]); | ||
| 24 | + assert!(result.packages.is_empty()); | ||
| 25 | + assert_eq!(ErrCode::Success as i32, result.error_code); | ||
| 26 | +} | ||
| 27 | + | ||
| 28 | + | ||
| 29 | +fn test_generate_controlled_device_package_single_valid_query() { | ||
| 30 | + reset_mock_state(); | ||
| 31 | + set_mock_sign_success(true); | ||
| 32 | + let query = create_test_permission_query( | ||
| 33 | + Role::Controlled, | ||
| 34 | + "test_domain", | ||
| 35 | + "test_ticket", | ||
| 36 | + vec![create_api_operation("ohos.permission.TEST")], | ||
| 37 | + ); | ||
| 38 | + let result = generate_controlled_device_package(vec![query]); | ||
| 39 | + assert_eq!(1, result.packages.len()); | ||
| 40 | + assert_eq!(ErrCode::Success as i32, result.error_code); | ||
| 41 | +} | ||
| 42 | + | ||
| 43 | + | ||
| 44 | +fn test_generate_controlled_device_package_multiple_valid_queries() { | ||
| 45 | + reset_mock_state(); | ||
| 46 | + set_mock_sign_success(true); | ||
| 47 | + let queries = vec![ | ||
| 48 | + create_test_permission_query( | ||
| 49 | + Role::Controlled, | ||
| 50 | + "domain1", | ||
| 51 | + "ticket1", | ||
| 52 | + vec![create_api_operation("perm1")], | ||
| 53 | + ), | ||
| 54 | + create_test_permission_query( | ||
| 55 | + Role::Controlled, | ||
| 56 | + "domain2", | ||
| 57 | + "ticket2", | ||
| 58 | + vec![create_cli_operation("cmd", "sub")], | ||
| 59 | + ), | ||
| 60 | + create_test_permission_query( | ||
| 61 | + Role::Controlled, | ||
| 62 | + "domain3", | ||
| 63 | + "ticket3", | ||
| 64 | + vec![create_api_operation("perm2"), create_cli_operation("cmd2", "sub2")], | ||
| 65 | + ), | ||
| 66 | + ]; | ||
| 67 | + let result = generate_controlled_device_package(queries); | ||
| 68 | + assert_eq!(3, result.packages.len()); | ||
| 69 | + assert_eq!(ErrCode::Success as i32, result.error_code); | ||
| 70 | +} | ||
| 71 | + | ||
| 72 | + | ||
| 73 | +fn test_generate_controlled_device_package_invalid_role() { | ||
| 74 | + let query = create_test_permission_query( | ||
| 75 | + Role::Controller, | ||
| 76 | + "test_domain", | ||
| 77 | + "test_ticket", | ||
| 78 | + vec![create_api_operation("ohos.permission.TEST")], | ||
| 79 | + ); | ||
| 80 | + let result = generate_controlled_device_package(vec![query]); | ||
| 81 | + assert_eq!(1, result.packages.len()); | ||
| 82 | + assert_eq!(ErrCode::GeneralError as i32, result.error_code); | ||
| 83 | + assert!(result.packages[0].remote_message.device_info.controller_device_id.is_empty()); | ||
| 84 | +} | ||
| 85 | + | ||
| 86 | + | ||
| 87 | +fn test_generate_controlled_device_package_empty_domain_id() { | ||
| 88 | + let query = create_test_permission_query( | ||
| 89 | + Role::Controlled, | ||
| 90 | + "", | ||
| 91 | + "test_ticket", | ||
| 92 | + vec![create_api_operation("ohos.permission.TEST")], | ||
| 93 | + ); | ||
| 94 | + let result = generate_controlled_device_package(vec![query]); | ||
| 95 | + assert_eq!(1, result.packages.len()); | ||
| 96 | + assert_eq!(ErrCode::GeneralError as i32, result.error_code); | ||
| 97 | + assert!(result.packages[0].remote_message.device_info.controller_device_id.is_empty()); | ||
| 98 | +} | ||
| 99 | + | ||
| 100 | + | ||
| 101 | +fn test_generate_controlled_device_package_empty_ticket() { | ||
| 102 | + let query = create_test_permission_query( | ||
| 103 | + Role::Controlled, | ||
| 104 | + "test_domain", | ||
| 105 | + "", | ||
| 106 | + vec![create_api_operation("ohos.permission.TEST")], | ||
| 107 | + ); | ||
| 108 | + let result = generate_controlled_device_package(vec![query]); | ||
| 109 | + assert_eq!(1, result.packages.len()); | ||
| 110 | + assert_eq!(ErrCode::GeneralError as i32, result.error_code); | ||
| 111 | + assert!(result.packages[0].remote_message.device_info.controller_device_id.is_empty()); | ||
| 112 | +} | ||
| 113 | + | ||
| 114 | + | ||
| 115 | +fn test_generate_controlled_device_package_partial_failure() { | ||
| 116 | + let queries = vec![ | ||
| 117 | + create_test_permission_query( | ||
| 118 | + Role::Controlled, | ||
| 119 | + "valid_domain", | ||
| 120 | + "valid_ticket", | ||
| 121 | + vec![create_api_operation("perm")], | ||
| 122 | + ), | ||
| 123 | + create_test_permission_query( | ||
| 124 | + Role::Controller, | ||
| 125 | + "invalid_domain", | ||
| 126 | + "invalid_ticket", | ||
| 127 | + vec![], | ||
| 128 | + ), | ||
| 129 | + ]; | ||
| 130 | + let result = generate_controlled_device_package(queries); | ||
| 131 | + assert_eq!(2, result.packages.len()); | ||
| 132 | + assert_eq!(ErrCode::GeneralError as i32, result.error_code); | ||
| 133 | +} | ||
| 134 | + | ||
| 135 | + | ||
| 136 | +fn test_validate_controlled_permission_query_invalid_role_controller() { | ||
| 137 | + let query = create_test_permission_query( | ||
| 138 | + Role::Controller, | ||
| 139 | + "test_domain", | ||
| 140 | + "test_ticket", | ||
| 141 | + vec![], | ||
| 142 | + ); | ||
| 143 | + let result = validate_controlled_permission_query_stub(&query); | ||
| 144 | + assert!(result.is_err()); | ||
| 145 | + assert_eq!(ErrCode::DataTypeMismatch, result.unwrap_err().code); | ||
| 146 | +} | ||
| 147 | + | ||
| 148 | + | ||
| 149 | +fn test_validate_controlled_permission_query_empty_domain_id() { | ||
| 150 | + let query = create_test_permission_query( | ||
| 151 | + Role::Controlled, | ||
| 152 | + "", | ||
| 153 | + "test_ticket", | ||
| 154 | + vec![], | ||
| 155 | + ); | ||
| 156 | + let result = validate_controlled_permission_query_stub(&query); | ||
| 157 | + assert!(result.is_err()); | ||
| 158 | + assert_eq!(ErrCode::ArgEmpty, result.unwrap_err().code); | ||
| 159 | +} | ||
| 160 | + | ||
| 161 | + | ||
| 162 | +fn test_validate_controlled_permission_query_empty_ticket() { | ||
| 163 | + let query = create_test_permission_query( | ||
| 164 | + Role::Controlled, | ||
| 165 | + "test_domain", | ||
| 166 | + "", | ||
| 167 | + vec![], | ||
| 168 | + ); | ||
| 169 | + let result = validate_controlled_permission_query_stub(&query); | ||
| 170 | + assert!(result.is_err()); | ||
| 171 | + assert_eq!(ErrCode::ArgEmpty, result.unwrap_err().code); | ||
| 172 | +} | ||
| 173 | + | ||
| 174 | + | ||
| 175 | +fn test_validate_controlled_permission_query_valid() { | ||
| 176 | + let query = create_test_permission_query( | ||
| 177 | + Role::Controlled, | ||
| 178 | + "test_domain", | ||
| 179 | + "test_ticket", | ||
| 180 | + vec![create_api_operation("test_permission")], | ||
| 181 | + ); | ||
| 182 | + let result = validate_controlled_permission_query_stub(&query); | ||
| 183 | + assert!(result.is_ok()); | ||
| 184 | +} | ||
| 185 | + | ||
| 186 | + | ||
| 187 | +fn test_parse_cli_and_permission_cli_operation() { | ||
| 188 | + let operations = vec![create_cli_operation("cmd", "sub")]; | ||
| 189 | + let result = parse_cli_and_permission_stub(&operations); | ||
| 190 | + assert!(result.is_ok()); | ||
| 191 | + let (cli_infos, api_permissions) = result.unwrap(); | ||
| 192 | + assert_eq!(1, cli_infos.len()); | ||
| 193 | + assert!(api_permissions.is_empty()); | ||
| 194 | + assert_eq!("cmd", cli_infos[0].cmd_name); | ||
| 195 | + assert_eq!("sub", cli_infos[0].sub_cmd); | ||
| 196 | +} | ||
| 197 | + | ||
| 198 | + | ||
| 199 | +fn test_parse_cli_and_permission_api_operation() { | ||
| 200 | + let operations = vec![create_api_operation("ohos.permission.TEST")]; | ||
| 201 | + let result = parse_cli_and_permission_stub(&operations); | ||
| 202 | + assert!(result.is_ok()); | ||
| 203 | + let (cli_infos, api_permissions) = result.unwrap(); | ||
| 204 | + assert!(cli_infos.is_empty()); | ||
| 205 | + assert_eq!(1, api_permissions.len()); | ||
| 206 | + assert_eq!("ohos.permission.TEST", api_permissions[0]); | ||
| 207 | +} | ||
| 208 | + | ||
| 209 | + | ||
| 210 | +fn test_parse_cli_and_permission_mixed_operations() { | ||
| 211 | + let operations = vec![ | ||
| 212 | + create_cli_operation("cmd1", "sub1"), | ||
| 213 | + create_api_operation("perm1"), | ||
| 214 | + create_cli_operation("cmd2", "sub2"), | ||
| 215 | + create_api_operation("perm2"), | ||
| 216 | + ]; | ||
| 217 | + let result = parse_cli_and_permission_stub(&operations); | ||
| 218 | + assert!(result.is_ok()); | ||
| 219 | + let (cli_infos, api_permissions) = result.unwrap(); | ||
| 220 | + assert_eq!(2, cli_infos.len()); | ||
| 221 | + assert_eq!(2, api_permissions.len()); | ||
| 222 | +} | ||
| 223 | + | ||
| 224 | + | ||
| 225 | +fn test_parse_cli_and_permission_empty_cli_cmd_name() { | ||
| 226 | + let operations = vec![create_cli_operation("", "sub")]; | ||
| 227 | + let result = parse_cli_and_permission_stub(&operations); | ||
| 228 | + assert!(result.is_err()); | ||
| 229 | + assert_eq!(ErrCode::ArgEmpty, result.unwrap_err().code); | ||
| 230 | +} | ||
| 231 | + | ||
| 232 | + | ||
| 233 | +fn test_parse_cli_and_permission_empty_api_permission() { | ||
| 234 | + let operations = vec![create_api_operation("")]; | ||
| 235 | + let result = parse_cli_and_permission_stub(&operations); | ||
| 236 | + assert!(result.is_err()); | ||
| 237 | + assert_eq!(ErrCode::ArgEmpty, result.unwrap_err().code); | ||
| 238 | +} | ||
| 239 | + | ||
| 240 | + | ||
| 241 | +fn test_create_empty_package() { | ||
| 242 | + let package = create_empty_package_stub(); | ||
| 243 | + assert!(package.remote_message.device_info.controller_device_id.is_empty()); | ||
| 244 | + assert!(package.remote_message.device_info.controlled_device_id.is_empty()); | ||
| 245 | + assert!(package.remote_message.remote_auth_message.is_empty()); | ||
| 246 | + assert!(package.remote_message.caller_bundle_name.is_empty()); | ||
| 247 | + assert!(package.challenge.is_empty()); | ||
| 248 | + assert!(package.ticket.is_empty()); | ||
| 249 | +} | ||
| 250 | + | ||
| 251 | + | ||
| 252 | +fn test_build_remote_auth_message_basic() { | ||
| 253 | + let query = create_test_permission_query( | ||
| 254 | + Role::Controlled, | ||
| 255 | + "test_domain", | ||
| 256 | + "test_ticket", | ||
| 257 | + vec![create_api_operation("test_permission")], | ||
| 258 | + ); | ||
| 259 | + let cli_infos = vec![]; | ||
| 260 | + let api_permissions = vec!["test_permission".to_string()]; | ||
| 261 | + | ||
| 262 | + let result = build_remote_auth_message_stub(&query, cli_infos, api_permissions, 1234567890); | ||
| 263 | + assert!(result.is_ok()); | ||
| 264 | + | ||
| 265 | + let json = result.unwrap(); | ||
| 266 | + assert!(json.contains("1234567890")); | ||
| 267 | + assert!(json.contains("test_permission")); | ||
| 268 | + assert!(json.contains("test_domain")); | ||
| 269 | +} | ||
| 270 | + | ||
| 271 | + | ||
| 272 | +fn test_build_remote_auth_message_with_cli() { | ||
| 273 | + let query = create_test_permission_query( | ||
| 274 | + Role::Controlled, | ||
| 275 | + "test_domain", | ||
| 276 | + "test_ticket", | ||
| 277 | + vec![ | ||
| 278 | + create_cli_operation("test_cmd", "test_sub"), | ||
| 279 | + create_api_operation("test_permission"), | ||
| 280 | + ], | ||
| 281 | + ); | ||
| 282 | + let cli_infos = vec![saf_definition::CommandInfo { | ||
| 283 | + cmd_name: "test_cmd".to_string(), | ||
| 284 | + sub_cmd: "test_sub".to_string(), | ||
| 285 | + }]; | ||
| 286 | + let api_permissions = vec!["test_permission".to_string()]; | ||
| 287 | + | ||
| 288 | + let result = build_remote_auth_message_stub(&query, cli_infos, api_permissions, 1234567890); | ||
| 289 | + assert!(result.is_ok()); | ||
| 290 | + | ||
| 291 | + let json = result.unwrap(); | ||
| 292 | + assert!(json.contains("test_cmd")); | ||
| 293 | + assert!(json.contains("test_sub")); | ||
| 294 | +} | ||
| 295 | + | ||
| 296 | +// ==================== Mock tests ==================== | ||
| 297 | + | ||
| 298 | + | ||
| 299 | +fn test_generate_controlled_device_package_sign_success() { | ||
| 300 | + reset_mock_state(); | ||
| 301 | + set_mock_sign_success(true); | ||
| 302 | + set_mock_local_udid("test_udid"); | ||
| 303 | + | ||
| 304 | + let query = create_test_permission_query( | ||
| 305 | + Role::Controlled, | ||
| 306 | + "test_domain", | ||
| 307 | + "test_ticket", | ||
| 308 | + vec![create_api_operation("ohos.permission.TEST")], | ||
| 309 | + ); | ||
| 310 | + let result = generate_controlled_device_package(vec![query]); | ||
| 311 | + | ||
| 312 | + assert_eq!(1, result.packages.len()); | ||
| 313 | + assert_eq!(ErrCode::Success as i32, result.error_code); | ||
| 314 | + assert!(!result.packages[0].ticket.is_empty()); | ||
| 315 | + assert_eq!("mock_controller_udid", result.packages[0].remote_message.device_info.controller_device_id); | ||
| 316 | +} | ||
| 317 | + | ||
| 318 | + | ||
| 319 | +fn test_generate_controlled_device_package_sign_failure() { | ||
| 320 | + reset_mock_state(); | ||
| 321 | + set_mock_sign_success(false); | ||
| 322 | + set_mock_local_udid("mock_local_udid"); | ||
| 323 | + | ||
| 324 | + let query = create_test_permission_query( | ||
| 325 | + Role::Controlled, | ||
| 326 | + "test_domain", | ||
| 327 | + "test_ticket", | ||
| 328 | + vec![create_api_operation("ohos.permission.TEST")], | ||
| 329 | + ); | ||
| 330 | + let result = generate_controlled_device_package(vec![query]); | ||
| 331 | + | ||
| 332 | + assert_eq!(1, result.packages.len()); | ||
| 333 | + assert_eq!(ErrCode::GeneralError as i32, result.error_code); | ||
| 334 | + assert!(result.packages[0].ticket.is_empty()); | ||
| 335 | +} | ||
| 336 | + | ||
| 337 | + | ||
| 338 | +fn test_generate_controlled_device_package_partial_sign_failure() { | ||
| 339 | + reset_mock_state(); | ||
| 340 | + set_mock_sign_success(true); | ||
| 341 | + set_mock_local_udid("mock_local_udid"); | ||
| 342 | + | ||
| 343 | + let valid_query = create_test_permission_query( | ||
| 344 | + Role::Controlled, | ||
| 345 | + "valid_domain", | ||
| 346 | + "valid_ticket", | ||
| 347 | + vec![create_api_operation("perm")], | ||
| 348 | + ); | ||
| 349 | + let invalid_query = create_test_permission_query( | ||
| 350 | + Role::Controlled, | ||
| 351 | + "", | ||
| 352 | + "test_ticket", | ||
| 353 | + vec![create_api_operation("perm")], | ||
| 354 | + ); | ||
| 355 | + | ||
| 356 | + let result = generate_controlled_device_package(vec![valid_query, invalid_query]); | ||
| 357 | + | ||
| 358 | + assert_eq!(2, result.packages.len()); | ||
| 359 | + assert_eq!(ErrCode::GeneralError as i32, result.error_code); | ||
| 360 | +} | ||
| @@ -0,0 +1,311 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | +//! Test cases for generate_controller_device_package | ||
| 17 | + | ||
| 18 | +use saf_definition::{ErrCode, Role, RemoteUserAuthItem, RemoteControlParams, RemoteInfo, PermissionQuery}; | ||
| 19 | +use secure_access_fence_service::ut_core_service_lib_stub::*; | ||
| 20 | + | ||
| 21 | + | ||
| 22 | +fn test_generate_controller_device_package_empty() { | ||
| 23 | + let result = generate_controller_device_package(vec![]); | ||
| 24 | + assert!(result.packages.is_empty()); | ||
| 25 | + assert_eq!(ErrCode::Success as i32, result.error_code); | ||
| 26 | +} | ||
| 27 | + | ||
| 28 | + | ||
| 29 | +fn test_generate_controller_device_package_single_valid() { | ||
| 30 | + let auth_result = create_test_remote_user_auth_results("ohos.permission.TEST", "granted"); | ||
| 31 | + let result = generate_controller_device_package(vec![auth_result]); | ||
| 32 | + assert_eq!(1, result.packages.len()); | ||
| 33 | + assert_eq!(ErrCode::Success as i32, result.error_code); | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | + | ||
| 37 | +fn test_generate_controller_device_package_multiple_valid() { | ||
| 38 | + let auth_results = vec![ | ||
| 39 | + create_test_remote_user_auth_results("perm1", "granted"), | ||
| 40 | + create_test_remote_user_auth_results("perm2", "denied"), | ||
| 41 | + create_test_remote_user_auth_results("perm3", "granted"), | ||
| 42 | + ]; | ||
| 43 | + let result = generate_controller_device_package(auth_results); | ||
| 44 | + assert_eq!(3, result.packages.len()); | ||
| 45 | + assert_eq!(ErrCode::Success as i32, result.error_code); | ||
| 46 | +} | ||
| 47 | + | ||
| 48 | + | ||
| 49 | +fn test_generate_controller_device_package_invalid_role() { | ||
| 50 | + let auth_result = saf_definition::RemoteUserAuthResults { | ||
| 51 | + results: vec![RemoteUserAuthItem { | ||
| 52 | + permission: "perm".to_string(), | ||
| 53 | + auth_result: "granted".to_string(), | ||
| 54 | + }], | ||
| 55 | + permission_query: PermissionQuery { | ||
| 56 | + operation_info: vec![], | ||
| 57 | + need_ticket: true, | ||
| 58 | + ticket_expire_time_ms: 3600000, | ||
| 59 | + caller_token_id: 0, | ||
| 60 | + domain_id: "test_domain".to_string(), | ||
| 61 | + remote_info: RemoteInfo { | ||
| 62 | + role: Role::Controlled, | ||
| 63 | + remote_id: "test_remote_id".to_string(), | ||
| 64 | + domain_id: "test_domain".to_string(), | ||
| 65 | + remote_control_params: RemoteControlParams { | ||
| 66 | + challenge: "1234567890".to_string(), | ||
| 67 | + remote_control_ticket: "test_ticket".to_string(), | ||
| 68 | + controlled_device_name: "controlled".to_string(), | ||
| 69 | + controller_device_name: "controller".to_string(), | ||
| 70 | + sign_verify_msg: String::new(), | ||
| 71 | + }, | ||
| 72 | + }, | ||
| 73 | + }, | ||
| 74 | + }; | ||
| 75 | + let result = generate_controller_device_package(vec![auth_result]); | ||
| 76 | + assert_eq!(1, result.packages.len()); | ||
| 77 | + assert_eq!(ErrCode::GeneralError as i32, result.error_code); | ||
| 78 | +} | ||
| 79 | + | ||
| 80 | + | ||
| 81 | +fn test_generate_controller_device_package_empty_challenge() { | ||
| 82 | + let auth_result = saf_definition::RemoteUserAuthResults { | ||
| 83 | + results: vec![RemoteUserAuthItem { | ||
| 84 | + permission: "perm".to_string(), | ||
| 85 | + auth_result: "granted".to_string(), | ||
| 86 | + }], | ||
| 87 | + permission_query: PermissionQuery { | ||
| 88 | + operation_info: vec![], | ||
| 89 | + need_ticket: true, | ||
| 90 | + ticket_expire_time_ms: 3600000, | ||
| 91 | + caller_token_id: 0, | ||
| 92 | + domain_id: "test_domain".to_string(), | ||
| 93 | + remote_info: RemoteInfo { | ||
| 94 | + role: Role::Controller, | ||
| 95 | + remote_id: "test_remote_id".to_string(), | ||
| 96 | + domain_id: "test_domain".to_string(), | ||
| 97 | + remote_control_params: RemoteControlParams { | ||
| 98 | + challenge: "".to_string(), | ||
| 99 | + remote_control_ticket: "test_ticket".to_string(), | ||
| 100 | + controlled_device_name: "controlled".to_string(), | ||
| 101 | + controller_device_name: "controller".to_string(), | ||
| 102 | + sign_verify_msg: String::new(), | ||
| 103 | + }, | ||
| 104 | + }, | ||
| 105 | + }, | ||
| 106 | + }; | ||
| 107 | + let result = generate_controller_device_package(vec![auth_result]); | ||
| 108 | + assert_eq!(1, result.packages.len()); | ||
| 109 | + assert_eq!(ErrCode::GeneralError as i32, result.error_code); | ||
| 110 | +} | ||
| 111 | + | ||
| 112 | + | ||
| 113 | +fn test_generate_controller_device_package_invalid_challenge_format() { | ||
| 114 | + let auth_result = saf_definition::RemoteUserAuthResults { | ||
| 115 | + results: vec![RemoteUserAuthItem { | ||
| 116 | + permission: "perm".to_string(), | ||
| 117 | + auth_result: "granted".to_string(), | ||
| 118 | + }], | ||
| 119 | + permission_query: PermissionQuery { | ||
| 120 | + operation_info: vec![], | ||
| 121 | + need_ticket: true, | ||
| 122 | + ticket_expire_time_ms: 3600000, | ||
| 123 | + caller_token_id: 0, | ||
| 124 | + domain_id: "test_domain".to_string(), | ||
| 125 | + remote_info: RemoteInfo { | ||
| 126 | + role: Role::Controller, | ||
| 127 | + remote_id: "test_remote_id".to_string(), | ||
| 128 | + domain_id: "test_domain".to_string(), | ||
| 129 | + remote_control_params: RemoteControlParams { | ||
| 130 | + challenge: "invalid_challenge".to_string(), | ||
| 131 | + remote_control_ticket: "test_ticket".to_string(), | ||
| 132 | + controlled_device_name: "controlled".to_string(), | ||
| 133 | + controller_device_name: "controller".to_string(), | ||
| 134 | + sign_verify_msg: String::new(), | ||
| 135 | + }, | ||
| 136 | + }, | ||
| 137 | + }, | ||
| 138 | + }; | ||
| 139 | + let result = generate_controller_device_package(vec![auth_result]); | ||
| 140 | + assert_eq!(1, result.packages.len()); | ||
| 141 | + assert_eq!(ErrCode::GeneralError as i32, result.error_code); | ||
| 142 | +} | ||
| 143 | + | ||
| 144 | + | ||
| 145 | +fn test_generate_controller_device_package_partial_failure() { | ||
| 146 | + let valid_auth = create_test_remote_user_auth_results("perm1", "granted"); | ||
| 147 | + let invalid_auth = saf_definition::RemoteUserAuthResults { | ||
| 148 | + results: vec![RemoteUserAuthItem { | ||
| 149 | + permission: "perm2".to_string(), | ||
| 150 | + auth_result: "granted".to_string(), | ||
| 151 | + }], | ||
| 152 | + permission_query: PermissionQuery { | ||
| 153 | + operation_info: vec![], | ||
| 154 | + need_ticket: true, | ||
| 155 | + ticket_expire_time_ms: 3600000, | ||
| 156 | + caller_token_id: 0, | ||
| 157 | + domain_id: "test_domain".to_string(), | ||
| 158 | + remote_info: RemoteInfo { | ||
| 159 | + role: Role::Controlled, | ||
| 160 | + remote_id: "test_remote_id".to_string(), | ||
| 161 | + domain_id: "test_domain".to_string(), | ||
| 162 | + remote_control_params: RemoteControlParams { | ||
| 163 | + challenge: "1234567890".to_string(), | ||
| 164 | + remote_control_ticket: "test_ticket".to_string(), | ||
| 165 | + controlled_device_name: "controlled".to_string(), | ||
| 166 | + controller_device_name: "controller".to_string(), | ||
| 167 | + sign_verify_msg: String::new(), | ||
| 168 | + }, | ||
| 169 | + }, | ||
| 170 | + }, | ||
| 171 | + }; | ||
| 172 | + let result = generate_controller_device_package(vec![valid_auth, invalid_auth]); | ||
| 173 | + assert_eq!(2, result.packages.len()); | ||
| 174 | + assert_eq!(ErrCode::GeneralError as i32, result.error_code); | ||
| 175 | +} | ||
| 176 | + | ||
| 177 | + | ||
| 178 | +fn test_validate_controller_permission_query_valid() { | ||
| 179 | + let query = PermissionQuery { | ||
| 180 | + operation_info: vec![], | ||
| 181 | + need_ticket: true, | ||
| 182 | + ticket_expire_time_ms: 3600000, | ||
| 183 | + caller_token_id: 0, | ||
| 184 | + domain_id: "test_domain".to_string(), | ||
| 185 | + remote_info: RemoteInfo { | ||
| 186 | + role: Role::Controller, | ||
| 187 | + remote_id: "test_remote_id".to_string(), | ||
| 188 | + domain_id: "test_domain".to_string(), | ||
| 189 | + remote_control_params: RemoteControlParams { | ||
| 190 | + challenge: "1234567890".to_string(), | ||
| 191 | + remote_control_ticket: "test_ticket".to_string(), | ||
| 192 | + controlled_device_name: "controlled".to_string(), | ||
| 193 | + controller_device_name: "controller".to_string(), | ||
| 194 | + sign_verify_msg: String::new(), | ||
| 195 | + }, | ||
| 196 | + }, | ||
| 197 | + }; | ||
| 198 | + let result = validate_controller_permission_query_stub(&query); | ||
| 199 | + assert!(result.is_ok()); | ||
| 200 | +} | ||
| 201 | + | ||
| 202 | + | ||
| 203 | +fn test_validate_controller_permission_query_invalid_role() { | ||
| 204 | + let query = PermissionQuery { | ||
| 205 | + operation_info: vec![], | ||
| 206 | + need_ticket: true, | ||
| 207 | + ticket_expire_time_ms: 3600000, | ||
| 208 | + caller_token_id: 0, | ||
| 209 | + domain_id: "test_domain".to_string(), | ||
| 210 | + remote_info: RemoteInfo { | ||
| 211 | + role: Role::Controlled, | ||
| 212 | + remote_id: "test_remote_id".to_string(), | ||
| 213 | + domain_id: "test_domain".to_string(), | ||
| 214 | + remote_control_params: RemoteControlParams { | ||
| 215 | + challenge: "1234567890".to_string(), | ||
| 216 | + remote_control_ticket: "test_ticket".to_string(), | ||
| 217 | + controlled_device_name: "controlled".to_string(), | ||
| 218 | + controller_device_name: "controller".to_string(), | ||
| 219 | + sign_verify_msg: String::new(), | ||
| 220 | + }, | ||
| 221 | + }, | ||
| 222 | + }; | ||
| 223 | + let result = validate_controller_permission_query_stub(&query); | ||
| 224 | + assert!(result.is_err()); | ||
| 225 | + assert_eq!(ErrCode::DataTypeMismatch, result.unwrap_err().code); | ||
| 226 | +} | ||
| 227 | + | ||
| 228 | + | ||
| 229 | +fn test_build_auth_results_json_single() { | ||
| 230 | + let results = vec![RemoteUserAuthItem { | ||
| 231 | + permission: "perm1".to_string(), | ||
| 232 | + auth_result: "granted".to_string(), | ||
| 233 | + }]; | ||
| 234 | + let result = build_auth_results_json_stub(&results); | ||
| 235 | + assert!(result.is_ok()); | ||
| 236 | + let json = result.unwrap(); | ||
| 237 | + assert!(json.contains("perm1")); | ||
| 238 | + assert!(json.contains("granted")); | ||
| 239 | +} | ||
| 240 | + | ||
| 241 | + | ||
| 242 | +fn test_build_auth_results_json_multiple() { | ||
| 243 | + let results = vec![ | ||
| 244 | + RemoteUserAuthItem { | ||
| 245 | + permission: "perm1".to_string(), | ||
| 246 | + auth_result: "granted".to_string(), | ||
| 247 | + }, | ||
| 248 | + RemoteUserAuthItem { | ||
| 249 | + permission: "perm2".to_string(), | ||
| 250 | + auth_result: "denied".to_string(), | ||
| 251 | + }, | ||
| 252 | + ]; | ||
| 253 | + let result = build_auth_results_json_stub(&results); | ||
| 254 | + assert!(result.is_ok()); | ||
| 255 | + let json = result.unwrap(); | ||
| 256 | + assert!(json.contains("perm1")); | ||
| 257 | + assert!(json.contains("perm2")); | ||
| 258 | +} | ||
| 259 | + | ||
| 260 | + | ||
| 261 | +fn test_build_auth_results_json_empty() { | ||
| 262 | + let results: Vec<RemoteUserAuthItem> = vec![]; | ||
| 263 | + let result = build_auth_results_json_stub(&results); | ||
| 264 | + assert!(result.is_ok()); | ||
| 265 | +} | ||
| 266 | + | ||
| 267 | +// ==================== Mock tests ==================== | ||
| 268 | + | ||
| 269 | + | ||
| 270 | +fn test_generate_controller_device_package_sign_success() { | ||
| 271 | + reset_mock_state(); | ||
| 272 | + set_mock_sign_success(true); | ||
| 273 | + set_mock_local_udid("test_udid"); | ||
| 274 | + | ||
| 275 | + let auth_result = create_test_remote_user_auth_results("ohos.permission.TEST", "granted"); | ||
| 276 | + let result = generate_controller_device_package(vec![auth_result]); | ||
| 277 | + | ||
| 278 | + assert_eq!(1, result.packages.len()); | ||
| 279 | + assert_eq!(ErrCode::Success as i32, result.error_code); | ||
| 280 | + assert!(!result.packages[0].ticket.is_empty()); | ||
| 281 | +} | ||
| 282 | + | ||
| 283 | + | ||
| 284 | +fn test_generate_controller_device_package_sign_failure() { | ||
| 285 | + reset_mock_state(); | ||
| 286 | + set_mock_sign_success(false); | ||
| 287 | + set_mock_local_udid("mock_local_udid"); | ||
| 288 | + | ||
| 289 | + let auth_result = create_test_remote_user_auth_results("ohos.permission.TEST", "granted"); | ||
| 290 | + let result = generate_controller_device_package(vec![auth_result]); | ||
| 291 | + | ||
| 292 | + assert_eq!(1, result.packages.len()); | ||
| 293 | + assert_eq!(ErrCode::GeneralError as i32, result.error_code); | ||
| 294 | + assert!(result.packages[0].ticket.is_empty()); | ||
| 295 | +} | ||
| 296 | + | ||
| 297 | + | ||
| 298 | +fn test_generate_controller_device_package_multiple_with_mock() { | ||
| 299 | + reset_mock_state(); | ||
| 300 | + set_mock_sign_success(true); | ||
| 301 | + set_mock_local_udid("mock_local_udid"); | ||
| 302 | + | ||
| 303 | + let auth_results = vec![ | ||
| 304 | + create_test_remote_user_auth_results("perm1", "granted"), | ||
| 305 | + create_test_remote_user_auth_results("perm2", "denied"), | ||
| 306 | + ]; | ||
| 307 | + let result = generate_controller_device_package(auth_results); | ||
| 308 | + | ||
| 309 | + assert_eq!(2, result.packages.len()); | ||
| 310 | + assert_eq!(ErrCode::Success as i32, result.error_code); | ||
| 311 | +} | ||
| @@ -0,0 +1,19 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | +//! the module test for remote_control | ||
| 17 | +mod generate_controlled_device_package_test; | ||
| 18 | +mod generate_controller_device_package_test; | ||
| 19 | +mod verify_device_package_test; | ||
| @@ -0,0 +1,281 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | +//! Test cases for verify_controlled_device_package and verify_controller_device_package | ||
| 17 | + | ||
| 18 | +use saf_definition::{Role, ErrCode}; | ||
| 19 | +use secure_access_fence_service::ut_core_service_lib_stub::*; | ||
| 20 | + | ||
| 21 | +// ==================== verify_controlled_device_package tests ==================== | ||
| 22 | + | ||
| 23 | + | ||
| 24 | +fn test_verify_controlled_device_package_empty() { | ||
| 25 | + let result = verify_controlled_device_package(100, vec![]); | ||
| 26 | + assert!(result.results.is_empty()); | ||
| 27 | + assert_eq!(result.error_code, ErrCode::InvalidArrayLen as i32); | ||
| 28 | +} | ||
| 29 | + | ||
| 30 | + | ||
| 31 | +fn test_check_controller_device_id_match_true() { | ||
| 32 | + let package = create_test_remote_auth_package("test_udid", "controlled_udid", "1234567890"); | ||
| 33 | + assert!(check_controller_device_id_match_stub("test_udid", &package)); | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | + | ||
| 37 | +fn test_check_controller_device_id_match_false() { | ||
| 38 | + let package = create_test_remote_auth_package("other_udid", "controlled_udid", "1234567890"); | ||
| 39 | + assert!(!check_controller_device_id_match_stub("test_udid", &package)); | ||
| 40 | +} | ||
| 41 | + | ||
| 42 | + | ||
| 43 | +fn test_check_controller_device_id_match_empty() { | ||
| 44 | + let package = create_test_remote_auth_package("", "controlled_udid", "1234567890"); | ||
| 45 | + assert!(!check_controller_device_id_match_stub("test_udid", &package)); | ||
| 46 | +} | ||
| 47 | + | ||
| 48 | + | ||
| 49 | +fn test_parse_challenge_valid() { | ||
| 50 | + assert_eq!(Some(1234567890u64), parse_challenge_stub("1234567890")); | ||
| 51 | +} | ||
| 52 | + | ||
| 53 | + | ||
| 54 | +fn test_parse_challenge_valid_large() { | ||
| 55 | + assert_eq!(Some(18446744073709551615u64), parse_challenge_stub("18446744073709551615")); | ||
| 56 | +} | ||
| 57 | + | ||
| 58 | + | ||
| 59 | +fn test_parse_challenge_invalid_format() { | ||
| 60 | + assert_eq!(None, parse_challenge_stub("invalid")); | ||
| 61 | +} | ||
| 62 | + | ||
| 63 | + | ||
| 64 | +fn test_parse_challenge_empty() { | ||
| 65 | + assert_eq!(None, parse_challenge_stub("")); | ||
| 66 | +} | ||
| 67 | + | ||
| 68 | + | ||
| 69 | +fn test_parse_challenge_negative() { | ||
| 70 | + assert_eq!(None, parse_challenge_stub("-123")); | ||
| 71 | +} | ||
| 72 | + | ||
| 73 | + | ||
| 74 | +fn test_validate_ticket_expiration_not_expired() { | ||
| 75 | + assert!(validate_ticket_expiration_stub(1000, 3600000, 2000)); | ||
| 76 | +} | ||
| 77 | + | ||
| 78 | + | ||
| 79 | +fn test_validate_ticket_expiration_exactly_not_expired() { | ||
| 80 | + assert!(validate_ticket_expiration_stub(1000, 1000, 2000)); | ||
| 81 | +} | ||
| 82 | + | ||
| 83 | + | ||
| 84 | +fn test_validate_ticket_expiration_expired() { | ||
| 85 | + assert!(!validate_ticket_expiration_stub(1000, 100, 2000)); | ||
| 86 | +} | ||
| 87 | + | ||
| 88 | + | ||
| 89 | +fn test_validate_ticket_expiration_expired_exactly() { | ||
| 90 | + assert!(!validate_ticket_expiration_stub(1000, 999, 2000)); | ||
| 91 | +} | ||
| 92 | + | ||
| 93 | + | ||
| 94 | +fn test_validate_ticket_expiration_zero_expire_time() { | ||
| 95 | + assert!(validate_ticket_expiration_stub(1000, 0, 500)); | ||
| 96 | +} | ||
| 97 | + | ||
| 98 | +// ==================== verify_controller_device_package tests ==================== | ||
| 99 | + | ||
| 100 | + | ||
| 101 | +fn test_verify_controller_device_package_empty() { | ||
| 102 | + let remote_info = create_test_remote_info(Role::Controller, "test_remote_id"); | ||
| 103 | + let result = verify_controller_device_package(100, vec![], &remote_info); | ||
| 104 | + assert!(result.results.is_empty()); | ||
| 105 | + assert_eq!(result.error_code, ErrCode::InvalidArrayLen as i32); | ||
| 106 | +} | ||
| 107 | + | ||
| 108 | + | ||
| 109 | +fn test_check_role_is_controller_true() { | ||
| 110 | + assert!(check_role_is_controller_stub(Role::Controller)); | ||
| 111 | +} | ||
| 112 | + | ||
| 113 | + | ||
| 114 | +fn test_check_role_is_controller_false() { | ||
| 115 | + assert!(!check_role_is_controller_stub(Role::Controlled)); | ||
| 116 | +} | ||
| 117 | + | ||
| 118 | +// ==================== Mock tests ==================== | ||
| 119 | + | ||
| 120 | + | ||
| 121 | +fn test_verify_controlled_device_package_with_mock() { | ||
| 122 | + reset_mock_state(); | ||
| 123 | + set_mock_local_udid("mock_controller_udid"); | ||
| 124 | + set_mock_verify_success(true); | ||
| 125 | + | ||
| 126 | + let package = create_test_remote_auth_package("mock_controller_udid", "controlled_udid", "1234567890"); | ||
| 127 | + let result = verify_controlled_device_package(100, vec![package]); | ||
| 128 | + | ||
| 129 | + assert_eq!(result.results.len(), 1); | ||
| 130 | + assert!(result.results[0]); | ||
| 131 | + assert_eq!(result.error_code, ErrCode::Success as i32); | ||
| 132 | +} | ||
| 133 | + | ||
| 134 | + | ||
| 135 | +fn test_verify_controlled_device_package_udid_mismatch_with_mock() { | ||
| 136 | + reset_mock_state(); | ||
| 137 | + set_mock_local_udid("different_udid"); | ||
| 138 | + set_mock_verify_success(true); | ||
| 139 | + | ||
| 140 | + let package = create_test_remote_auth_package("mock_controller_udid", "controlled_udid", "1234567890"); | ||
| 141 | + let result = verify_controlled_device_package(100, vec![package]); | ||
| 142 | + | ||
| 143 | + assert_eq!(result.results.len(), 1); | ||
| 144 | + assert!(!result.results[0]); | ||
| 145 | + assert_eq!(result.error_code, ErrCode::Success as i32); | ||
| 146 | +} | ||
| 147 | + | ||
| 148 | + | ||
| 149 | +fn test_verify_controller_device_package_with_mock() { | ||
| 150 | + reset_mock_state(); | ||
| 151 | + set_mock_local_udid("mock_local_udid"); | ||
| 152 | + set_mock_verify_success(true); | ||
| 153 | + | ||
| 154 | + let remote_info = create_test_remote_info(Role::Controller, "mock_controller_udid"); | ||
| 155 | + let package = create_test_remote_auth_package("mock_controller_udid", "controlled_udid", "1234567890"); | ||
| 156 | + let result = verify_controller_device_package(100, vec![package], &remote_info); | ||
| 157 | + | ||
| 158 | + assert_eq!(result.results.len(), 1); | ||
| 159 | + assert!(result.results[0]); | ||
| 160 | + assert_eq!(result.error_code, ErrCode::Success as i32); | ||
| 161 | +} | ||
| 162 | + | ||
| 163 | + | ||
| 164 | +fn test_verify_controller_device_package_role_mismatch() { | ||
| 165 | + let remote_info = create_test_remote_info(Role::Controlled, "mock_controller_udid"); | ||
| 166 | + let package = create_test_remote_auth_package("mock_controller_udid", "controlled_udid", "1234567890"); | ||
| 167 | + let result = verify_controller_device_package(100, vec![package], &remote_info); | ||
| 168 | + | ||
| 169 | + assert!(result.results.is_empty()); | ||
| 170 | + assert_eq!(result.error_code, ErrCode::DataTypeMismatch as i32); | ||
| 171 | +} | ||
| 172 | + | ||
| 173 | + | ||
| 174 | +fn test_verify_controller_device_package_verify_failure() { | ||
| 175 | + reset_mock_state(); | ||
| 176 | + set_mock_local_udid("mock_local_udid"); | ||
| 177 | + set_mock_verify_success(false); | ||
| 178 | + | ||
| 179 | + let remote_info = create_test_remote_info(Role::Controller, "mock_controller_udid"); | ||
| 180 | + let package = create_test_remote_auth_package("mock_controller_udid", "controlled_udid", "1234567890"); | ||
| 181 | + let result = verify_controller_device_package(100, vec![package], &remote_info); | ||
| 182 | + | ||
| 183 | + assert_eq!(result.results.len(), 1); | ||
| 184 | + assert!(!result.results[0]); | ||
| 185 | + assert_eq!(result.error_code, ErrCode::Success as i32); | ||
| 186 | +} | ||
| 187 | + | ||
| 188 | +// ==================== Exception tests ==================== | ||
| 189 | + | ||
| 190 | + | ||
| 191 | +fn test_verify_controlled_device_package_invalid_challenge() { | ||
| 192 | + reset_mock_state(); | ||
| 193 | + set_mock_local_udid("mock_controller_udid"); | ||
| 194 | + set_mock_verify_success(true); | ||
| 195 | + | ||
| 196 | + let package = create_test_remote_auth_package("mock_controller_udid", "controlled_udid", "invalid_challenge"); | ||
| 197 | + let result = verify_controlled_device_package(100, vec![package]); | ||
| 198 | + | ||
| 199 | + assert_eq!(result.results.len(), 1); | ||
| 200 | + assert!(!result.results[0]); | ||
| 201 | + assert_eq!(result.error_code, ErrCode::Success as i32); | ||
| 202 | +} | ||
| 203 | + | ||
| 204 | + | ||
| 205 | +fn test_verify_controlled_device_package_empty_challenge() { | ||
| 206 | + reset_mock_state(); | ||
| 207 | + set_mock_local_udid("mock_controller_udid"); | ||
| 208 | + set_mock_verify_success(true); | ||
| 209 | + | ||
| 210 | + let package = create_test_remote_auth_package("mock_controller_udid", "controlled_udid", ""); | ||
| 211 | + let result = verify_controlled_device_package(100, vec![package]); | ||
| 212 | + | ||
| 213 | + assert_eq!(result.results.len(), 1); | ||
| 214 | + assert!(!result.results[0]); | ||
| 215 | + assert_eq!(result.error_code, ErrCode::Success as i32); | ||
| 216 | +} | ||
| 217 | + | ||
| 218 | + | ||
| 219 | +fn test_verify_controlled_device_package_multiple_packages_partial_failure() { | ||
| 220 | + reset_mock_state(); | ||
| 221 | + set_mock_local_udid("mock_controller_udid"); | ||
| 222 | + set_mock_verify_success(true); | ||
| 223 | + | ||
| 224 | + let valid_package = create_test_remote_auth_package("mock_controller_udid", "controlled_udid", "1234567890"); | ||
| 225 | + let invalid_package = create_test_remote_auth_package("different_udid", "controlled_udid", "1234567890"); | ||
| 226 | + | ||
| 227 | + let result = verify_controlled_device_package(100, vec![valid_package, invalid_package]); | ||
| 228 | + | ||
| 229 | + assert_eq!(result.results.len(), 2); | ||
| 230 | + assert!(result.results[0]); | ||
| 231 | + assert!(!result.results[1]); | ||
| 232 | + assert_eq!(result.error_code, ErrCode::Success as i32); | ||
| 233 | +} | ||
| 234 | + | ||
| 235 | + | ||
| 236 | +fn test_verify_controller_device_package_invalid_challenge() { | ||
| 237 | + reset_mock_state(); | ||
| 238 | + set_mock_local_udid("mock_local_udid"); | ||
| 239 | + set_mock_verify_success(true); | ||
| 240 | + | ||
| 241 | + let remote_info = create_test_remote_info(Role::Controller, "mock_controller_udid"); | ||
| 242 | + let package = create_test_remote_auth_package("mock_controller_udid", "controlled_udid", "invalid_challenge"); | ||
| 243 | + let result = verify_controller_device_package(100, vec![package], &remote_info); | ||
| 244 | + | ||
| 245 | + assert_eq!(result.results.len(), 1); | ||
| 246 | + assert!(!result.results[0]); | ||
| 247 | + assert_eq!(result.error_code, ErrCode::Success as i32); | ||
| 248 | +} | ||
| 249 | + | ||
| 250 | + | ||
| 251 | +fn test_verify_controller_device_package_empty_challenge() { | ||
| 252 | + reset_mock_state(); | ||
| 253 | + set_mock_local_udid("mock_local_udid"); | ||
| 254 | + set_mock_verify_success(true); | ||
| 255 | + | ||
| 256 | + let remote_info = create_test_remote_info(Role::Controller, "mock_controller_udid"); | ||
| 257 | + let package = create_test_remote_auth_package("mock_controller_udid", "controlled_udid", ""); | ||
| 258 | + let result = verify_controller_device_package(100, vec![package], &remote_info); | ||
| 259 | + | ||
| 260 | + assert_eq!(result.results.len(), 1); | ||
| 261 | + assert!(!result.results[0]); | ||
| 262 | + assert_eq!(result.error_code, ErrCode::Success as i32); | ||
| 263 | +} | ||
| 264 | + | ||
| 265 | + | ||
| 266 | +fn test_verify_controller_device_package_multiple_packages_partial_failure() { | ||
| 267 | + reset_mock_state(); | ||
| 268 | + set_mock_local_udid("mock_local_udid"); | ||
| 269 | + set_mock_verify_success(true); | ||
| 270 | + | ||
| 271 | + let remote_info = create_test_remote_info(Role::Controller, "mock_controller_udid"); | ||
| 272 | + let valid_package = create_test_remote_auth_package("mock_controller_udid", "controlled_udid", "1234567890"); | ||
| 273 | + let invalid_package = create_test_remote_auth_package("different_id", "controlled_udid", "1234567890"); | ||
| 274 | + | ||
| 275 | + let result = verify_controller_device_package(100, vec![valid_package, invalid_package], &remote_info); | ||
| 276 | + | ||
| 277 | + assert_eq!(result.results.len(), 2); | ||
| 278 | + assert!(result.results[0]); | ||
| 279 | + assert!(!result.results[1]); | ||
| 280 | + assert_eq!(result.error_code, ErrCode::Success as i32); | ||
| 281 | +} | ||
| @@ -0,0 +1,179 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | +/// the module test stub for controlled_device | ||
| 17 | + | ||
| 18 | +use saf_definition::{ | ||
| 19 | + CommandInfo, OperationInfo, OperationType, PermissionQuery, RemoteInfo, | ||
| 20 | + RemoteControlParams, Role, | ||
| 21 | +}; | ||
| 22 | +use crate::remote_control::controlled_device::*; | ||
| 23 | + | ||
| 24 | +pub use crate::remote_control::{ | ||
| 25 | + BatchGenerateResult, | ||
| 26 | + BatchVerifyResult, | ||
| 27 | + generate_controlled_device_package, | ||
| 28 | + verify_controlled_device_package, | ||
| 29 | +}; | ||
| 30 | + | ||
| 31 | +/// validate_controlled_permission_query stub | ||
| 32 | + | ||
| 33 | +pub fn validate_controlled_permission_query_stub(query: &PermissionQuery) -> saf_definition::Result<()> { | ||
| 34 | + validate_controlled_permission_query(query) | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | +/// parse_cli_and_permission stub | ||
| 38 | + | ||
| 39 | +pub fn parse_cli_and_permission_stub( | ||
| 40 | + operation_info: &[OperationInfo] | ||
| 41 | +) -> saf_definition::Result<(Vec<CommandInfo>, Vec<String>)> { | ||
| 42 | + parse_cli_and_permission(operation_info) | ||
| 43 | +} | ||
| 44 | + | ||
| 45 | +/// build_remote_auth_message stub | ||
| 46 | + | ||
| 47 | +pub fn build_remote_auth_message_stub( | ||
| 48 | + query: &PermissionQuery, | ||
| 49 | + cli_infos: Vec<CommandInfo>, | ||
| 50 | + api_permissions: Vec<String>, | ||
| 51 | + challenge: u64, | ||
| 52 | +) -> saf_definition::Result<String> { | ||
| 53 | + build_remote_auth_message(query, cli_infos, api_permissions, challenge) | ||
| 54 | +} | ||
| 55 | + | ||
| 56 | +/// create_empty_package stub | ||
| 57 | + | ||
| 58 | +pub fn create_empty_package_stub() -> saf_definition::RemoteAuthPackage { | ||
| 59 | + crate::remote_control::create_empty_package() | ||
| 60 | +} | ||
| 61 | + | ||
| 62 | +/// check_controller_device_id_match stub | ||
| 63 | + | ||
| 64 | +pub fn check_controller_device_id_match_stub(local_udid: &str, package: &saf_definition::RemoteAuthPackage) -> bool { | ||
| 65 | + check_controller_device_id_match(local_udid, package) | ||
| 66 | +} | ||
| 67 | + | ||
| 68 | +/// parse_challenge stub | ||
| 69 | + | ||
| 70 | +pub fn parse_challenge_stub(challenge_str: &str) -> Option<u64> { | ||
| 71 | + crate::remote_control::parse_challenge(challenge_str) | ||
| 72 | +} | ||
| 73 | + | ||
| 74 | +/// validate_ticket_expiration stub | ||
| 75 | + | ||
| 76 | +pub fn validate_ticket_expiration_stub(challenge: u64, expire_time_ms: u64, current_time: u64) -> bool { | ||
| 77 | + match challenge.checked_add(expire_time_ms) { | ||
| 78 | + Some(expire_time) => expire_time >= current_time, | ||
| 79 | + None => false, | ||
| 80 | + } | ||
| 81 | +} | ||
| 82 | + | ||
| 83 | +/// create_test_remote_auth_package | ||
| 84 | +pub fn create_test_remote_auth_package(controller_id: &str, controlled_id: &str, challenge: &str) -> saf_definition::RemoteAuthPackage { | ||
| 85 | + let expire_time_ms: u64 = 86400000; // 24h | ||
| 86 | + let now_ms: u64 = std::time::SystemTime::now() | ||
| 87 | + .duration_since(std::time::UNIX_EPOCH) | ||
| 88 | + .unwrap() | ||
| 89 | + .as_millis() as u64; | ||
| 90 | + let challenge_value = match challenge.parse::<u64>() { | ||
| 91 | + Ok(_) => now_ms.to_string(), | ||
| 92 | + Err(_) => challenge.to_string(), | ||
| 93 | + }; | ||
| 94 | + saf_definition::RemoteAuthPackage { | ||
| 95 | + remote_message: saf_definition::RemoteMessage { | ||
| 96 | + device_info: saf_definition::DeviceIdHeader { | ||
| 97 | + controller_device_id: controller_id.to_string(), | ||
| 98 | + controlled_device_id: controlled_id.to_string(), | ||
| 99 | + }, | ||
| 100 | + remote_auth_message: format!("{{\"ticketExpireTimeMs\":\"{}\"}}", expire_time_ms), | ||
| 101 | + caller_bundle_name: String::new(), | ||
| 102 | + }, | ||
| 103 | + challenge: challenge_value, | ||
| 104 | + ticket: String::new(), | ||
| 105 | + } | ||
| 106 | +} | ||
| 107 | + | ||
| 108 | +/// create_test_permission_query | ||
| 109 | +pub fn create_test_permission_query( | ||
| 110 | + role: Role, | ||
| 111 | + domain_id: &str, | ||
| 112 | + remote_control_ticket: &str, | ||
| 113 | + operation_info: Vec<OperationInfo>, | ||
| 114 | +) -> PermissionQuery { | ||
| 115 | + PermissionQuery { | ||
| 116 | + operation_info, | ||
| 117 | + need_ticket: true, | ||
| 118 | + ticket_expire_time_ms: 3600000, | ||
| 119 | + caller_token_id: 0, | ||
| 120 | + domain_id: domain_id.to_string(), | ||
| 121 | + remote_info: RemoteInfo { | ||
| 122 | + role, | ||
| 123 | + remote_id: "test_remote_id".to_string(), | ||
| 124 | + domain_id: domain_id.to_string(), | ||
| 125 | + remote_control_params: RemoteControlParams { | ||
| 126 | + challenge: "1234567890".to_string(), | ||
| 127 | + remote_control_ticket: remote_control_ticket.to_string(), | ||
| 128 | + controlled_device_name: "controlled_device".to_string(), | ||
| 129 | + controller_device_name: "controller_device".to_string(), | ||
| 130 | + sign_verify_msg: "sign_verify_msg".to_string(), | ||
| 131 | + }, | ||
| 132 | + }, | ||
| 133 | + } | ||
| 134 | +} | ||
| 135 | + | ||
| 136 | +/// create_cli_operation | ||
| 137 | +pub fn create_cli_operation(cmd_name: &str, sub_cmd: &str) -> OperationInfo { | ||
| 138 | + OperationInfo { | ||
| 139 | + operation_type: OperationType::Cli, | ||
| 140 | + cli_cmd_info: CommandInfo { | ||
| 141 | + cmd_name: cmd_name.to_string(), | ||
| 142 | + sub_cmd: sub_cmd.to_string(), | ||
| 143 | + }, | ||
| 144 | + permission: String::new(), | ||
| 145 | + } | ||
| 146 | +} | ||
| 147 | + | ||
| 148 | +/// create_api_operation | ||
| 149 | +pub fn create_api_operation(permission: &str) -> OperationInfo { | ||
| 150 | + OperationInfo { | ||
| 151 | + operation_type: OperationType::Api, | ||
| 152 | + cli_cmd_info: CommandInfo { | ||
| 153 | + cmd_name: String::new(), | ||
| 154 | + sub_cmd: String::new(), | ||
| 155 | + }, | ||
| 156 | + permission: permission.to_string(), | ||
| 157 | + } | ||
| 158 | +} | ||
| 159 | + | ||
| 160 | +/// set_mock_local_udid | ||
| 161 | +pub fn set_mock_local_udid(udid: &str) { | ||
| 162 | + saf_common::set_mock_local_udid(udid); | ||
| 163 | +} | ||
| 164 | + | ||
| 165 | +/// set_mock_sign_success | ||
| 166 | +pub fn set_mock_sign_success(success: bool) { | ||
| 167 | + crate::remote_control::account_based_auth_manager::set_mock_sign_success(success); | ||
| 168 | +} | ||
| 169 | + | ||
| 170 | +/// set_mock_verify_success | ||
| 171 | +pub fn set_mock_verify_success(success: bool) { | ||
| 172 | + crate::remote_control::account_based_auth_manager::set_mock_verify_success(success); | ||
| 173 | +} | ||
| 174 | + | ||
| 175 | +/// reset all mock state to defaults | ||
| 176 | +pub fn reset_mock_state() { | ||
| 177 | + crate::remote_control::account_based_auth_manager::reset_mock_state(); | ||
| 178 | + saf_common::reset_mock_local_udid(); | ||
| 179 | +} | ||
| @@ -0,0 +1,93 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | +/// the module test stub for controller_device | ||
| 17 | + | ||
| 18 | +use saf_definition::{Role, RemoteUserAuthItem, PermissionQuery}; | ||
| 19 | +use crate::remote_control::controller_device::*; | ||
| 20 | + | ||
| 21 | +pub use crate::remote_control::{ | ||
| 22 | + BatchGenerateResult, | ||
| 23 | + BatchVerifyResult, | ||
| 24 | + generate_controller_device_package, | ||
| 25 | + verify_controller_device_package, | ||
| 26 | +}; | ||
| 27 | + | ||
| 28 | +/// validate_controller_permission_query stub | ||
| 29 | + | ||
| 30 | +pub fn validate_controller_permission_query_stub(query: &PermissionQuery) -> saf_definition::Result<()> { | ||
| 31 | + validate_controller_permission_query(query) | ||
| 32 | +} | ||
| 33 | + | ||
| 34 | +/// build_auth_results_json stub | ||
| 35 | + | ||
| 36 | +pub fn build_auth_results_json_stub(results: &[RemoteUserAuthItem]) -> saf_definition::Result<String> { | ||
| 37 | + build_auth_results_json(results) | ||
| 38 | +} | ||
| 39 | + | ||
| 40 | +/// build_controller_remote_auth_message stub | ||
| 41 | + | ||
| 42 | +pub fn build_controller_remote_auth_message_stub( | ||
| 43 | + query: &PermissionQuery, | ||
| 44 | + auth_results_json: &str, | ||
| 45 | + challenge: u64, | ||
| 46 | + local_device_id: &str, | ||
| 47 | + permissions: &[String], | ||
| 48 | +) -> saf_definition::Result<String> { | ||
| 49 | + build_controller_remote_auth_message(query, auth_results_json, challenge, local_device_id, permissions) | ||
| 50 | +} | ||
| 51 | + | ||
| 52 | +/// check_role_is_controller stub | ||
| 53 | + | ||
| 54 | +pub fn check_role_is_controller_stub(role: saf_definition::Role) -> bool { | ||
| 55 | + check_role_is_controller(role) | ||
| 56 | +} | ||
| 57 | + | ||
| 58 | +use crate::remote_control::controlled_device::ut_controlled_device_stub::{create_test_permission_query, create_api_operation}; | ||
| 59 | + | ||
| 60 | +/// create_test_remote_user_auth_results | ||
| 61 | +pub fn create_test_remote_user_auth_results( | ||
| 62 | + permission: &str, | ||
| 63 | + auth_result: &str, | ||
| 64 | +) -> saf_definition::RemoteUserAuthResults { | ||
| 65 | + saf_definition::RemoteUserAuthResults { | ||
| 66 | + results: vec![RemoteUserAuthItem { | ||
| 67 | + permission: permission.to_string(), | ||
| 68 | + auth_result: auth_result.to_string(), | ||
| 69 | + }], | ||
| 70 | + permission_query: create_test_permission_query( | ||
| 71 | + Role::Controller, | ||
| 72 | + "test_domain", | ||
| 73 | + "test_ticket", | ||
| 74 | + vec![create_api_operation(permission)], | ||
| 75 | + ), | ||
| 76 | + } | ||
| 77 | +} | ||
| 78 | + | ||
| 79 | +/// create_test_remote_info | ||
| 80 | +pub fn create_test_remote_info(role: saf_definition::Role, remote_id: &str) -> saf_definition::RemoteInfo { | ||
| 81 | + saf_definition::RemoteInfo { | ||
| 82 | + role, | ||
| 83 | + remote_id: remote_id.to_string(), | ||
| 84 | + domain_id: "test_domain".to_string(), | ||
| 85 | + remote_control_params: saf_definition::RemoteControlParams { | ||
| 86 | + challenge: "1234567890".to_string(), | ||
| 87 | + remote_control_ticket: "test_ticket".to_string(), | ||
| 88 | + controlled_device_name: "controlled".to_string(), | ||
| 89 | + controller_device_name: "controller".to_string(), | ||
| 90 | + sign_verify_msg: String::new(), | ||
| 91 | + }, | ||
| 92 | + } | ||
| 93 | +} | ||
| @@ -0,0 +1,18 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | +/// the module test stub for secure_access_fence core_service | ||
| 17 | +pub use crate::remote_control::ut_controlled_device_stub::*; | ||
| 18 | +pub use crate::remote_control::ut_controller_device_stub::*; | ||