Skip to content

Commit 0b6e032

Browse files
author
Adam Tomat
committed
Bring forked dcrypt library into core
1 parent 1058e8c commit 0b6e032

24 files changed

Lines changed: 1229 additions & 11 deletions

composer.json

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,6 @@
22
"name": "rareloop/lumberjack-core",
33
"description": "A powerful MVC framework for the modern WordPress developer. Write better, more expressive and easier to maintain code",
44
"license": "MIT",
5-
"repositories": [
6-
{
7-
"type": "vcs",
8-
"url": "https://github.com/Rareloop/dcrypt"
9-
}
10-
],
115
"require": {
126
"php": ">=8.1",
137
"php-di/php-di": "^7",
@@ -25,7 +19,6 @@
2519
"mindplay/middleman": "^4.0.4",
2620
"psr/log": "^2.0.0",
2721
"symfony/var-dumper": "^5.0||^6.3.6",
28-
"mmeyer2k/dcrypt": "^8.3.2",
2922
"spatie/ignition": "^1.15",
3023
"laminas/laminas-httphandlerrunner": "^2.12",
3124
"symfony/error-handler": "^6.0"
@@ -55,4 +48,4 @@
5548
"composer/installers": true
5649
}
5750
}
58-
}
51+
}

src/Dcrypt/AesCbc.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
/**
4+
* AesCbc.php
5+
*
6+
* PHP version 7
7+
*
8+
* @category Dcrypt
9+
* @package Dcrypt
10+
* @author Michael Meyer (mmeyer2k) <m.meyer2k@gmail.com>
11+
* @license http://opensource.org/licenses/MIT The MIT License (MIT)
12+
* @link https://github.com/mmeyer2k/dcrypt
13+
*/
14+
15+
namespace Rareloop\Lumberjack\Dcrypt;
16+
17+
/**
18+
* Symmetric AES-256-CBC encryption functions powered by OpenSSL.
19+
*
20+
* @category Dcrypt
21+
* @package Dcrypt
22+
* @author Michael Meyer (mmeyer2k) <m.meyer2k@gmail.com>
23+
* @license http://opensource.org/licenses/MIT The MIT License (MIT)
24+
* @link https://github.com/mmeyer2k/dcrypt
25+
* @link https://apigen.ci/github/mmeyer2k/dcrypt/namespace-Dcrypt.html
26+
*/
27+
class AesCbc extends OpensslBridge
28+
{
29+
/**
30+
* AES-256 cipher identifier that will be passed to openssl
31+
*
32+
* @var string
33+
*/
34+
const CIPHER = 'aes-256-cbc';
35+
36+
/**
37+
* Specify sha256 for message authentication
38+
*
39+
* @var string
40+
*/
41+
const CHKSUM = 'sha256';
42+
}

src/Dcrypt/AesCtr.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
/**
4+
* AesCtr.php
5+
*
6+
* PHP version 7
7+
*
8+
* @category Dcrypt
9+
* @package Dcrypt
10+
* @author Michael Meyer (mmeyer2k) <m.meyer2k@gmail.com>
11+
* @license http://opensource.org/licenses/MIT The MIT License (MIT)
12+
* @link https://github.com/mmeyer2k/dcrypt
13+
*/
14+
15+
namespace Rareloop\Lumberjack\Dcrypt;
16+
17+
/**
18+
* Symmetric AES-256-CTR encryption functions powered by OpenSSL.
19+
*
20+
* @category Dcrypt
21+
* @package Dcrypt
22+
* @author Michael Meyer (mmeyer2k) <m.meyer2k@gmail.com>
23+
* @license http://opensource.org/licenses/MIT The MIT License (MIT)
24+
* @link https://github.com/mmeyer2k/dcrypt
25+
* @link https://apigen.ci/github/mmeyer2k/dcrypt/namespace-Dcrypt.html
26+
*/
27+
class AesCtr extends AesCbc
28+
{
29+
/**
30+
* AES-256 cipher identifier that will be passed to openssl
31+
*
32+
* @var string
33+
*/
34+
const CIPHER = 'aes-256-ctr';
35+
}

src/Dcrypt/Hash.php

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
<?php
2+
3+
/**
4+
* Hash.php
5+
*
6+
* PHP version 7
7+
*
8+
* @category Dcrypt
9+
* @package Dcrypt
10+
* @author Michael Meyer (mmeyer2k) <m.meyer2k@gmail.com>
11+
* @license http://opensource.org/licenses/MIT The MIT License (MIT)
12+
* @link https://github.com/mmeyer2k/dcrypt
13+
*/
14+
15+
namespace Rareloop\Lumberjack\Dcrypt;
16+
17+
/**
18+
* An opaque 512 bit iterative hash function.
19+
*
20+
* 16 bytes => iv
21+
* 12 bytes => cost checksum
22+
* 4 bytes => cost
23+
* 32 bytes => hmac
24+
*
25+
* ivivivivivivivivsssssssssssscosthmachmachmachmachmachmachmachmac
26+
*
27+
* @category Dcrypt
28+
* @package Dcrypt
29+
* @author Michael Meyer (mmeyer2k) <m.meyer2k@gmail.com>
30+
* @license http://opensource.org/licenses/MIT The MIT License (MIT)
31+
* @link https://github.com/mmeyer2k/dcrypt
32+
* @link https://apigen.ci/github/mmeyer2k/dcrypt/namespace-Dcrypt.html
33+
*/
34+
class Hash
35+
{
36+
const ALGO = 'sha256';
37+
38+
/**
39+
* Internal function used to build the actual hash.
40+
*
41+
* @param string $input Data to hash
42+
* @param string $password Password to use in HMAC call
43+
* @param int $cost Number of iterations to use
44+
* @param string|null $salt Initialization vector to use in HMAC calls
45+
* @return string
46+
*/
47+
private static function build(string $input, string $password, int $cost, ?string $salt = null): string
48+
{
49+
// Generate salt if needed
50+
$salt = $salt ?? \random_bytes(16);
51+
52+
// Verify and normalize cost value
53+
$cost = self::cost($cost);
54+
55+
// Create key to use for hmac operations
56+
$key = self::hmac($salt, $password, self::ALGO);
57+
58+
// Perform hash iterations. Get a 32 byte output value
59+
$hash = self::ihmac($input, $key, $cost, self::ALGO);
60+
61+
// Return the salt + cost blob + hmac
62+
return $salt . self::costHash($cost, $salt, $password) . $hash;
63+
}
64+
65+
/**
66+
* Return a normalized cost value.
67+
*
68+
* @param int $cost Number of iterations to use.
69+
* @return int
70+
*/
71+
private static function cost(int $cost): int
72+
{
73+
return $cost % \pow(2, 32);
74+
}
75+
76+
/**
77+
* Performs hashing functions
78+
*
79+
* @param int $cost
80+
* @param string $salt
81+
* @param string $password
82+
* @return string
83+
*/
84+
private static function costHash(int $cost, string $salt, string $password): string
85+
{
86+
// Hash and return first 12 bytes
87+
$hash = Str::substr(self::hmac($cost, $salt, self::ALGO), 0, 12);
88+
89+
// Convert cost to base 256 then encrypt with OTP stream cipher
90+
$cost = Otp::crypt(self::dec2bin($cost), $password);
91+
92+
return $hash . $cost;
93+
}
94+
95+
/**
96+
* Perform a raw iterative HMAC operation with a configurable algo.
97+
*
98+
* @param string $data Data to hash.
99+
* @param string $key Key to use to authenticate the hash.
100+
* @param int $iter Number of times to iteratate the hash
101+
* @param string $algo Name of algo (sha256 or sha512 recommended)
102+
* @return string
103+
*/
104+
public static function ihmac(string $data, string $key, int $iter, string $algo = 'sha256'): string
105+
{
106+
// Can't perform negative iterations
107+
$iter = \abs($iter);
108+
109+
// Perform iterative hmac calls
110+
// Make sure $iter value of 0 is handled
111+
for ($i = 0; $i <= $iter; $i++) {
112+
$data = self::hmac($data . $i . $iter, $key, $algo);
113+
}
114+
115+
return $data;
116+
}
117+
118+
/**
119+
* Perform a single hmac iteration. This adds an extra layer of safety because hash_hmac can return false if algo
120+
* is not valid. Return type hint will throw an exception if this happens.
121+
*
122+
* @param string $data Data to hash.
123+
* @param string $key Key to use to authenticate the hash.
124+
* @param string $algo Name of algo
125+
* @return string
126+
*/
127+
public static function hmac(string $data, string $key, string $algo): string
128+
{
129+
return \hash_hmac($algo, $data, $key, true);
130+
}
131+
132+
/**
133+
* Hash an input string into a salted 512 byte hash.
134+
*
135+
* @param string $input Data to hash.
136+
* @param string $password HMAC validation password.
137+
* @param int $cost Cost value of the hash.
138+
* @return string
139+
*/
140+
public static function make(string $input, string $password, int $cost = 250000): string
141+
{
142+
return self::build($input, $password, $cost, null);
143+
}
144+
145+
/**
146+
* Check the validity of a hash.
147+
*
148+
* @param string $input Input to test.
149+
* @param string $hash Known hash to validate against.
150+
* @param string $password HMAC password to use during iterative hash.
151+
* @return boolean
152+
*/
153+
public static function verify(string $input, string $hash, string $password): bool
154+
{
155+
// Get the salt value from the decrypted prefix
156+
$salt = Str::substr($hash, 0, 16);
157+
158+
// Get the encrypted cost bytes
159+
$cost = self::bin2dec(Otp::crypt(Str::substr($hash, 28, 4), $password));
160+
161+
// Get the entire cost+hash blob for comparison
162+
$blob = Str::substr($hash, 16, 16);
163+
164+
if (!Str::equal(self::costHash($cost, $salt, $password), $blob)) {
165+
return false;
166+
}
167+
168+
// Return the boolean equivalence
169+
return Str::equal($hash, self::build($input, $password, $cost, $salt));
170+
}
171+
172+
/**
173+
* Turns an integer into a 4 byte binary representation
174+
*
175+
* @param int $dec Integer to convert to binary
176+
* @return string
177+
*/
178+
private static function dec2bin(int $dec): string
179+
{
180+
return \hex2bin(\str_pad(\dechex($dec), 8, '0', STR_PAD_LEFT));
181+
}
182+
183+
/**
184+
* Reverses dec2bin
185+
*
186+
* @param string $bin Binary string to convert to decimal
187+
* @return string
188+
*/
189+
private static function bin2dec(string $bin): string
190+
{
191+
return \hexdec(\bin2hex($bin));
192+
}
193+
}

0 commit comments

Comments
 (0)