/** * 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 CryptoAlert\InvalidKey('Expected a secret key'); } if (!$secretkey->isAsymmetricKey()) { throw new CryptoAlert\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; }
/** * Decrypt a sealed message with our private key * * @param string $source Encrypted message (string or resource for a file) * @param Contract\CryptoKeyInterface $privateKey * @param boolean $raw Don't hex decode the input? * * @return string */ public static function unseal($source, Contract\CryptoKeyInterface $privateKey, $raw = false) { if (!$raw) { $source = \Sodium\hex2bin($source); } if ($privateKey->isSecretKey()) { if (function_exists('\\Sodium\\crypto_box_seal_open')) { // Get a box keypair (needed by crypto_box_seal_open) $secret_key = $privateKey->get(); $public_key = \Sodium\crypto_box_publickey_from_secretkey($secret_key); $kp = \Sodium\crypto_box_keypair_from_secretkey_and_publickey($secret_key, $public_key); // Now let's open that sealed box $message = \Sodium\crypto_box_seal_open($source, $kp); // Always memzero after retrieving a value \Sodium\memzero($secret_key); \Sodium\memzero($public_key); \Sodium\memzero($kp); } else { /** * Polyfill for libsodium < 1.0.3 */ // Let's generate the box keypair $my_secret = $privateKey->get(); $my_public = \Sodium\crypto_box_publickey_from_secretkey($my_secret); $eph_public = mb_substr($source, 0, \Sodium\CRYPTO_BOX_PUBLICKEYBYTES, '8bit'); $box_kp = \Sodium\crypto_box_keypair_from_secretkey_and_publickey($my_secret, $eph_public); // Calculate the nonce as libsodium does $nonce = \Sodium\crypto_generichash($eph_public . $my_public, null, \Sodium\CRYPTO_BOX_NONCEBYTES); // $boxed is the ciphertext from crypto_box_seal $boxed = mb_substr($source, \Sodium\CRYPTO_BOX_PUBLICKEYBYTES, null, '8bit'); $message = \Sodium\crypto_box_open($boxed, $nonce, $box_kp); \Sodium\memzero($my_secret); \Sodium\memzero($my_public); \Sodium\memzero($box_kp); \Sodium\memzero($nonce); \Sodium\memzero($eph_public); } if ($message === false) { throw new CryptoAlert\InvalidKey('Incorrect secret key'); } // We have our encrypted message here return $message; } throw new CryptoAlert\InvalidKey('Expected a secret key'); }
/** * Decrypt a sealed message with our private key * * @param string $source Encrypted message (string or resource for a file) * @param Contract\CryptoKeyInterface $privateKey * @param boolean $raw Don't hex decode the input? * * @return string */ public static function unseal($source, Contract\CryptoKeyInterface $privateKey, $raw = false) { if (!$raw) { $source = \Sodium\hex2bin($source); } if ($privateKey->isSecretKey()) { if (function_exists('\\Sodium\\crypto_box_seal_open')) { // Get a box keypair (needed by crypto_box_seal_open) $secret_key = $privateKey->get(); $public_key = \Sodium\crypto_box_publickey_from_secretkey($secret_key); $kp = \Sodium\crypto_box_keypair_from_secretkey_and_publickey($secret_key, $public_key); // Now let's open that sealed box $message = \Sodium\crypto_box_seal_open($source, $kp); // Always memzero after retrieving a value \Sodium\memzero($secret_key); \Sodium\memzero($public_key); \Sodium\memzero($kp); } else { throw new CryptoException\CannotPerformOperation('crypto_box_seal_open is not available'); } if ($message === false) { throw new CryptoException\InvalidKey('Incorrect secret key'); } // We have our encrypted message here return $message; } throw new CryptoException\InvalidKey('Expected a secret key'); }