/**
* Unseal a (file handle)
*
* @param $input
* @param $output
* @param \ParagonIE\Halite\Contract\CryptoKeyInterface $secretkey
*/
public static function unsealResource($input, $output, \ParagonIE\Halite\Contract\CryptoKeyInterface $secretkey)
{
// Input validation
if (!\is_resource($input)) {
throw new \ParagonIE\Halite\Alerts\InvalidType('Expected input handle to be a resource');
}
if (!\is_resource($output)) {
throw new \ParagonIE\Halite\Alerts\InvalidType('Expected output handle to be a resource');
}
if (!$secretkey->isSecretKey()) {
throw new CryptoException\InvalidKey('Expected a secret key');
}
if (!$secretkey->isAsymmetricKey()) {
throw new CryptoException\InvalidKey('Expected a key intended for asymmetric-key cryptography');
}
$secret_key = $secretkey->get();
$public_key = \Sodium\crypto_box_publickey_from_secretkey($secret_key);
// Parse the header, ensuring we get 4 bytes
$header = self::readBytes($input, Halite::VERSION_TAG_LEN);
// Load the config
$config = self::getConfig($header, 'seal');
// Let's grab the public key and salt
$eph_public = self::readBytes($input, $config['PUBLICKEY_BYTES']);
$hkdfsalt = self::readBytes($input, $config['HKDF_SALT_LEN']);
$nonce = \Sodium\crypto_generichash($eph_public . $public_key, null, \Sodium\CRYPTO_STREAM_NONCEBYTES);
$ephemeral = new Key($eph_public, true, false, true);
$key = Asymmetric::getSharedSecret($secretkey, $ephemeral, true);
list($encKey, $authKey) = self::splitKeys($key, $hkdfsalt);
// We no longer need the original key after we split it
unset($key);
$mac = \hash_init('sha256', HASH_HMAC, $authKey);
\hash_update($mac, $header);
\hash_update($mac, $eph_public);
\hash_update($mac, $hkdfsalt);
// This will throw an exception if it fails.
$old_macs = self::streamVerify($input, \hash_copy($mac), $config);
$ret = self::streamDecrypt($input, $output, new Key($encKey), $nonce, $mac, $config, $old_macs);
unset($encKey);
unset($authKey);
unset($nonce);
unset($mac);
unset($config);
unset($old_macs);
return $ret;
}