Completed
Push — master ( 8fe1b2...42aba3 )
by Michael
03:39
created

OpensslKeyGenerator::authenticationKey()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
rs 10
cc 1
nc 1
nop 0
1
<?php declare(strict_types=1);
2
3
/**
4
 * OpensslKeyGenerator.php
5
 *
6
 * PHP version 7
7
 *
8
 * @category Dcrypt
9
 * @package  Dcrypt
10
 * @author   Michael Meyer (mmeyer2k) <[email protected]>
11
 * @license  http://opensource.org/licenses/MIT The MIT License (MIT)
12
 * @link     https://github.com/mmeyer2k/dcrypt
13
 */
14
15
namespace Dcrypt;
16
17
use Dcrypt\Exceptions\InvalidKeyException;
18
use Dcrypt\Exceptions\InvalidPasswordException;
19
20
/**
21
 * Provides key derivation functions
22
 *
23
 * @category Dcrypt
24
 * @package  Dcrypt
25
 * @author   Michael Meyer (mmeyer2k) <[email protected]>
26
 * @license  http://opensource.org/licenses/MIT The MIT License (MIT)
27
 * @link     https://github.com/mmeyer2k/dcrypt
28
 */
29
final class OpensslKeyGenerator
30
{
31
    /**
32
     * @var string
33
     */
34
    private $key;
35
36
    /**
37
     * @var string
38
     */
39
    private $algo;
40
41
    /**
42
     * @var string
43
     */
44
    private $ivr;
45
46
    /**
47
     * @var string
48
     */
49
    private $cipher;
50
51
    /**
52
     * OpensslKeyGenerator constructor.
53
     *
54
     * @param string $algo
55
     * @param string $passkey
56
     * @param string $cipher
57
     * @param string $ivr
58
     * @param int $cost
59
     */
60
    public function __construct(string $algo, string $passkey, string $cipher, string $ivr, int $cost)
61
    {
62
        // When cost is 0 then we are in key mode
63
        if ($cost === 0) {
64
            // Attempt to decode the passkey
65
            $passkey = \base64_decode($passkey);
66
67
            // Make sure key was properly decoded and meets minimum required length
68
            if (Str::strlen($passkey) < 256) {
69
                throw new InvalidKeyException("Key must be at least 2048 bits long and base64 encoded.");
70
            }
71
72
            // Store the key as what was supplied
73
            $this->key = $passkey;
74
        } else {
75
            // Make sure that the user is not attempting to use a key in password word mode
76
            if (Str::strlen($passkey) >= 256) {
77
                throw new InvalidPasswordException("Passwords must be less than 2048 bits (256 bytes) long.");
78
            }
79
80
            // Derive the key from the password and store in object
81
            $this->key = \hash_pbkdf2($algo, $passkey, $ivr, $cost, 0, true);
82
        }
83
84
        // Store the cipher string
85
        $this->cipher = $cipher;
86
87
        // Store algo in object
88
        $this->algo = $algo;
89
90
        // Store init vector in object
91
        $this->ivr = $ivr;
92
    }
93
94
    /**
95
     * Generate the authentication key
96
     *
97
     * @return string
98
     */
99
    public function authenticationKey(): string
100
    {
101
        return $this->deriveKey(__FUNCTION__);
102
    }
103
104
    /**
105
     * Generate the encryption key
106
     *
107
     * @return string
108
     */
109
    public function encryptionKey(): string
110
    {
111
        return $this->deriveKey(__FUNCTION__);
112
    }
113
114
    /**
115
     * Derive a key with differing authinfo strings
116
     *
117
     * @param string $info
118
     * @return string
119
     * @throws \Exception
120
     */
121
    public function deriveKey(string $info): string
122
    {
123
        $info = $info . '|' . $this->cipher;
124
125
        $key = \hash_hkdf($this->algo, $this->key, 0, $info, $this->ivr);
126
127
        if ($key === false) {
128
            throw new Exceptions\InvalidAlgoException("Hash algo $this->algo is not supported by hash_hkdf.");
129
        }
130
131
        return $key;
132
    }
133
134
    /**
135
     * Generate a new key that meets requirements for dcrypt
136
     *
137
     * @param int $size
138
     * @return string
139
     * @throws Exceptions\InvalidKeyException
140
     */
141
    public static function newKey(int $size = 2048): string
142
    {
143
        if ($size < 2048) {
144
            throw new InvalidKeyException('Key must be at least 2048 bits long.');
145
        }
146
147
        if ($size % 8 !== 0) {
148
            throw new InvalidKeyException('Key must be divisible by 8.');
149
        }
150
151
        return \base64_encode(\random_bytes($size / 8));
152
    }
153
}