Exemple #1
0
 public function __construct($config_array)
 {
     $expected_keys = array("cipher_method", "block_byte_size", "key_byte_size", "salt_byte_size", "mac_byte_size", "hash_function_name", "encryption_info_string", "authentication_info_string");
     if (sort($expected_keys) !== true) {
         throw Ex\CannotPerformOperationException("sort() failed.");
     }
     $actual_keys = array_keys($config_array);
     if (sort($actual_keys) !== true) {
         throw Ex\CannotPerformOperationException("sort() failed.");
     }
     if ($expected_keys !== $actual_keys) {
         throw new Ex\CannotPerformOperationException("Trying to instantiate a bad configuration.");
     }
     $this->cipher_method = $config_array["cipher_method"];
     $this->block_byte_size = $config_array["block_byte_size"];
     $this->key_byte_size = $config_array["key_byte_size"];
     $this->salt_byte_size = $config_array["salt_byte_size"];
     $this->mac_byte_size = $config_array["mac_byte_size"];
     $this->hash_function_name = $config_array["hash_function_name"];
     $this->encryption_info_string = $config_array["encryption_info_string"];
     $this->authentication_info_string = $config_array["authentication_info_string"];
     Core::ensureFunctionExists('openssl_get_cipher_methods');
     if (\in_array($this->cipher_method, \openssl_get_cipher_methods()) === false) {
         throw new Ex\CannotPerformOperationException("Configuration contains an invalid OpenSSL cipher method.");
     }
     if (!\is_int($this->block_byte_size) || $this->block_byte_size <= 0) {
         throw new Ex\CannotPerformOperationException("Configuration contains an invalid block byte size.");
     }
     if (!\is_int($this->key_byte_size) || $this->key_byte_size <= 0) {
         throw new Ex\CannotPerformOperationException("Configuration contains an invalid key byte size.");
     }
     if ($this->salt_byte_size !== false) {
         if (!is_int($this->salt_byte_size) || $this->salt_byte_size <= 0) {
             throw new Ex\CannotPerformOperationException("Configuration contains an invalid salt byte size.");
         }
     }
     if (!\is_int($this->mac_byte_size) || $this->mac_byte_size <= 0) {
         throw new Ex\CannotPerformOperationException("Configuration contains an invalid MAC byte size.");
     }
     if (\in_array($this->hash_function_name, \hash_algos()) === false) {
         throw new Ex\CannotPerformOperationException("Configuration contains an invalid hash function name.");
     }
     if (!\is_string($this->encryption_info_string) || $this->encryption_info_string === "") {
         throw new Ex\CannotPerformOperationException("Configuration contains an invalid encryption info string.");
     }
     if (!\is_string($this->authentication_info_string) || $this->authentication_info_string === "") {
         throw new Ex\CannotPerformOperationException("Configuration contains an invalid authentication info string.");
     }
 }
 /**
  * Runs the runtime tests.
  *
  * @throws Ex\EnvironmentIsBrokenException
  */
 public static function runtimeTest()
 {
     // 0: Tests haven't been run yet.
     // 1: Tests have passed.
     // 2: Tests are running right now.
     // 3: Tests have failed.
     static $test_state = 0;
     if ($test_state === 1 || $test_state === 2) {
         return;
     }
     if ($test_state === 3) {
         /* If an intermittent problem caused a test to fail previously, we
          * want that to be indicated to the user with every call to this
          * library. This way, if the user first does something they really
          * don't care about, and just ignores all exceptions, they won't get
          * screwed when they then start to use the library for something
          * they do care about. */
         throw new Ex\EnvironmentIsBrokenException('Tests failed previously.');
     }
     try {
         $test_state = 2;
         Core::ensureFunctionExists('openssl_get_cipher_methods');
         if (\in_array(Core::CIPHER_METHOD, \openssl_get_cipher_methods()) === false) {
             throw new Ex\EnvironmentIsBrokenException('Cipher method not supported. This is normally caused by an outdated ' . 'version of OpenSSL (and/or OpenSSL compiled for FIPS compliance). ' . 'Please upgrade to a newer version of OpenSSL that supports ' . Core::CIPHER_METHOD . ' to use this library.');
         }
         RuntimeTests::AESTestVector();
         RuntimeTests::HMACTestVector();
         RuntimeTests::HKDFTestVector();
         RuntimeTests::testEncryptDecrypt();
         if (Core::ourStrlen(Key::createNewRandomKey()->getRawBytes()) != Core::KEY_BYTE_SIZE) {
             throw new Ex\EnvironmentIsBrokenException();
         }
         if (Core::ENCRYPTION_INFO_STRING == Core::AUTHENTICATION_INFO_STRING) {
             throw new Ex\EnvironmentIsBrokenException();
         }
     } catch (Ex\EnvironmentIsBrokenException $ex) {
         // Do this, otherwise it will stay in the "tests are running" state.
         $test_state = 3;
         throw $ex;
     }
     // Change this to '0' make the tests always re-run (for benchmarking).
     $test_state = 1;
 }
Exemple #3
0
 /**
  * Raw unauthenticated decryption (insecure on its own).
  *
  * @param string $ciphertext
  * @param string $key
  * @param string $iv
  * @param string $cipherMethod
  *
  * @throws Ex\EnvironmentIsBrokenException
  *
  * @return string
  */
 protected static function plainDecrypt($ciphertext, $key, $iv, $cipherMethod)
 {
     Core::ensureConstantExists('OPENSSL_RAW_DATA');
     Core::ensureFunctionExists('openssl_decrypt');
     $plaintext = \openssl_decrypt($ciphertext, $cipherMethod, $key, OPENSSL_RAW_DATA, $iv);
     if ($plaintext === false) {
         throw new Ex\EnvironmentIsBrokenException('openssl_decrypt() failed.');
     }
     return $plaintext;
 }
Exemple #4
0
 /**
  * Encrypt the contents of a file handle $inputHandle and store the results
  * in $outputHandle using HKDF of $key to perform authenticated encryption
  *
  * @param resource $inputHandle
  * @param resource $outputHandle
  * @param Key $key
  * @return boolean
  */
 public static function encryptResource($inputHandle, $outputHandle, Key $key)
 {
     // Because we don't have strict typing in PHP 5
     if (!\is_resource($inputHandle)) {
         throw new Ex\InvalidInput('Input handle must be a resource!');
     }
     if (!\is_resource($outputHandle)) {
         throw new Ex\InvalidInput('Output handle must be a resource!');
     }
     $config = self::getFileVersionConfigFromHeader(Core::CURRENT_FILE_VERSION, Core::CURRENT_FILE_VERSION);
     $inputStat = \fstat($inputHandle);
     $inputSize = $inputStat['size'];
     // Let's add this check before anything
     if (!\in_array($config->hashFunctionName(), \hash_algos())) {
         throw new Ex\CannotPerformOperationException('The specified hash function does not exist');
     }
     /**
      *  Let's split our keys
      */
     $file_salt = Core::secureRandom($config->saltByteSize());
     // $ekey -- Encryption Key -- used for AES
     $ekey = Core::HKDF($config->hashFunctionName(), $key->getRawBytes(), $config->keyByteSize(), $config->encryptionInfoString(), $file_salt, $config);
     // $akey -- Authentication Key -- used for HMAC
     $akey = Core::HKDF($config->hashFunctionName(), $key->getRawBytes(), $config->keyByteSize(), $config->authenticationInfoString(), $file_salt, $config);
     /**
      *  Generate a random initialization vector.
      */
     Core::ensureFunctionExists("openssl_cipher_iv_length");
     $ivsize = \openssl_cipher_iv_length($config->cipherMethod());
     if ($ivsize === false || $ivsize <= 0) {
         throw new Ex\CannotPerformOperationException('Improper IV size');
     }
     $iv = Core::secureRandom($ivsize);
     /**
      * First let's write our header, file salt, and IV to the first N blocks of the output file
      */
     self::writeBytes($outputHandle, Core::CURRENT_FILE_VERSION . $file_salt . $iv, Core::HEADER_VERSION_SIZE + $config->saltByteSize() + $ivsize);
     /**
      * We're going to initialize a HMAC-SHA256 with the given $akey
      * and update it with each ciphertext chunk
      */
     $hmac = \hash_init($config->hashFunctionName(), HASH_HMAC, $akey);
     if ($hmac === false) {
         throw new Ex\CannotPerformOperationException('Cannot initialize a hash context');
     }
     /**
      * We operate on $thisIv using a hash-based PRF derived from the initial
      * IV for the first block
      */
     $thisIv = $iv;
     /**
      * How much do we increase the counter after each buffered encryption to
      * prevent nonce reuse?
      */
     $inc = $config->bufferByteSize() / $config->blockByteSize();
     /**
      * Let's MAC our salt and IV/nonce
      */
     \hash_update($hmac, Core::CURRENT_FILE_VERSION);
     \hash_update($hmac, $file_salt);
     \hash_update($hmac, $iv);
     /**
      * Iterate until we reach the end of the input file
      */
     $breakR = false;
     while (!\feof($inputHandle)) {
         $pos = \ftell($inputHandle);
         if ($pos + $config->bufferByteSize() >= $inputSize) {
             $breakR = true;
             // We need to break after this loop iteration
             $read = self::readBytes($inputHandle, $inputSize - $pos);
         } else {
             $read = self::readBytes($inputHandle, $config->bufferByteSize());
         }
         $thisIv = Core::incrementCounter($thisIv, $inc, $config);
         /**
          * Perform the AES encryption. Encrypts the plaintext.
          */
         $encrypted = \openssl_encrypt($read, $config->cipherMethod(), $ekey, OPENSSL_RAW_DATA, $thisIv);
         /**
          * Check that the encryption was performed successfully
          */
         if ($encrypted === false) {
             throw new Ex\CannotPerformOperationException('OpenSSL encryption error');
         }
         /**
          * Write the ciphertext to the output file
          */
         self::writeBytes($outputHandle, $encrypted, Core::ourStrlen($encrypted));
         /**
          * Update the HMAC for the entire file with the data from this block
          */
         \hash_update($hmac, $encrypted);
         if ($breakR) {
             break;
         }
     }
     // Now let's get our HMAC and append it
     $finalHMAC = \hash_final($hmac, true);
     self::writeBytes($outputHandle, $finalHMAC, $config->macByteSize());
     return true;
 }
Exemple #5
0
 /**
  * You MUST NOT call this method directly.
  *
  * Unauthenticated message deryption.
  *
  * @param string $ciphertext
  * @param string $key
  * @param string $iv
  * @param array $config
  * @return string
  * @throws Ex\CannotPerformOperationException
  */
 protected static function plainDecrypt($ciphertext, $key, $iv, $config)
 {
     Core::ensureConstantExists("OPENSSL_RAW_DATA");
     Core::ensureFunctionExists("openssl_decrypt");
     $plaintext = \openssl_decrypt($ciphertext, $config->cipherMethod(), $key, OPENSSL_RAW_DATA, $iv);
     if ($plaintext === false) {
         throw new Ex\CannotPerformOperationException("openssl_decrypt() failed.");
     }
     return $plaintext;
 }