示例#1
0
 /**
  * Decrypt then verify a password
  * 
  * @param string $password - The user-provided password
  * @param string $stored   - The encrypted password hash
  * @param Key $secret_key  - The master key for all passwords
  * @return boolean
  */
 public static function verify($password, $stored, \ParagonIE\Halite\Contract\CryptoKeyInterface $secret_key)
 {
     // First let's decrypt the hash
     $hash_str = Symmetric::decrypt($stored, $secret_key);
     // And now to verify the hash
     return \Sodium\crypto_pwhash_scryptsalsa208sha256_str_verify($hash_str, $password);
 }
示例#2
0
 /**
  * Decrypt a string using asymmetric cryptography
  * Wraps Symmetric::decrypt()
  * 
  * @param string $source Ciphertext
  * @param string $ourPrivateKey Our private key
  * @param string $theirPublicKey Their public key
  * @param boolean $raw Don't hex decode the input?
  * 
  * @return string
  */
 public static function decrypt($source, Contract\CryptoKeyInterface $ourPrivateKey, Contract\CryptoKeyInterface $theirPublicKey, $raw = false)
 {
     list($secret, $public) = self::judgeKeys($ourPrivateKey, $theirPublicKey);
     $ecdh = new Key(self::getSharedSecret($secret, $public), false, false);
     $ciphertext = Symmetric::decrypt($source, $ecdh, $raw);
     unset($ecdh);
     return $ciphertext;
 }
示例#3
0
 /**
  * Decrypt then verify a password
  * 
  * @param string $password          - The user-provided password
  * @param string $stored            - The encrypted password hash
  * @param EncryptionKey $secret_key  - The master key for all passwords
  * @return boolean
  */
 public static function verify($password, $stored, KeyInterface $secret_key)
 {
     if (!$secret_key instanceof EncryptionKey) {
         throw new \ParagonIE\Halite\Alerts\InvalidKey('Argument 3: Expected an instance of EncryptionKey');
     }
     // First let's decrypt the hash
     $hash_str = Crypto::decrypt($stored, $secret_key);
     // Upon successful decryption, verify the password is correct
     return \Sodium\crypto_pwhash_scryptsalsa208sha256_str_verify($hash_str, $password);
 }
示例#4
0
 /**
  * Decrypt a string using asymmetric cryptography
  * Wraps SymmetricCrypto::decrypt()
  * 
  * @param string $source Ciphertext
  * @param EncryptionSecretKey $ourPrivateKey Our private key
  * @param EncryptionPublicKey $theirPublicKey Their public key
  * @param boolean $raw Don't hex decode the input?
  * 
  * @return string
  * 
  * @throws CryptoException\InvalidKey
  */
 public static function decrypt($source, Contract\KeyInterface $ourPrivateKey, Contract\KeyInterface $theirPublicKey, $raw = false)
 {
     if (!$ourPrivateKey instanceof EncryptionSecretKey) {
         throw new CryptoException\InvalidKey('Argument 2: Expected an instance of EncryptionSecretKey');
     }
     if (!$theirPublicKey instanceof EncryptionPublicKey) {
         throw new CryptoException\InvalidKey('Argument 3: Expected an instance of EncryptionPublicKey');
     }
     $ecdh = new EncryptionKey(self::getSharedSecret($ourPrivateKey, $theirPublicKey));
     $ciphertext = SymmetricCrypto::decrypt($source, $ecdh, $raw);
     unset($ecdh);
     return $ciphertext;
 }
示例#5
0
 public function testEncryptFail()
 {
     $key = new \ParagonIE\Halite\Key(\str_repeat('A', 32));
     $message = Symmetric::encrypt('test message', $key, true);
     $r = \Sodium\randombytes_uniform(\mb_strlen($message, '8bit'));
     $message[$r] = \chr(\ord($message[$r]) ^ 1 << \Sodium\randombytes_uniform(8));
     try {
         $plain = Symmetric::decrypt($message, $key, true);
         $this->assertEquals($plain, $message);
         throw new Exception('ERROR: THIS SHOULD ALWAYS FAIL');
     } catch (CryptoException\InvalidMessage $e) {
         $this->assertTrue($e instanceof CryptoException\InvalidMessage);
     }
 }
示例#6
0
 public function testEncryptFail()
 {
     $key = new EncryptionKey(\str_repeat('A', 32));
     $message = Symmetric::encrypt('test message', $key, true);
     $r = \Sodium\randombytes_uniform(\mb_strlen($message, '8bit'));
     $message[$r] = \chr(\ord($message[$r]) ^ 1 << \Sodium\randombytes_uniform(8));
     try {
         $plain = Symmetric::decrypt($message, $key, true);
         $this->assertEquals($plain, $message);
         $this->fail('This should have thrown an InvalidMessage exception!');
     } catch (CryptoException\InvalidMessage $e) {
         $this->assertTrue($e instanceof CryptoException\InvalidMessage);
     }
 }
示例#7
0
 /**
  * Store a value in an encrypted cookie
  * 
  * @param string $name
  * @param mixed $value
  * @param int $expire    (defaults to 0)
  * @param string $path   (defaults to '/')
  * @param string $domain (defaults to NULL)
  * @param bool $secure   (defaults to TRUE)
  * @param bool $httponly (defaults to TRUE)
  * @return bool
  */
 public function store($name, $value, $expire = 0, $path = '/', $domain = null, $secure = true, $httponly = true)
 {
     return \setcookie($name, Crypto::encrypt(\json_encode($value), $this->key), $expire, $path, $domain, $secure, $httponly);
 }
示例#8
0
 /**
  * @param string $token
  *
  * @return string
  *
  * @throws \ParagonIE\Halite\Alerts\InvalidKey
  * @throws \ParagonIE\Halite\Alerts\InvalidMessage
  * @throws \ParagonIE\Halite\Alerts\InvalidType
  */
 public function decryptToken($token = '')
 {
     $decyrpted = Symmetric::decrypt($token, $this->key);
     return $decyrpted;
 }
 public function it_fails_when_the_user_tuple_and_random_verify_do_not_match($authenticationMapper, $userMapper)
 {
     $systemKey = $this->systemEncryptionKey;
     $userKey = new EncryptionKey($this->authenticationData->getSessionKey());
     $hashCookieName = hash_hmac('sha256', $this->authenticationData->getSessionKey() . $this->authenticationData->getUsername(), $systemKey);
     $userTuple = base64_encode(Crypto::encrypt($this->authenticationData->getUserId() . ":" . $hashCookieName, $systemKey));
     $hashCookieContents = base64_encode(Crypto::encrypt(time() . ':' . 2 . ':' . $this->authenticationData->getUsername(), $userKey));
     $_COOKIE[AuthenticationService::COOKIE_USER] = $userTuple;
     $_COOKIE[AuthenticationService::COOKIE_HASH_PREFIX . $hashCookieName] = $hashCookieContents;
     $_COOKIE[AuthenticationService::COOKIE_VERIFY_A] = hash_hmac('sha256', $userTuple, $systemKey);
     $_COOKIE[AuthenticationService::COOKIE_VERIFY_B] = hash_hmac('sha256', $hashCookieContents, $userKey) . 'b';
     $userMapper->getUser(Argument::any())->shouldNotBeCalled();
     $this->getIdentity()->shouldBe(null);
 }
示例#10
0
 /**
  * @covers Symmetric::unpackMessageForDecryption()
  */
 public function testUnpack()
 {
     $key = new EncryptionKey(new HiddenString(\str_repeat('A', 32)));
     // Randomly sized plaintext
     $size = \Sodium\randombytes_uniform(1023) + 1;
     $plaintext = \Sodium\randombytes_buf($size);
     $message = Symmetric::encrypt(new HiddenString($plaintext), $key, true);
     // Let's unpack our message
     $unpacked = Symmetric::unpackMessageForDecryption($message);
     // Now to test our expected results!
     $this->assertSame(Util::safeStrlen($unpacked[0]), Halite::VERSION_TAG_LEN);
     $this->assertTrue($unpacked[1] instanceof \ParagonIE\Halite\Symmetric\Config);
     $config = $unpacked[1];
     if ($config instanceof \ParagonIE\Halite\Symmetric\Config) {
         $this->assertSame(Util::safeStrlen($unpacked[2]), $config->HKDF_SALT_LEN);
         $this->assertSame(Util::safeStrlen($unpacked[3]), \Sodium\CRYPTO_STREAM_NONCEBYTES);
         $this->assertSame(Util::safeStrlen($unpacked[4]), Util::safeStrlen($message) - (Halite::VERSION_TAG_LEN + $config->HKDF_SALT_LEN + \Sodium\CRYPTO_STREAM_NONCEBYTES + $config->MAC_SIZE));
         $this->assertSame(Util::safeStrlen($unpacked[5]), $config->MAC_SIZE);
     } else {
         $this->fail('Cannot continue');
     }
 }
 /**
  * Rifle through 4 cookies, ensuring that all details line up.  If they do, we accept that the cookies authenticate
  * a specific user.
  *
  * Some notes:
  *
  *  - COOKIE_VERIFY_A is a do-not-decrypt check of COOKIE_USER
  *  - COOKIE_VERIFY_B is a do-not-decrypt check of the random-named-cookie specified by COOKIE_USER
  *  - COOKIE_USER has its contents encrypted by the system key
  *  - the random-named-cookie has its contents encrypted by the user key
  *
  * @see self::setSessionCookies
  * @return User|null
  */
 public function getIdentity()
 {
     if ($this->identity) {
         return $this->identity;
     }
     if (!isset($_COOKIE[self::COOKIE_VERIFY_A])) {
         return null;
     }
     if (!isset($_COOKIE[self::COOKIE_USER])) {
         return null;
     }
     $systemKey = new EncryptionKey($this->systemEncryptionKey);
     $verificationCookie = $_COOKIE[self::COOKIE_VERIFY_A];
     $hashPass = hash_equals(hash_hmac('sha256', $_COOKIE[self::COOKIE_USER], $systemKey), $verificationCookie);
     //
     // 1. Is the verify cookie still equivalent to the user cookie, if so, do not decrypt
     //
     if (!$hashPass) {
         return null;
     }
     //
     // 2. If the user cookie was not tampered with, decrypt its contents with the system key
     //
     try {
         $userTuple = Crypto::decrypt(base64_decode($_COOKIE[self::COOKIE_USER]), $systemKey);
         if (strpos($userTuple, ':') === false) {
             throw new \Exception();
         }
         // paranoid, make sure we have everything we need
         @(list($cookieUserId, $hashCookieSuffix) = @explode(":", $userTuple, 2));
         if (!isset($cookieUserId) || !isset($hashCookieSuffix) || !is_numeric($cookieUserId) || !trim($hashCookieSuffix)) {
             throw new \Exception();
         }
         /** @var AuthenticationRecordInterface $auth */
         if (!($auth = $this->authenticationProvider->findByUserId($cookieUserId))) {
             throw new \Exception();
         }
         $hashCookieName = self::COOKIE_HASH_PREFIX . $hashCookieSuffix;
         //
         // 2. Check the hashCookie for corroborating data
         //
         if (!isset($_COOKIE[$hashCookieName])) {
             throw new \Exception();
         }
         $userKey = new EncryptionKey($auth->getSessionKey());
         $hashPass = hash_equals(hash_hmac('sha256', $_COOKIE[$hashCookieName], $userKey), $_COOKIE[self::COOKIE_VERIFY_B]);
         if (!$hashPass) {
             throw new \Exception();
         }
         //
         // 3. Decrypt the hash cookie with the user key
         //
         $hashedCookieContents = Crypto::decrypt(base64_decode($_COOKIE[$hashCookieName]), $userKey);
         if (!substr_count($hashedCookieContents, ':') == 2) {
             throw new \Exception();
         }
         list(, $hashedUserId, $hashedUsername) = explode(':', $hashedCookieContents);
         if ($hashedUserId != $cookieUserId) {
             throw new \Exception();
         }
         if ($hashedUsername != $auth->getUsername()) {
             throw new \Exception();
         }
         $this->purgeHashCookies($hashCookieName);
         //
         // 4. Cookies check out - it's up to the user provider now
         //
         $user = $this->userProvider->getUser($auth->getUserId());
         if ($user) {
             $this->setIdentity($user);
             return $this->identity;
         }
     } catch (\Exception $x) {
         $this->purgeHashCookies();
     }
     return null;
 }
示例#12
0
 /**
  * Perform decryption
  *
  * @param $text
  * @return mixed
  */
 public static function decrypt($text)
 {
     $key = KeyFactory::loadEncryptionKey(config('laracrypt.path'));
     return Crypto::decrypt($text, $key);
 }
示例#13
0
 /**
  * Get the user's two-factor authentication secret
  *
  * @param int $userID
  * @return bool
  */
 public function resetTwoFactorSecret(int $userID) : bool
 {
     $state = State::instance();
     $this->db->beginTransaction();
     $secret = \random_bytes(20);
     $this->db->update('airship_users', ['totp_secret' => Symmetric::encrypt($secret, $state->keyring['auth.password_key'])], ['userid' => $userID]);
     return $this->db->commit();
 }
示例#14
0
 /**
  * Let's do an automatic login
  *
  * @param string $token
  * @param string $uid_idx
  * @param string $token_idx
  * @return bool
  * @throws LongTermAuthAlert (only in debug mode)
  * @throws \TypeError
  */
 protected function doAutoLogin(string $token, string $uid_idx, string $token_idx) : bool
 {
     if (!$this->airship_auth instanceof Authentication) {
         $this->tightenSecurityBolt();
     }
     $state = State::instance();
     try {
         $userId = $this->airship_auth->loginByToken($token);
         \Sodium\memzero($token);
         if (!$this->verifySessionCanary($userId, false)) {
             return false;
         }
         // Regenerate session ID:
         Session::regenerate(true);
         // Set session variable
         $_SESSION[$uid_idx] = $userId;
         $autoPilot = Gears::getName('AutoPilot');
         if (IDE_HACKS) {
             // We're using getName(), this is just to fool IDEs.
             $autoPilot = new AutoPilot();
         }
         $httpsOnly = (bool) $autoPilot::isHTTPSConnection();
         // Rotate the authentication token:
         Cookie::setcookie($token_idx, Symmetric::encrypt($this->airship_auth->rotateToken($token, $userId), $state->keyring['cookie.encrypt_key']), \time() + ($state->universal['long-term-auth-expire'] ?? self::DEFAULT_LONGTERMAUTH_EXPIRE), '/', '', $httpsOnly ?? false, true);
         return true;
     } catch (LongTermAuthAlert $e) {
         $state = State::instance();
         // Let's wipe our long-term authentication cookies
         Cookie::setcookie($token_idx, null, 0, '/', '', $httpsOnly ?? false, true);
         // Let's log this incident
         if (\property_exists($this, 'log')) {
             $this->log($e->getMessage(), LogLevel::CRITICAL, ['exception' => \Airship\throwableToArray($e)]);
         } else {
             $state->logger->log(LogLevel::CRITICAL, $e->getMessage(), ['exception' => \Airship\throwableToArray($e)]);
         }
         // In debug mode, re-throw the exception:
         if ($state->universal['debug']) {
             throw $e;
         }
     }
     return false;
 }