elchris/hashtuner

View on GitHub
src/RunTime/ArgonRunTime.php

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
<?php

namespace ChrisHolland\HashTuner\RunTime;

use ChrisHolland\HashTuner\Exception\FirstDimensionLimitViolation;
use ChrisHolland\HashTuner\SystemInfo;

class ArgonRunTime implements HashRunTime
{
    public const MEMORY_INCREASE_PERCENTAGE = 0.10;
    private float $memory;
    private int $iterations;
    private int $threads;
    private float $execTime;
    private SystemInfo $systemInfo;
    private ?int $memoryLimitInKilobytesOverride;
    /** @var array<string> */
    private array $info;

    public function __construct(
        int $initialMemory,
        int $initialIterations,
        ?int $memoryLimitInKilobytesOverride = null
    ) {
        $this->systemInfo = new SystemInfo();
        $this->memory = (float)$initialMemory;
        $this->iterations = $initialIterations;
        $this->threads = $this->getThreadsFromSystem();
        $this->execute();
        $this->memoryLimitInKilobytesOverride = $memoryLimitInKilobytesOverride;
    }

    private function execute() : void
    {
        $start = microtime(true);
        $hash = password_hash(
            'i am not secure',
            PASSWORD_ARGON2ID,
            [
                'threads' => $this->threads,
                'memory_cost' => (int)$this->memory,
                'time_cost' => $this->iterations
            ]
        );
        $this->info = password_get_info($hash);
        $end = microtime(true);
        $this->execTime = ($end - $start);
    }

    public function getFirstDimension(): float
    {
        return $this->memory;
    }

    public function getExecutionTime(): float
    {
        return $this->execTime;
    }

    /**
     * @throws FirstDimensionLimitViolation
     */
    public function bumpFirstDimension(): void
    {
        $limitInKiloBytes = $this->getHardMemoryLimitInKilobytes();

        $targetMemory = $this->memory + (self::MEMORY_INCREASE_PERCENTAGE * $this->memory);
        if ($targetMemory > $limitInKiloBytes) {
            throw new FirstDimensionLimitViolation('Hard Memory Limit Reached');
        }
        $this->memory = $targetMemory;
        $this->execute();
    }

    public function getSecondDimension(): int
    {
        return $this->iterations;
    }

    public function bumpSecondDimension(): void
    {
        $this->iterations++;
        $this->execute();
    }

    public function lowerSecondDimensionOneStep(): void
    {
        $this->iterations--;
        $this->execute();
    }

    public function getThirdDimension(): int
    {
        return $this->threads;
    }

    private function getThreadsFromSystem() : int
    {
        //hard-coding to 1 because php doesn't support settingThreads
        //return $this->systemInfo->getCores() * 2;
        return 1;
    }
    
    public function getHardMemoryLimitInKilobytes(): int
    {
        return $this->memoryLimitInKilobytesOverride ?? $this->systemInfo->getMemoryLimitInKiloBytes();
    }

    /**
     * @return string[]
     */
    public function getInfo(): array
    {
        return $this->info;
    }
}