diff options
author | Christoph Wurst <christoph@winzerhof-wurst.at> | 2016-06-04 14:16:18 +0300 |
---|---|---|
committer | Christoph Wurst <christoph@winzerhof-wurst.at> | 2016-06-04 14:16:18 +0300 |
commit | ed7d8759dbc637e121c732bfeb3cfc2042d818ff (patch) | |
tree | b1c7fc438a4464f85b6cdb1f4e7026556dfbf5e9 | |
parent | b6943ad9d26b7e70b47ed644114a6a4e0b2fa30b (diff) |
first working version
-rw-r--r-- | appinfo/database.xml | 41 | ||||
-rw-r--r-- | appinfo/info.xml | 2 | ||||
-rw-r--r-- | lib/Db/TotpSecret.php | 40 | ||||
-rw-r--r-- | lib/Db/TotpSecretMapper.php | 57 | ||||
-rw-r--r-- | lib/Exception/NoTotpSecretFoundException.php | 28 | ||||
-rw-r--r-- | lib/Exception/TotpSecretAlreadySet.php | 28 | ||||
-rw-r--r-- | lib/Provider/TotpProvider.php (renamed from lib/Provider/SmsProvider.php) | 26 | ||||
-rw-r--r-- | lib/Service/ITotp.php | 46 | ||||
-rw-r--r-- | lib/Service/Totp.php | 88 | ||||
-rw-r--r-- | templates/challenge.php | 8 |
10 files changed, 350 insertions, 14 deletions
diff --git a/appinfo/database.xml b/appinfo/database.xml new file mode 100644 index 0000000..b4daaa4 --- /dev/null +++ b/appinfo/database.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="ISO-8859-1" ?> +<database> + <name>*dbname*</name> + <create>true</create> + <overwrite>false</overwrite> + <charset>utf8</charset> + <table> + <name>*dbprefix*twofactor_totp_secrets</name> + <declaration> + <field> + <name>id</name> + <type>integer</type> + <autoincrement>1</autoincrement> + <default>0</default> + <notnull>true</notnull> + <length>4</length> + </field> + <field> + <name>user_id</name> + <type>text</type> + <default></default> + <notnull>true</notnull> + <length>64</length> + </field> + <field> + <name>secret</name> + <type>clob</type> + <notnull>true</notnull> + </field> + + <index> + <name>totp_secrets_user_id</name> + <unique>true</unique> + <field> + <name>user_id</name> + <sorting>ascending</sorting> + </field> + </index> + </declaration> + </table> +</database> diff --git a/appinfo/info.xml b/appinfo/info.xml index 7f9cbd8..44d1105 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -5,7 +5,7 @@ <description>A Two-Factor-Auth Provider for TOTP (e.g. Google Authenticator) for ownCloud 9.1</description> <licence>AGPL</licence> <author>Christoph Wurst</author> - <version>0.0.1</version> + <version>0.0.2</version> <namespace>TwoFactorTotp</namespace> <category>other</category> <types> diff --git a/lib/Db/TotpSecret.php b/lib/Db/TotpSecret.php new file mode 100644 index 0000000..75b06bb --- /dev/null +++ b/lib/Db/TotpSecret.php @@ -0,0 +1,40 @@ +<?php + +/** + * @author Christoph Wurst <christoph@winzerhof-wurst.at> + * + * ownCloud - Two-factor TOTP + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\TwoFactorTotp\Db; + +use OCP\AppFramework\Db\Entity; + +/** + * @method string getUserId() + * @method void setUserId(string $userId) + * @method string getSecret() + * @method void setSecret(string $secret) + */ +class TotpSecret extends Entity { + + /** @var string */ + protected $userId; + + /** @var string */ + protected $secret; + +} diff --git a/lib/Db/TotpSecretMapper.php b/lib/Db/TotpSecretMapper.php new file mode 100644 index 0000000..5a3c847 --- /dev/null +++ b/lib/Db/TotpSecretMapper.php @@ -0,0 +1,57 @@ +<?php + +/** + * @author Christoph Wurst <christoph@winzerhof-wurst.at> + * + * ownCloud - Two-factor TOTP + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\TwoFactorTotp\Db; + +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\Mapper; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDb; +use OCP\IUser; + +class TotpSecretMapper extends Mapper { + + public function __construct(IDb $db) { + parent::__construct($db, 'twofactor_totp_secrets'); + } + + /** + * @param IUser $user + * @throws DoesNotExistException + * @return TotpSecret + */ + public function getSecret(IUser $user) { + /* @var $qb IQueryBuilder */ + $qb = $this->db->getQueryBuilder(); + + $qb->select('id', 'user_id', 'secret') + ->from('twofactor_totp_secrets') + ->where($qb->expr()->eq('user_id', $qb->createNamedParameter($user->getUID()))); + $result = $qb->execute(); + + $row = $result->fetch(); + if ($row === false) { + throw new DoesNotExistException(); + } + return TotpSecret::fromRow($row); + } + +} diff --git a/lib/Exception/NoTotpSecretFoundException.php b/lib/Exception/NoTotpSecretFoundException.php new file mode 100644 index 0000000..d8d09a0 --- /dev/null +++ b/lib/Exception/NoTotpSecretFoundException.php @@ -0,0 +1,28 @@ +<?php + +/** + * @author Christoph Wurst <christoph@winzerhof-wurst.at> + * + * ownCloud - Two-factor TOTP + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\TwoFactorTotp\Exception; + +use Exception; + +class NoTotpSecretFoundException extends Exception { + +} diff --git a/lib/Exception/TotpSecretAlreadySet.php b/lib/Exception/TotpSecretAlreadySet.php new file mode 100644 index 0000000..b79f33a --- /dev/null +++ b/lib/Exception/TotpSecretAlreadySet.php @@ -0,0 +1,28 @@ +<?php + +/** + * @author Christoph Wurst <christoph@winzerhof-wurst.at> + * + * ownCloud - Two-factor TOTP + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\TwoFactorTotp\Exception; + +use Exception; + +class TotpSecretAlreadySet extends Exception { + +} diff --git a/lib/Provider/SmsProvider.php b/lib/Provider/TotpProvider.php index 68d4760..4cb8eec 100644 --- a/lib/Provider/SmsProvider.php +++ b/lib/Provider/TotpProvider.php @@ -21,15 +21,21 @@ namespace OCA\TwoFactorTotp\Provider; -use Base32\Base32; +use OCA\TwoFactorTotp\Exception\NoTotpSecretFoundException; +use OCA\TwoFactorTotp\Service\Totp; use OCP\Authentication\TwoFactorAuth\IProvider; use OCP\IUser; use OCP\Template; -use Otp\GoogleAuthenticator; -use Otp\Otp; class TotpProvider implements IProvider { + /** @var Totp */ + private $totp; + + public function __construct(Totp $totp) { + $this->totp = $totp; + } + /** * Get unique identifier of this 2FA provider * @@ -76,13 +82,14 @@ class TotpProvider implements IProvider { * @return Template */ public function getTemplate(IUser $user) { - $otp = new Otp(); - //$secret = $this->random->generate(16); - $secret = GoogleAuthenticator::generateRandom(); - $totp = $otp->totp(Base32::decode($secret)); + try { + $this->totp->getSecret($user); + } catch (NoTotpSecretFoundException $ex) { + $qr = $this->totp->createSecret($user); + } $tmpl = new Template('twofactor_totp', 'challenge'); - $tmpl->assign('secret', $totp); + $tmpl->assign('qr', $qr); return $tmpl; } @@ -95,8 +102,7 @@ class TotpProvider implements IProvider { * @param string $challenge */ public function verifyChallenge(IUser $user, $challenge) { - $otp = new Otp(); - return $otp->checkTotp(Base32::decode($secret), $challenge); + return $this->totp->validateSecret($user, $challenge); } /** diff --git a/lib/Service/ITotp.php b/lib/Service/ITotp.php new file mode 100644 index 0000000..601a2d4 --- /dev/null +++ b/lib/Service/ITotp.php @@ -0,0 +1,46 @@ +<?php + +/** + * @author Christoph Wurst <christoph@winzerhof-wurst.at> + * + * ownCloud - Two-factor TOTP + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\TwoFactorTotp\Service; + +use OCA\TwoFactorTotp\Exception\TotpSecretAlreadySet; +use OCP\IUser; + +interface ITotp { + + /** + * @param IUser $user + */ + public function getSecret(IUser $user); + + /** + * @param IUser $user + * @throws TotpSecretAlreadySet + */ + public function createSecret(IUser $user); + + /** + * @param IUser $user + * @param string $key + */ + public function validateSecret(IUser $user, $key); + +} diff --git a/lib/Service/Totp.php b/lib/Service/Totp.php new file mode 100644 index 0000000..4d94365 --- /dev/null +++ b/lib/Service/Totp.php @@ -0,0 +1,88 @@ +<?php + +/** + * @author Christoph Wurst <christoph@winzerhof-wurst.at> + * + * ownCloud - Two-factor TOTP + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\TwoFactorTotp\Service; + +use Base32\Base32; +use OCA\TwoFactorTotp\Db\TotpSecret; +use OCA\TwoFactorTotp\Db\TotpSecretMapper; +use OCA\TwoFactorTotp\Exception\NoTotpSecretFoundException; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\IUser; +use OCP\Security\ICrypto; +use Otp\GoogleAuthenticator; +use Otp\Otp; + +class Totp implements ITotp { + + /** @var TotpSecretMapper */ + private $secretMapper; + + /** @var ICrypto */ + private $crypto; + + public function __construct(TotpSecretMapper $secretMapper, ICrypto $crypto) { + $this->secretMapper = $secretMapper; + $this->crypto = $crypto; + } + + public function getSecret(IUser $user) { + try { + $secret = $this->secretMapper->getSecret($user); + } catch (DoesNotExistException $ex) { + throw new NoTotpSecretFoundException(); + } + + $encryptedSecret = $secret->getSecret(); + return $this->crypto->decrypt($encryptedSecret); + } + + /** + * @todo prevent duplicates + * + * @param IUser $user + */ + public function createSecret(IUser $user) { + $secret = GoogleAuthenticator::generateRandom(); + + $dbSecret = new TotpSecret(); + $dbSecret->setUserId($user->getUID()); + $dbSecret->setSecret($this->crypto->encrypt($secret)); + + $this->secretMapper->insert($dbSecret); + + return GoogleAuthenticator::getQrCodeUrl('totp', 'ownCloud TOTP', $secret); + } + + public function validateSecret(IUser $user, $key) { + try { + $dbSecret = $this->secretMapper->getSecret($user); + } catch (DoesNotExistException $ex) { + throw new NoTotpSecretFoundException(); + } + + $secret = $this->crypto->decrypt($dbSecret->getSecret()); + + $otp = new Otp(); + return $otp->checkTotp(Base32::decode($secret), $key, 3); + } + +} diff --git a/templates/challenge.php b/templates/challenge.php index 59fdf73..ca462e9 100644 --- a/templates/challenge.php +++ b/templates/challenge.php @@ -1,6 +1,8 @@ -<p>TOTP: <?php p($_['secret']); ?></p> +<?php if (isset($_['qr'])): ?> +<a href="<?php echo $_['qr']; ?>" target="_blank">Scan QR Code</a> +<?php endif; ?> <form method="POST"> - <input type="text" name="challenge" required="required"> - <input type="submit" class="button" value="Verify"> + <input type="text" name="challenge" required="required"> + <input type="submit" class="button" value="Verify"> </form> |