/** * @param int $length * @return string */ public static function generateSecret($length = 20) { $security = new Security(); $full = Base32::encode($security->generateRandomString($length)); return substr($full, 0, $length); }
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); }
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'); } }
/** * @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.'); } }
/** * @param string|null $secret */ private function setSecret($secret) { Assertion::nullOrString($secret, 'The secret must be a string or null.'); if (null === $secret) { $secret = trim(Base32::encode(random_bytes(32)), '='); } $this->parameters['secret'] = $secret; }
/** * @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]); }
public static function generate($ext = '') { // Generate UUIDv4 $bytes = random_bytes(16); $bytes[6] = chr(0x4 << 4 | ord($bytes[6]) & 0xf); $bytes[8] = chr(ord($bytes[8]) & 0x3f | 0x80); $base32 = rtrim(Base32::encode($bytes), '='); $base32 = strtolower($base32); $filename = $base32 . ($ext != '' ? '.' . $ext : ''); return $filename; }
public function tfaEnableAction() { if (!$this->session2FA->secretCode) { $this->session2FA->secretCode = Base32::encode(random_bytes(256)); } $totp = new \OTPHP\TOTP('Zource', $this->session2FA->secretCode); if ($this->getRequest()->isPost()) { $code = $this->getRequest()->getPost('code'); var_dump($totp->verify($code)); } return new ViewModel(['secretCode' => $this->session2FA->secretCode]); }
/** * Encoder tests, reverse of the decodes */ public function testEncode() { // RFC test vectors say that empty string returns empty string $this->assertEquals('', Base32::encode('')); // these strings are taken from the RFC $this->assertEquals('MY======', Base32::encode('f')); $this->assertEquals('MZXQ====', Base32::encode('fo')); $this->assertEquals('MZXW6===', Base32::encode('foo')); $this->assertEquals('MZXW6YQ=', Base32::encode('foob')); $this->assertEquals('MZXW6YTB', Base32::encode('fooba')); $this->assertEquals('MZXW6YTBOI======', Base32::encode('foobar')); }
/** * 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'); } }
/** * 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; }
/** * Create an OTP URI for use with Google Authenticator. * * Note that this is not a URI for the QR code used by Google Authenticator. * The URI produced by this method is used as the actual content of the QR * code, and follows a special set of conventions understood by Google * Authenticator, and other OTP apps. * * @param string $type The otp type identifier. * @param string $parameters Additional URI parameters. * @param HotpBasedConfigurationInterface $configuration The OTP configuration. * @param OtpSharedParametersInterface $shared The shared parameters. * @param string $label The label for the account. * @param string|null $issuer The issuer name. * @param boolean|null $issuerInLabel True if legacy issuer support should be enabled by prefixing the label with the issuer name. * * @return string The OTP URI. */ protected function buildUri($type, $parameters, HotpBasedConfigurationInterface $configuration, OtpSharedParametersInterface $shared, $label, $issuer = null, $issuerInLabel = null) { if (null === $issuerInLabel) { $issuerInLabel = false; } if (6 !== $configuration->digits()) { $parameters .= '&digits=' . rawurlencode($configuration->digits()); } if (HotpHashAlgorithm::SHA1() !== $configuration->algorithm()) { $parameters .= '&algorithm=' . rawurlencode($configuration->algorithm()->value()); } $legacyIssuer = ''; if (null !== $issuer) { if ($issuerInLabel) { $legacyIssuer = rawurlencode($issuer) . ':'; } $parameters .= '&issuer=' . rawurlencode($issuer); } return sprintf('otpauth://%s/%s%s?secret=%s%s', rawurlencode($type), $legacyIssuer, rawurlencode($label), rawurlencode(Base32::encode($shared->secret())), $parameters); }
/** * @param Parser $parser * @return Ipv4|Ipv6|Onion * @throws \BitWasp\Buffertools\Exceptions\ParserOutOfRange * @throws \Exception */ public function fromParser(Parser $parser) { $buffer = $parser->readBytes(16); $binary = $buffer->getBinary(); if (Onion::MAGIC === substr($binary, 0, strlen(Onion::MAGIC))) { $addr = strtolower(Base32::encode($buffer->slice(strlen(Onion::MAGIC))->getBinary())) . '.onion'; $ip = new Onion($addr); } elseif (Ipv4::MAGIC === substr($binary, 0, strlen(Ipv4::MAGIC))) { $end = $buffer->slice(strlen(Ipv4::MAGIC), 4); $ip = new Ipv4(long2ip($end->getInt())); } else { $addr = []; foreach (str_split($binary, 2) as $segment) { $addr[] = bin2hex($segment); } $addr = implode(":", $addr); $ip = new Ipv6($addr); } return $ip; }
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'); }
/** * @throws \InvalidArgumentException * * @return string */ private function getDecodedSecret() { $secret = Base32::decode($this->getSecret()); return $secret; }
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; }
/** * 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 makeTOTP($provider = null) { /*** * Assign a user a multifactor authentication code * * @param string $provider The provider giving 2FA. * @return array with the status in the key "status", errors in "error" and "human_error", * username in "username", and provisioning data in "uri" ***/ if (empty($this->username)) { $this->getUser(); # We MUST have this properly assigned if (empty($this->username)) { return array('status' => false, 'error' => 'Unable to get user.'); } } if ($this->getSecret() !== false) { return array('status' => false, 'error' => '2FA has already been enabled for this user.', 'human_error' => "You've already enabled 2-factor authentication.", 'username' => $this->username); } try { if (!class_exists('Stronghash')) { require_once dirname(__FILE__) . '/../core/stronghash/php-stronghash.php'; } $salt = Stronghash::createSalt(); require_once dirname(__FILE__) . '/../base32/src/Base32/Base32.php'; $secret = Base32::encode($salt); ## The resulting provisioning URI should now be sent to the user ## Flag should be set server-side indicating the change id pending $l = $this->openDB(); $query = 'UPDATE `' . $this->getTable() . '` SET `' . $this->tmpColumn . "`='{$secret}' WHERE `" . $this->userColumn . "`='" . $this->username . "'"; $r = mysqli_query($l, $query); if ($r === false) { return array('status' => false, 'human_error' => 'Database error', 'error' => mysqli_error($l)); } # The data was saved correctly # Let's create the provisioning stuff! self::doLoadOTP(); $totp = new OTPHP\TOTP($secret); $totp->setDigest($this->getDigest()); $totp->setLabel($this->username); $totp->setIssuer($provider); $uri = $totp->getProvisioningURI($label, $provider); # iPhones don't actually accept the full, valid URI $unsafe_uri = urldecode($uri); $uri_args = explode('?', $unsafe_uri); $iphone_uri = $uri_args[0] . '?'; $iphone_args = array(); $iphone_safe_args = array('secret', 'issuer'); foreach (explode('&', $uri_args[1]) as $paramval) { $pv = explode('=', $paramval); $param = $pv[0]; $val = $pv[1]; if (in_array($param, $iphone_safe_args)) { $iphone_args[] = $param . '=' . $val; } } $iphone_uri .= implode('&', $iphone_args); /* $iphone32 = str_replace("=","",$secret_part[1]); */ /* $iphone_uri = $secret_part[0]."secret=".$iphone32; #still no good */ $retarr = self::generateQR($iphone_uri); # Let's get a human-readable secret $human_secret0 = str_replace('=', '', $secret); $i = 0; $human_secret = ''; foreach (str_split($human_secret0) as $char) { $human_secret .= $char; ++$i; if ($i == 4) { $human_secret .= ' '; $i = 0; } } $retarr['secret'] = $secret; $retarr['human_secret'] = $human_secret; $retarr['username'] = $this->username; return $retarr; } catch (Exception $e) { return array('status' => false, 'human_error' => 'Unexpected error in makeTOTP', 'error' => $e->getMessage(), 'username' => $this->username, 'provider' => $provider, 'label' => $totp->getLabel(), 'uri' => $uri, 'secret' => $secret); } }
/** * Encode a string to Base32. * * @param $string * @return mixed */ public function toBase32($string) { $encoded = Base32::encode($string); return str_replace('=', '', $encoded); }
/** * Create a 64 bit secret string * @return string */ private function createSecret() { return Base32::encode(random_bytes(64)); }
public function fromBase32String() { return Base32::decode($this->digest); }
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); }
/** * Returns the binary value of the base32 encoded secret skey * * @return binary secret key */ public function decode_key() { return \Base32\Base32::decode($this->skey); }
<?php use cebe\pulse\discover\Address; use cebe\pulse\discover\Device; use cebe\pulse\discover\DiscoveryManager; use cebe\pulse\discover\Packet; require __DIR__ . '/vendor/autoload.php'; $loop = React\EventLoop\Factory::create(); // config // generates a random node ID, should be replaced with a real hash of the TLS cert $id = rtrim(\Base32\Base32::encode(hash('sha256', rand() . microtime(true), true)), '='); $servicePort = rand(1337, 32000); // setup $discoveryManager = new DiscoveryManager($id); $discoveryManager->servicePort = $servicePort; $discoveryManager->start($loop); // TCP Server $socket = new React\Socket\Server($loop); $socket->on('connection', function ($conn) { // $conn->write("Hello there!\n"); // $conn->write("Welcome to this amazing server!\n"); // $conn->write("Here's a tip: don't say anything.\n"); echo "connection from " . $conn->getRemoteAddress() . "\n"; $conn->on('data', function ($data) use($conn) { echo "{$data}\n"; $conn->close(); }); }); $socket->listen($servicePort); $loop->run();
/** * 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); }
public function __toString() { return Base32::encode($this->value); }