public function twoFactor(array $envData) { $userId = self::getUserId($envData['common_name']); // use username field to specify OTP type, for now we only support 'totp' $otpType = $envData['username']; if ('totp' !== $otpType) { throw new TwoFactorException('invalid OTP type specified in username field'); } $otpKey = $envData['password']; // validate the OTP key if (0 === preg_match('/^[0-9]{6}$/', $otpKey)) { throw new TwoFactorException('invalid OTP key format specified'); } $dataDir = sprintf('%s/data/%s', $this->baseDir, $envData['INSTANCE_ID']); if (false === ($otpSecret = @file_get_contents(sprintf('%s/users/otp_secrets/%s', $dataDir, $userId)))) { throw new TwoFactorException('no OTP secret registered'); } $otp = new Otp(); if ($otp->checkTotp(Base32::decode($otpSecret), $otpKey)) { if (false === $this->otpLog->record($userId, $otpKey, time())) { throw new TwoFactorException('OTP replayed'); } } else { throw new TwoFactorException('invalid OTP key'); } }
public function calculateCode($secret, $timeSlice = null) { // If we haven't been fed a timeSlice, then get one. // It looks a bit unclean doing it like this, but it allows us to write testable code $timeSlice = $timeSlice ? $timeSlice : $this->getTimeSlice(); // Packs the timeslice as a "unsigned long" (always 32 bit, big endian byte order) $timeSlice = pack("N", $timeSlice); // Then pad it with the null terminator $timeSlice = str_pad($timeSlice, 8, chr(0), STR_PAD_LEFT); // Hash it with SHA1. The spec does offer the idea of other algorithms, but notes that the authenticator is currently // ignoring it... $hash = hash_hmac("SHA1", $timeSlice, Base32::decode($secret), true); // Last 4 bits are an offset apparently $offset = ord(substr($hash, -1)) & 0xf; // Grab the last 4 bytes $result = substr($hash, $offset, 4); // Unpack it again $value = unpack('N', $result)[1]; // Only 32 bits $value = $value & 0x7fffffff; // Modulo down to the right number of digits $modulo = pow(10, $this->codeLength); // Finally, pad out the string with 0s return str_pad($value % $modulo, $this->codeLength, '0', STR_PAD_LEFT); }
/** * @param Seed|string|null * @param ITimestampProvider|int|null * @param int * @throws \Exception */ public function __construct($seed = NULL, $timestamp = NULL, $period = 30) { if ($seed === NULL) { $this->seed = Seed::generate(); } elseif (is_string($seed) && ($decoded = Base32::decode($seed)) !== '') { $this->seed = new Seed($decoded); } elseif ($seed instanceof Seed) { $this->seed = $seed; } else { throw new \Exception('Seed must be valid base32 encoded string or instance of Seed.'); } if ($timestamp === NULL) { $this->timestampProvider = SystemTimestampProvider::getInstance(); } elseif (is_int($timestamp) || ctype_digit($timestamp)) { $this->timestampProvider = new ConstantTimestampProvider((int) $timestamp); } elseif ($timestamp instanceof ITimestampProvider) { $this->timestampProvider = $timestamp; } else { throw new \Exception('Timestamp must be integer or instance of ITimestampProvider.'); } if ((is_int($period) || ctype_digit($period)) && $period > 0) { $this->period = (int) $period; } else { throw new \Exception('Period must be integer greater then 0.'); } }
/** * @expectedException \SURFnet\VPN\Server\Exception\TwoFactorException * @expectedExceptionMessage OTP replayed */ public function testTwoFactorReplay() { $o = new Otp(); $otpKey = $o->totp(Base32::decode('QPXDFE7G7VNRR4BH')); $c = new TwoFactor(__DIR__, $this->otpLog); $c->twoFactor(['INSTANCE_ID' => 'vpn.example', 'POOL_ID' => 'internet', 'common_name' => 'foo_xyz', 'username' => 'totp', 'password' => $otpKey]); // replay $c->twoFactor(['INSTANCE_ID' => 'vpn.example', 'POOL_ID' => 'internet', 'common_name' => 'foo_xyz', 'username' => 'totp', 'password' => $otpKey]); }
/** * Tests Base32->decode() * * Testing test vectors according to RFC 4648 * http://www.ietf.org/rfc/rfc4648.txt */ public function testDecode() { // RFC test vectors say that empty string returns empty string $this->assertEquals('', Base32::decode('')); // these strings are taken from the RFC $this->assertEquals('f', Base32::decode('MY======')); $this->assertEquals('fo', Base32::decode('MZXQ====')); $this->assertEquals('foo', Base32::decode('MZXW6===')); $this->assertEquals('foob', Base32::decode('MZXW6YQ=')); $this->assertEquals('fooba', Base32::decode('MZXW6YTB')); $this->assertEquals('foobar', Base32::decode('MZXW6YTBOI======')); }
/** * Ajout de $secret en DB et activation l'authentification à 2 facteurs */ public function totp_post(Request $request) { $otp = new Otp(); $secret = session()->get('secret'); $key = $request->get("code"); $user = $this->auth->user(); if ($otp->checkTotp(Base32::decode($secret), $key)) { DB::table('users')->where('id', $user->id)->update(array('totp_key' => $secret)); Session::forget('code'); return redirect(url('profil'))->with('success', 'L\'authentification à 2 facteurs à bien été activer'); } else { return redirect(url('profil/totp'))->with('error', 'Ce code ne correspond pas, veuillez recommencer l\'opération'); } }
/** * Tests Base32->decode() * * Testing test vectors according to RFC 4648 * http://www.ietf.org/rfc/rfc4648.txt */ public function testDecode() { // RFC test vectors say that empty string returns empty string $this->assertEquals('', Base32::decode('')); // these strings are taken from the RFC $this->assertEquals('f', Base32::decode('MY======')); $this->assertEquals('fo', Base32::decode('MZXQ====')); $this->assertEquals('foo', Base32::decode('MZXW6===')); $this->assertEquals('foob', Base32::decode('MZXW6YQ=')); $this->assertEquals('fooba', Base32::decode('MZXW6YTB')); $this->assertEquals('foobar', Base32::decode('MZXW6YTBOI======')); // Decoding a string made up entirely of invalid characters $this->assertEquals('', Base32::decode('8908908908908908')); }
/** * Get the code from the time and secret * * @param string $secret * @param int|null $time * * @return string */ public function getCode($secret, $time = null) { if (!$time) { $time = floor(time() / 30); } $secret = Base32::decode($secret); $time = pack('N', $time); $time = str_pad($time, 8, chr(0), STR_PAD_LEFT); $hash = hash_hmac('sha1', $time, $secret, true); $offset = ord(substr($hash, -1)); $offset = $offset & 0xf; $tHash = $this->hashToInt($hash, $offset) & 0x7fffffff; $pin = str_pad($tHash % $this->pinModulo, 6, "0", STR_PAD_LEFT); return $pin; }
public function store() { $response = array(); $otp = new Otp(); $secret = Input::get('s'); $key = Input::get('txCodigo'); if ($otp->checkTotp(Base32::decode($secret), $key, 0)) { DB::table('authusuarios')->where('usuarioid', Auth::user()->usuarioid)->update(['twostepsecret' => $secret]); $response['result'] = true; return json_encode($response); } else { $response['result'] = false; return json_encode($response); } }
public static function getCode($secret, $timeSlice = null) { if ($timeSlice === null) { $timeSlice = floor(time() / self::TIME_STEP); } $secretKey = \Base32\Base32::decode($secret); $binaryTimestamp = pack('N*', 0) . pack('N*', $timeSlice); $hmac = hash_hmac('SHA1', $binaryTimestamp, $secretKey, true); $offset = ord(substr($hmac, -1)) & 0xf; $part = substr($hmac, $offset, 4); $unpack_value_array = unpack('N', $part); $unpack_value = $unpack_value_array[1]; $seed = $unpack_value & 0x7fffffff; $pow = pow(10, self::CODE_LENGTH); return str_pad($seed % $pow, self::CODE_LENGTH, '0', STR_PAD_LEFT); }
public function authenticate($username, $password, $htop_value) { if (isset($this->credentials[$username])) { list($user_password, $user_htop_secret) = $this->credentials[$username]; if ($user_password === $password) { $otp = new Otp(); if ($otp->checkTotp(Base32::decode($user_htop_secret), $htop_value)) { $this->session->username = $username; $this->session->valid_host = $_SERVER['HTTP_HOST']; $this->session->authenticated = true; return true; } } } return false; }
/** * Onion constructor. * @param string $onionHost */ public function __construct($onionHost) { $array = explode(".", $onionHost); if (count($array) !== 2) { throw new \InvalidArgumentException('Malformed onion address'); } list($ident, $onion) = $array; if ($onion !== 'onion') { throw new \InvalidArgumentException('Malformed onion address'); } $decoded = Base32::decode($ident); if (strlen($decoded) !== 10) { throw new \InvalidArgumentException('Malformed onion address'); } $this->identifier = $decoded; $this->host = $onionHost; }
/** * Validates a TOTP request. * * @todo Prevent TOTP replay attack. * @param string $key The TOTP token sent in. * @param string $secret The TOTP secret. * @return bool */ public function validateTOTP($key, $secret) { $otp = new Otp(); return $otp->checkTotp(Base32::decode($secret), $key); }
$user_u = $DB->execute("UPDATE users SET password = :password WHERE iduser = :iduser", array("iduser" => $iduser, "password" => $en_pass)); if ($user_u == 1) { $text = "Le mot de passe de l'utilisateur <strong>" . $username . "</strong> à été changer avec succès !"; $addNotif = $DB->execute("INSERT INTO notif(idnotif, iduser, type, notification, date_notification, vu) VALUES (NULL , :iduser, :type, :notification, :date_notification, :vu)", array("iduser" => $iduser, "type" => 2, "notification" => $user->prenom_user . " à modifier le mot de passe de sont Espace.", "date_notification" => $date_format->format_strt(date("d-m-Y H:i:s")), "vu" => 0)); $fonction->redirect("profil", "", "", "success", "edit-password", $text); } else { $fonction->redirect("error", "", "", "code", "USR4", ""); } } if (isset($_POST['action']) && $_POST['action'] == 'active_totp') { session_start(); require "../application/classe.php"; $iduser = $user->iduser; $username = $user->username; $otp = new Otp(); if ($otp->checkTotp(Base32::decode($_SESSION['user']['totp_secret']), $_POST['code'])) { $user_u = $DB->execute("UPDATE users SET totp = 1, totp_token = :totp_token WHERE iduser = :iduser", array("totp_token" => $_SESSION['user']['totp_secret'], "iduser" => $iduser)); $_SESSION['user']['totp_secret'] = ""; if ($user_u == 1) { $text = "L'authentificateur 2 Facteur à été activé pour l'utilisateur <strong>" . $username . "</strong>."; $addNotif = $DB->execute("INSERT INTO notif(idnotif, iduser, type, notification, date_notification, vu) VALUES (NULL , :iduser, :type, :notification, :date_notification, :vu)", array("iduser" => $iduser, "type" => 1, "notification" => $user->prenom_user . " à activé l'authentification à 2 facteur.", "date_notification" => $date_format->format_strt(date("d-m-Y H:i:s")), "vu" => 0)); $fonction->redirect("profil", "", "", "success", "active_totp", $text); } else { $fonction->redirect("error", "", "", "code", "USR5", ""); } } else { $fonction->redirect("profil", "", "", "error", "active_totp", "Ce code ne correspond pas !!!"); } } if (isset($_GET['action']) && $_GET['action'] == 'desactive_totp') { session_start();
public function totp(Request $request) { $user_id = session()->get('user_id'); $key = $request->get("code"); if (empty($user_id)) { return redirect('/auth/login'); } $otp = new Otp(); $user = User::where('id', $user_id)->first(); if ($key) { if ($otp->checkTotp(Base32::decode($user->totp_key), $key)) { $this->auth->login($user, $request->has('remember')); return redirect()->intended($this->redirectPath()); } else { return redirect(url('totp'))->with('error', 'Ce code ne correspond pas, veuillez recommencer l\'opération'); } } return view('auth.totp')->with('error', 'Ce compte à activer \'authentification à 2 facteurs'); }
/** * Check 2FA * * @access public */ public function check() { $user = $this->getUser(); $this->checkCurrentUser($user); $otp = new Otp(); $values = $this->request->getValues(); if (!empty($values['code']) && $otp->checkTotp(Base32::decode($user['twofactor_secret']), $values['code'])) { $this->sessionStorage->postAuth['validated'] = true; $this->flash->success(t('The two factor authentication code is valid.')); $this->response->redirect($this->helper->url->to('app', 'index')); } else { $this->flash->failure(t('The two factor authentication code is not valid.')); $this->response->redirect($this->helper->url->to('twofactor', 'code')); } }
public function setSecret($value) { if (strlen($value) !== $this->secretLength) { throw new InvalidConfigException('Otp::setSecret length is not equal to ' . $this->secretLength . ' ([\'length\'] component settenings)'); } elseif (strlen(Base32::decode($value)) < 1) { throw new InvalidConfigException('Otp::setSecret incorect, encode as Base32'); } $this->otp->setSecret($value); $this->_secret = $value; }
/** * @param int $timeIndex * @return string */ public function getCode($timeIndex = null) { if (is_null($timeIndex)) { $timeIndex = $this->getTimeIndex(); } $secretkey = Base32::decode($this->secretKey); $hm = hash_hmac('SHA1', chr(0) . chr(0) . chr(0) . chr(0) . pack('N*', $timeIndex), $secretkey, true); $value = unpack('N', substr($hm, ord(substr($hm, -1)) & 0xf, 4)); $value = $value[1] & 0x7fffffff; return str_pad($value % pow(10, static::CODE_LENGTH), static::CODE_LENGTH, '0', STR_PAD_LEFT); }
</form> <br /> Output:<br /> <br /> <?php if (isset($_POST['otpkey'])) { // Sanatizing, this should take care of it $key = preg_replace('/[^0-9]/', '', $_POST['otpkey']); // Standard is 6 for keys, but can be changed with setDigits on $otp if (strlen($key) == 6) { // Remember that the secret is a base32 string that needs decoding // to use it here! if ($otp->checkTotp(Base32::decode($secret), $key)) { echo 'Key correct!'; // Add here something that makes note of this key and will not allow // the use of it, for this user for the next 2 minutes. This way you // prevent a replay attack. Otherwise your OTP is missing one of the // key features it can bring in security to your application! } else { echo 'Wrong key!'; } } else { echo 'Key not the correct size'; } } ?> </body>
/** * @throws \InvalidArgumentException * * @return string */ private function getDecodedSecret() { $secret = Base32::decode($this->getSecret()); return $secret; }
/** * @return string */ private function byteSecret() { return Base32::decode($this->getSecret()); }
public static function generate($length = 16) { return new static(Base32::decode(Strings::random($length, 'A-Z2-7'))); }
/** * Authenticate the user * * @access public * @return boolean */ public function authenticate() { $otp = new Otp(); return $otp->checkTotp(Base32::decode($this->secret), $this->code); }
public function __toString() { $id = \Base32\Base32::decode(strtoupper(str_replace('-', '', $this->_id))); return pack('N', strlen($id)) . $id . pack('N', count($this->_addresses)) . implode('', $this->_addresses); }
public function fromBase32String() { return Base32::decode($this->digest); }
/** * Returns the binary value of the base32 encoded secret skey * * @return binary secret key */ public function decode_key() { return \Base32\Base32::decode($this->skey); }