diff --git a/tfa.admin.inc b/tfa.admin.inc
index 28319af..c063e08 100644
--- a/tfa.admin.inc
+++ b/tfa.admin.inc
@@ -25,10 +25,10 @@ function tfa_admin_settings($form, $form_state) {
}
}
- // Check if mcrypt plugin is available.
- if (!extension_loaded('mcrypt')) {
+ // Check if openssl or mcrypt extensions are available.
+ if (!extension_loaded('openssl') && !extension_loaded('mcrypt')) {
// @todo allow alter in case of other encryption libs.
- drupal_set_message(t('The TFA module requires the PHP Mcrypt extension be installed on the web server. See the TFA help documentation for setup.', array('!link' => url('admin/help/tfa'))), 'error');
+ drupal_set_message(t('The TFA module requires one of the PHP OpenSSL or MCrypt extensions to be installed on the web server. See the TFA help documentation for setup.', array('!link' => url('admin/help/tfa'))), 'error');
return array();
}
diff --git a/tfa.inc b/tfa.inc
index bb9ab82..9571fe5 100644
--- a/tfa.inc
+++ b/tfa.inc
@@ -376,6 +376,8 @@ public function getContext() {
*/
abstract class TfaBasePlugin {
+ const CRYPT_VERSION = '1';
+
/**
* @var string
*/
@@ -502,10 +504,39 @@ protected function generate() {
*
* Should be used when writing codes to storage.
*
- * @param string.
+ * @param string $text
+ * The plaintext to be encrypted.
+ *
* @return string
+ * The encrypted text.
*/
protected function encrypt($text) {
+ // Backwards compatibility with MCrypt.
+ if (!extension_loaded('openssl') && extension_loaded('mcrypt')) {
+ return $this->encryptWithMCrypt($text);
+ }
+ $iv = drupal_random_bytes(16);
+ // Using 1 instead of the constant OPENSSL_RAW_DATA, for PHP 5.3.
+ $ciphertext = openssl_encrypt($text, 'AES-256-CBC', $this->encryptionKey, 1, $iv);
+ $crypto_data = array(
+ 'version' => self::CRYPT_VERSION,
+ 'iv_base64' => base64_encode($iv),
+ 'ciphertext_base64' => base64_encode($ciphertext),
+ );
+ $json_encoded_crypto_data = drupal_json_encode($crypto_data);
+ return $json_encoded_crypto_data;
+ }
+
+ /**
+ * Encrypt using the deprecated MCrypt extension.
+ *
+ * @param string $text
+ * The text to encrypt.
+ *
+ * @return string
+ * The text encrypted using MCrypt.
+ */
+ protected function encryptWithMCrypt($text) {
$td = mcrypt_module_open('rijndael-128', '', 'cbc', '');
$iv = drupal_random_bytes(mcrypt_enc_get_iv_size($td));
@@ -528,10 +559,42 @@ protected function encrypt($text) {
*
* Should be used when reading codes from storage.
*
- * @param string
- * @return string
+ * @param string $data
+ * The encrypted text.
+ *
+ * @return string|boolean
+ * The plaintext, or FALSE on failure.
*/
protected function decrypt($data) {
+ $is_legacy = TRUE;
+ $crypto_data = drupal_json_decode($data);
+ if (!empty($crypto_data['version']) && !empty($crypto_data['iv_base64']) && !empty($crypto_data['ciphertext_base64'])) {
+ $iv = base64_decode($crypto_data['iv_base64']);
+ $ciphertext = base64_decode($crypto_data['ciphertext_base64']);
+ return openssl_decrypt($ciphertext, 'AES-256-CBC', $this->encryptionKey, TRUE, $iv);
+ }
+
+ // Backwards compatibility with the old MCrypt scheme.
+ if (extension_loaded('openssl')) {
+ return $this->decryptLegacyDataWithOpenSSL($data);
+ }
+ if (extension_loaded('mcrypt')) {
+ return $this->decryptLegacyDataWithMCrypt($data);
+ }
+
+ return FALSE;
+ }
+
+ /**
+ * Decrypt using the deprecated MCrypt extension.
+ *
+ * @param string $data
+ * The data to be decrypted.
+ *
+ * @return string|boolean
+ * The plaintext, or FALSE on failure.
+ */
+ protected function decryptLegacyDataWithMCrypt($data) {
$td = mcrypt_module_open('rijndael-128', '', 'cbc', '');
$iv = substr($data, 0, mcrypt_enc_get_iv_size($td));
@@ -551,6 +614,37 @@ protected function decrypt($data) {
return $text;
}
+ /**
+ * Use OpenSSL to decrypt data that was originally encrypted with MCrypt.
+ *
+ * As used by an earlier version of this module.
+ *
+ * @param string $data
+ * The data to be decrypted.
+ *
+ * @return string|boolean
+ * The plaintext, or FALSE on failure.
+ */
+ protected function decryptLegacyDataWithOpenSSL($data) {
+ $key_size = 32; // Based on return value of mcrypt_enc_get_key_size($td).
+ $iv_size = 16; // Based on return value of mcrypt_enc_get_iv_size($td).
+ $key = substr($this->encryptionKey, 0, $key_size);
+ $iv = substr($data, 0, $iv_size);
+ $data = substr($data, $iv_size);
+ // Using 3 instead of the constant OPENSSL_NO_PADDING, for PHP 5.3.
+ $decrypted_text = openssl_decrypt($data, 'AES-256-CBC', $key, 3, $iv);
+ if ($decrypted_text === FALSE) {
+ return FALSE;
+ }
+
+ // Return only the message and none of its padding.
+ if (strpos($decrypted_text, '|') !== FALSE) {
+ list($length, $padded_data) = explode('|', $decrypted_text, 2);
+ $decrypted_text = substr($padded_data, 0, $length);
+ }
+
+ return $decrypted_text;
+ }
}
/**
@@ -565,8 +659,12 @@ interface TfaValidationPluginInterface {
* Get TFA process form from plugin.
*
* @param array $form
+ * The form array structure.
* @param array $form_state
- * @return array Form API array.
+ * The current form state array.
+ *
+ * @return array
+ * Form API array.
*/
public function getForm(array $form, array &$form_state);
@@ -574,8 +672,12 @@ public function getForm(array $form, array &$form_state);
* Validate form.
*
* @param array $form
+ * The form array structure.
* @param array $form_state
- * @return bool Whether form passes validation or not
+ * The current form state array.
+ *
+ * @return bool
+ * Whether form passes validation or not.
*/
public function validateForm(array $form, array &$form_state);
}
@@ -592,6 +694,7 @@ interface TfaLoginPluginInterface {
* Whether authentication should be interrupted.
*
* @return bool
+ * Indicates whether authentication should be interrupted.
*/
public function loginAllowed();
}
@@ -623,20 +726,28 @@ interface TfaSetupPluginInterface {
/**
* @param array $form
+ * The form array structure.
* @param array $form_state
+ * The current form state array.
*/
public function getSetupForm(array $form, array &$form_state);
/**
* @param array $form
+ * The form array structure.
* @param array $form_state
+ * The current form state array.
*/
public function validateSetupForm(array $form, array &$form_state);
/**
* @param array $form
+ * The form array structure.
* @param array $form_state
+ * The current form state array.
+ *
* @return bool
+ * Indicates whether the form submission succeeded.
*/
public function submitSetupForm(array $form, array &$form_state);
diff --git a/tfa.install b/tfa.install
index 9d8adf3..8471e27 100644
--- a/tfa.install
+++ b/tfa.install
@@ -27,13 +27,19 @@ function tfa_uninstall() {
function tfa_requirements($phase) {
$t = get_t();
if ($phase == 'runtime') {
- if (!extension_loaded('mcrypt')) {
- $requirement_severity = REQUIREMENT_ERROR;
- $description = $t('The TFA module requires the PHP Mcrypt extension be installed on the web server.');
+ if (!extension_loaded('openssl')) {
+ if (extension_loaded('mcrypt')) {
+ $requirement_severity = REQUIREMENT_WARNING;
+ $description = t('The TFA module recommends the PHP OpenSSL extension to be installed on the web server.');
+ }
+ else {
+ $requirement_severity = REQUIREMENT_ERROR;
+ $description = t('The TFA module requires either the PHP OpenSSL or MCrypt extensions to be installed on the web server.');
+ }
}
else {
$requirement_severity = REQUIREMENT_OK;
- $description= '';
+ $description = '';
}
$enabled = variable_get('tfa_enabled', 0);