2021-12-08 16:53:43 +01:00
|
|
|
<?php
|
|
|
|
|
2022-11-18 18:06:46 +01:00
|
|
|
use Core\Objects\AesStream;
|
2021-12-08 16:53:43 +01:00
|
|
|
|
|
|
|
class AesStreamTest extends PHPUnit\Framework\TestCase {
|
|
|
|
|
|
|
|
static string $TEMP_FILE;
|
|
|
|
|
|
|
|
public static function setUpBeforeClass(): void {
|
|
|
|
AesStreamTest::$TEMP_FILE = tempnam(sys_get_temp_dir(), 'aesTest');
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function tearDownAfterClass(): void {
|
|
|
|
unlink(AesStreamTest::$TEMP_FILE);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testConstructorInvalidKey1() {
|
|
|
|
$this->expectExceptionMessage("Invalid Key Size");
|
|
|
|
$this->expectException(\Exception::class);
|
|
|
|
new AesStream("", "");
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testConstructorInvalidKey2() {
|
|
|
|
$this->expectExceptionMessage("Invalid Key Size");
|
|
|
|
$this->expectException(\Exception::class);
|
|
|
|
new AesStream(str_repeat("A",15), "");
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testConstructorInvalidKey3() {
|
|
|
|
$this->expectExceptionMessage("Invalid Key Size");
|
|
|
|
$this->expectException(\Exception::class);
|
|
|
|
new AesStream(str_repeat("A",33), "");
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testConstructorInvalidIV1() {
|
|
|
|
$this->expectExceptionMessage("Invalid IV Size");
|
|
|
|
$this->expectException(\Exception::class);
|
|
|
|
new AesStream(str_repeat("A",32), str_repeat("B", 17));
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testConstructorValid() {
|
|
|
|
$key = str_repeat("A",32);
|
|
|
|
$iv = str_repeat("B", 16);
|
|
|
|
$aesStream = new AesStream($key, $iv);
|
|
|
|
$this->assertInstanceOf(AesStream::class, $aesStream);
|
|
|
|
$this->assertEquals($key, $aesStream->getKey());
|
|
|
|
$this->assertEquals($iv, $aesStream->getIV());
|
|
|
|
$this->assertEquals("aes-256-ctr", $aesStream->getCipherMode());
|
|
|
|
}
|
|
|
|
|
|
|
|
private function getOutput(string $chunk, string &$data) {
|
|
|
|
$data .= $chunk;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testEncrypt() {
|
|
|
|
$key = str_repeat("A", 32);
|
|
|
|
$iv = str_repeat("B", 16);
|
|
|
|
$aesStream = new AesStream($key, $iv);
|
|
|
|
|
|
|
|
$data = [
|
|
|
|
"43" => "8c", # small block test 1 (1 byte)
|
|
|
|
"abcd" => "6424", # small block test 2 (2 byte)
|
|
|
|
"a37c599429cfdefde6546ad6d7082a" => "6c9539264abc8cae39308dbc86e768", # small block test 3 (15 byte)
|
|
|
|
"43b3504077482bd9bf8c3c08ad3c937f" => "8c5a30f2143b798a60e8db62fcd3d1f7", # one block (16 byte)
|
|
|
|
"9b241a3d7e9f03f6e66a8fa0cba3221008eda86f465e3fbfb0f3a4d3527cffb7"
|
|
|
|
=> "54cd7a8f1dec51a5390e68ca9a4c60986aaafadd42b6960a09deedfa7f2cf1c3" # two blocks (16 byte)
|
|
|
|
];
|
|
|
|
|
|
|
|
foreach ($data as $pt => $ct) {
|
|
|
|
$output = "";
|
|
|
|
file_put_contents(AesStreamTest::$TEMP_FILE, hex2bin($pt));
|
|
|
|
$aesStream->setInputFile(AesStreamTest::$TEMP_FILE);
|
|
|
|
$aesStream->setOutput(function($chunk) use (&$output) { $this->getOutput($chunk, $output); });
|
|
|
|
$aesStream->start();
|
|
|
|
$this->assertEquals($ct, bin2hex($output), $ct . " != " . bin2hex($output));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private function openssl(AesStream $aesStream) {
|
|
|
|
// check if openssl util produce the same output
|
|
|
|
$cmd = ["/usr/bin/openssl", $aesStream->getCipherMode(), "-K", bin2hex($aesStream->getKey()), "-iv", bin2hex($aesStream->getIV()), "-in", AesStreamTest::$TEMP_FILE];
|
|
|
|
$proc = proc_open($cmd, [1 => ["pipe", "w"]], $pipes);
|
|
|
|
$this->assertTrue(is_resource($proc));
|
|
|
|
$this->assertTrue(is_resource($pipes[1]));
|
|
|
|
$output = stream_get_contents($pipes[1]);
|
|
|
|
proc_close($proc);
|
|
|
|
return $output;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function testEncryptDecrypt($key, $iv, $inputData) {
|
|
|
|
$aesStream = new AesStream($key, $iv);
|
|
|
|
$inputSize = strlen($inputData);
|
|
|
|
file_put_contents(AesStreamTest::$TEMP_FILE, $inputData);
|
|
|
|
|
|
|
|
$output = "";
|
|
|
|
$aesStream->setInputFile(AesStreamTest::$TEMP_FILE);
|
|
|
|
$aesStream->setOutput(function($chunk) use (&$output) { $this->getOutput($chunk, $output); });
|
|
|
|
$aesStream->start();
|
|
|
|
|
|
|
|
$this->assertEquals($inputSize, strlen($output));
|
|
|
|
$this->assertNotEquals($inputData, $output);
|
|
|
|
|
|
|
|
// check if openssl util produce the same output
|
|
|
|
$this->assertEquals($this->openssl($aesStream), $output);
|
|
|
|
|
|
|
|
file_put_contents(AesStreamTest::$TEMP_FILE, $output);
|
|
|
|
$output = "";
|
|
|
|
$aesStream->setInputFile(AesStreamTest::$TEMP_FILE);
|
|
|
|
$aesStream->setOutput(function($chunk) use (&$output) { $this->getOutput($chunk, $output); });
|
|
|
|
$aesStream->start();
|
|
|
|
$this->assertEquals($inputData, $output);
|
|
|
|
|
|
|
|
// check if openssl util produce the same output
|
|
|
|
$this->assertEquals($this->openssl($aesStream), $output);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testEncryptDecryptRandom() {
|
|
|
|
$chunkSize = 65536;
|
|
|
|
$key = random_bytes(32);
|
|
|
|
$iv = random_bytes(16);
|
|
|
|
$inputSize = 10 * $chunkSize;
|
|
|
|
$inputData = random_bytes($inputSize);
|
|
|
|
$this->testEncryptDecrypt($key, $iv, $inputData);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testEncryptDecryptLargeIV() {
|
|
|
|
$chunkSize = 65536;
|
|
|
|
$key = random_bytes(32);
|
|
|
|
$iv = hex2bin(str_repeat("FF", 16));
|
|
|
|
$inputSize = 10 * $chunkSize;
|
|
|
|
$inputData = random_bytes($inputSize);
|
|
|
|
$this->testEncryptDecrypt($key, $iv, $inputData);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testEncryptDecryptZeroIV() {
|
|
|
|
$chunkSize = 65536;
|
|
|
|
$key = random_bytes(32);
|
|
|
|
$iv = hex2bin(str_repeat("00", 16));
|
|
|
|
$inputSize = 10 * $chunkSize;
|
|
|
|
$inputData = random_bytes($inputSize);
|
|
|
|
$this->testEncryptDecrypt($key, $iv, $inputData);
|
|
|
|
}
|
2022-02-20 16:53:26 +01:00
|
|
|
|
|
|
|
public function testEncryptDecryptPartial() {
|
|
|
|
$key = random_bytes(32);
|
|
|
|
$iv = hex2bin(str_repeat("00", 16));
|
|
|
|
$chunkSize = 65536;
|
|
|
|
|
|
|
|
$ranges = [[500,100,200],[10*$chunkSize,100,5*$chunkSize+100],[10*$chunkSize,0,10*$chunkSize],[10*$chunkSize,$chunkSize-1,3*$chunkSize-1]];
|
|
|
|
foreach ($ranges as $range) {
|
|
|
|
list ($total, $offset, $length) = $range;
|
|
|
|
$inputData = random_bytes($total);
|
|
|
|
file_put_contents(AesStreamTest::$TEMP_FILE, $inputData);
|
|
|
|
|
|
|
|
$output = "";
|
|
|
|
$aesStream = new AesStream($key, $iv);
|
|
|
|
$aesStream->setRange($offset, $length);
|
|
|
|
$aesStream->setInputFile(AesStreamTest::$TEMP_FILE);
|
|
|
|
$aesStream->setOutput(function($chunk) use (&$output) { $this->getOutput($chunk, $output); });
|
|
|
|
$aesStream->start();
|
|
|
|
|
|
|
|
$outputComplete = "";
|
|
|
|
$aesStream = new AesStream($key, $iv);
|
|
|
|
$aesStream->setInputFile(AesStreamTest::$TEMP_FILE);
|
|
|
|
$aesStream->setOutput(function($chunk) use (&$outputComplete) { $this->getOutput($chunk, $outputComplete); });
|
|
|
|
$aesStream->start();
|
|
|
|
|
|
|
|
$this->assertEquals($length, strlen($output), "total=$total offset=$offset length=$length");
|
|
|
|
$this->assertEquals(substr($outputComplete, $offset, $length), $output, "total=$total offset=$offset length=$length");
|
|
|
|
}
|
|
|
|
}
|
2021-12-08 16:53:43 +01:00
|
|
|
}
|