Odyssey
2019040800_updateTrustedDetailsParms.php
1 <?php
2 /**
3  * Notes:
4  * 1. Anything complicated needs to be done with SQL.
5  * 2. Always have a check to know if the migration needs to occur (up or down).
6  * 3. Use up() and down(), not change(), because of the SQL
7  */
8 use Phinx\Migration\AbstractMigration;
9 // Constants for parm related encryption
10 define("PARMENCDEC_CIPHER_MODE", "aes-256-cbc");
11 // allowed values: sha1, sha256
12 define("PARMENCDEC_AUTH_HASH_ALGO", "sha256");
13 
14 function GetOpenSSLKeyBilbo() {
15 
16  return "VXVtbYOrbt5avJme6ihbeS9MznYMPe9a";
17 }
18 
19 function GetOpenSSLKeyBugs() {
20  return "w0ki5QwpYWkM2FpitNEsosG9HGL9uogS";
21 }
22 
23 /**
24  * COPY FROM hcuCommon.i
25  */
26 function getOpenSSLKey($key_suffix, $bit_size='256') {
27  // output raw binary data for key
28  $raw_output = true;
29 
30  if ($bit_size != '256')
31  throw new exception("Invalid key size. Supported: [256] bits.", 3);
32 
33  $hashKeyBilbo = GetOpenSSLKeyBilbo() . $key_suffix;
34  $hashKeyBugs = GetOpenSSLKeyBugs() . $key_suffix;
35 
36  // TODO: consider sha3-256?
37  $defaultKey = hash_hmac('sha256', $hashKeyBilbo, $hashKeyBugs, $raw_output); // Get 256-bit key
38 
39  switch($bit_size) {
40  case '256':
41  return $defaultKey;
42  break;
43 
44  default:
45  return $defaultKey;
46  }
47 }
48 
49 /**
50  * COPY FROM hcuCommon.i
51  */
52 function getEncryptionAuthHash($message, $key_suffix, $auth_hash_algo, $auth_hash_binary) {
53  if ($auth_hash_algo != 'sha1' && $auth_hash_algo != 'sha256') {
54  throw new exception("Invalid hash algorithm: ". $auth_hash_algo, 2);
55  }
56  $hashKeyBilbo = GetOpenSSLKeyBilbo() . $key_suffix;
57  return hash_hmac($auth_hash_algo, $message, $hashKeyBilbo, $auth_hash_binary);
58 }
59 
60 
61 /**
62  * COPY FROM hcuCommon.i
63  */
64 function hcuOpenSSLEncrypt($message,
65  $key_suffix,
66  $method='aes-256-cbc',
67  $auth_hash_algo='sha1',
68  $auth_hash_binary=true,
69  $iv="",
70  $context="default") {
71 
72  // auth algo validation
73  if ($auth_hash_algo != 'sha1' && $auth_hash_algo != 'sha256') {
74  throw new exception("Invalid hash algorithm: ". $auth_hash_algo, 2);
75  }
76  // if initialization vector is not provided, we generate one by default
77  // Obviously, IV would need to be returned along with the encrypted
78  // ciphercode for various encryption modes, eg. except ECB. use context
79  // argument to handle how to return the IV value back to the caller
80  // Default context appends the IV to the ciphertext.
81  $ivsize = openssl_cipher_iv_length($method);
82  if ($iv == "") {
83  $iv = openssl_random_pseudo_bytes($ivsize);
84  } else {
85  $iv = mb_substr($iv, 0, $ivsize, '8bit');
86  }
87 
88  // Set encryption options before executing w.r.t. $context
89  // OPTIONS:
90  // OPENSSL_ZERO_PADDING: expects data to be space padded before encryption
91  // OPENSSL_RAW_DATA: generate raw binary ciphertext
92  if($context == "connect_chkfree" ||
93  $context == "connect_ezcard" ||
94  $context == "connect_certegy" ||
95  $context == "connect_digital" ||
96  $context == "connect_ipay" ||
97  $context == "connect_vsoft" ||
98  $context == "connect_mvi")
99  {
100  // expects that the data is already padded appropriately
101  // otherwise encryption with these options will fail
102  $openssl_options = OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING;
103  $encryptionKey = $key_suffix;
104  }
105  // defaults
106  else {
107  // PKCS7 padding and RAW ciphertext
108  $openssl_options = OPENSSL_RAW_DATA;
109  $encryptionKey = getOpenSSLKey($key_suffix);
110  }
111 
112  // ENCRYPTION
113  $ciphertext = openssl_encrypt($message,
114  $method,
115  $encryptionKey,
116  $openssl_options,
117  $iv);
118 
119  // iv and ciphertext are part of the default message
120  // Default context appends the IV to the ciphertext in a raw format.
121  $defaultCipher = $iv . $ciphertext;
122  $defaultHash = getEncryptionAuthHash($defaultCipher,
123  $key_suffix,
124  $auth_hash_algo,
125  $auth_hash_binary);
126  $defaultResp = array("message" => $defaultCipher, "hash" => $defaultHash);
127 
128  // Prepare response after encryption based on the context of the encryption
129  switch($context) {
130  case "connect_chkfree":
131  case "connect_ezcard":
132  case "connect_certegy":
133  case "connect_digital":
134  case "connect_ipay":
135  case "connect_vsoft":
136  case "connect_mvi":
137  $connectCipher = $ciphertext;
138  $returnArray = array("message" => $connectCipher, "iv" => $iv);
139  break;
140 
141  case "credentials":
142  $credentialCipher = $ciphertext;
143  $returnArray = array("message" => $credentialCipher, "iv" => $iv);
144  break;
145 
146  // also for context="default" (case "default":)
147  case "default":
148  default:
149  $returnArray= $defaultResp;
150  }
151  return $returnArray;
152 }
153 
154 /**
155  * COPY FROM hcuCommon.i
156  */
157 function hcuOpenSSLDecrypt($message,
158  $hash,
159  $key_suffix,
160  $method='aes-256-cbc',
161  $auth_hash_algo="sha1",
162  $auth_hash_binary=true,
163  $iv="",
164  $context="default")
165 {
166  $hashKeyBilbo = GetOpenSSLKeyBilbo() . $key_suffix;
167  $ivsize = openssl_cipher_iv_length($method);
168  if ($iv != "") {
169  $iv = mb_substr($iv, 0, $ivsize, '8bit');
170  }
171 
172  // DEFAULTS
173  // OPTIONS:
174  // OPENSSL_ZERO_PADDING: expects data to be space padded before encryption
175  // OPENSSL_RAW_DATA: generate raw binary ciphertext
176  $openssl_options = OPENSSL_RAW_DATA;
177  $encryptionKey = getOpenSSLKey($key_suffix);
178  $ciphertext = $message;
179 
180  switch($context) {
181  case "connect_chkfree":
182  case "connect_ezcard":
183  case "connect_certegy":
184  case "connect_digital":
185  case "connect_ipay":
186  case "connect_vsoft":
187  case "connect_mvi":
188  $openssl_options = OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING;
189  $encryptionKey = $key_suffix;
190  break;
191 
192  case "credentials":
193  if ($iv == "") {
194  throw new exception("IV cannot be empty for Credentials.", 3);
195  }
196  break;
197 
198  case "default":
199  default:
200  // $expectedHash = hash_hmac($auth_hash_algo, $message, $hashKeyBilbo, $auth_hash_binary);
201  $expectedHash = getEncryptionAuthHash($message,
202  $key_suffix,
203  $auth_hash_algo,
204  $auth_hash_binary);
205  if (md5($expectedHash) !== md5($hash))
206  throw new exception("Hash doesn't match!", 2);
207 
208  // iv and ciphertext are the part of the default $message
209  $iv = mb_substr($message, 0, $ivsize, '8bit');
210  $ciphertext = mb_substr($message, $ivsize, null, '8bit');
211  }
212 
213  // DECRYPTION
214  return openssl_decrypt($ciphertext,
215  $method,
216  $encryptionKey,
217  $openssl_options,
218  $iv);
219 }
220 /**
221  * COPY FROM cutrusted.i
222  */
223 function addpadding($string, $blocksize = 32) {
224  # implements PKCS7 padding
225  $len = strlen($string);
226  $pad = $blocksize - ($len % $blocksize);
227  $string .= str_repeat(chr($pad), $pad);
228  return $string;
229 }
230 
231 /**
232  * COPY FROM cutrusted.i
233  */
234 function strippadding($string) {
235  $slast = ord(substr($string, -1));
236  $slastc = chr($slast);
237 
238  $match= "/\\x" . dechex($slast) . "{" . $slast . "}/";
239 
240  if (strlen($string) && preg_match($match, $string)) {
241  $string = substr($string, 0, strlen($string) - $slast);
242  return $string;
243  } else {
244  return false;
245  }
246 }
247 
248 /**
249  * COPIED/MODIFIED from cutrusted.i
250  */
251 function parmencrypt($str,
252  $key,
253  $cipher_method=PARMENCDEC_CIPHER_MODE) {
254  try {
255  $enc_resp = hcuOpenSSLEncrypt($str,
256  $key,
257  $method=$cipher_method,
258  $auth_hash_algo=PARMENCDEC_AUTH_HASH_ALGO);
259 
260  $ciphertext = $enc_resp["message"];
261  $hash_hmac = $enc_resp["hash"];
262  return base64_encode($hash_hmac.$ciphertext);
263  } catch (Exception $ex) {
264  throw $ex;
265  }
266 
267 }
268 
269 /**
270  * COPIED/MODIFIED from cutrusted.i
271  */
272 function parmencrypt_mcrypt($str, $key) {
273  $key_size = mcrypt_get_key_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
274  $key = substr($key, 0, $key_size);
275  $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
276  $blocksize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
277  $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
278  $str = addpadding($str, $blocksize);
279  return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $str, MCRYPT_MODE_ECB, $iv));
280 }
281 
282 /**
283  * COPIED/MODIFIED from cutrusted.i
284  */
285 function parmdecrypt_openssl($str,
286  $key,
287  $cipher_method) {
288  try {
289  $cipher_all = base64_decode($str);
290 
291  if(PARMENCDEC_AUTH_HASH_ALGO == "sha256") {
292  $auth_hash_len = 32; // bytes
293  }
294  // PARMENCDEC_AUTH_HASH_ALGO == "sha1"
295  else {
296  $auth_hash_len = 16; //bytes
297  }
298 
299  $encrypted_hash = substr($cipher_all, 0, $auth_hash_len);
300  $ciphertext = substr($cipher_all, $auth_hash_len);
301 
302  return hcuOpenSSLDecrypt($ciphertext,
303  $encrypted_hash,
304  $key,
305  $method=$cipher_method,
306  $auth_hash_algo=PARMENCDEC_AUTH_HASH_ALGO);
307  } catch (Exception $ex) {
308  return False;
309  }
310 }
311 
312 
313 /**
314  * COPIED/MODIFIED from cutrusted.i
315  */
316 function parmdecrypt_mcrypt($orig_str, $orig_key) {
317  try{
318  $base64_str = base64_decode($orig_str);
319  $key_size = mcrypt_get_key_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
320  $key = substr($orig_key, 0, $key_size);
321  $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
322  $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
323  $str = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $base64_str, MCRYPT_MODE_ECB, $iv);
324  return strippadding($str);
325  } catch (exception $ex) {
326  return false;
327  }
328 
329 }
330 
331 /**
332  * COPIED/MODIFIED from cutrusted.i
333  */
334 function parmdecrypt($orig_str,
335  $orig_key,
336  $cipher_method=PARMENCDEC_CIPHER_MODE) {
337  try {
338  $openssl_decrypt_result = parmdecrypt_openssl($orig_str,
339  $orig_key,
340  $cipher_method=$cipher_method);
341  // NOTE: code block in if section will be deleted along with
342  // parmdecrypt_mcrypt function and parmdecrypt_openssl will be
343  // merged here
344 
345  // unable to decrypt with openssl_decrypt
346  if($openssl_decrypt_result == False) {
347  $mcrypt_decrypt_result = parmdecrypt_mcrypt($orig_str, $orig_key);
348  if ($mcrypt_decrypt_result == false) {
349  // Error, could not be decrypted with either api: mcrypt or openssl
350  throw new exception("Error: Parm could not be decrypted.");
351  }
352  else {
353  // successfully decrypted using mcrypt
354  return $mcrypt_decrypt_result;
355  }
356  } else {
357  // successfully decrypted using openssl
358  return $openssl_decrypt_result;
359  }
360  } catch(Exception $ex){
361  throw $ex;
362  }
363 }
364 
365 function mcrypt_lib_exists() {
366  // since cumanage is still based on the base image, we would know
367  // if the latest php version includes the mcrypt based functions
368  // or not
369  if(function_exists('mcrypt_encrypt')) {
370  return True;
371  } else {
372  return False;
373  }
374 }
375 
376 class UpdateTrustedDetailsParms extends AbstractMigration {
377 
378  public function up() {
379  if (mcrypt_lib_exists()) {
380 
381  $tableName = "cutrusteddetail";
382  $exists = $this->hasTable($tableName);
383 
384  if ($exists) {
385  $sql = "select cu, trustedid, parms from ${tableName}";
386  $trustedDetailsList = $this->fetchAll($sql);
387 
388  for ($i=0; $i< count($trustedDetailsList); $i++) {
389 
390  $Cu = strtoupper(trim($trustedDetailsList[$i]["cu"]));
391  $trustedid = trim($trustedDetailsList[$i]["trustedid"]);
392  $tddkey = sha1("${Cu}:3pk4osso");
393 
394  $parms_old_encrypt = trim($trustedDetailsList[$i]["parms"]);
395  if (!is_null($parms_old_encrypt) && $parms_old_encrypt != '') {
396  // decrypt the ciphertext regardless of the library
397  $decrypt_content = parmdecrypt($parms_old_encrypt, $tddkey);
398 
399  // encrypt with openssl
400  $enc_content = parmencrypt($decrypt_content, $tddkey);
401  $sql = "update ${tableName} set parms='${enc_content}' where cu='${Cu}' and trustedid='${trustedid}'";
402  $this->query($sql);
403 
404  }
405  }
406  }
407  }
408  }
409 
410  /* To execute this you need to run the following command:
411  * php vendor/bin/phinx rollback (using correct path to phinx)
412  */
413 
414  public function down() {
415  if (mcrypt_lib_exists()) {
416 
417  $tableName = "cutrusteddetail";
418  $exists = $this->hasTable($tableName);
419 
420  if ($exists) {
421  $sql = "select cu, trustedid, parms from ${tableName}";
422  $trustedDetailsList = $this->fetchAll($sql);
423  for ($i=0; $i< count($trustedDetailsList); $i++) {
424 
425  $Cu = strtoupper(trim($trustedDetailsList[$i]["cu"]));
426  $trustedid = trim($trustedDetailsList[$i]["trustedid"]);
427  $tddkey = sha1("${Cu}:3pk4osso");
428 
429  $parms_old_encrypt = trim($trustedDetailsList[$i]["parms"]);
430 
431  if (!is_null($parms_old_encrypt) && $parms_old_encrypt != '') {
432  // decrypt the ciphertext regardless of the library
433  $decrypt_content = parmdecrypt($parms_old_encrypt, $tddkey);
434 
435  // encrypt back with mcrypt
436  $enc_content = parmencrypt_mcrypt($decrypt_content, $tddkey);
437  $sql = "update ${tableName} set parms='${enc_content}' where cu='${Cu}' and trustedid='${trustedid}'";
438  $this->query($sql);
439  }
440  }
441  }
442  }
443  }
444 
445 }
446 
447 ?>