Example #1
0
 /**
  * Split a key (using HKDF-BLAKE2b instead of HKDF-HMAC-*)
  * 
  * @param EncryptionKey $master
  * @param string $salt
  * @param BaseConfig $config
  * @return string[]
  */
 public static function splitKeys(EncryptionKey $master, string $salt = '', BaseConfig $config = null) : array
 {
     $binary = $master->getRawKeyMaterial();
     return [CryptoUtil::hkdfBlake2b($binary, \Sodium\CRYPTO_SECRETBOX_KEYBYTES, $config->HKDF_SBOX, $salt), CryptoUtil::hkdfBlake2b($binary, \Sodium\CRYPTO_AUTH_KEYBYTES, $config->HKDF_AUTH, $salt)];
 }
Example #2
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;
 }
Example #3
0
 /**
  * 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;
 }