Skip to content

Commit

Permalink
Groundwork to support different 2FAKey types. #35
Browse files Browse the repository at this point in the history
  • Loading branch information
ShaneMcC committed Nov 11, 2018
1 parent 9af5db8 commit bad6c98
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 6 deletions.
10 changes: 10 additions & 0 deletions admin/init_functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,16 @@ public function run($pdo) {
$dataChanges[20] = new DBChange(<<<MYSQLQUERY
ALTER TABLE `domains` ADD COLUMN `nsec3params` varchar(50) DEFAULT NULL AFTER `defaultttl`;
MYSQLQUERY
);

// ------------------------------------------------------------------------
// Additional 2FA Key Types
// ------------------------------------------------------------------------
$dataChanges[21] = new DBChange(<<<MYSQLQUERY
ALTER TABLE `twofactorkeys`
ADD COLUMN `type` VARCHAR(32) NOT NULL DEFAULT 'rfc6238' AFTER `active`,
ADD COLUMN `onetime` ENUM('false', 'true') NOT NULL DEFAULT 'false' AFTER `type`;
MYSQLQUERY
);

return $dataChanges;
Expand Down
59 changes: 56 additions & 3 deletions classes/twofactorkey.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ class TwoFactorKey extends DBObject {
'created' => 0,
'lastused' => 0,
'active' => false,
'type' => 'rfc6238',
'onetime' => false,
];
protected static $_key = 'id';
protected static $_table = 'twofactorkeys';
Expand All @@ -21,8 +23,20 @@ public function __construct($db) {

public function setKey($value) {
if ($value === TRUE) {
$ga = new PHPGangsta_GoogleAuthenticator();
$value = $ga->createSecret();
$type = $this->getType();
switch ($type) {
case "rfc6238":
$ga = new PHPGangsta_GoogleAuthenticator();
$value = $ga->createSecret();
break;

case "plain":
$value = implode("-", str_split(strtoupper(substr(sha1(openssl_random_pseudo_bytes('512')), 0, 16)), 4));
break;

default:
throw new Exception('Unknown key type: ' . $type);
}
}
return $this->setData('key', $value);
}
Expand All @@ -47,6 +61,14 @@ public function setActive($value) {
return $this->setData('active', parseBool($value) ? 'true' : 'false');
}

public function setType($value) {
return $this->setData('type', strtolower($value));
}

public function setOneTime($value) {
return $this->setData('onetime', parseBool($value) ? 'true' : 'false');
}

public function getID() {
return $this->getData('id');
}
Expand Down Expand Up @@ -75,6 +97,19 @@ public function isActive() {
return parseBool($this->getData('active'));
}

public function getType() {
return $this->getData('type');
}

public function isOneTime() {
return parseBool($this->getData('onetime'));
}

public function isUsableKey() {
// Key is active and either multi-use or unused.
return $this->isActive() && (!$this->isOneTime() || $this->getLastUsed() == 0);
}

/**
* Load an object from the database based on user_id AND the key id.
*
Expand All @@ -96,14 +131,32 @@ public function validate() {
$required = ['key', 'user_id', 'description'];
foreach ($required as $r) {
if (!$this->hasData($r)) {
throw new ValidationFailed('Missing required field: '. $r);
throw new ValidationFailed('Missing required field: ' . $r);
}
}

if (!in_array($this->getType(), ["rfc6238", "plain"])) {
throw new ValidationFailed('Unknown key type: ' . $this->getType());
}

return TRUE;
}

public function verify($code, $discrepancy = 1) {
$type = $this->getType();
switch ($type) {
case "rfc6238":
return verify_rfc6238($code, $discrepancy);

case "plain":
return strtoupper($code) == strtoupper($this->getKey());

default:
throw new Exception('Unknown key type: ' . $type);
}
}

private function verify_rfc6238($code, $discrepancy = 1) {
$ga = new PHPGangsta_GoogleAuthenticator();

$minTimeSlice = floor($this->getLastUsed() / 30);
Expand Down
4 changes: 1 addition & 3 deletions web/1.0/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -233,11 +233,9 @@ function getKnownDevice($user, &$context) {
$valid = false;
$testCode = isset($_SERVER['HTTP_X_2FA_KEY']) ? $_SERVER['HTTP_X_2FA_KEY'] : NULL;

$ga = new PHPGangsta_GoogleAuthenticator();

if ($testCode !== NULL) {
foreach ($keys as $key) {
if ($key->verify($testCode, 1)) {
if ($key->isUsableKey() && $key->verify($testCode, 1)) {
$valid = true;
$key->setLastUsed(time())->save();

Expand Down

0 comments on commit bad6c98

Please sign in to comment.