abstract class TfaBasePlugin in Two-factor Authentication (TFA) 7.2
Base plugin class.
Hierarchy
- class \TfaBasePlugin
Expanded class hierarchy of TfaBasePlugin
1 string reference to 'TfaBasePlugin'
- tfa_admin_settings in ./
tfa.admin.inc - Admin settings form.
File
- ./
tfa.inc, line 426 - TFA module classes.
View source
abstract class TfaBasePlugin {
const CRYPT_VERSION = '1';
/**
* TFA code.
*
* @var string
*/
protected $code;
/**
* Code Length.
*
* @var int
*/
protected $codeLength;
/**
* Context of current TFA process.
*
* @var array
*/
protected $context;
/**
* Error messages.
*
* @var array
*/
protected $errorMessages = array();
/**
* Code is valid.
*
* @var bool
*/
protected $isValid;
/**
* Encryption key.
*
* @var string
*/
protected $encryptionKey;
/**
* Plugin constructor.
*
* @param array $context
* Context of current TFA process.
* Must include key:
* - 'uid'
* Account uid of user in TFA process.
* May include keys:
* - 'validate_context'
* Plugin-specific context for use during Tfa validation.
* - 'setup_context'
* Plugin-specific context for use during TfaSetup.
*/
public function __construct(array $context = array()) {
$this->context = $context;
// Default code length is 6.
$this->codeLength = 6;
$this->isValid = FALSE;
}
/**
* Determine if the plugin can run for the current TFA context.
*
* @return bool
* Whether the plugin can run.
*/
public function ready() {
return TRUE;
}
/**
* Get error messages suitable for form_set_error().
*
* @return array
* Error messages.
*/
public function getErrorMessages() {
return $this->errorMessages;
}
/**
* Submit form.
*
* @param array $form
* The form array structure.
* @param array $form_state
* The current form state array.
*
* @return bool
* Whether plugin form handling is complete. Plugins should return FALSE to
* invoke multi-step.
*/
public function submitForm(array $form, array &$form_state) {
return $this->isValid;
}
/**
* Validate code.
*
* Note, plugins overriding validate() should be sure to set isValid property
* correctly or else also override submitForm().
*
* @param string $code
* Code to be validated.
*
* @return bool
* Whether code is valid.
*/
protected function validate($code) {
if ($this
->timingSafeEquals((string) $code, (string) $this->code)) {
$this->isValid = TRUE;
return TRUE;
}
else {
return FALSE;
}
}
/**
* A timing safe equals comparison.
*
* More info here: http://blog.ircmaxell.com/2014/11/its-all-about-time.html.
*
* @param string $safeString
* The internal (safe) value to be checked.
* @param string $userString
* The user submitted (unsafe) value.
*
* @return bool
* True if the two strings are identical.
*/
private function timingSafeEquals($safeString, $userString) {
if (function_exists('hash_equals')) {
return hash_equals($safeString, $userString);
}
$safeLen = strlen($safeString);
$userLen = strlen($userString);
if ($userLen != $safeLen) {
return FALSE;
}
$result = 0;
for ($i = 0; $i < $userLen; ++$i) {
$result |= ord($safeString[$i]) ^ ord($userString[$i]);
}
// They are only identical strings if $result is exactly 0.
return $result === 0;
}
/**
* Generate a random string of characters of length $this->codeLength.
*
* @return string
* Generated random string of characters.
*/
protected function generate() {
$string = '';
do {
$chars = strtolower(base64_encode(drupal_random_bytes($this->codeLength)));
// Remove some characters that are more difficult to distinguish or type.
$string .= strtr($chars, array(
'+' => '',
'/' => '',
'=' => '',
'-' => '',
'_' => '',
'0' => '',
'o' => '',
));
} while (strlen($string) <= $this->codeLength);
return substr($string, 0, $this->codeLength);
}
/**
* Encrypt a plaintext string.
*
* Should be used when writing codes to storage.
*
* @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));
$key = substr($this->encryptionKey, 0, mcrypt_enc_get_key_size($td));
mcrypt_generic_init($td, $key, $iv);
// Encrypt with message length so decryption can return message without
// padding.
$text = strlen($text) . '|' . $text;
$data = mcrypt_generic($td, $text);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return $iv . $data;
}
/**
* Decrypt a encrypted string.
*
* Should be used when reading codes from storage.
*
* @param string $data
* The encrypted text.
*
* @return string
* The plaintext, or empty string on failure.
*/
protected function decrypt($data) {
$crypto_data = drupal_json_decode($data);
if (empty($crypto_data['version']) || empty($crypto_data['iv_base64']) || empty($crypto_data['ciphertext_base64'])) {
// Backwards compatibility with the old Mcrypt scheme.
if (extension_loaded('mcrypt')) {
return $this
->decryptLegacyDataWithMcrypt($data);
}
if (extension_loaded('openssl')) {
return $this
->decryptLegacyDataWithOpenSSL($data);
}
return '';
}
$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);
}
/**
* Decrypt using the deprecated Mcrypt extension.
*
* @param string $data
* The data to be decrypted.
*
* @return string
* The plaintext, or empty string on failure.
*/
protected function decryptLegacyDataWithMcrypt($data) {
$td = mcrypt_module_open('rijndael-128', '', 'cbc', '');
$iv = substr($data, 0, mcrypt_enc_get_iv_size($td));
$data = substr($data, mcrypt_enc_get_iv_size($td));
$key = substr($this->encryptionKey, 0, mcrypt_enc_get_key_size($td));
mcrypt_generic_init($td, $key, $iv);
$decrypted_text = mdecrypt_generic($td, $data);
// Return only the message and none of its padding.
list($length, $padded_data) = explode('|', $decrypted_text, 2);
$text = substr($padded_data, 0, $length);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
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
* The plaintext, or empty string on failure.
*
* phpcs:disable Drupal.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
*/
protected function decryptLegacyDataWithOpenSSL($data) {
// Based on return value of mcrypt_enc_get_key_size($td).
$key_size = 32;
// Based on return value of mcrypt_enc_get_iv_size($td).
$iv_size = 16;
$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);
// 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;
}
else {
return '';
}
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
TfaBasePlugin:: |
protected | property | TFA code. | |
TfaBasePlugin:: |
protected | property | Code Length. | |
TfaBasePlugin:: |
protected | property | Context of current TFA process. | |
TfaBasePlugin:: |
protected | property | Encryption key. | |
TfaBasePlugin:: |
protected | property | Error messages. | |
TfaBasePlugin:: |
protected | property | Code is valid. | |
TfaBasePlugin:: |
constant | |||
TfaBasePlugin:: |
protected | function | Decrypt a encrypted string. | |
TfaBasePlugin:: |
protected | function | Decrypt using the deprecated Mcrypt extension. | |
TfaBasePlugin:: |
protected | function | Use OpenSSL to decrypt data that was originally encrypted with Mcrypt. | |
TfaBasePlugin:: |
protected | function | Encrypt a plaintext string. | |
TfaBasePlugin:: |
protected | function | Encrypt using the deprecated Mcrypt extension. | |
TfaBasePlugin:: |
protected | function | Generate a random string of characters of length $this->codeLength. | |
TfaBasePlugin:: |
public | function | Get error messages suitable for form_set_error(). | |
TfaBasePlugin:: |
public | function | Determine if the plugin can run for the current TFA context. | 2 |
TfaBasePlugin:: |
public | function | Submit form. | 1 |
TfaBasePlugin:: |
private | function | A timing safe equals comparison. | |
TfaBasePlugin:: |
protected | function | Validate code. | |
TfaBasePlugin:: |
public | function | Plugin constructor. | 3 |