Skip to content

file integrity checker #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .env
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
SECRET_KEY="rahul_chavan"
SECRET_KEY="rahul_chavan"
FILE_NAME='/home/rahul/Documents/hashlist'
2 changes: 1 addition & 1 deletion src/Exception/FileEncryptorException.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

class FileEncryptorException extends Exception
{
public function __construct($message = "could not encrypt file", $code = 0, Throwable $previous = null)
public function __construct(string $message = "could not encrypt file", int $code = 0, Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Exception/FileHandlerException.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

class FileHandlerException extends Exception
{
public function __construct($message = "There was an error", $code = 0, Throwable $previous = null)
public function __construct(string $message = "There was an error", int $code = 0, Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
}
Expand Down
14 changes: 14 additions & 0 deletions src/Exception/HashException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace rcsofttech85\FileHandler\Exception;

use Exception;
use Throwable;

class HashException extends Exception
{
public function __construct(string $message = "There was an error", int $code = 0, Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
}
}
2 changes: 1 addition & 1 deletion src/Exception/StreamException.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

class StreamException extends Exception
{
public function __construct($message = "could not stream file", $code = 0, Throwable $previous = null)
public function __construct(string $message = "could not stream file", int $code = 0, Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
}
Expand Down
75 changes: 75 additions & 0 deletions src/FileHashChecker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

namespace rcsofttech85\FileHandler;

use rcsofttech85\FileHandler\Exception\HashException;

class FileHashChecker
{
const ALGO_256 = 'sha3-256';
const ALGO_512 = 'sha3-512';

/**
* @param string $filename
* @throws HashException
*/
public function __construct(private readonly string $filename)
{
if (!file_exists($this->filename)) {
throw new HashException('file not found');
}
}

/**
* @param object $fileHandler
* @param string $storedHashesFile
* @param string $algo
* @return bool
* @throws Exception\FileHandlerException
* @throws HashException
*/

public function verifyHash(object $fileHandler, string $storedHashesFile, string $algo = self::ALGO_256): bool
{
if (!$fileHandler instanceof FileHandler) {
throw new HashException("object must be instance of " . FileHandler::class);
}

if (!$storedHashesFile) {
throw new HashException('file not found');
}

$file = $fileHandler->open(filename: $storedHashesFile)->searchInCsvFile(
$this->filename,
'File',
FileHandler::ARRAY_FORMAT
);

if (!$file) {
throw new HashException('this file is not hashed');
}

$expectedHash = $file['Hash'];
$hash = $this->hashFile($algo);

if ($hash !== $expectedHash) {
return false;
}

return true;
}

/**
* @param string $algo
* @return string
* @throws HashException
*/

public function hashFile(string $algo = self::ALGO_256): string
{
if (!in_array($algo, [self::ALGO_512, self::ALGO_256])) {
throw new HashException('algorithm not supported');
}
return hash_file($algo, $this->filename);
}
}
30 changes: 16 additions & 14 deletions tests/unit/FileEncryptorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,26 @@ class FileEncryptorTest extends TestCase

private static string $secret;

protected function setUp(): void
{
$this->fileEncryptor = new FileEncryptor('movie.csv', self::$secret);
parent::setUp();
}

protected function tearDown(): void
{
parent::tearDown();
$this->fileEncryptor = null;
}


public static function setUpBeforeClass(): void
{
$dotenv = new Dotenv();
$dotenv->load('.env');
self::$secret = getenv('SECRET_KEY');
self::$secret = $_ENV['SECRET_KEY'];


$content = "Film,Genre,Lead Studio,Audience score %,Profitability,Rotten Tomatoes %,Worldwide Gross,Year\n"
. "Zack and Miri Make a Porno,Romance,The Weinstein Company,70,1.747541667,64,$41.94 ,2008\n"
. "Youth in Revolt,Comedy,The Weinstein Company,52,1.09,68,$19.62 ,2010\n"
Expand Down Expand Up @@ -62,7 +76,7 @@ public function throwExceptionIfAlreadyEncrypted()
#[Test]
public function throwExceptionIfDecryptionFails()
{
$this->fileEncryptor = new FileEncryptor("movie.csv", 'wrongSecret');
$this->fileEncryptor = new FileEncryptor("movie.csv", 'wrong');
$this->expectException(FileEncryptorException::class);
$this->expectExceptionMessage('could not decrypt file');
$this->fileEncryptor->decryptFile();
Expand All @@ -75,16 +89,4 @@ public function canDecryptFile()

$this->assertTrue($isFileDecrypted);
}

protected function setUp(): void
{
$this->fileEncryptor = new FileEncryptor('movie.csv', self::$secret);
parent::setUp();
}

protected function tearDown(): void
{
parent::tearDown();
$this->fileEncryptor = null;
}
}
106 changes: 106 additions & 0 deletions tests/unit/FileHashCheckerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php

namespace unit;

use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use rcsofttech85\FileHandler\Exception\HashException;
use rcsofttech85\FileHandler\FileHandler;
use rcsofttech85\FileHandler\FileHashChecker;
use Symfony\Component\Dotenv\Dotenv;

class FileHashCheckerTest extends TestCase
{
private static string $file;
private FileHashChecker|null $fileHasher = null;

public static function setUpBeforeClass(): void
{
parent::setUpBeforeClass();

$dotenv = new Dotenv();
$dotenv->load('.env');

$file = $_ENV['FILE_NAME']; // this file contains list of all hashes

self::$file = $file;


file_put_contents("test", "hello world");
}

public static function tearDownAfterClass(): void
{
parent::tearDownAfterClass();
unlink("test");
unlink("sample");
}

#[Test]
public function shouldGenerateValidHashForDifferentAlgo()
{
$expectedHash = "644bcc7e564373040999aac89e7622f3ca71fba1d972fd94a31c3bfbf24e3938";

$actualHash = $this->fileHasher->hashFile(); //default ALGO_256

$this->assertEquals($expectedHash, $actualHash);

$expectedHash = "840006653e9ac9e95117a15c915caab81662918e925de9e004f774ff82d7079a40d4d27b1b372657c61d46d470304c88c788b3a4527ad074d1dccbee5dbaa99a";

$actualHash = $this->fileHasher->hashFile(FileHashChecker::ALGO_512);

$this->assertEquals($expectedHash, $actualHash);
}

#[Test]
public function checkFileIntegrityReturnsTrueIfHashMatch()
{
$isVerified = $this->fileHasher->verifyHash(new FileHandler(), self::$file);

$this->assertTrue($isVerified);
}

#[Test]
public function shouldReturnFalseIfFileIsModified()
{
$backup = file_get_contents("test");
file_put_contents("test", "modified", FILE_APPEND);

$isVerified = $this->fileHasher->verifyHash(new FileHandler(), self::$file);

$this->assertfalse($isVerified);

file_put_contents("test", $backup);
}

#[Test]
public function shouldReturnFalseIfDifferentAlgoIsUsedForVerifyHash()
{
$isVerified = $this->fileHasher->verifyHash(new FileHandler(), self::$file, FileHashChecker::ALGO_512);

$this->assertFalse($isVerified);
}

#[Test]
public function shouldThrowExceptionIfFileIsNotHashed()
{
file_put_contents("sample", "this file is not hashed");
$this->fileHasher = new FileHashChecker("sample");

$this->expectException(HashException::class);
$this->expectExceptionMessage("this file is not hashed");
$this->fileHasher->verifyHash(new FileHandler(), self::$file, FileHashChecker::ALGO_512);
}

protected function setUp(): void
{
parent::setUp();
$this->fileHasher = new FileHashChecker("test");
}

protected function tearDown(): void
{
parent::tearDown();
$this->fileHasher = null;
}
}