Passed
Branch master (ad9152)
by Tony Karavasilev (Тони
02:47
created

isFileSaltingForcingNativeHashing()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 6
c 1
b 0
f 0
dl 0
loc 11
ccs 5
cts 5
cp 1
rs 10
cc 4
nc 5
nop 0
crap 4
1
<?php
2
3
/**
4
 * Abstraction for unkeyed hash objects like checksums and plain cryptographic hash functions.
5
 */
6
7
namespace CryptoManana\Core\Abstractions\MessageDigestion;
8
9
use \CryptoManana\Core\Abstractions\MessageDigestion\AbstractHashAlgorithm as HashAlgorithm;
10
use \CryptoManana\Core\Interfaces\MessageDigestion\ObjectHashingInterface as ObjectHashing;
11
use \CryptoManana\Core\Interfaces\MessageDigestion\FileHashingInterface as FileHashing;
12
use \CryptoManana\Core\Traits\MessageDigestion\ObjectHashingTrait as HashObjects;
13
use \CryptoManana\Core\StringBuilder as StringBuilder;
14
15
/**
16
 * Class AbstractUnkeyedHashFunction - Abstraction for unkeyed hash classes.
17
 *
18
 * @package CryptoManana\Core\Abstractions\MessageDigestion
19
 *
20
 * @mixin HashObjects
21
 */
22
abstract class AbstractUnkeyedHashFunction extends HashAlgorithm implements ObjectHashing, FileHashing
23
{
24
    /**
25
     * Object hashing capabilities.
26
     *
27
     * {@internal Reusable implementation of `ObjectHashingInterface`. }}
28
     */
29
    use HashObjects;
30
31
    /**
32
     * The internal name of the algorithm.
33
     */
34
    const ALGORITHM_NAME = 'none';
35
36
    /**
37
     * Flag to force native code polyfill realizations, if available.
38
     *
39
     * @var bool Flag to force native realizations.
40
     */
41
    protected $useNative = false;
42
43
    /**
44
     * Internal method for location and filename validation.
45
     *
46
     * @param string $filename The filename and location.
47
     *
48
     * @throws \Exception Validation errors.
49
     */
50 44
    protected function validateFileNamePath($filename)
51
    {
52 44
        $filename = StringBuilder::stringReplace("\0", '', $filename); // (ASCII 0 (0x00))
53 44
        $filename = realpath($filename); // Path traversal escape and absolute path fetching
54
55
        // Clear path cache
56 44
        if (!empty($filename)) {
57 22
            clearstatcache(true, $filename);
58
        }
59
60
        // Check if path is valid and the file is readable
61 44
        if ($filename === false || !file_exists($filename) || !is_readable($filename) || !is_file($filename)) {
62 22
            throw new \RuntimeException('File is not found or can not be accessed.');
63
        }
64 22
    }
65
66
    /**
67
     * Internal method for checking if native file hashing should be used by force.
68
     *
69
     * @return bool Is native hashing needed for the current salting mode.
70
     */
71 22
    protected function isFileSaltingForcingNativeHashing()
72
    {
73
        return (
74
            (
75
                // If there is an non-empty salt string set and salting is enabled
76 22
                $this->salt !== '' &&
77 22
                $this->saltingMode !== self::SALTING_MODE_NONE
78
            ) || (
79
                // If there is an empty salt string set and the salting mode duplicates/manipulates the input
80 22
                $this->salt === '' &&
81 22
                in_array($this->saltingMode, [self::SALTING_MODE_INFIX_SALT, self::SALTING_MODE_PALINDROME_MIRRORING])
82
            )
83
        );
84
    }
85
86
    /**
87
     * Unkeyed hash algorithm constructor.
88
     */
89 374
    public function __construct()
90
    {
91 374
    }
92
93
    /**
94
     * Calculates a hash value for the given data.
95
     *
96
     * @param string $data The input string.
97
     *
98
     * @return string The digest.
99
     * @throws \Exception Validation errors.
100
     */
101 140
    public function hashData($data)
102
    {
103 140
        if (!is_string($data)) {
104 14
            throw new \InvalidArgumentException('The data for hashing must be a string or a binary string.');
105
        }
106
107 126
        $data = $this->addSaltString($data);
108
109 126
        $digest = hash(
110 126
            static::ALGORITHM_NAME,
111
            $data,
112 126
            ($this->digestFormat === self::DIGEST_OUTPUT_RAW)
113
        );
114
115 126
        $digest = $this->changeOutputFormat($digest);
116
117 126
        return $digest;
118
    }
119
120
    /**
121
     * Calculates a hash value for the content of the given filename and location.
122
     *
123
     * @param string $filename The full path and name of the file for hashing.
124
     *
125
     * @return string The digest.
126
     * @throws \Exception Validation errors.
127
     */
128 66
    public function hashFile($filename)
129
    {
130
        // Validate input type
131 66
        if (!is_string($filename)) {
132 22
            throw new \InvalidArgumentException('The file path must be of type string.');
133
        }
134
135 44
        $this->validateFileNamePath($filename);
136
137 22
        $useFileSalting = $this->isFileSaltingForcingNativeHashing();
138
139 22
        if ($this->useNative || $useFileSalting) {
140
            /**
141
             * {@internal An optimization for native performance that spears string manipulations and function calls. }}
142
             */
143 22
            if (!$useFileSalting) {
144 22
                $oldSalt = $this->salt;
145 22
                $oldMode = $this->saltingMode;
146
147 22
                $this->salt = '';
148 22
                $this->saltingMode = self::SALTING_MODE_NONE;
149
            }
150
151 22
            $digest = $this->hashData(file_get_contents($filename));
152
153 22
            if (!$useFileSalting && isset($oldSalt, $oldMode)) {
154 22
                $this->salt = $oldSalt;
155 22
                $this->saltingMode = $oldMode;
156
            }
157
        } else {
158 22
            $digest = hash_file(
159 22
                static::ALGORITHM_NAME,
160
                $filename,
161 22
                ($this->digestFormat === self::DIGEST_OUTPUT_RAW)
162
            );
163
164 22
            $digest = $this->changeOutputFormat($digest);
165
        }
166
167 22
        return $digest;
168
    }
169
170
    /**
171
     * Get debug information for the class instance.
172
     *
173
     * @return array Debug information.
174
     */
175 22
    public function __debugInfo()
176
    {
177
        return [
178 22
            'standard' => static::ALGORITHM_NAME,
179 22
            'type' => 'unkeyed digestion or checksum',
180 22
            'salt' => $this->salt,
181 22
            'mode' => $this->saltingMode,
182
        ];
183
    }
184
}
185