From c3ba0c28b19e24e23d6741d03c182a92c6d13b79 Mon Sep 17 00:00:00 2001 From: Daniel Seifert Date: Sat, 27 Jul 2019 00:03:22 +0200 Subject: [PATCH] add backup code generation --- composer.json | 3 +- .../Controller/Admin/d3user_totp.php | 5 + src/Application/Model/d3totp.php | 52 +++++++++ .../views/admin/de/d3totp_lang.php | 3 + .../views/admin/en/d3totp_lang.php | 3 + .../views/admin/tpl/d3user_totp.tpl | 26 +++++ src/Setup/Installation.php | 100 ++++++++++++++++++ 7 files changed, 191 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 83058d8..a0fa277 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,8 @@ "php": ">=5.6", "oxid-esales/oxideshop-metapackage-ce": "~6.0.3 || ~6.1.0", "spomky-labs/otphp": "^8.3", - "bacon/bacon-qr-code": "^1.0" + "bacon/bacon-qr-code": "^1.0", + "ircmaxell/random-lib": "^1.2" }, "autoload": { "psr-4": { diff --git a/src/Application/Controller/Admin/d3user_totp.php b/src/Application/Controller/Admin/d3user_totp.php index 3130bc9..2e8a1c1 100644 --- a/src/Application/Controller/Admin/d3user_totp.php +++ b/src/Application/Controller/Admin/d3user_totp.php @@ -17,8 +17,10 @@ namespace D3\Totp\Application\Controller\Admin; use D3\Totp\Application\Model\d3totp; use D3\Totp\Modules\Application\Model\d3_totp_user; +use Doctrine\DBAL\DBALException; use OxidEsales\Eshop\Application\Controller\Admin\AdminDetailsController; use OxidEsales\Eshop\Application\Model\User; +use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException; use OxidEsales\Eshop\Core\Exception\StandardException; use OxidEsales\Eshop\Core\Registry; @@ -30,6 +32,8 @@ class d3user_totp extends AdminDetailsController /** * @return string + * @throws DBALException + * @throws DatabaseConnectionException */ public function render() { @@ -87,6 +91,7 @@ class d3user_totp extends AdminDetailsController $oTotp->saveSecret($seed); $oTotp->assign($aParams); $oTotp->verify($otp, $seed); + $this->addTplParam('aBackupCodes', $oTotp->generateBackupCodes()); $oTotp->setId(); } $oTotp->save(); diff --git a/src/Application/Model/d3totp.php b/src/Application/Model/d3totp.php index 702f9ec..fc764fb 100644 --- a/src/Application/Model/d3totp.php +++ b/src/Application/Model/d3totp.php @@ -26,6 +26,8 @@ use OxidEsales\Eshop\Core\DatabaseProvider; use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException; use OxidEsales\Eshop\Core\Model\BaseModel; use OxidEsales\Eshop\Core\Registry; +use RandomLib\Factory; +use RandomLib\Generator; class d3totp extends BaseModel { @@ -35,6 +37,7 @@ class d3totp extends BaseModel public $userId; public $totp; protected $timeWindow = 2; + protected $_backupCodes = array(); /** * d3totp constructor. @@ -116,6 +119,21 @@ class d3totp extends BaseModel return null; } + /** + * @return array + */ + public function generateBackupCodes() + { + $factory = new Factory(); + $generator = $factory->getLowStrengthGenerator(); + + for ($i = 0; $i < 10; $i++) { + $this->_backupCodes[] = $generator->generateString(6, Generator::CHAR_DIGITS); + } + + return $this->_backupCodes; + } + /** * @param $seed * @return TOTP @@ -181,6 +199,40 @@ class d3totp extends BaseModel ); } + /** + * @param $code + * @return false|string + * @throws DatabaseConnectionException + */ + public function d3EncodeBC($code) + { + $oDb = DatabaseProvider::getDb(); + $salt = $this->getUser()->getFieldData('oxpasssalt'); + $sSelect = "SELECT BINARY MD5( CONCAT( " . $oDb->quote($code) . ", UNHEX( ".$oDb->quote($salt)." ) ) )"; + + return $oDb->getOne($sSelect); + } + + public function save() + { + $this->assign( + array( + 'bc1' => $this->d3EncodeBC($this->_backupCodes[0]), + 'bc2' => $this->d3EncodeBC($this->_backupCodes[1]), + 'bc3' => $this->d3EncodeBC($this->_backupCodes[2]), + 'bc4' => $this->d3EncodeBC($this->_backupCodes[3]), + 'bc5' => $this->d3EncodeBC($this->_backupCodes[4]), + 'bc6' => $this->d3EncodeBC($this->_backupCodes[5]), + 'bc7' => $this->d3EncodeBC($this->_backupCodes[6]), + 'bc8' => $this->d3EncodeBC($this->_backupCodes[7]), + 'bc9' => $this->d3EncodeBC($this->_backupCodes[8]), + 'bc10' => $this->d3EncodeBC($this->_backupCodes[9]), + ) + ); + + return parent::save(); + } + /** * @param $totp * @param $seed diff --git a/src/Application/views/admin/de/d3totp_lang.php b/src/Application/views/admin/de/d3totp_lang.php index e67dde1..bd8c346 100644 --- a/src/Application/views/admin/de/d3totp_lang.php +++ b/src/Application/views/admin/de/d3totp_lang.php @@ -40,6 +40,9 @@ $aLang = [ 'D3_TOTP_REGISTERDELETE' => 'Registrierung löschen', 'D3_TOTP_REGISTERDELETE_DESC' => 'Um die Registrierung zu ändern, löschen Sie diese bitte vorerst. Sie können sofort im Anschluss eine neue Registrierung anlegen.
Wenn Sie die Registrierung löschen, ist das Konto nicht mehr durch die Zweifaktorauthentisierung geschützt.', + 'D3_TOTP_BACKUPCODES' => 'Backup-Codes', + 'D3_TOTP_BACKUPCODES_DESC' => 'Mit diesen Backup-Codes können Sie sich anmelden, wenn die Generierung des Einmalpasswortes nicht möglich ist (z.B. Gerät verloren oder neu installiert). Sie können dann die Einstellungen zur Verwendung der 2-Faktor-Autetizierung aändern oder einen neuen Zugang erstellen. Speichern Sie sich diese Codes bitte in diesem Moment sicher ab. Nach Verlassen dieser Seite können diese Codes nicht erneut angezeigt werden.', + 'D3_TOTP_SAVE' => 'Speichern', 'D3_TOTP_ERROR_UNVALID' => 'Das Einmalpasswort ist ungültig.', diff --git a/src/Application/views/admin/en/d3totp_lang.php b/src/Application/views/admin/en/d3totp_lang.php index 2b486fa..18c807e 100644 --- a/src/Application/views/admin/en/d3totp_lang.php +++ b/src/Application/views/admin/en/d3totp_lang.php @@ -40,6 +40,9 @@ $aLang = [ 'D3_TOTP_REGISTERDELETE' => 'Delete registration', 'D3_TOTP_REGISTERDELETE_DESC' => 'To change the registration, please delete it. You can then immediately create a new registration.
If you delete the registration, the account is no longer protected by the two-factor authentication.', + 'D3_TOTP_BACKUPCODES' => 'backup codes', + 'D3_TOTP_BACKUPCODES_DESC' => 'You can use these backup codes to log on if it is not possible to generate the one-time password (e.g. device lost or newly installed). You can then change the settings to use 2-factor authentication or create a new 2FA login. Please save these codes safely at this moment. After leaving this page, these codes cannot be displayed again.', + 'D3_TOTP_SAVE' => 'Save', 'D3_TOTP_ERROR_UNVALID' => 'The one-time password is invalid.', diff --git a/src/Application/views/admin/tpl/d3user_totp.tpl b/src/Application/views/admin/tpl/d3user_totp.tpl index 9aab0ec..759cd96 100644 --- a/src/Application/views/admin/tpl/d3user_totp.tpl +++ b/src/Application/views/admin/tpl/d3user_totp.tpl @@ -8,6 +8,12 @@ [{assign var="readonly" value=""}] [{/if}] + +
[{$oViewConf->getHiddenSid()}] @@ -117,6 +123,26 @@ + [{else}] + [{if $aBackupCodes}] + + + +

[{oxmultilang ident="D3_TOTP_BACKUPCODES"}]

+ + + + + +
+
+ + + + [{/if}] [{/if}] [{/block}] diff --git a/src/Setup/Installation.php b/src/Setup/Installation.php index 971d84c..37ec5d1 100644 --- a/src/Setup/Installation.php +++ b/src/Setup/Installation.php @@ -75,6 +75,106 @@ class Installation extends d3install_updatebase 'sComment' => '', 'sExtra' => '', 'blMultilang' => false, + ), + 'BC1' => array( + 'sTableName' => 'd3totp', + 'sFieldName' => 'BC1', + 'sType' => 'VARCHAR(64)', + 'blNull' => false, + 'sDefault' => false, + 'sComment' => 'BackupCode #1', + 'sExtra' => '', + 'blMultilang' => false, + ), + 'BC2' => array( + 'sTableName' => 'd3totp', + 'sFieldName' => 'BC2', + 'sType' => 'VARCHAR(64)', + 'blNull' => false, + 'sDefault' => false, + 'sComment' => 'BackupCode #2', + 'sExtra' => '', + 'blMultilang' => false, + ), + 'BC3' => array( + 'sTableName' => 'd3totp', + 'sFieldName' => 'BC3', + 'sType' => 'VARCHAR(64)', + 'blNull' => false, + 'sDefault' => false, + 'sComment' => 'BackupCode #3', + 'sExtra' => '', + 'blMultilang' => false, + ), + 'BC4' => array( + 'sTableName' => 'd3totp', + 'sFieldName' => 'BC4', + 'sType' => 'VARCHAR(64)', + 'blNull' => false, + 'sDefault' => false, + 'sComment' => 'BackupCode #4', + 'sExtra' => '', + 'blMultilang' => false, + ), + 'BC5' => array( + 'sTableName' => 'd3totp', + 'sFieldName' => 'BC5', + 'sType' => 'VARCHAR(64)', + 'blNull' => false, + 'sDefault' => false, + 'sComment' => 'BackupCode #5', + 'sExtra' => '', + 'blMultilang' => false, + ), + 'BC6' => array( + 'sTableName' => 'd3totp', + 'sFieldName' => 'BC6', + 'sType' => 'VARCHAR(64)', + 'blNull' => false, + 'sDefault' => false, + 'sComment' => 'BackupCode #6', + 'sExtra' => '', + 'blMultilang' => false, + ), + 'BC7' => array( + 'sTableName' => 'd3totp', + 'sFieldName' => 'BC7', + 'sType' => 'VARCHAR(64)', + 'blNull' => false, + 'sDefault' => false, + 'sComment' => 'BackupCode #7', + 'sExtra' => '', + 'blMultilang' => false, + ), + 'BC8' => array( + 'sTableName' => 'd3totp', + 'sFieldName' => 'BC8', + 'sType' => 'VARCHAR(64)', + 'blNull' => false, + 'sDefault' => false, + 'sComment' => 'BackupCode #8', + 'sExtra' => '', + 'blMultilang' => false, + ), + 'BC9' => array( + 'sTableName' => 'd3totp', + 'sFieldName' => 'BC9', + 'sType' => 'VARCHAR(64)', + 'blNull' => false, + 'sDefault' => false, + 'sComment' => 'BackupCode #9', + 'sExtra' => '', + 'blMultilang' => false, + ), + 'BC10' => array( + 'sTableName' => 'd3totp', + 'sFieldName' => 'BC10', + 'sType' => 'VARCHAR(64)', + 'blNull' => false, + 'sDefault' => false, + 'sComment' => 'BackupCode #10', + 'sExtra' => '', + 'blMultilang' => false, ) );