1+ <?php namespace App \Utils ;
2+ /*
3+ * Copyright 2024 OpenStack Foundation
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ * http://www.apache.org/licenses/LICENSE-2.0
8+ * Unless required by applicable law or agreed to in writing, software
9+ * distributed under the License is distributed on an "AS IS" BASIS,
10+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+ * See the License for the specific language governing permissions and
12+ * limitations under the License.
13+ **/
14+
15+ /**
16+ * Class AES256GCM
17+ * @package App\Utils
18+ */
19+ final class AES256GCM
20+ {
21+
22+ const CIPHER = 'AES-256-GCM ' ;
23+
24+ /**
25+ * Encoded/Decoded data
26+ *
27+ * @var null|string
28+ */
29+ protected $ data ;
30+ /**
31+ * Initialization vector value
32+ *
33+ * @var string
34+ */
35+ protected $ iv ;
36+ /**
37+ * Error message if operation failed
38+ *
39+ * @var null|string
40+ */
41+ protected $ errorMessage ;
42+
43+ /**
44+ * AesCipher constructor.
45+ *
46+ * @param string $iv Initialization vector value
47+ * @param string|null $data Encoded/Decoded data
48+ * @param string|null $errorMessage Error message if operation failed
49+ */
50+ public function __construct (string $ iv , string $ data = null , string $ errorMessage = null )
51+ {
52+ $ this ->iv = $ iv ;
53+ $ this ->data = $ data ;
54+ $ this ->errorMessage = $ errorMessage ;
55+ }
56+
57+ /**
58+ * @param string $key
59+ * @param string $data
60+ * @return AES256GCM
61+ */
62+ public static function encrypt (string $ key , string $ data ): AES256GCM
63+ {
64+ try {
65+ // Check secret length
66+ if (!AES256GCM ::isKeyLengthValid ($ key )) {
67+ throw new \InvalidArgumentException ("Secret key's length must be 128, 192 or 256 bits " );
68+ }
69+
70+ $ iv_len = openssl_cipher_iv_length (AES256GCM ::CIPHER );
71+ // Get random initialization vector
72+ $ iv = random_bytes ($ iv_len );
73+ $ tag = '' ;
74+ // Encrypt input text
75+ $ raw = openssl_encrypt (
76+ $ data ,
77+ AES256GCM ::CIPHER ,
78+ $ key ,
79+ OPENSSL_RAW_DATA ,
80+ $ iv ,
81+ $ tag ,
82+ '' ,
83+ 16
84+ );
85+
86+ // Return base64-encoded string: initVector + encrypted result
87+ $ result = base64_encode ($ iv ) . base64_encode ($ raw . $ tag );
88+
89+ if ($ result === false ) {
90+ // Operation failed
91+ return new AES256GCM ($ iv , null , openssl_error_string ());
92+ }
93+
94+ // Return successful encoded object
95+ return new AES256GCM ($ iv , $ result );
96+ } catch (\Exception $ e ) {
97+ // Operation failed
98+ return new AES256GCM (isset ($ iv ), null , $ e ->getMessage ());
99+ }
100+ }
101+
102+ /**
103+ * @param string $key
104+ * @param string $data
105+ * @return AES256GCM
106+ */
107+ public static function decrypt (string $ key , string $ data ): AES256GCM
108+ {
109+ try {
110+ // Check secret length
111+ if (!AES256GCM ::isKeyLengthValid ($ key )) {
112+ throw new \InvalidArgumentException ("Secret key's length must be 128, 192 or 256 bits " );
113+ }
114+
115+ $ iv = base64_decode (substr ($ data , 0 , 16 ));
116+ $ data = base64_decode (substr ($ data , 16 ));
117+ $ tag = substr ($ data , strlen ($ data ) - 16 );
118+ $ data = substr ($ data , 0 , strlen ($ data ) - 16 );
119+
120+ // Trying to get decrypted text
121+ $ decoded = openssl_decrypt (
122+ $ data ,
123+ AES256GCM ::CIPHER ,
124+ $ key ,
125+ OPENSSL_RAW_DATA ,
126+ $ iv ,
127+ $ tag
128+ );
129+
130+ if ($ decoded === false ) {
131+ // Operation failed
132+ return new AES256GCM (isset ($ iv ), null , openssl_error_string ());
133+ }
134+
135+ // Return successful decoded object
136+ return new AES256GCM ($ iv , $ decoded );
137+ } catch (\Exception $ e ) {
138+ // Operation failed
139+ return new AES256GCM (isset ($ iv ), null , $ e ->getMessage ());
140+ }
141+ }
142+
143+ /**
144+ * Check that secret password length is valid
145+ *
146+ * @param string $key 16/24/32 -characters secret password
147+ *
148+ * @return bool
149+ */
150+ public static function isKeyLengthValid (string $ key ): bool
151+ {
152+ $ length = strlen ($ key );
153+
154+ return $ length == 16 || $ length == 24 || $ length == 32 ;
155+ }
156+
157+ /**
158+ * Get encoded/decoded data
159+ *
160+ * @return string|null
161+ */
162+ public function getData (): ?string
163+ {
164+ return $ this ->data ;
165+ }
166+
167+ /**
168+ * Get initialization vector value
169+ *
170+ * @return string|null
171+ */
172+ public function getInitVector (): ?string
173+ {
174+ return $ this ->iv ;
175+ }
176+
177+ /**
178+ * Get error message
179+ *
180+ * @return string|null
181+ */
182+ public function getErrorMessage (): ?string
183+ {
184+ return $ this ->errorMessage ;
185+ }
186+
187+ /**
188+ * Check that operation failed
189+ *
190+ * @return bool
191+ */
192+ public function hasError (): bool
193+ {
194+ return $ this ->errorMessage !== null ;
195+ }
196+
197+ /**
198+ * To string return resulting data
199+ *
200+ * @return null|string
201+ */
202+ public function __toString (): ?string
203+ {
204+ return $ this ->getData ();
205+ }
206+ }
0 commit comments