Semisecure Login Reimagined ist ein Plugin für WordPress, um die Anmeldung von WordPress über unverschlüsselte Verbindungen zu schützen. Dabei werden die Login-Daten lokal auf dem Client des Anwenders verschlüsselt und erst dann über HTTP an den Server geschickt. Dort entschlüsselt das Plugin die Informationen und leitet sie an die zuständige Instanz weiter. Es ist also möglich ohne SSL/TLS und ohne Zertifikat, welches dennoch dringend empfohlen wird, Daten zu verschlüsseln.
[English:] Semisecure Login Reimagined is a WordPress plugin which encrypt the login through HTTP without SSL requirements. Credentials will be encrypted locally and sent to server whose decrypt data. You don’t need a certificate.
Problem
Das Plugin ist nunmehr seit über einem Jahr nicht mehr aktualisiert worden. Doch die Welt dreht sich weiter und mit dem Update auf PHP 5.4 funktioniert das Plugin nicht mehr. Je nach Einstellung des Webservers kommt es zu einem internen Serverfehler (Error Code 500) oder zu folgenden Fehlermeldungen.
[English:] After upgrading to PHP 5.4 you got errors while using like following.
PHP Fatal error: Call-time pass-by-reference has been removed in D:\wordpress\wp-content\plugins\semisecure-login-reimagined\classes\crypto-js\Cryptojs_MARC4.php on line 38
PHP Fatal error: Call-time pass-by-reference has been removed in D:\wordpress\wp-content\plugins\semisecure-login-reimagined\classes\crypto-js\Cryptojs_AES.php on line 44
Ursache / Cause
Seit PHP 5.4 können nun nur noch Variablen und “New”-Ausdrücke per Referenz übergeben werden. Da der Code jedoch längere Zeit nicht mehr angepasst wurde, wird diese Einschränkung nicht berücksichtigt und die Nutzung schlägt eben fehl.
[English:] Since PHP 5.4 it’s not allowed to pass parameters by reference. You can pass a variable or New statements by reference to a function so the function can modify the variable.
Lösung / Solution
Einzig bestehende Alternative ist, den Code selbständig anzupassen. Ich habe sowohl die AES- als auch MARC4-Verschlüsselungsfunktion abgeändert und erfolgreich getestet. Anbei die Änderungen der letzten Version (3.2.0). Zu ändernde Abschnitte wurden hervorgehoben.
Wer das modifizierte Plugin Semisecure Login Reimagined (v 3.2.1) herunterladen möchte, kann den folgenden Abschnitt überspringen.
[English:] See the code that have to change for. Walk through Download Section to get the modified Semisecure Login Reimagined Plugin (v 3.2.1).
semisecure-login-reimagined\classes\crypto-js\Cryptojs_MARC4.php (Original)
* Note: Rabbit currently requires 64-bit PHP (does not work correctly with 32-bit PHP) * Note: SHA-256 and HMAC-SHA256 require 'hash', 'mhash', suhosin, or 3rd-party support * * Usage: * $crypted = Cryptojs_MARC4::encrypt('message', 'secret passphrase'); * $plain = Cryptojs_MARC4::decrypt($crypted, 'secret passphrase'); * * Original JavaScript version: * Crypto-JS v2.0.0 * https://code.google.com/archive/p/crypto-js * Copyright (c) 2009, Jeff Mott. All rights reserved. * https://code.google.com/archive/p/crypto-js/wikis/License.wiki */ class Cryptojs_MARC4 { function encrypt($message, $password) { // Convert to bytes $m = Cryptojs_UTF8::stringToBytes($message); // Generate random IV $iv = Cryptojs_util::randomBytes(16); // Generate key $k = (!is_array($password)) ? // Derive key from passphrase Cryptojs::PBKDF2($password, $iv, 32, array('asBytes' => true)) : // else, assume byte array representing cryptographic key $password; // Encrypt Cryptojs_MARC4::_marc4(&$m, $k, 1536); // Return ciphertext return Cryptojs_util::bytesToBase64(Cryptojs_util::concat($iv, $m)); } function decrypt($ciphertext, $password) { // Convert to bytes $c = Cryptojs_util::base64ToBytes($ciphertext); // Separate IV and message $iv = array_splice(&$c, 0, 16); // Generate key $k = (!is_array($password)) ? // Derive key from passphrase Cryptojs::PBKDF2($password, $iv, 32, array('asBytes' => true)) : // else, assume byte array representing cryptographic key $password; // Decrypt Cryptojs_MARC4::_marc4(&$c, $k, 1536); // Return plaintext return Cryptojs_UTF8::bytesToString($c); } function _marc4(&$m, $k, $drop) { // State variables $i; $j; $s; $temp; // Key Setup for ($i = 0, $s = array(); $i < 256; $i++) $s[$i] = $i; for ($i = 0, $j = 0; $i < 256; $i++) { $j = ($j + $s[$i] + $k[$i % count($k)]) % 256; // Swap $temp = $s[$i]; $s[$i] = $s[$j]; $s[$j] = $temp; } // Clear counters $i = $j = 0; // Encryption for ($k = 0 - $drop; $k < count($m); $k++) { $i = ($i + 1) % 256; $j = ($j + $s[$i]) % 256; // Swap $temp = $s[$i]; $s[$i] = $s[$j]; $s[$j] = $temp; // Stop here if we're still dropping keystream if ($k < 0) continue; // Encrypt $m[$k] ^= $s[($s[$i] + $s[$j]) % 256]; } } } endif; ?>
semisecure-login-reimagined\classes\crypto-js\Cryptojs_MARC4.php (modifiziert)
* Note: Rabbit currently requires 64-bit PHP (does not work correctly with 32-bit PHP) * Note: SHA-256 and HMAC-SHA256 require 'hash', 'mhash', suhosin, or 3rd-party support * * Usage: * $crypted = Cryptojs_MARC4::encrypt('message', 'secret passphrase'); * $plain = Cryptojs_MARC4::decrypt($crypted, 'secret passphrase'); * * Original JavaScript version: * Crypto-JS v2.0.0 * https://code.google.com/archive/p/crypto-js * Copyright (c) 2009, Jeff Mott. All rights reserved. * https://code.google.com/archive/p/crypto-js/wikis/License.wiki */ class Cryptojs_MARC4 { function encrypt($message, $password) { // Convert to bytes $m = Cryptojs_UTF8::stringToBytes($message); // Generate random IV $iv = Cryptojs_util::randomBytes(16); // Generate key $k = (!is_array($password)) ? // Derive key from passphrase Cryptojs::PBKDF2($password, $iv, 32, array('asBytes' => true)) : // else, assume byte array representing cryptographic key $password; // Encrypt Cryptojs_MARC4::_marc4($m, $k, 1536); // Return ciphertext return Cryptojs_util::bytesToBase64(Cryptojs_util::concat($iv, $m)); } function decrypt($ciphertext, $password) { // Convert to bytes $c = Cryptojs_util::base64ToBytes($ciphertext); // Separate IV and message $iv = array_splice($c, 0, 16); // Generate key $k = (!is_array($password)) ? // Derive key from passphrase Cryptojs::PBKDF2($password, $iv, 32, array('asBytes' => true)) : // else, assume byte array representing cryptographic key $password; // Decrypt Cryptojs_MARC4::_marc4($c, $k, 1536); // Return plaintext return Cryptojs_UTF8::bytesToString($c); } function _marc4(&$m, $k, $drop) { // State variables $i; $j; $s; $temp; // Key Setup for ($i = 0, $s = array(); $i < 256; $i++) $s[$i] = $i; for ($i = 0, $j = 0; $i < 256; $i++) { $j = ($j + $s[$i] + $k[$i % count($k)]) % 256; // Swap $temp = $s[$i]; $s[$i] = $s[$j]; $s[$j] = $temp; } // Clear counters $i = $j = 0; // Encryption for ($k = 0 - $drop; $k < count($m); $k++) { $i = ($i + 1) % 256; $j = ($j + $s[$i]) % 256; // Swap $temp = $s[$i]; $s[$i] = $s[$j]; $s[$j] = $temp; // Stop here if we're still dropping keystream if ($k < 0) continue; // Encrypt $m[$k] ^= $s[($s[$i] + $s[$j]) % 256]; } } } endif; ?>
semisecure-login-reimagined\classes\crypto-js\Cryptojs_AES.php (Original)
* Note: Rabbit currently requires 64-bit PHP (does not work correctly with 32-bit PHP) * Note: SHA-256 and HMAC-SHA256 require 'hash', 'mhash', suhosin, or 3rd-party support * * Usage: * $crypted = Cryptojs_AES::encrypt('message', 'secret passphrase'); * $plain = Cryptojs_AES::decrypt($crypted, 'secret passphrase'); * * Original JavaScript version: * Crypto-JS v2.0.0 * https://code.google.com/archive/p/crypto-js * Copyright (c) 2009, Jeff Mott. All rights reserved. * https://code.google.com/archive/p/crypto-js/wikis/License.wiki */ class Cryptojs_AES { // Possible todo: check if mcrypt can be used here (probably faster than PHP code) function encrypt($message, $password, $options=null) { // Convert to bytes $m = Cryptojs_UTF8::stringToBytes($message); // Generate random IV $iv = Cryptojs_util::randomBytes(Cryptojs_AES::_blocksize() * 4); // Generate key $k = (!is_array($password)) ? // Derive key from passphrase Cryptojs::PBKDF2($password, $iv, 32, array('asBytes' => true)) : // else, assume byte array representing cryptographic key $password; // Determine mode if (is_array($options) && isset($options['mode'])) $mode = strtolower($options['mode']); if ($mode != 'ofb' && $mode != 'cbc') $mode = 'ofb'; // Encrypt Cryptojs_AES::_init($k, $mode, &$m, $iv, true); // Return ciphertext return Cryptojs_util::bytesToBase64(Cryptojs_util::concat($iv, $m)); } function decrypt($ciphertext, $password, $options=null) { // Cconvert to bytes $c = Cryptojs_util::base64ToBytes($ciphertext); // Separate IV and message $iv = array_splice(&$c, 0, Cryptojs_AES::_blocksize() * 4); // Generate key $k = (!is_array($password)) ? // Derive key from passphrase Cryptojs::PBKDF2($password, $iv, 32, array('asBytes' => true)) : // else, assume byte array representing cryptographic key $password; // Determine mode if (is_array($options) && isset($options['mode'])) $mode = strtolower($options['mode']); if ($mode != 'ofb' && $mode != 'cbc') $mode = 'ofb'; // Decrypt Cryptojs_AES::_init($k, $mode, &$c, $iv, false); // Return plaintext return Cryptojs_UTF8::bytesToString($c); } function _blocksize() { return 4; } function _init($k, $mode, &$m, $iv, $encrypt) { // Precomputed SBOX static $SBOX = array( 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ); static $INVSBOX = array(); if (empty($INVSBOX)) { // Compute inverse SBOX lookup table for ($i = 0; $i < 256; $i++) $INVSBOX[$SBOX[$i]] = $i; } // Compute multiplication in GF(2^8) lookup tables static $MULT2 = array(); static $MULT3 = array(); static $MULT9 = array(); static $MULTB = array(); static $MULTD = array(); static $MULTE = array(); if (empty($MULT2) || empty($MULT3) || empty($MULT9) || empty($MULTB) || empty($MULTD) || empty($MULTE)) { for ($i = 0; $i < 256; $i++) { $MULT2[$i] = Cryptojs_AES::_xtime($i,2); $MULT3[$i] = Cryptojs_AES::_xtime($i,3); $MULT9[$i] = Cryptojs_AES::_xtime($i,9); $MULTB[$i] = Cryptojs_AES::_xtime($i,0xB); $MULTD[$i] = Cryptojs_AES::_xtime($i,0xD); $MULTE[$i] = Cryptojs_AES::_xtime($i,0xE); } } // Precomputed RCon lookup static $RCON = array(0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36); // Inner state static $state = array(array(), array(), array(), array()); static $keylength; static $nrounds; static $keyschedule; $keylength = count($k) / 4; $nrounds = $keylength + 6; Cryptojs_AES::_keyexpansion($k, &$keyschedule, $keylength, $nrounds, $SBOX, $RCON); if ($mode == 'ofb') { Cryptojs_AES::_ofb(&$m, $iv, &$state, $nrounds, $keyschedule, $SBOX, $INVSBOX, $MULT2, $MULT3, $MULT9, $MULTB, $MULTD, $MULTE); } else { // cbc if ($encrypt) Cryptojs_AES::_cbcEncrypt(&$m, $iv, &$state, $nrounds, $keyschedule, $SBOX, $INVSBOX, $MULT2, $MULT3, $MULT9, $MULTB, $MULTD, $MULTE); else Cryptojs_AES::_cbcDecrypt(&$m, $iv, &$state, $nrounds, $keyschedule, $SBOX, $INVSBOX, $MULT2, $MULT3, $MULT9, $MULTB, $MULTD, $MULTE); } } function _encryptblock(&$m, $offset, &$state, $nrounds, $keyschedule, $SBOX, $INVSBOX, $MULT2, $MULT3, $MULT9, $MULTB, $MULTD, $MULTE) { // Set input for ($row = 0; $row < Cryptojs_AES::_blocksize(); $row++) { for ($col = 0; $col < 4; $col++) $state[$row][$col] = $m[$offset + $col * 4 + $row]; } // Add round key for($row = 0; $row < 4; $row++) { for($col = 0; $col < 4; $col++) $state[$row][$col] ^= $keyschedule[$col][$row]; } for ($round = 1; $round < $nrounds; $round++) { // Sub bytes for ($row = 0; $row < 4; $row++) { for ($col = 0; $col < 4; $col++) $state[$row][$col] = $SBOX[$state[$row][$col]]; } // Shift rows $state[1][] = array_shift(&$state[1]); $state[2][] = array_shift(&$state[2]); $state[2][] = array_shift(&$state[2]); array_unshift(&$state[3], array_pop(&$state[3])); // Mix columns for ($col = 0; $col < 4; $col++) { $s0 = $state[0][$col]; $s1 = $state[1][$col]; $s2 = $state[2][$col]; $s3 = $state[3][$col]; $state[0][$col] = $MULT2[$s0] ^ $MULT3[$s1] ^ $s2 ^ $s3; $state[1][$col] = $s0 ^ $MULT2[$s1] ^ $MULT3[$s2] ^ $s3; $state[2][$col] = $s0 ^ $s1 ^ $MULT2[$s2] ^ $MULT3[$s3]; $state[3][$col] = $MULT3[$s0] ^ $s1 ^ $s2 ^ $MULT2[$s3]; } // Add round key for ($row = 0; $row < 4; $row++) { for ($col = 0; $col < 4; $col++) $state[$row][$col] ^= $keyschedule[$round * 4 + $col][$row]; } } // Sub byte for ($row = 0; $row < 4; $row++) { for ($col = 0; $col < 4; $col++) $state[$row][$col] = $SBOX[$state[$row][$col]]; } // Shift rows $state[1][] = array_shift(&$state[1]); $state[2][] = array_shift(&$state[2]); $state[2][] = array_shift(&$state[2]); array_unshift(&$state[3], array_pop(&$state[3])); // Add round key for ($row = 0; $row < 4; $row++) { for ($col = 0; $col < 4; $col++) $state[$row][$col] ^= $keyschedule[$nrounds * 4 + $col][$row]; } // Set output for ($row = 0; $row < Cryptojs_AES::_blocksize(); $row++) { for ($col = 0; $col < 4; $col++) $m[$offset + $col * 4 + $row] = $state[$row][$col]; } } function _decryptblock(&$c, $offset, &$state, $nrounds, $keyschedule, $SBOX, $INVSBOX, $MULT2, $MULT3, $MULT9, $MULTB, $MULTD, $MULTE) { // Set input for ($row = 0; $row < Cryptojs_AES::_blocksize(); $row++) { for ($col = 0; $col < 4; $col++) $state[$row][$col] = $c[$offset + $col * 4 + $row]; } // Add round key for ($row = 0; $row < 4; $row++) { for ($col = 0; $col < 4; $col++) $state[$row][$col] ^= $keyschedule[$nrounds * 4 + $col][$row]; } for ($round = 1; $round < $nrounds; $round++) { // Inv shift rows array_unshift(&$state[1], array_pop(&$state[1])); $state[2][] = array_shift(&$state[2]); $state[2][] = array_shift(&$state[2]); $state[3][] = array_shift(&$state[3]); // Inv sub bytes for ($row = 0; $row < 4; $row++) { for ($col = 0; $col < 4; $col++) $state[$row][$col] = $INVSBOX[$state[$row][$col]]; } // Add round key for ($row = 0; $row < 4; $row++) { for ($col = 0; $col < 4; $col++) $state[$row][$col] ^= $keyschedule[($nrounds - $round) * 4 + $col][$row]; } // Inv mix columns for ($col = 0; $col < 4; $col++) { $s0 = $state[0][$col]; $s1 = $state[1][$col]; $s2 = $state[2][$col]; $s3 = $state[3][$col]; $state[0][$col] = $MULTE[$s0] ^ $MULTB[$s1] ^ $MULTD[$s2] ^ $MULT9[$s3]; $state[1][$col] = $MULT9[$s0] ^ $MULTE[$s1] ^ $MULTB[$s2] ^ $MULTD[$s3]; $state[2][$col] = $MULTD[$s0] ^ $MULT9[$s1] ^ $MULTE[$s2] ^ $MULTB[$s3]; $state[3][$col] = $MULTB[$s0] ^ $MULTD[$s1] ^ $MULT9[$s2] ^ $MULTE[$s3]; } } // Inv shift rows array_unshift(&$state[1], array_pop(&$state[1])); $state[2][] = array_shift(&$state[2]); $state[2][] = array_shift(&$state[2]); $state[3][] = array_shift(&$state[3]); // Inv sub bytes for ($row = 0; $row < 4; $row++) { for ($col = 0; $col < 4; $col++) $state[$row][$col] = $INVSBOX[$state[$row][$col]]; } // Add round key for ($row = 0; $row < 4; $row++) { for ($col = 0; $col < 4; $col++) $state[$row][$col] ^= $keyschedule[$col][$row]; } // Set output for ($row = 0; $row < Cryptojs_AES::_blocksize(); $row++) { for ($col = 0; $col < 4; $col++) $c[$offset + $col * 4 + $row] = $state[$row][$col]; } } function _keyexpansion($k, &$keyschedule, $keylength, $nrounds, $SBOX, $RCON) { $keyschedule = array(); for ($row = 0; $row < $keylength; $row++) { $keyschedule[$row] = array( $k[$row * 4], $k[$row * 4 + 1], $k[$row * 4 + 2], $k[$row * 4 + 3] ); } for ($row = $keylength; $row < Cryptojs_AES::_blocksize() * ($nrounds + 1); $row++) { $temp = array( $keyschedule[$row - 1][0], $keyschedule[$row - 1][1], $keyschedule[$row - 1][2], $keyschedule[$row - 1][3] ); if ($row % $keylength == 0) { // Rot word $temp[] = array_shift(&$temp); // Sub word $temp[0] = $SBOX[$temp[0]]; $temp[1] = $SBOX[$temp[1]]; $temp[2] = $SBOX[$temp[2]]; $temp[3] = $SBOX[$temp[3]]; $temp[0] ^= $RCON[$row / $keylength]; } else if ($keylength > 6 && $row % $keylength == 4) { // Sub word $temp[0] = $SBOX[$temp[0]]; $temp[1] = $SBOX[$temp[1]]; $temp[2] = $SBOX[$temp[2]]; $temp[3] = $SBOX[$temp[3]]; } $keyschedule[$row] = array( $keyschedule[$row - $keylength][0] ^ $temp[0], $keyschedule[$row - $keylength][1] ^ $temp[1], $keyschedule[$row - $keylength][2] ^ $temp[2], $keyschedule[$row - $keylength][3] ^ $temp[3] ); } } function _xtime($a, $b) { for ($result = 0, $i = 0; $i < 8; $i++) { if ($b & 1) $result ^= $a; $hiBitSet = $a & 0x80; $a = ($a << 1) & 0xFF; if ($hiBitSet) $a ^= 0x1b; $b = Cryptojs_util::urs($b, 1); } return $result; } /* * Mode specific methods */ function _cbcEncrypt(&$m, $iv, &$state, $nrounds, $keyschedule, $SBOX, $INVSBOX, $MULT2, $MULT3, $MULT9, $MULTB, $MULTD, $MULTE) { $blockSizeInBytes = Cryptojs_AES::_blocksize() * 4; // Pad $m[] = 0x80; // Encrypt each block for ($offset = 0; $offset < count($m); $offset += $blockSizeInBytes) { if ($offset == 0) { // XOR first block using IV for ($i = 0; $i < $blockSizeInBytes; $i++) $m[$i] ^= $iv[$i]; } else { // XOR this block using previous crypted block for ($i = 0; $i < $blockSizeInBytes; $i++) $m[$offset + $i] ^= $m[$offset + $i - $blockSizeInBytes]; } // Encrypt block Cryptojs_AES::_encryptblock(&$m, $offset, &$state, $nrounds, $keyschedule, $SBOX, $INVSBOX, $MULT2, $MULT3, $MULT9, $MULTB, $MULTD, $MULTE); } } function _cbcDecrypt(&$c, $iv, &$state, $nrounds, $keyschedule, $SBOX, $INVSBOX, $MULT2, $MULT3, $MULT9, $MULTB, $MULTD, $MULTE) { $blockSizeInBytes = Cryptojs_AES::_blocksize() * 4; // Decrypt each block for ($offset = 0; $offset < count($c); $offset += $blockSizeInBytes) { // Save this crypted block $thisCryptedBlock = array_slice(&$c, $offset, $offset + $blockSizeInBytes); // Decrypt block Cryptojs_AES::_decryptblock(&$c, $offset, &$state, $nrounds, $keyschedule, $SBOX, $INVSBOX, $MULT2, $MULT3, $MULT9, $MULTB, $MULTD, $MULTE); if ($offset == 0) { // XOR first block using IV for ($i = 0; $i < $blockSizeInBytes; $i++) $c[$i] ^= $iv[$i]; } else { // XOR decrypted block using previous crypted block for($i = 0; $i < $blockSizeInBytes; $i++) $c[$offset + $i] ^= $prevCryptedBlock[$i]; } // This crypted block is the new previous crypted block $prevCryptedBlock = $thisCryptedBlock; } // Strip padding while(array_pop(&$c) != 0x80) {} } function _ofb(&$m, $iv, &$state, $nrounds, $keyschedule, $SBOX, $INVSBOX, $MULT2, $MULT3, $MULT9, $MULTB, $MULTD, $MULTE) { $blockSizeInBytes = Cryptojs_AES::_blocksize() * 4; $keystream = array_slice($iv, 0); // Encrypt each byte for ($i = 0; $i < count($m); $i++) { // Generate keystream if ($i % $blockSizeInBytes == 0) Cryptojs_AES::_encryptblock(&$keystream, 0, &$state, $nrounds, $keyschedule, $SBOX, $INVSBOX, $MULT2, $MULT3, $MULT9, $MULTB, $MULTD, $MULTE); // Encrypt byte $m[$i] ^= $keystream[$i % $blockSizeInBytes]; } } } endif; ?>
semisecure-login-reimagined\classes\crypto-js\Cryptojs_AES.php (modifiziert)
* Note: Rabbit currently requires 64-bit PHP (does not work correctly with 32-bit PHP) * Note: SHA-256 and HMAC-SHA256 require 'hash', 'mhash', suhosin, or 3rd-party support * * Usage: * $crypted = Cryptojs_AES::encrypt('message', 'secret passphrase'); * $plain = Cryptojs_AES::decrypt($crypted, 'secret passphrase'); * * Original JavaScript version: * Crypto-JS v2.0.0 * https://code.google.com/archive/p/crypto-js * Copyright (c) 2009, Jeff Mott. All rights reserved. * https://code.google.com/archive/p/crypto-js/wikis/License.wiki */ class Cryptojs_AES { // Possible todo: check if mcrypt can be used here (probably faster than PHP code) function encrypt($message, $password, $options=null) { // Convert to bytes $m = Cryptojs_UTF8::stringToBytes($message); // Generate random IV $iv = Cryptojs_util::randomBytes(Cryptojs_AES::_blocksize() * 4); // Generate key $k = (!is_array($password)) ? // Derive key from passphrase Cryptojs::PBKDF2($password, $iv, 32, array('asBytes' => true)) : // else, assume byte array representing cryptographic key $password; // Determine mode if (is_array($options) && isset($options['mode'])) $mode = strtolower($options['mode']); if ($mode != 'ofb' && $mode != 'cbc') $mode = 'ofb'; // Encrypt Cryptojs_AES::_init($k, $mode, $m, $iv, true); // Return ciphertext return Cryptojs_util::bytesToBase64(Cryptojs_util::concat($iv, $m)); } function decrypt($ciphertext, $password, $options=null) { // Cconvert to bytes $c = Cryptojs_util::base64ToBytes($ciphertext); // Separate IV and message $iv = array_splice($c, 0, Cryptojs_AES::_blocksize() * 4); // Generate key $k = (!is_array($password)) ? // Derive key from passphrase Cryptojs::PBKDF2($password, $iv, 32, array('asBytes' => true)) : // else, assume byte array representing cryptographic key $password; // Determine mode if (is_array($options) && isset($options['mode'])) $mode = strtolower($options['mode']); if ($mode != 'ofb' && $mode != 'cbc') $mode = 'ofb'; // Decrypt Cryptojs_AES::_init($k, $mode, $c, $iv, false); // Return plaintext return Cryptojs_UTF8::bytesToString($c); } function _blocksize() { return 4; } function _init($k, $mode, &$m, $iv, $encrypt) { // Precomputed SBOX static $SBOX = array( 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ); static $INVSBOX = array(); if (empty($INVSBOX)) { // Compute inverse SBOX lookup table for ($i = 0; $i < 256; $i++) $INVSBOX[$SBOX[$i]] = $i; } // Compute multiplication in GF(2^8) lookup tables static $MULT2 = array(); static $MULT3 = array(); static $MULT9 = array(); static $MULTB = array(); static $MULTD = array(); static $MULTE = array(); if (empty($MULT2) || empty($MULT3) || empty($MULT9) || empty($MULTB) || empty($MULTD) || empty($MULTE)) { for ($i = 0; $i < 256; $i++) { $MULT2[$i] = Cryptojs_AES::_xtime($i,2); $MULT3[$i] = Cryptojs_AES::_xtime($i,3); $MULT9[$i] = Cryptojs_AES::_xtime($i,9); $MULTB[$i] = Cryptojs_AES::_xtime($i,0xB); $MULTD[$i] = Cryptojs_AES::_xtime($i,0xD); $MULTE[$i] = Cryptojs_AES::_xtime($i,0xE); } } // Precomputed RCon lookup static $RCON = array(0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36); // Inner state static $state = array(array(), array(), array(), array()); static $keylength; static $nrounds; static $keyschedule; $keylength = count($k) / 4; $nrounds = $keylength + 6; Cryptojs_AES::_keyexpansion($k, $keyschedule, $keylength, $nrounds, $SBOX, $RCON); if ($mode == 'ofb') { Cryptojs_AES::_ofb($m, $iv, $state, $nrounds, $keyschedule, $SBOX, $INVSBOX, $MULT2, $MULT3, $MULT9, $MULTB, $MULTD, $MULTE); } else { // cbc if ($encrypt) Cryptojs_AES::_cbcEncrypt($m, $iv, $state, $nrounds, $keyschedule, $SBOX, $INVSBOX, $MULT2, $MULT3, $MULT9, $MULTB, $MULTD, $MULTE); else Cryptojs_AES::_cbcDecrypt($m, $iv, $state, $nrounds, $keyschedule, $SBOX, $INVSBOX, $MULT2, $MULT3, $MULT9, $MULTB, $MULTD, $MULTE); } } function _encryptblock(&$m, $offset, &$state, $nrounds, $keyschedule, $SBOX, $INVSBOX, $MULT2, $MULT3, $MULT9, $MULTB, $MULTD, $MULTE) { // Set input for ($row = 0; $row < Cryptojs_AES::_blocksize(); $row++) { for ($col = 0; $col < 4; $col++) $state[$row][$col] = $m[$offset + $col * 4 + $row]; } // Add round key for($row = 0; $row < 4; $row++) { for($col = 0; $col < 4; $col++) $state[$row][$col] ^= $keyschedule[$col][$row]; } for ($round = 1; $round < $nrounds; $round++) { // Sub bytes for ($row = 0; $row < 4; $row++) { for ($col = 0; $col < 4; $col++) $state[$row][$col] = $SBOX[$state[$row][$col]]; } // Shift rows $state[1][] = array_shift($state[1]); $state[2][] = array_shift($state[2]); $state[2][] = array_shift($state[2]); array_unshift($state[3], array_pop($state[3])); // Mix columns for ($col = 0; $col < 4; $col++) { $s0 = $state[0][$col]; $s1 = $state[1][$col]; $s2 = $state[2][$col]; $s3 = $state[3][$col]; $state[0][$col] = $MULT2[$s0] ^ $MULT3[$s1] ^ $s2 ^ $s3; $state[1][$col] = $s0 ^ $MULT2[$s1] ^ $MULT3[$s2] ^ $s3; $state[2][$col] = $s0 ^ $s1 ^ $MULT2[$s2] ^ $MULT3[$s3]; $state[3][$col] = $MULT3[$s0] ^ $s1 ^ $s2 ^ $MULT2[$s3]; } // Add round key for ($row = 0; $row < 4; $row++) { for ($col = 0; $col < 4; $col++) $state[$row][$col] ^= $keyschedule[$round * 4 + $col][$row]; } } // Sub byte for ($row = 0; $row < 4; $row++) { for ($col = 0; $col < 4; $col++) $state[$row][$col] = $SBOX[$state[$row][$col]]; } // Shift rows $state[1][] = array_shift($state[1]); $state[2][] = array_shift($state[2]); $state[2][] = array_shift($state[2]); array_unshift($state[3], array_pop($state[3])); // Add round key for ($row = 0; $row < 4; $row++) { for ($col = 0; $col < 4; $col++) $state[$row][$col] ^= $keyschedule[$nrounds * 4 + $col][$row]; } // Set output for ($row = 0; $row < Cryptojs_AES::_blocksize(); $row++) { for ($col = 0; $col < 4; $col++) $m[$offset + $col * 4 + $row] = $state[$row][$col]; } } function _decryptblock(&$c, $offset, &$state, $nrounds, $keyschedule, $SBOX, $INVSBOX, $MULT2, $MULT3, $MULT9, $MULTB, $MULTD, $MULTE) { // Set input for ($row = 0; $row < Cryptojs_AES::_blocksize(); $row++) { for ($col = 0; $col < 4; $col++) $state[$row][$col] = $c[$offset + $col * 4 + $row]; } // Add round key for ($row = 0; $row < 4; $row++) { for ($col = 0; $col < 4; $col++) $state[$row][$col] ^= $keyschedule[$nrounds * 4 + $col][$row]; } for ($round = 1; $round < $nrounds; $round++) { // Inv shift rows array_unshift($state[1], array_pop($state[1])); $state[2][] = array_shift($state[2]); $state[2][] = array_shift($state[2]); $state[3][] = array_shift($state[3]); // Inv sub bytes for ($row = 0; $row < 4; $row++) { for ($col = 0; $col < 4; $col++) $state[$row][$col] = $INVSBOX[$state[$row][$col]]; } // Add round key for ($row = 0; $row < 4; $row++) { for ($col = 0; $col < 4; $col++) $state[$row][$col] ^= $keyschedule[($nrounds - $round) * 4 + $col][$row]; } // Inv mix columns for ($col = 0; $col < 4; $col++) { $s0 = $state[0][$col]; $s1 = $state[1][$col]; $s2 = $state[2][$col]; $s3 = $state[3][$col]; $state[0][$col] = $MULTE[$s0] ^ $MULTB[$s1] ^ $MULTD[$s2] ^ $MULT9[$s3]; $state[1][$col] = $MULT9[$s0] ^ $MULTE[$s1] ^ $MULTB[$s2] ^ $MULTD[$s3]; $state[2][$col] = $MULTD[$s0] ^ $MULT9[$s1] ^ $MULTE[$s2] ^ $MULTB[$s3]; $state[3][$col] = $MULTB[$s0] ^ $MULTD[$s1] ^ $MULT9[$s2] ^ $MULTE[$s3]; } } // Inv shift rows array_unshift($state[1], array_pop($state[1])); $state[2][] = array_shift($state[2]); $state[2][] = array_shift($state[2]); $state[3][] = array_shift($state[3]); // Inv sub bytes for ($row = 0; $row < 4; $row++) { for ($col = 0; $col < 4; $col++) $state[$row][$col] = $INVSBOX[$state[$row][$col]]; } // Add round key for ($row = 0; $row < 4; $row++) { for ($col = 0; $col < 4; $col++) $state[$row][$col] ^= $keyschedule[$col][$row]; } // Set output for ($row = 0; $row < Cryptojs_AES::_blocksize(); $row++) { for ($col = 0; $col < 4; $col++) $c[$offset + $col * 4 + $row] = $state[$row][$col]; } } function _keyexpansion($k, &$keyschedule, $keylength, $nrounds, $SBOX, $RCON) { $keyschedule = array(); for ($row = 0; $row < $keylength; $row++) { $keyschedule[$row] = array( $k[$row * 4], $k[$row * 4 + 1], $k[$row * 4 + 2], $k[$row * 4 + 3] ); } for ($row = $keylength; $row < Cryptojs_AES::_blocksize() * ($nrounds + 1); $row++) { $temp = array( $keyschedule[$row - 1][0], $keyschedule[$row - 1][1], $keyschedule[$row - 1][2], $keyschedule[$row - 1][3] ); if ($row % $keylength == 0) { // Rot word $temp[] = array_shift($temp); // Sub word $temp[0] = $SBOX[$temp[0]]; $temp[1] = $SBOX[$temp[1]]; $temp[2] = $SBOX[$temp[2]]; $temp[3] = $SBOX[$temp[3]]; $temp[0] ^= $RCON[$row / $keylength]; } else if ($keylength > 6 && $row % $keylength == 4) { // Sub word $temp[0] = $SBOX[$temp[0]]; $temp[1] = $SBOX[$temp[1]]; $temp[2] = $SBOX[$temp[2]]; $temp[3] = $SBOX[$temp[3]]; } $keyschedule[$row] = array( $keyschedule[$row - $keylength][0] ^ $temp[0], $keyschedule[$row - $keylength][1] ^ $temp[1], $keyschedule[$row - $keylength][2] ^ $temp[2], $keyschedule[$row - $keylength][3] ^ $temp[3] ); } } function _xtime($a, $b) { for ($result = 0, $i = 0; $i < 8; $i++) { if ($b & 1) $result ^= $a; $hiBitSet = $a & 0x80; $a = ($a << 1) & 0xFF; if ($hiBitSet) $a ^= 0x1b; $b = Cryptojs_util::urs($b, 1); } return $result; } /* * Mode specific methods */ function _cbcEncrypt(&$m, $iv, &$state, $nrounds, $keyschedule, $SBOX, $INVSBOX, $MULT2, $MULT3, $MULT9, $MULTB, $MULTD, $MULTE) { $blockSizeInBytes = Cryptojs_AES::_blocksize() * 4; // Pad $m[] = 0x80; // Encrypt each block for ($offset = 0; $offset < count($m); $offset += $blockSizeInBytes) { if ($offset == 0) { // XOR first block using IV for ($i = 0; $i < $blockSizeInBytes; $i++) $m[$i] ^= $iv[$i]; } else { // XOR this block using previous crypted block for ($i = 0; $i < $blockSizeInBytes; $i++) $m[$offset + $i] ^= $m[$offset + $i - $blockSizeInBytes]; } // Encrypt block Cryptojs_AES::_encryptblock($m, $offset, $state, $nrounds, $keyschedule, $SBOX, $INVSBOX, $MULT2, $MULT3, $MULT9, $MULTB, $MULTD, $MULTE); } } function _cbcDecrypt(&$c, $iv, &$state, $nrounds, $keyschedule, $SBOX, $INVSBOX, $MULT2, $MULT3, $MULT9, $MULTB, $MULTD, $MULTE) { $blockSizeInBytes = Cryptojs_AES::_blocksize() * 4; // Decrypt each block for ($offset = 0; $offset < count($c); $offset += $blockSizeInBytes) { // Save this crypted block $thisCryptedBlock = array_slice($c, $offset, $offset + $blockSizeInBytes); // Decrypt block Cryptojs_AES::_decryptblock($c, $offset, $state, $nrounds, $keyschedule, $SBOX, $INVSBOX, $MULT2, $MULT3, $MULT9, $MULTB, $MULTD, $MULTE); if ($offset == 0) { // XOR first block using IV for ($i = 0; $i < $blockSizeInBytes; $i++) $c[$i] ^= $iv[$i]; } else { // XOR decrypted block using previous crypted block for($i = 0; $i < $blockSizeInBytes; $i++) $c[$offset + $i] ^= $prevCryptedBlock[$i]; } // This crypted block is the new previous crypted block $prevCryptedBlock = $thisCryptedBlock; } // Strip padding while(array_pop($c) != 0x80) {} } function _ofb(&$m, $iv, &$state, $nrounds, $keyschedule, $SBOX, $INVSBOX, $MULT2, $MULT3, $MULT9, $MULTB, $MULTD, $MULTE) { $blockSizeInBytes = Cryptojs_AES::_blocksize() * 4; $keystream = array_slice($iv, 0); // Encrypt each byte for ($i = 0; $i < count($m); $i++) { // Generate keystream if ($i % $blockSizeInBytes == 0) Cryptojs_AES::_encryptblock($keystream, 0, $state, $nrounds, $keyschedule, $SBOX, $INVSBOX, $MULT2, $MULT3, $MULT9, $MULTB, $MULTD, $MULTE); // Encrypt byte $m[$i] ^= $keystream[$i % $blockSizeInBytes]; } } } endif; ?>
3 Kommentare
Michal · 23. Dezember 2014 um 13:41
Very useful. Big thanks for sharing.
GC · 22. Juli 2014 um 07:12
Wow… thanks for taking care of this and posting a fix!!
Peter · 31. Mai 2012 um 03:23
Danke! This is very helpful.