@@ -24,6 +24,18 @@ class Encrypter implements EncrypterContract, StringEncrypter
24
24
*/
25
25
protected $ cipher ;
26
26
27
+ /**
28
+ * The supported cipher algorithms and their properties.
29
+ *
30
+ * @var array
31
+ */
32
+ private static $ supportedCiphers = [
33
+ 'AES-128-CBC ' => ['size ' => 16 , 'aead ' => false ],
34
+ 'AES-256-CBC ' => ['size ' => 32 , 'aead ' => false ],
35
+ 'AES-128-GCM ' => ['size ' => 16 , 'aead ' => true ],
36
+ 'AES-256-GCM ' => ['size ' => 32 , 'aead ' => true ],
37
+ ];
38
+
27
39
/**
28
40
* Create a new encrypter instance.
29
41
*
@@ -37,12 +49,14 @@ public function __construct($key, $cipher = 'AES-128-CBC')
37
49
{
38
50
$ key = (string ) $ key ;
39
51
40
- if (static ::supported ($ key , $ cipher )) {
41
- $ this ->key = $ key ;
42
- $ this ->cipher = $ cipher ;
43
- } else {
44
- throw new RuntimeException ('The only supported ciphers are AES-128-CBC, AES-256-CBC, AES-128-GCM, and AES-256-GCM with the correct key lengths. ' );
52
+ if (! static ::supported ($ key , $ cipher )) {
53
+ $ ciphers = implode (', ' , array_keys (self ::$ supportedCiphers ));
54
+
55
+ throw new RuntimeException ("Unsupported cipher or incorrect key length. Supported ciphers are: {$ ciphers }. " );
45
56
}
57
+
58
+ $ this ->key = $ key ;
59
+ $ this ->cipher = $ cipher ;
46
60
}
47
61
48
62
/**
@@ -54,12 +68,11 @@ public function __construct($key, $cipher = 'AES-128-CBC')
54
68
*/
55
69
public static function supported ($ key , $ cipher )
56
70
{
57
- $ length = mb_strlen ($ key , '8bit ' );
71
+ if (! isset (self ::$ supportedCiphers [$ cipher ])) {
72
+ return false ;
73
+ }
58
74
59
- return ($ cipher === 'AES-128-CBC ' && $ length === 16 ) ||
60
- ($ cipher === 'AES-256-CBC ' && $ length === 32 ) ||
61
- ($ cipher === 'AES-128-GCM ' && $ length === 16 ) ||
62
- ($ cipher === 'AES-256-GCM ' && $ length === 32 );
75
+ return mb_strlen ($ key , '8bit ' ) === self ::$ supportedCiphers [$ cipher ]['size ' ];
63
76
}
64
77
65
78
/**
@@ -70,7 +83,7 @@ public static function supported($key, $cipher)
70
83
*/
71
84
public static function generateKey ($ cipher )
72
85
{
73
- return random_bytes ($ cipher === ' AES-128-CBC ' ? 16 : 32 );
86
+ return random_bytes (self :: $ supportedCiphers [ $ cipher][ ' size ' ] );
74
87
}
75
88
76
89
/**
@@ -86,32 +99,28 @@ public function encrypt($value, $serialize = true)
86
99
{
87
100
$ iv = random_bytes (openssl_cipher_iv_length ($ this ->cipher ));
88
101
89
- $ tag = in_array ($ this ->cipher , ['AES-128-GCM ' , 'AES-256-GCM ' ]) ? '' : null ;
90
-
91
- // First we will encrypt the value using OpenSSL. After this is encrypted we
92
- // will proceed to calculating a MAC for the encrypted value so that this
93
- // value can be verified later as not having been changed by the users.
94
- $ value =
95
- in_array ($ this ->cipher , ['AES-128-GCM ' , 'AES-256-GCM ' ]) ?
96
- \openssl_encrypt (
97
- $ serialize ? serialize ($ value ) : $ value ,
98
- $ this ->cipher , $ this ->key , 0 , $ iv , $ tag
99
- ) :
100
- \openssl_encrypt (
101
- $ serialize ? serialize ($ value ) : $ value ,
102
- $ this ->cipher , $ this ->key , 0 , $ iv
103
- );
102
+ $ tag = '' ;
103
+
104
+ $ value = self ::$ supportedCiphers [$ this ->cipher ]['aead ' ]
105
+ ? \openssl_encrypt (
106
+ $ serialize ? serialize ($ value ) : $ value ,
107
+ $ this ->cipher , $ this ->key , 0 , $ iv , $ tag
108
+ )
109
+ : \openssl_encrypt (
110
+ $ serialize ? serialize ($ value ) : $ value ,
111
+ $ this ->cipher , $ this ->key , 0 , $ iv
112
+ );
104
113
105
114
if ($ value === false ) {
106
115
throw new EncryptException ('Could not encrypt the data. ' );
107
116
}
108
117
109
- // Once we get the encrypted value we'll go ahead and base64_encode the input
110
- // vector and create the MAC for the encrypted value so we can then verify
111
- // its authenticity. Then, we'll JSON the data into the "payload" array.
112
- $ mac = $ this ->hash (
113
- $ iv = base64_encode ( $ iv ), $ value , $ tag = $ tag ? base64_encode ( $ tag ) : ''
114
- );
118
+ $ iv = base64_encode ( $ iv );
119
+ $ tag = base64_encode ( $ tag );
120
+
121
+ $ mac = self :: $ supportedCiphers [ $ this ->cipher ][ ' aead ' ]
122
+ ? '' // For AEAD-algoritms, the tag / MAC is returned by openssl_encrypt...
123
+ : $ this -> hash ( $ iv , $ value );
115
124
116
125
$ json = json_encode (compact ('iv ' , 'value ' , 'mac ' , 'tag ' ), JSON_UNESCAPED_SLASHES );
117
126
@@ -184,12 +193,11 @@ public function decryptString($payload)
184
193
*
185
194
* @param string $iv
186
195
* @param mixed $value
187
- * @param string $tag
188
196
* @return string
189
197
*/
190
- protected function hash ($ iv , $ value, $ tag = '' )
198
+ protected function hash ($ iv , $ value )
191
199
{
192
- return hash_hmac ('sha256 ' , $ tag . $ iv .$ value , $ this ->key );
200
+ return hash_hmac ('sha256 ' , $ iv .$ value , $ this ->key );
193
201
}
194
202
195
203
/**
@@ -211,7 +219,7 @@ protected function getJsonPayload($payload)
211
219
throw new DecryptException ('The payload is invalid. ' );
212
220
}
213
221
214
- if (! $ this ->validMac ($ payload )) {
222
+ if (! self :: $ supportedCiphers [ $ this -> cipher ][ ' aead ' ] && ! $ this ->validMac ($ payload )) {
215
223
throw new DecryptException ('The MAC is invalid. ' );
216
224
}
217
225
@@ -239,7 +247,7 @@ protected function validPayload($payload)
239
247
protected function validMac (array $ payload )
240
248
{
241
249
return hash_equals (
242
- $ this ->hash ($ payload ['iv ' ], $ payload ['value ' ], $ payload [ ' tag ' ] ?? '' ), $ payload ['mac ' ]
250
+ $ this ->hash ($ payload ['iv ' ], $ payload ['value ' ]), $ payload ['mac ' ]
243
251
);
244
252
}
245
253
0 commit comments