/** * Unseal a (file handle) * * @param ReadOnlyFile $input * @param MutableFile $output * @param EncryptionSecretKey $secretkey */ public static function unsealStream(ReadOnlyFile $input, MutableFile $output, EncryptionSecretKey $secretkey) { if (!$secretkey instanceof EncryptionSecretKey) { throw new \ParagonIE\Halite\Alerts\InvalidKey('Argument 3: Expected an instance of EncryptionSecretKey'); } $secret_key = $secretkey->get(); $public_key = \Sodium\crypto_box_publickey_from_secretkey($secret_key); // Parse the header, ensuring we get 4 bytes $header = $input->readBytes(Halite::VERSION_TAG_LEN); // Load the config $config = self::getConfig($header, 'seal'); // Let's grab the public key and salt $eph_public = $input->readBytes($config->PUBLICKEY_BYTES); $hkdfsalt = $input->readBytes($config->HKDF_SALT_LEN); $nonce = \Sodium\crypto_generichash($eph_public . $public_key, null, \Sodium\CRYPTO_STREAM_NONCEBYTES); $ephemeral = new EncryptionPublicKey($eph_public); $key = AsymmetricCrypto::getSharedSecret($secretkey, $ephemeral, true); list($encKey, $authKey) = self::splitKeys($key, $hkdfsalt, $config); // 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 EncryptionKey($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 EncryptionSecretKey $privateKey * @param boolean $raw Don't hex decode the input? * @return string * @throws CryptoException\InvalidKey */ public static function unseal(string $source, EncryptionSecretKey $privateKey, bool $raw = false) : string { if (!$raw) { $source = \Sodium\hex2bin($source); } // Get a box keypair (needed by crypto_box_seal_open) $secret_key = $privateKey->getRawKeyMaterial(); $public_key = \Sodium\crypto_box_publickey_from_secretkey($secret_key); $kp = \Sodium\crypto_box_keypair_from_secretkey_and_publickey($secret_key, $public_key); // Wipe these immediately: \Sodium\memzero($secret_key); \Sodium\memzero($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($kp); if ($message === false) { throw new CryptoException\InvalidKey('Incorrect secret key for this sealed message'); } // We have our encrypted message here return $message; }
/** * Generate a new keypair * * @param int $type Key flags * @param &string $secret_key - Reference to optional variable to store secret key in * @return KeyPair * @throws CryptoException\InvalidKey */ public static function generate($type = Key::CRYPTO_BOX, &$secret_key = null) { if (Key::doesNotHaveFlag($type, Key::ASYMMETRIC)) { throw new CryptoException\InvalidKey('An asymmetric key type must be passed to KeyPair::generate()'); } if (Key::hasFlag($type, Key::ENCRYPTION)) { $key = EncryptionSecretKey::generate(Key::CRYPTO_BOX, $secret_key); $keypair = new EncryptionKeyPair(...$key); return $keypair; } throw new CryptoException\InvalidKey('Only encryption keys can be generated.'); }