public static function unseal(string $ciphertext, EncryptionSecretKey $privateKey, $encoding = Halite::ENCODE_BASE64URLSAFE) : HiddenString
{
$decoder = Halite::chooseEncoder($encoding, true);
if ($decoder) {
// We were given hex data:
try {
$ciphertext = $decoder($ciphertext);
} catch (\RangeException $ex) {
throw new InvalidMessage('Invalid character encoding');
}
}
// Get a box keypair (needed by crypto_box_seal_open)
$secret_key = $privateKey->getRawKeyMaterial();
$public_key = \Sodium\crypto_box_publickey_from_secretkey($secret_key);
$key_pair = \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($ciphertext, $key_pair);
// Always memzero after retrieving a value
\Sodium\memzero($key_pair);
if ($message === false) {
throw new InvalidKey('Incorrect secret key for this sealed message');
}
// We have our encrypted message here
return new HiddenString($message);
}
public function testSealFail() { $alice = KeyPair::generate(); $message = 'This is for your eyes only'; $sealed = Asymmetric::seal($message, $alice->getPublicKey(), true); // Let's flip one bit, randomly: $r = \Sodium\randombytes_uniform(\mb_strlen($sealed, '8bit')); $amt = 1 << \Sodium\randombytes_uniform(8); $sealed[$r] = \chr(\ord($sealed[$r]) ^ $amt); // This should throw an exception try { $opened = Asymmetric::unseal($sealed, $alice->getSecretKey(), true); $this->assertEquals($opened, $message); throw new Exception('ERROR: THIS SHOULD ALWAYS FAIL'); } catch (CryptoException\InvalidKey $e) { $this->assertTrue($e instanceof CryptoException\InvalidKey); } catch (CryptoException\InvalidMessage $e) { $this->assertTrue($e instanceof CryptoException\InvalidMessage); } }