public static function unpackMessageForDecryption(string $ciphertext) : array
{
$length = CryptoUtil::safeStrlen($ciphertext);
// Fail fast on invalid messages
if ($length < Halite::VERSION_TAG_LEN) {
throw new InvalidMessage('Message is too short');
}
// The first 4 bytes are reserved for the version size
$version = CryptoUtil::safeSubstr($ciphertext, 0, Halite::VERSION_TAG_LEN);
$config = SymmetricConfig::getConfig($version, 'encrypt');
if ($length < $config->SHORTEST_CIPHERTEXT_LENGTH) {
throw new InvalidMessage('Message is too short');
}
// The salt is used for key splitting (via HKDF)
$salt = CryptoUtil::safeSubstr($ciphertext, Halite::VERSION_TAG_LEN, $config->HKDF_SALT_LEN);
// This is the nonce (we authenticated it):
$nonce = CryptoUtil::safeSubstr($ciphertext, Halite::VERSION_TAG_LEN + $config->HKDF_SALT_LEN, \Sodium\CRYPTO_STREAM_NONCEBYTES);
// This is the crypto_stream_xor()ed ciphertext
$encrypted = CryptoUtil::safeSubstr($ciphertext, Halite::VERSION_TAG_LEN + $config->HKDF_SALT_LEN + \Sodium\CRYPTO_STREAM_NONCEBYTES, $length - (Halite::VERSION_TAG_LEN + $config->HKDF_SALT_LEN + \Sodium\CRYPTO_STREAM_NONCEBYTES + $config->MAC_SIZE));
// $auth is the last 32 bytes
$auth = CryptoUtil::safeSubstr($ciphertext, $length - $config->MAC_SIZE);
// We don't need this anymore.
\Sodium\memzero($ciphertext);
// Now we return the pieces in a specific order:
return [$version, $config, $salt, $nonce, $encrypted, $auth];
}