- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
270 lines
7.5 KiB
PHP
270 lines
7.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Framework\Cryptography\DerivedKey;
|
|
|
|
it('creates derived key with valid parameters', function () {
|
|
$key = str_repeat('a', 32);
|
|
$salt = str_repeat('b', 32);
|
|
|
|
$derivedKey = new DerivedKey(
|
|
key: $key,
|
|
salt: $salt,
|
|
algorithm: 'pbkdf2-sha256',
|
|
iterations: 100000,
|
|
keyLength: 32
|
|
);
|
|
|
|
expect($derivedKey->getKey())->toBe($key);
|
|
expect($derivedKey->getSalt())->toBe($salt);
|
|
expect($derivedKey->getAlgorithm())->toBe('pbkdf2-sha256');
|
|
expect($derivedKey->getIterations())->toBe(100000);
|
|
expect($derivedKey->getKeyLength())->toBe(32);
|
|
});
|
|
|
|
it('throws exception for empty key', function () {
|
|
expect(fn () => new DerivedKey(
|
|
key: '',
|
|
salt: str_repeat('b', 32),
|
|
algorithm: 'pbkdf2-sha256',
|
|
iterations: 100000,
|
|
keyLength: 32
|
|
))->toThrow(InvalidArgumentException::class, 'Key cannot be empty');
|
|
});
|
|
|
|
it('throws exception for empty salt', function () {
|
|
expect(fn () => new DerivedKey(
|
|
key: str_repeat('a', 32),
|
|
salt: '',
|
|
algorithm: 'pbkdf2-sha256',
|
|
iterations: 100000,
|
|
keyLength: 32
|
|
))->toThrow(InvalidArgumentException::class, 'Salt cannot be empty');
|
|
});
|
|
|
|
it('throws exception for key length mismatch', function () {
|
|
expect(fn () => new DerivedKey(
|
|
key: str_repeat('a', 16), // 16 bytes
|
|
salt: str_repeat('b', 32),
|
|
algorithm: 'pbkdf2-sha256',
|
|
iterations: 100000,
|
|
keyLength: 32 // Claims 32 bytes
|
|
))->toThrow(InvalidArgumentException::class, 'Key length does not match specified length');
|
|
});
|
|
|
|
it('provides hex representation', function () {
|
|
$key = "\x01\x02\x03\x04";
|
|
$salt = "\x05\x06\x07\x08";
|
|
|
|
$derivedKey = new DerivedKey(
|
|
key: $key,
|
|
salt: $salt,
|
|
algorithm: 'pbkdf2-sha256',
|
|
iterations: 100000,
|
|
keyLength: 4
|
|
);
|
|
|
|
expect($derivedKey->getKeyHex())->toBe('01020304');
|
|
expect($derivedKey->getSaltHex())->toBe('05060708');
|
|
});
|
|
|
|
it('provides base64 representation', function () {
|
|
$key = 'test-key-data';
|
|
$salt = 'test-salt-data';
|
|
|
|
$derivedKey = new DerivedKey(
|
|
key: $key,
|
|
salt: $salt,
|
|
algorithm: 'pbkdf2-sha256',
|
|
iterations: 100000,
|
|
keyLength: strlen($key)
|
|
);
|
|
|
|
expect($derivedKey->getKeyBase64())->toBe(base64_encode($key));
|
|
expect($derivedKey->getSaltBase64())->toBe(base64_encode($salt));
|
|
});
|
|
|
|
it('checks equality correctly', function () {
|
|
$key = str_repeat('a', 32);
|
|
$salt = str_repeat('b', 32);
|
|
|
|
$derivedKey1 = new DerivedKey(
|
|
key: $key,
|
|
salt: $salt,
|
|
algorithm: 'pbkdf2-sha256',
|
|
iterations: 100000,
|
|
keyLength: 32
|
|
);
|
|
|
|
$derivedKey2 = new DerivedKey(
|
|
key: $key,
|
|
salt: $salt,
|
|
algorithm: 'pbkdf2-sha256',
|
|
iterations: 100000,
|
|
keyLength: 32
|
|
);
|
|
|
|
$derivedKey3 = new DerivedKey(
|
|
key: str_repeat('c', 32), // Different key
|
|
salt: $salt,
|
|
algorithm: 'pbkdf2-sha256',
|
|
iterations: 100000,
|
|
keyLength: 32
|
|
);
|
|
|
|
expect($derivedKey1->equals($derivedKey2))->toBeTrue();
|
|
expect($derivedKey1->equals($derivedKey3))->toBeFalse();
|
|
});
|
|
|
|
it('exports to array correctly', function () {
|
|
$derivedKey = new DerivedKey(
|
|
key: str_repeat('a', 32),
|
|
salt: str_repeat('b', 32),
|
|
algorithm: 'argon2id',
|
|
iterations: 4,
|
|
keyLength: 32,
|
|
memoryCost: 65536,
|
|
threads: 3
|
|
);
|
|
|
|
$array = $derivedKey->toArray();
|
|
|
|
expect($array)->toHaveKey('key');
|
|
expect($array)->toHaveKey('salt');
|
|
expect($array)->toHaveKey('algorithm');
|
|
expect($array)->toHaveKey('iterations');
|
|
expect($array)->toHaveKey('key_length');
|
|
expect($array)->toHaveKey('memory_cost');
|
|
expect($array)->toHaveKey('threads');
|
|
|
|
expect($array['algorithm'])->toBe('argon2id');
|
|
expect($array['memory_cost'])->toBe(65536);
|
|
expect($array['threads'])->toBe(3);
|
|
});
|
|
|
|
it('creates from array correctly', function () {
|
|
$originalKey = new DerivedKey(
|
|
key: str_repeat('a', 32),
|
|
salt: str_repeat('b', 32),
|
|
algorithm: 'pbkdf2-sha256',
|
|
iterations: 100000,
|
|
keyLength: 32
|
|
);
|
|
|
|
$array = $originalKey->toArray();
|
|
$restoredKey = DerivedKey::fromArray($array);
|
|
|
|
expect($restoredKey->equals($originalKey))->toBeTrue();
|
|
});
|
|
|
|
it('creates from hex correctly', function () {
|
|
$keyHex = '0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20';
|
|
$saltHex = 'abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef';
|
|
|
|
$derivedKey = DerivedKey::fromHex(
|
|
keyHex: $keyHex,
|
|
saltHex: $saltHex,
|
|
algorithm: 'pbkdf2-sha256',
|
|
iterations: 100000,
|
|
keyLength: 32
|
|
);
|
|
|
|
expect($derivedKey->getKeyHex())->toBe($keyHex);
|
|
expect($derivedKey->getSaltHex())->toBe($saltHex);
|
|
});
|
|
|
|
it('provides summary information', function () {
|
|
$derivedKey = new DerivedKey(
|
|
key: str_repeat('a', 32),
|
|
salt: str_repeat('b', 16),
|
|
algorithm: 'pbkdf2-sha256',
|
|
iterations: 100000,
|
|
keyLength: 32
|
|
);
|
|
|
|
$summary = $derivedKey->getSummary();
|
|
|
|
expect($summary)->toHaveKey('algorithm');
|
|
expect($summary)->toHaveKey('iterations');
|
|
expect($summary)->toHaveKey('key_length');
|
|
expect($summary)->toHaveKey('salt_length');
|
|
expect($summary['salt_length'])->toBe(16);
|
|
});
|
|
|
|
it('identifies algorithm types correctly', function () {
|
|
$pbkdf2Key = new DerivedKey(
|
|
key: str_repeat('a', 32),
|
|
salt: str_repeat('b', 32),
|
|
algorithm: 'pbkdf2-sha256',
|
|
iterations: 100000,
|
|
keyLength: 32
|
|
);
|
|
|
|
$argon2Key = new DerivedKey(
|
|
key: str_repeat('a', 32),
|
|
salt: str_repeat('b', 32),
|
|
algorithm: 'argon2id',
|
|
iterations: 4,
|
|
keyLength: 32
|
|
);
|
|
|
|
$scryptKey = new DerivedKey(
|
|
key: str_repeat('a', 32),
|
|
salt: str_repeat('b', 32),
|
|
algorithm: 'scrypt',
|
|
iterations: 16384,
|
|
keyLength: 32
|
|
);
|
|
|
|
expect($pbkdf2Key->isPbkdf2())->toBeTrue();
|
|
expect($pbkdf2Key->isArgon2())->toBeFalse();
|
|
expect($pbkdf2Key->isScrypt())->toBeFalse();
|
|
|
|
expect($argon2Key->isArgon2())->toBeTrue();
|
|
expect($argon2Key->isPbkdf2())->toBeFalse();
|
|
|
|
expect($scryptKey->isScrypt())->toBeTrue();
|
|
expect($scryptKey->isPbkdf2())->toBeFalse();
|
|
});
|
|
|
|
it('handles scrypt parameters correctly', function () {
|
|
$derivedKey = new DerivedKey(
|
|
key: str_repeat('a', 32),
|
|
salt: str_repeat('b', 32),
|
|
algorithm: 'scrypt',
|
|
iterations: 16384,
|
|
keyLength: 32,
|
|
blockSize: 8,
|
|
parallelization: 1
|
|
);
|
|
|
|
expect($derivedKey->getBlockSize())->toBe(8);
|
|
expect($derivedKey->getParallelization())->toBe(1);
|
|
});
|
|
|
|
it('throws exception for missing required fields in fromArray', function () {
|
|
$incompleteData = [
|
|
'key' => base64_encode(str_repeat('a', 32)),
|
|
'salt' => base64_encode(str_repeat('b', 32)),
|
|
'algorithm' => 'pbkdf2-sha256',
|
|
// Missing iterations and key_length
|
|
];
|
|
|
|
expect(fn () => DerivedKey::fromArray($incompleteData))
|
|
->toThrow(InvalidArgumentException::class, 'Missing required field');
|
|
});
|
|
|
|
it('throws exception for invalid base64 in fromArray', function () {
|
|
$invalidData = [
|
|
'key' => 'invalid-base64!@#',
|
|
'salt' => base64_encode(str_repeat('b', 32)),
|
|
'algorithm' => 'pbkdf2-sha256',
|
|
'iterations' => 100000,
|
|
'key_length' => 32,
|
|
];
|
|
|
|
expect(fn () => DerivedKey::fromArray($invalidData))
|
|
->toThrow(InvalidArgumentException::class, 'Failed to decode Base64 data');
|
|
});
|