예제 #1
0
 /**
  * Encrypt a message using the Halite encryption protocol
  * (Encrypt then MAC -- Xsalsa20 then HMAC-SHA-512/256)
  * 
  * @param string $plaintext
  * @param EncryptionKey $secretKey
  * @param boolean $raw Don't hex encode the output?
  * @return string
  */
 public static function encrypt(string $plaintext, EncryptionKey $secretKey, bool $raw = false) : string
 {
     $config = SymmetricConfig::getConfig(Halite::HALITE_VERSION, 'encrypt');
     // Generate a nonce and HKDF salt:
     $nonce = \Sodium\randombytes_buf(\Sodium\CRYPTO_SECRETBOX_NONCEBYTES);
     $salt = \Sodium\randombytes_buf($config->HKDF_SALT_LEN);
     // Split our keys according to the HKDF salt:
     list($eKey, $aKey) = self::splitKeys($secretKey, $salt, $config);
     // Encrypt our message with the encryption key:
     $xored = \Sodium\crypto_stream_xor($plaintext, $nonce, $eKey);
     \Sodium\memzero($eKey);
     // Calculate an authentication tag:
     $auth = self::calculateMAC(Halite::HALITE_VERSION . $salt . $nonce . $xored, $aKey);
     \Sodium\memzero($aKey);
     if (!$raw) {
         return \Sodium\bin2hex(Halite::HALITE_VERSION . $salt . $nonce . $xored . $auth);
     }
     return Halite::HALITE_VERSION . $salt . $nonce . $xored . $auth;
 }
예제 #2
0
 /**
  * Encrypt a message using the Halite encryption protocol
  * 
  * @param string $plaintext
  * @param Key $secretKey
  * @param boolean $raw Don't hex encode the output?
  * @return string
  */
 public static function encrypt($plaintext, Contract\CryptoKeyInterface $secretKey, $raw = false)
 {
     if ($secretKey->isAsymmetricKey()) {
         throw new CryptoAlert\InvalidKey('Expected a symmetric key, not an asymmetric key');
     }
     if (!$secretKey->isEncryptionKey()) {
         throw new CryptoAlert\InvalidKey('Encryption key expected');
     }
     $nonce = \Sodium\randombytes_buf(\Sodium\CRYPTO_SECRETBOX_NONCEBYTES);
     $salt = \Sodium\randombytes_buf(Config::HKDF_SALT_LEN);
     list($eKey, $aKey) = self::splitKeys($secretKey, $salt);
     $xored = \Sodium\crypto_stream_xor($plaintext, $nonce, $eKey);
     $auth = self::calculateMAC(Config::HALITE_VERSION . $salt . $nonce . $xored, $aKey);
     \Sodium\memzero($eKey);
     \Sodium\memzero($aKey);
     if (!$raw) {
         return \Sodium\bin2hex(Config::HALITE_VERSION . $salt . $nonce . $xored . $auth);
     }
     return Config::HALITE_VERSION . $salt . $nonce . $xored . $auth;
 }
예제 #3
0
 /**
  * Stream decryption - Do not call directly
  * 
  * @param resource $input
  * @param resource $output
  * @param Key $encKey
  * @param string $nonce
  * @param resource $mac (hash context)
  * @param &array $config
  * @throws FileAlert\AccessDenied
  */
 private static final function streamDecrypt($input, $output, \ParagonIE\Halite\Contract\CryptoKeyInterface $encKey, $nonce, $mac, array $config, array &$chunk_macs)
 {
     // Reset the stream pointer to the beginning of the ciphertext
     $start = \ftell($input);
     if (\fseek($input, -1 * $config['MAC_SIZE'], SEEK_END) === false) {
         throw new CryptoAlert\CannotPerformOperation('Stream error');
     }
     $cipher_end = \ftell($input) - 1;
     if (\fseek($input, $start, SEEK_SET) === false) {
         throw new CryptoAlert\CannotPerformOperation('Stream error');
     }
     $break = false;
     while (!$break) {
         $pos = \ftell($input);
         if ($pos === false) {
             throw new CryptoAlert\CannotPerformOperation('Stream error');
         }
         // Read the data from the input buffer
         if ($pos + $config['BUFFER'] >= $cipher_end) {
             $break = true;
             $read = self::readBytes($input, $cipher_end - $pos + 1);
         } else {
             $read = self::readBytes($input, $config['BUFFER']);
         }
         // Let's reculcualte the MAC of this chunk, then verify it
         \hash_update($mac, $read);
         $calcMAC = \hash_copy($mac);
         if ($calcMAC === false) {
             throw new CryptoAlert\CannotPerformOperation('An unknown error has occurred');
         }
         $calc = \hash_final($calcMAC, true);
         if (empty($chunk_macs)) {
             throw new CryptoAlert\InvalidMessage('Invalid message authentication code');
         } elseif (!\hash_equals(\array_shift($chunk_macs), $calc)) {
             throw new CryptoAlert\InvalidMessage('Invalid message authentication code');
         }
         $decrypted = \Sodium\crypto_stream_xor($read, $nonce, $encKey->get());
         $written = \fwrite($output, $decrypted);
         if ($written === false) {
             throw new FileAlert\AccessDenied('Could not write to the file');
         }
         \Sodium\increment($nonce);
     }
 }
예제 #4
0
파일: Crypto.php 프로젝트: tecnom1k3/halite
 /**
  * Encrypt a message using the Halite encryption protocol
  * 
  * @param string $plaintext
  * @param EncryptionKey $secretKey
  * @param boolean $raw Don't hex encode the output?
  * @return string
  */
 public static function encrypt($plaintext, Contract\KeyInterface $secretKey, $raw = false)
 {
     if (!$secretKey instanceof EncryptionKey) {
         throw new CryptoException\InvalidKey('Expected an instance of EncryptionKey');
     }
     $config = SymmetricConfig::getConfig(Halite::HALITE_VERSION, 'encrypt');
     $nonce = \Sodium\randombytes_buf(\Sodium\CRYPTO_SECRETBOX_NONCEBYTES);
     $salt = \Sodium\randombytes_buf($config->HKDF_SALT_LEN);
     list($eKey, $aKey) = self::splitKeys($secretKey, $salt, $config);
     $xored = \Sodium\crypto_stream_xor($plaintext, $nonce, $eKey);
     $auth = self::calculateMAC(Halite::HALITE_VERSION . $salt . $nonce . $xored, $aKey);
     \Sodium\memzero($eKey);
     \Sodium\memzero($aKey);
     if (!$raw) {
         return \Sodium\bin2hex(Halite::HALITE_VERSION . $salt . $nonce . $xored . $auth);
     }
     return Halite::HALITE_VERSION . $salt . $nonce . $xored . $auth;
 }
예제 #5
0
 /**
  * Stream decryption - Do not call directly
  *
  * @param ReadOnlyFile $input
  * @param MutableFile $output
  * @param Key $encKey
  * @param string $nonce
  * @param resource $mac (hash context)
  * @param Config $config
  * @return bool
  * @throws CryptoException\AccessDenied
  * @throws CryptoException\CannotPerformOperation
  * @throws CryptoException\FileModified
  * @throws CryptoException\InvalidKey
  * @throws CryptoException\InvalidMessage
  */
 private static final function streamDecrypt(ReadOnlyFile $input, MutableFile $output, EncryptionKey $encKey, string $nonce, $mac, Config $config, array &$chunk_macs) : bool
 {
     $start = $input->getPos();
     $cipher_end = $input->getSize() - $config->MAC_SIZE;
     // Begin the streaming decryption
     $input->reset($start);
     while ($input->remainingBytes() > $config->MAC_SIZE) {
         /**
          * Would a full BUFFER read put it past the end of the
          * ciphertext? If so, only return a portion of the file.
          */
         if ($input->getPos() + $config->BUFFER > $cipher_end) {
             $read = $input->readBytes($cipher_end - $input->getPos());
         } else {
             $read = $input->readBytes($config->BUFFER);
         }
         if ($config->USE_BLAKE2B) {
             \Sodium\crypto_generichash_update($mac, $read);
             $calcMAC = '' . $mac;
             $calc = \Sodium\crypto_generichash_final($calcMAC, $config->MAC_SIZE);
         } else {
             \hash_update($mac, $read);
             $calcMAC = \hash_copy($mac);
             if ($calcMAC === false) {
                 throw new CryptoException\CannotPerformOperation('An unknown error has occurred');
             }
             $calc = \hash_final($calcMAC, true);
         }
         if (empty($chunk_macs)) {
             throw new CryptoException\InvalidMessage('Invalid message authentication code');
         } else {
             $chkmac = \array_shift($chunk_macs);
             if (!\hash_equals($chkmac, $calc)) {
                 throw new CryptoException\InvalidMessage('Invalid message authentication code');
             }
         }
         $decrypted = \Sodium\crypto_stream_xor($read, $nonce, $encKey->getRawKeyMaterial());
         $output->writeBytes($decrypted);
         \Sodium\increment($nonce);
     }
     \Sodium\memzero($nonce);
     return true;
 }
예제 #6
0
 /**
  * Stream decryption - Do not call directly
  * 
  * @param ReadOnlyFile $input
  * @param MutableFile $output
  * @param Key $encKey
  * @param string $nonce
  * @param resource $mac (hash context)
  * @param Config $config
  * @throws FileAlert\AccessDenied
  */
 private static final function streamDecrypt(ReadOnlyFile $input, MutableFile $output, KeyInterface $encKey, $nonce, $mac, Config $config, array &$chunk_macs)
 {
     if (!$encKey instanceof EncryptionKey) {
         throw new \ParagonIE\Halite\Alerts\InvalidKey('Argument 3: Expected an instance of EncryptionKey');
     }
     $start = $input->getPos();
     $cipher_end = $input->getSize() - $config->MAC_SIZE;
     // Begin the streaming decryption
     $input->reset($start);
     while ($input->remainingBytes() > $config->MAC_SIZE) {
         if ($input->getPos() + $config->BUFFER > $cipher_end) {
             $read = $input->readBytes($cipher_end - $input->getPos());
         } else {
             $read = $input->readBytes($config->BUFFER);
         }
         \hash_update($mac, $read);
         $calcMAC = \hash_copy($mac);
         if ($calcMAC === false) {
             throw new CryptoException\CannotPerformOperation('An unknown error has occurred');
         }
         $calc = \hash_final($calcMAC, true);
         if (empty($chunk_macs)) {
             throw new CryptoException\InvalidMessage('Invalid message authentication code');
         } else {
             $chkmac = \array_shift($chunk_macs);
             if (!\hash_equals($chkmac, $calc)) {
                 throw new CryptoException\InvalidMessage('Invalid message authentication code');
             }
         }
         $decrypted = \Sodium\crypto_stream_xor($read, $nonce, $encKey->get());
         $output->writeBytes($decrypted);
         \Sodium\increment($nonce);
     }
     \Sodium\memzero($nonce);
     return true;
 }
예제 #7
0
파일: Crypto.php 프로젝트: paragonie/halite
 /**
  * Encrypt a message using the Halite encryption protocol
  *
  * (Encrypt then MAC -- xsalsa20 then keyed-Blake2b)
  * You don't need to worry about chosen-ciphertext attacks.
  *
  * @param HiddenString $plaintext
  * @param EncryptionKey $secretKey
  * @param mixed $encoding
  * @return string
  */
 public static function encrypt(HiddenString $plaintext, EncryptionKey $secretKey, $encoding = Halite::ENCODE_BASE64URLSAFE) : string
 {
     $config = SymmetricConfig::getConfig(Halite::HALITE_VERSION, 'encrypt');
     // Generate a nonce and HKDF salt:
     $nonce = \Sodium\randombytes_buf(\Sodium\CRYPTO_SECRETBOX_NONCEBYTES);
     $salt = \Sodium\randombytes_buf($config->HKDF_SALT_LEN);
     /* Split our key into two keys: One for encryption, the other for
                authentication. By using separate keys, we can reasonably dismiss
                likely cross-protocol attacks.
     
                This uses salted HKDF to split the keys, which is why we need the
                salt in the first place. */
     list($encKey, $authKey) = self::splitKeys($secretKey, $salt, $config);
     // Encrypt our message with the encryption key:
     $encrypted = \Sodium\crypto_stream_xor($plaintext->getString(), $nonce, $encKey);
     \Sodium\memzero($encKey);
     // Calculate an authentication tag:
     $auth = self::calculateMAC(Halite::HALITE_VERSION . $salt . $nonce . $encrypted, $authKey, $config);
     \Sodium\memzero($authKey);
     $message = Halite::HALITE_VERSION . $salt . $nonce . $encrypted . $auth;
     // Wipe every superfluous piece of data from memory
     \Sodium\memzero($nonce);
     \Sodium\memzero($salt);
     \Sodium\memzero($encrypted);
     \Sodium\memzero($auth);
     $encoder = Halite::chooseEncoder($encoding);
     if ($encoder) {
         return $encoder($message);
     }
     return $message;
 }
예제 #8
0
파일: File.php 프로젝트: paragonie/halite
 /**
  * Stream decryption - Do not call directly
  *
  * @param ReadOnlyFile $input
  * @param MutableFile $output
  * @param EncryptionKey $encKey
  * @param string $nonce
  * @param string $mac (hash context for BLAKE2b)
  * @param Config $config
  * @param array &$chunk_macs
  * @return bool
  * @throws FileAccessDenied
  * @throws CannotPerformOperation
  * @throws FileModified
  * @throws InvalidKey
  * @throws InvalidMessage
  */
 private static final function streamDecrypt(ReadOnlyFile $input, MutableFile $output, EncryptionKey $encKey, string $nonce, string $mac, Config $config, array &$chunk_macs) : bool
 {
     $start = $input->getPos();
     $cipher_end = $input->getSize() - $config->MAC_SIZE;
     // Begin the streaming decryption
     $input->reset($start);
     while ($input->remainingBytes() > $config->MAC_SIZE) {
         /**
          * Would a full BUFFER read put it past the end of the
          * ciphertext? If so, only return a portion of the file.
          */
         if ($input->getPos() + $config->BUFFER > $cipher_end) {
             $read = $input->readBytes($cipher_end - $input->getPos());
         } else {
             $read = $input->readBytes($config->BUFFER);
         }
         // Version 2+ uses a keyed BLAKE2b hash instead of HMAC
         \Sodium\crypto_generichash_update($mac, $read);
         $calcMAC = Util::safeStrcpy($mac);
         $calc = \Sodium\crypto_generichash_final($calcMAC, $config->MAC_SIZE);
         if (empty($chunk_macs)) {
             // Someone attempted to add a chunk at the end.
             throw new InvalidMessage('Invalid message authentication code');
         } else {
             $chunkMAC = \array_shift($chunk_macs);
             if (!\hash_equals($chunkMAC, $calc)) {
                 // This chunk was altered after the original MAC was verified
                 throw new InvalidMessage('Invalid message authentication code');
             }
         }
         // This is where the decryption actually occurs:
         $decrypted = \Sodium\crypto_stream_xor($read, $nonce, $encKey->getRawKeyMaterial());
         $output->writeBytes($decrypted);
         \Sodium\increment($nonce);
     }
     \Sodium\memzero($nonce);
     return true;
 }
예제 #9
0
 /**
  * Message encryption (secret-key)
  * 
  * @param string|resource $plaintext
  * @param EncryptionKey $key
  * 
  * @return string
  */
 public function encryptSymmetric($plaintext, EncryptionKey $key, array $options = []) : string
 {
     // Build our header:
     // [VV][VV]:
     $message = \chr(Common::VERSION_MAJOR);
     $message .= \chr(Common::VERSION_MAJOR);
     // [DD]:
     $message .= \chr(0x7f & self::DRIVER_ID);
     // [CC]:
     $message .= \chr(Common::VERSION_MAJOR ^ Common::VERSION_MINOR ^ 0x7f & self::DRIVER_ID);
     // Salt:
     $salt = \random_bytes(\Sodium\CRYPTO_GENERICHASH_KEYBYTES);
     // Split keys:
     list($encKey, $authKey) = $this->splitSymmetricKey($key, $salt);
     $message .= $salt;
     // HKDF salt
     // Nonce:
     $nonce = \random_bytes(\Sodium\CRYPTO_STREAM_NONCEBYTES);
     $message .= $nonce;
     // Nonce for the stream cipher
     // Encrypt:
     $message .= \Sodium\crypto_stream_xor($plaintext, $nonce, $encKey->getRawBytes());
     unset($encKey);
     // Authenticate:
     $message .= \Sodium\crypto_auth($message, $authKey->getRawBytes());
     unset($authKey);
     // Return:
     return $message;
 }