/** * @expectedException \Defuse\Crypto\Exception\BadFormatException * @expectedExceptionMessage not a hex string */ public function testBadHexEncoding() { $header = Core::secureRandom(Core::HEADER_VERSION_SIZE); $str = Encoding::saveBytesToChecksummedAsciiSafeString($header, Core::secureRandom(Core::KEY_BYTE_SIZE)); $str[0] = 'Z'; Encoding::loadBytesFromChecksummedAsciiSafeString($header, $str); }
/** * Encrypts a string with either a key or a password. * * @param string $plaintext * @param KeyOrPassword $secret * @param bool $raw_binary * * @return string */ private static function encryptInternal($plaintext, KeyOrPassword $secret, $raw_binary) { RuntimeTests::runtimeTest(); $salt = Core::secureRandom(Core::SALT_BYTE_SIZE); $keys = $secret->deriveKeys($salt); $ekey = $keys->getEncryptionKey(); $akey = $keys->getAuthenticationKey(); $iv = Core::secureRandom(Core::BLOCK_BYTE_SIZE); $ciphertext = Core::CURRENT_VERSION . $salt . $iv . self::plainEncrypt($plaintext, $ekey, $iv); $auth = \hash_hmac(Core::HASH_FUNCTION_NAME, $ciphertext, $akey, true); $ciphertext = $ciphertext . $auth; if ($raw_binary) { return $ciphertext; } return Encoding::binToHex($ciphertext); }
/** * 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; }
/** * Encrypts a message. * * $plaintext is the message to encrypt. * $key is the encryption key, a value generated by CreateNewRandomKey(). * You MUST catch exceptions thrown by this function. Read the docs. * * @param string $plaintext * @param string $key * @param boolean $raw_binary * @return string * @throws Ex\CannotPerformOperationException * @throws Ex\CryptoTestFailedException */ public static function encrypt($plaintext, $key, $raw_binary = false) { RuntimeTests::runtimeTest(); /* Attempt to validate that the key was generated safely. */ if (!is_a($key, "\\Defuse\\Crypto\\Key")) { throw new Ex\CannotPerformOperationException("The given key is not a valid Key object."); } $key = $key->getRawBytes(); $config = self::getVersionConfigFromHeader(Core::CURRENT_VERSION, Core::CURRENT_VERSION); if (Core::ourStrlen($key) !== $config->keyByteSize()) { throw new Ex\CannotPerformOperationException("Key is the wrong size."); } $salt = Core::secureRandom($config->saltByteSize()); // Generate a sub-key for encryption. $ekey = Core::HKDF($config->hashFunctionName(), $key, $config->keyByteSize(), $config->encryptionInfoString(), $salt, $config); // Generate a sub-key for authentication and apply the HMAC. $akey = Core::HKDF($config->hashFunctionName(), $key, $config->keyByteSize(), $config->authenticationInfoString(), $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("Could not get the IV length from OpenSSL"); } $iv = Core::secureRandom($ivsize); $ciphertext = $salt . $iv . self::plainEncrypt($plaintext, $ekey, $iv, $config); $auth = \hash_hmac($config->hashFunctionName(), Core::CURRENT_VERSION . $ciphertext, $akey, true); // We're now appending the header as of 2.00 $ciphertext = Core::CURRENT_VERSION . $auth . $ciphertext; if ($raw_binary) { return $ciphertext; } return Encoding::binToHex($ciphertext); }
public static function CreateNewRandomKey() { return Key::CreateKey(function ($size) { return Core::secureRandom($size); }); }
/** * Encrypts a resource with either a key or a password. * * @param resource $inputHandle * @param resource $outputHandle * @param KeyOrPassword $secret * * @throws Defuse\Crypto\Exception\EnvironmentIsBrokenException * @throws Defuse\Crypto\Exception\IOException */ private static function encryptResourceInternal($inputHandle, $outputHandle, KeyOrPassword $secret) { if (!\is_resource($inputHandle)) { throw new Ex\IOException('Input handle must be a resource!'); } if (!\is_resource($outputHandle)) { throw new Ex\IOException('Output handle must be a resource!'); } $inputStat = \fstat($inputHandle); $inputSize = $inputStat['size']; $file_salt = Core::secureRandom(Core::SALT_BYTE_SIZE); $keys = $secret->deriveKeys($file_salt); $ekey = $keys->getEncryptionKey(); $akey = $keys->getAuthenticationKey(); $ivsize = Core::BLOCK_BYTE_SIZE; $iv = Core::secureRandom($ivsize); /* Initialize a streaming HMAC state. */ $hmac = \hash_init(Core::HASH_FUNCTION_NAME, HASH_HMAC, $akey); if ($hmac === false) { throw new Ex\EnvironmentIsBrokenException('Cannot initialize a hash context'); } /* Write the header, salt, and IV. */ self::writeBytes($outputHandle, Core::CURRENT_VERSION . $file_salt . $iv, Core::HEADER_VERSION_SIZE + Core::SALT_BYTE_SIZE + $ivsize); /* Add the header, salt, and IV to the HMAC. */ \hash_update($hmac, Core::CURRENT_VERSION); \hash_update($hmac, $file_salt); \hash_update($hmac, $iv); /* $thisIv will be incremented after each call to the encryption. */ $thisIv = $iv; /* How many blocks do we encrypt at a time? We increment by this value. */ $inc = Core::BUFFER_BYTE_SIZE / Core::BLOCK_BYTE_SIZE; /* Loop until we reach the end of the input file. */ $at_file_end = false; while (!(\feof($inputHandle) || $at_file_end)) { /* Find out if we can read a full buffer, or only a partial one. */ $pos = \ftell($inputHandle); if ($pos === false) { throw new Ex\IOException('Could not get current position in input file during encryption'); } if ($pos + Core::BUFFER_BYTE_SIZE >= $inputSize) { /* We're at the end of the file, so we need to break out of the loop. */ $at_file_end = true; $read = self::readBytes($inputHandle, $inputSize - $pos); } else { $read = self::readBytes($inputHandle, Core::BUFFER_BYTE_SIZE); } /* Encrypt this buffer. */ $encrypted = \openssl_encrypt($read, Core::CIPHER_METHOD, $ekey, OPENSSL_RAW_DATA, $thisIv); if ($encrypted === false) { throw new Ex\EnvironmentIsBrokenException('OpenSSL encryption error'); } /* Write this buffer's ciphertext. */ self::writeBytes($outputHandle, $encrypted, Core::ourStrlen($encrypted)); /* Add this buffer's ciphertext to the HMAC. */ \hash_update($hmac, $encrypted); /* Increment the counter by the number of blocks in a buffer. */ $thisIv = Core::incrementCounter($thisIv, $inc); /* WARNING: Usually, unless the file is a multiple of the buffer * size, $thisIv will contain an incorrect value here on the last * iteration of this loop. */ } /* Get the HMAC and append it to the ciphertext. */ $final_mac = \hash_final($hmac, true); self::writeBytes($outputHandle, $final_mac, CORE::MAC_BYTE_SIZE); }