1656 lines
54 KiB
PHP
1656 lines
54 KiB
PHP
|
<?php
|
||
|
|
||
|
if (class_exists('ParagonIE_Sodium_Crypto', false)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Class ParagonIE_Sodium_Crypto
|
||
|
*
|
||
|
* ATTENTION!
|
||
|
*
|
||
|
* If you are using this library, you should be using
|
||
|
* ParagonIE_Sodium_Compat in your code, not this class.
|
||
|
*/
|
||
|
abstract class ParagonIE_Sodium_Crypto
|
||
|
{
|
||
|
const aead_chacha20poly1305_KEYBYTES = 32;
|
||
|
const aead_chacha20poly1305_NSECBYTES = 0;
|
||
|
const aead_chacha20poly1305_NPUBBYTES = 8;
|
||
|
const aead_chacha20poly1305_ABYTES = 16;
|
||
|
|
||
|
const aead_chacha20poly1305_IETF_KEYBYTES = 32;
|
||
|
const aead_chacha20poly1305_IETF_NSECBYTES = 0;
|
||
|
const aead_chacha20poly1305_IETF_NPUBBYTES = 12;
|
||
|
const aead_chacha20poly1305_IETF_ABYTES = 16;
|
||
|
|
||
|
const aead_xchacha20poly1305_IETF_KEYBYTES = 32;
|
||
|
const aead_xchacha20poly1305_IETF_NSECBYTES = 0;
|
||
|
const aead_xchacha20poly1305_IETF_NPUBBYTES = 24;
|
||
|
const aead_xchacha20poly1305_IETF_ABYTES = 16;
|
||
|
|
||
|
const box_curve25519xsalsa20poly1305_SEEDBYTES = 32;
|
||
|
const box_curve25519xsalsa20poly1305_PUBLICKEYBYTES = 32;
|
||
|
const box_curve25519xsalsa20poly1305_SECRETKEYBYTES = 32;
|
||
|
const box_curve25519xsalsa20poly1305_BEFORENMBYTES = 32;
|
||
|
const box_curve25519xsalsa20poly1305_NONCEBYTES = 24;
|
||
|
const box_curve25519xsalsa20poly1305_MACBYTES = 16;
|
||
|
const box_curve25519xsalsa20poly1305_BOXZEROBYTES = 16;
|
||
|
const box_curve25519xsalsa20poly1305_ZEROBYTES = 32;
|
||
|
|
||
|
const onetimeauth_poly1305_BYTES = 16;
|
||
|
const onetimeauth_poly1305_KEYBYTES = 32;
|
||
|
|
||
|
const secretbox_xsalsa20poly1305_KEYBYTES = 32;
|
||
|
const secretbox_xsalsa20poly1305_NONCEBYTES = 24;
|
||
|
const secretbox_xsalsa20poly1305_MACBYTES = 16;
|
||
|
const secretbox_xsalsa20poly1305_BOXZEROBYTES = 16;
|
||
|
const secretbox_xsalsa20poly1305_ZEROBYTES = 32;
|
||
|
|
||
|
const secretbox_xchacha20poly1305_KEYBYTES = 32;
|
||
|
const secretbox_xchacha20poly1305_NONCEBYTES = 24;
|
||
|
const secretbox_xchacha20poly1305_MACBYTES = 16;
|
||
|
const secretbox_xchacha20poly1305_BOXZEROBYTES = 16;
|
||
|
const secretbox_xchacha20poly1305_ZEROBYTES = 32;
|
||
|
|
||
|
const stream_salsa20_KEYBYTES = 32;
|
||
|
|
||
|
/**
|
||
|
* AEAD Decryption with ChaCha20-Poly1305
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $message
|
||
|
* @param string $ad
|
||
|
* @param string $nonce
|
||
|
* @param string $key
|
||
|
* @return string
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function aead_chacha20poly1305_decrypt(
|
||
|
$message = '',
|
||
|
$ad = '',
|
||
|
$nonce = '',
|
||
|
$key = ''
|
||
|
) {
|
||
|
/** @var int $len - Length of message (ciphertext + MAC) */
|
||
|
$len = ParagonIE_Sodium_Core_Util::strlen($message);
|
||
|
|
||
|
/** @var int $clen - Length of ciphertext */
|
||
|
$clen = $len - self::aead_chacha20poly1305_ABYTES;
|
||
|
|
||
|
/** @var int $adlen - Length of associated data */
|
||
|
$adlen = ParagonIE_Sodium_Core_Util::strlen($ad);
|
||
|
|
||
|
/** @var string $mac - Message authentication code */
|
||
|
$mac = ParagonIE_Sodium_Core_Util::substr(
|
||
|
$message,
|
||
|
$clen,
|
||
|
self::aead_chacha20poly1305_ABYTES
|
||
|
);
|
||
|
|
||
|
/** @var string $ciphertext - The encrypted message (sans MAC) */
|
||
|
$ciphertext = ParagonIE_Sodium_Core_Util::substr($message, 0, $clen);
|
||
|
|
||
|
/** @var string The first block of the chacha20 keystream, used as a poly1305 key */
|
||
|
$block0 = ParagonIE_Sodium_Core_ChaCha20::stream(
|
||
|
32,
|
||
|
$nonce,
|
||
|
$key
|
||
|
);
|
||
|
|
||
|
/* Recalculate the Poly1305 authentication tag (MAC): */
|
||
|
$state = new ParagonIE_Sodium_Core_Poly1305_State($block0);
|
||
|
try {
|
||
|
ParagonIE_Sodium_Compat::memzero($block0);
|
||
|
} catch (SodiumException $ex) {
|
||
|
$block0 = null;
|
||
|
}
|
||
|
$state->update($ad);
|
||
|
$state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen));
|
||
|
$state->update($ciphertext);
|
||
|
$state->update(ParagonIE_Sodium_Core_Util::store64_le($clen));
|
||
|
$computed_mac = $state->finish();
|
||
|
|
||
|
/* Compare the given MAC with the recalculated MAC: */
|
||
|
if (!ParagonIE_Sodium_Core_Util::verify_16($computed_mac, $mac)) {
|
||
|
throw new SodiumException('Invalid MAC');
|
||
|
}
|
||
|
|
||
|
// Here, we know that the MAC is valid, so we decrypt and return the plaintext
|
||
|
return ParagonIE_Sodium_Core_ChaCha20::streamXorIc(
|
||
|
$ciphertext,
|
||
|
$nonce,
|
||
|
$key,
|
||
|
ParagonIE_Sodium_Core_Util::store64_le(1)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* AEAD Encryption with ChaCha20-Poly1305
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $message
|
||
|
* @param string $ad
|
||
|
* @param string $nonce
|
||
|
* @param string $key
|
||
|
* @return string
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function aead_chacha20poly1305_encrypt(
|
||
|
$message = '',
|
||
|
$ad = '',
|
||
|
$nonce = '',
|
||
|
$key = ''
|
||
|
) {
|
||
|
/** @var int $len - Length of the plaintext message */
|
||
|
$len = ParagonIE_Sodium_Core_Util::strlen($message);
|
||
|
|
||
|
/** @var int $adlen - Length of the associated data */
|
||
|
$adlen = ParagonIE_Sodium_Core_Util::strlen($ad);
|
||
|
|
||
|
/** @var string The first block of the chacha20 keystream, used as a poly1305 key */
|
||
|
$block0 = ParagonIE_Sodium_Core_ChaCha20::stream(
|
||
|
32,
|
||
|
$nonce,
|
||
|
$key
|
||
|
);
|
||
|
$state = new ParagonIE_Sodium_Core_Poly1305_State($block0);
|
||
|
try {
|
||
|
ParagonIE_Sodium_Compat::memzero($block0);
|
||
|
} catch (SodiumException $ex) {
|
||
|
$block0 = null;
|
||
|
}
|
||
|
|
||
|
/** @var string $ciphertext - Raw encrypted data */
|
||
|
$ciphertext = ParagonIE_Sodium_Core_ChaCha20::streamXorIc(
|
||
|
$message,
|
||
|
$nonce,
|
||
|
$key,
|
||
|
ParagonIE_Sodium_Core_Util::store64_le(1)
|
||
|
);
|
||
|
|
||
|
$state->update($ad);
|
||
|
$state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen));
|
||
|
$state->update($ciphertext);
|
||
|
$state->update(ParagonIE_Sodium_Core_Util::store64_le($len));
|
||
|
return $ciphertext . $state->finish();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $message
|
||
|
* @param string $ad
|
||
|
* @param string $nonce
|
||
|
* @param string $key
|
||
|
* @return string
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function aead_chacha20poly1305_ietf_decrypt(
|
||
|
$message = '',
|
||
|
$ad = '',
|
||
|
$nonce = '',
|
||
|
$key = ''
|
||
|
) {
|
||
|
/** @var int $adlen - Length of associated data */
|
||
|
$adlen = ParagonIE_Sodium_Core_Util::strlen($ad);
|
||
|
|
||
|
/** @var int $len - Length of message (ciphertext + MAC) */
|
||
|
$len = ParagonIE_Sodium_Core_Util::strlen($message);
|
||
|
|
||
|
/** @var int $clen - Length of ciphertext */
|
||
|
$clen = $len - self::aead_chacha20poly1305_IETF_ABYTES;
|
||
|
|
||
|
/** @var string The first block of the chacha20 keystream, used as a poly1305 key */
|
||
|
$block0 = ParagonIE_Sodium_Core_ChaCha20::ietfStream(
|
||
|
32,
|
||
|
$nonce,
|
||
|
$key
|
||
|
);
|
||
|
|
||
|
/** @var string $mac - Message authentication code */
|
||
|
$mac = ParagonIE_Sodium_Core_Util::substr(
|
||
|
$message,
|
||
|
$len - self::aead_chacha20poly1305_IETF_ABYTES,
|
||
|
self::aead_chacha20poly1305_IETF_ABYTES
|
||
|
);
|
||
|
|
||
|
/** @var string $ciphertext - The encrypted message (sans MAC) */
|
||
|
$ciphertext = ParagonIE_Sodium_Core_Util::substr(
|
||
|
$message,
|
||
|
0,
|
||
|
$len - self::aead_chacha20poly1305_IETF_ABYTES
|
||
|
);
|
||
|
|
||
|
/* Recalculate the Poly1305 authentication tag (MAC): */
|
||
|
$state = new ParagonIE_Sodium_Core_Poly1305_State($block0);
|
||
|
try {
|
||
|
ParagonIE_Sodium_Compat::memzero($block0);
|
||
|
} catch (SodiumException $ex) {
|
||
|
$block0 = null;
|
||
|
}
|
||
|
$state->update($ad);
|
||
|
$state->update(str_repeat("\x00", ((0x10 - $adlen) & 0xf)));
|
||
|
$state->update($ciphertext);
|
||
|
$state->update(str_repeat("\x00", (0x10 - $clen) & 0xf));
|
||
|
$state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen));
|
||
|
$state->update(ParagonIE_Sodium_Core_Util::store64_le($clen));
|
||
|
$computed_mac = $state->finish();
|
||
|
|
||
|
/* Compare the given MAC with the recalculated MAC: */
|
||
|
if (!ParagonIE_Sodium_Core_Util::verify_16($computed_mac, $mac)) {
|
||
|
throw new SodiumException('Invalid MAC');
|
||
|
}
|
||
|
|
||
|
// Here, we know that the MAC is valid, so we decrypt and return the plaintext
|
||
|
return ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
|
||
|
$ciphertext,
|
||
|
$nonce,
|
||
|
$key,
|
||
|
ParagonIE_Sodium_Core_Util::store64_le(1)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $message
|
||
|
* @param string $ad
|
||
|
* @param string $nonce
|
||
|
* @param string $key
|
||
|
* @return string
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function aead_chacha20poly1305_ietf_encrypt(
|
||
|
$message = '',
|
||
|
$ad = '',
|
||
|
$nonce = '',
|
||
|
$key = ''
|
||
|
) {
|
||
|
/** @var int $len - Length of the plaintext message */
|
||
|
$len = ParagonIE_Sodium_Core_Util::strlen($message);
|
||
|
|
||
|
/** @var int $adlen - Length of the associated data */
|
||
|
$adlen = ParagonIE_Sodium_Core_Util::strlen($ad);
|
||
|
|
||
|
/** @var string The first block of the chacha20 keystream, used as a poly1305 key */
|
||
|
$block0 = ParagonIE_Sodium_Core_ChaCha20::ietfStream(
|
||
|
32,
|
||
|
$nonce,
|
||
|
$key
|
||
|
);
|
||
|
$state = new ParagonIE_Sodium_Core_Poly1305_State($block0);
|
||
|
try {
|
||
|
ParagonIE_Sodium_Compat::memzero($block0);
|
||
|
} catch (SodiumException $ex) {
|
||
|
$block0 = null;
|
||
|
}
|
||
|
|
||
|
/** @var string $ciphertext - Raw encrypted data */
|
||
|
$ciphertext = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
|
||
|
$message,
|
||
|
$nonce,
|
||
|
$key,
|
||
|
ParagonIE_Sodium_Core_Util::store64_le(1)
|
||
|
);
|
||
|
|
||
|
$state->update($ad);
|
||
|
$state->update(str_repeat("\x00", ((0x10 - $adlen) & 0xf)));
|
||
|
$state->update($ciphertext);
|
||
|
$state->update(str_repeat("\x00", ((0x10 - $len) & 0xf)));
|
||
|
$state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen));
|
||
|
$state->update(ParagonIE_Sodium_Core_Util::store64_le($len));
|
||
|
return $ciphertext . $state->finish();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $message
|
||
|
* @param string $ad
|
||
|
* @param string $nonce
|
||
|
* @param string $key
|
||
|
* @return string
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function aead_xchacha20poly1305_ietf_decrypt(
|
||
|
$message = '',
|
||
|
$ad = '',
|
||
|
$nonce = '',
|
||
|
$key = ''
|
||
|
) {
|
||
|
$subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20(
|
||
|
ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16),
|
||
|
$key
|
||
|
);
|
||
|
$nonceLast = "\x00\x00\x00\x00" .
|
||
|
ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8);
|
||
|
|
||
|
return self::aead_chacha20poly1305_ietf_decrypt($message, $ad, $nonceLast, $subkey);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $message
|
||
|
* @param string $ad
|
||
|
* @param string $nonce
|
||
|
* @param string $key
|
||
|
* @return string
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function aead_xchacha20poly1305_ietf_encrypt(
|
||
|
$message = '',
|
||
|
$ad = '',
|
||
|
$nonce = '',
|
||
|
$key = ''
|
||
|
) {
|
||
|
$subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20(
|
||
|
ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16),
|
||
|
$key
|
||
|
);
|
||
|
$nonceLast = "\x00\x00\x00\x00" .
|
||
|
ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8);
|
||
|
|
||
|
return self::aead_chacha20poly1305_ietf_encrypt($message, $ad, $nonceLast, $subkey);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* HMAC-SHA-512-256 (a.k.a. the leftmost 256 bits of HMAC-SHA-512)
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $message
|
||
|
* @param string $key
|
||
|
* @return string
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function auth($message, $key)
|
||
|
{
|
||
|
return ParagonIE_Sodium_Core_Util::substr(
|
||
|
hash_hmac('sha512', $message, $key, true),
|
||
|
0,
|
||
|
32
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* HMAC-SHA-512-256 validation. Constant-time via hash_equals().
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $mac
|
||
|
* @param string $message
|
||
|
* @param string $key
|
||
|
* @return bool
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function auth_verify($mac, $message, $key)
|
||
|
{
|
||
|
return ParagonIE_Sodium_Core_Util::hashEquals(
|
||
|
$mac,
|
||
|
self::auth($message, $key)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* X25519 key exchange followed by XSalsa20Poly1305 symmetric encryption
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $plaintext
|
||
|
* @param string $nonce
|
||
|
* @param string $keypair
|
||
|
* @return string
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function box($plaintext, $nonce, $keypair)
|
||
|
{
|
||
|
$c = self::secretbox(
|
||
|
$plaintext,
|
||
|
$nonce,
|
||
|
self::box_beforenm(
|
||
|
self::box_secretkey($keypair),
|
||
|
self::box_publickey($keypair)
|
||
|
)
|
||
|
);
|
||
|
return $c;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* X25519-XSalsa20-Poly1305 with one ephemeral X25519 keypair.
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $message
|
||
|
* @param string $publicKey
|
||
|
* @return string
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function box_seal($message, $publicKey)
|
||
|
{
|
||
|
/** @var string $ephemeralKeypair */
|
||
|
$ephemeralKeypair = self::box_keypair();
|
||
|
|
||
|
/** @var string $ephemeralSK */
|
||
|
$ephemeralSK = self::box_secretkey($ephemeralKeypair);
|
||
|
|
||
|
/** @var string $ephemeralPK */
|
||
|
$ephemeralPK = self::box_publickey($ephemeralKeypair);
|
||
|
|
||
|
/** @var string $nonce */
|
||
|
$nonce = self::generichash(
|
||
|
$ephemeralPK . $publicKey,
|
||
|
'',
|
||
|
24
|
||
|
);
|
||
|
|
||
|
/** @var string $keypair - The combined keypair used in crypto_box() */
|
||
|
$keypair = self::box_keypair_from_secretkey_and_publickey($ephemeralSK, $publicKey);
|
||
|
|
||
|
/** @var string $ciphertext Ciphertext + MAC from crypto_box */
|
||
|
$ciphertext = self::box($message, $nonce, $keypair);
|
||
|
try {
|
||
|
ParagonIE_Sodium_Compat::memzero($ephemeralKeypair);
|
||
|
ParagonIE_Sodium_Compat::memzero($ephemeralSK);
|
||
|
ParagonIE_Sodium_Compat::memzero($nonce);
|
||
|
} catch (SodiumException $ex) {
|
||
|
$ephemeralKeypair = null;
|
||
|
$ephemeralSK = null;
|
||
|
$nonce = null;
|
||
|
}
|
||
|
return $ephemeralPK . $ciphertext;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Opens a message encrypted via box_seal().
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $message
|
||
|
* @param string $keypair
|
||
|
* @return string
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function box_seal_open($message, $keypair)
|
||
|
{
|
||
|
/** @var string $ephemeralPK */
|
||
|
$ephemeralPK = ParagonIE_Sodium_Core_Util::substr($message, 0, 32);
|
||
|
|
||
|
/** @var string $ciphertext (ciphertext + MAC) */
|
||
|
$ciphertext = ParagonIE_Sodium_Core_Util::substr($message, 32);
|
||
|
|
||
|
/** @var string $secretKey */
|
||
|
$secretKey = self::box_secretkey($keypair);
|
||
|
|
||
|
/** @var string $publicKey */
|
||
|
$publicKey = self::box_publickey($keypair);
|
||
|
|
||
|
/** @var string $nonce */
|
||
|
$nonce = self::generichash(
|
||
|
$ephemeralPK . $publicKey,
|
||
|
'',
|
||
|
24
|
||
|
);
|
||
|
|
||
|
/** @var string $keypair */
|
||
|
$keypair = self::box_keypair_from_secretkey_and_publickey($secretKey, $ephemeralPK);
|
||
|
|
||
|
/** @var string $m */
|
||
|
$m = self::box_open($ciphertext, $nonce, $keypair);
|
||
|
try {
|
||
|
ParagonIE_Sodium_Compat::memzero($secretKey);
|
||
|
ParagonIE_Sodium_Compat::memzero($ephemeralPK);
|
||
|
ParagonIE_Sodium_Compat::memzero($nonce);
|
||
|
} catch (SodiumException $ex) {
|
||
|
$secretKey = null;
|
||
|
$ephemeralPK = null;
|
||
|
$nonce = null;
|
||
|
}
|
||
|
return $m;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Used by crypto_box() to get the crypto_secretbox() key.
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $sk
|
||
|
* @param string $pk
|
||
|
* @return string
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function box_beforenm($sk, $pk)
|
||
|
{
|
||
|
return ParagonIE_Sodium_Core_HSalsa20::hsalsa20(
|
||
|
str_repeat("\x00", 16),
|
||
|
self::scalarmult($sk, $pk)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @return string
|
||
|
* @throws Exception
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function box_keypair()
|
||
|
{
|
||
|
$sKey = random_bytes(32);
|
||
|
$pKey = self::scalarmult_base($sKey);
|
||
|
return $sKey . $pKey;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $seed
|
||
|
* @return string
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function box_seed_keypair($seed)
|
||
|
{
|
||
|
$sKey = ParagonIE_Sodium_Core_Util::substr(
|
||
|
hash('sha512', $seed, true),
|
||
|
0,
|
||
|
32
|
||
|
);
|
||
|
$pKey = self::scalarmult_base($sKey);
|
||
|
return $sKey . $pKey;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $sKey
|
||
|
* @param string $pKey
|
||
|
* @return string
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function box_keypair_from_secretkey_and_publickey($sKey, $pKey)
|
||
|
{
|
||
|
return ParagonIE_Sodium_Core_Util::substr($sKey, 0, 32) .
|
||
|
ParagonIE_Sodium_Core_Util::substr($pKey, 0, 32);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $keypair
|
||
|
* @return string
|
||
|
* @throws RangeException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function box_secretkey($keypair)
|
||
|
{
|
||
|
if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== 64) {
|
||
|
throw new RangeException(
|
||
|
'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.'
|
||
|
);
|
||
|
}
|
||
|
return ParagonIE_Sodium_Core_Util::substr($keypair, 0, 32);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $keypair
|
||
|
* @return string
|
||
|
* @throws RangeException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function box_publickey($keypair)
|
||
|
{
|
||
|
if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) {
|
||
|
throw new RangeException(
|
||
|
'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.'
|
||
|
);
|
||
|
}
|
||
|
return ParagonIE_Sodium_Core_Util::substr($keypair, 32, 32);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $sKey
|
||
|
* @return string
|
||
|
* @throws RangeException
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function box_publickey_from_secretkey($sKey)
|
||
|
{
|
||
|
if (ParagonIE_Sodium_Core_Util::strlen($sKey) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES) {
|
||
|
throw new RangeException(
|
||
|
'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES bytes long.'
|
||
|
);
|
||
|
}
|
||
|
return self::scalarmult_base($sKey);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Decrypt a message encrypted with box().
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $ciphertext
|
||
|
* @param string $nonce
|
||
|
* @param string $keypair
|
||
|
* @return string
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function box_open($ciphertext, $nonce, $keypair)
|
||
|
{
|
||
|
return self::secretbox_open(
|
||
|
$ciphertext,
|
||
|
$nonce,
|
||
|
self::box_beforenm(
|
||
|
self::box_secretkey($keypair),
|
||
|
self::box_publickey($keypair)
|
||
|
)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Calculate a BLAKE2b hash.
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $message
|
||
|
* @param string|null $key
|
||
|
* @param int $outlen
|
||
|
* @return string
|
||
|
* @throws RangeException
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function generichash($message, $key = '', $outlen = 32)
|
||
|
{
|
||
|
// This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized
|
||
|
ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor();
|
||
|
|
||
|
$k = null;
|
||
|
if (!empty($key)) {
|
||
|
/** @var SplFixedArray $k */
|
||
|
$k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key);
|
||
|
if ($k->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) {
|
||
|
throw new RangeException('Invalid key size');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** @var SplFixedArray $in */
|
||
|
$in = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($message);
|
||
|
|
||
|
/** @var SplFixedArray $ctx */
|
||
|
$ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outlen);
|
||
|
ParagonIE_Sodium_Core_BLAKE2b::update($ctx, $in, $in->count());
|
||
|
|
||
|
/** @var SplFixedArray $out */
|
||
|
$out = new SplFixedArray($outlen);
|
||
|
$out = ParagonIE_Sodium_Core_BLAKE2b::finish($ctx, $out);
|
||
|
|
||
|
/** @var array<int, int> */
|
||
|
$outArray = $out->toArray();
|
||
|
return ParagonIE_Sodium_Core_Util::intArrayToString($outArray);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Finalize a BLAKE2b hashing context, returning the hash.
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $ctx
|
||
|
* @param int $outlen
|
||
|
* @return string
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function generichash_final($ctx, $outlen = 32)
|
||
|
{
|
||
|
if (!is_string($ctx)) {
|
||
|
throw new TypeError('Context must be a string');
|
||
|
}
|
||
|
$out = new SplFixedArray($outlen);
|
||
|
|
||
|
/** @var SplFixedArray $context */
|
||
|
$context = ParagonIE_Sodium_Core_BLAKE2b::stringToContext($ctx);
|
||
|
|
||
|
/** @var SplFixedArray $out */
|
||
|
$out = ParagonIE_Sodium_Core_BLAKE2b::finish($context, $out);
|
||
|
|
||
|
/** @var array<int, int> */
|
||
|
$outArray = $out->toArray();
|
||
|
return ParagonIE_Sodium_Core_Util::intArrayToString($outArray);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initialize a hashing context for BLAKE2b.
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $key
|
||
|
* @param int $outputLength
|
||
|
* @return string
|
||
|
* @throws RangeException
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function generichash_init($key = '', $outputLength = 32)
|
||
|
{
|
||
|
// This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized
|
||
|
ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor();
|
||
|
|
||
|
$k = null;
|
||
|
if (!empty($key)) {
|
||
|
$k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key);
|
||
|
if ($k->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) {
|
||
|
throw new RangeException('Invalid key size');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** @var SplFixedArray $ctx */
|
||
|
$ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outputLength);
|
||
|
|
||
|
return ParagonIE_Sodium_Core_BLAKE2b::contextToString($ctx);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initialize a hashing context for BLAKE2b.
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $key
|
||
|
* @param int $outputLength
|
||
|
* @param string $salt
|
||
|
* @param string $personal
|
||
|
* @return string
|
||
|
* @throws RangeException
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function generichash_init_salt_personal(
|
||
|
$key = '',
|
||
|
$outputLength = 32,
|
||
|
$salt = '',
|
||
|
$personal = ''
|
||
|
) {
|
||
|
// This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized
|
||
|
ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor();
|
||
|
|
||
|
$k = null;
|
||
|
if (!empty($key)) {
|
||
|
$k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key);
|
||
|
if ($k->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) {
|
||
|
throw new RangeException('Invalid key size');
|
||
|
}
|
||
|
}
|
||
|
if (!empty($salt)) {
|
||
|
$s = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($salt);
|
||
|
} else {
|
||
|
$s = null;
|
||
|
}
|
||
|
if (!empty($salt)) {
|
||
|
$p = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($personal);
|
||
|
} else {
|
||
|
$p = null;
|
||
|
}
|
||
|
|
||
|
/** @var SplFixedArray $ctx */
|
||
|
$ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outputLength, $s, $p);
|
||
|
|
||
|
return ParagonIE_Sodium_Core_BLAKE2b::contextToString($ctx);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Update a hashing context for BLAKE2b with $message
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $ctx
|
||
|
* @param string $message
|
||
|
* @return string
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function generichash_update($ctx, $message)
|
||
|
{
|
||
|
// This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized
|
||
|
ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor();
|
||
|
|
||
|
/** @var SplFixedArray $context */
|
||
|
$context = ParagonIE_Sodium_Core_BLAKE2b::stringToContext($ctx);
|
||
|
|
||
|
/** @var SplFixedArray $in */
|
||
|
$in = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($message);
|
||
|
|
||
|
ParagonIE_Sodium_Core_BLAKE2b::update($context, $in, $in->count());
|
||
|
|
||
|
return ParagonIE_Sodium_Core_BLAKE2b::contextToString($context);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Libsodium's crypto_kx().
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $my_sk
|
||
|
* @param string $their_pk
|
||
|
* @param string $client_pk
|
||
|
* @param string $server_pk
|
||
|
* @return string
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function keyExchange($my_sk, $their_pk, $client_pk, $server_pk)
|
||
|
{
|
||
|
return ParagonIE_Sodium_Compat::crypto_generichash(
|
||
|
ParagonIE_Sodium_Compat::crypto_scalarmult($my_sk, $their_pk) .
|
||
|
$client_pk .
|
||
|
$server_pk
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ECDH over Curve25519
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $sKey
|
||
|
* @param string $pKey
|
||
|
* @return string
|
||
|
*
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function scalarmult($sKey, $pKey)
|
||
|
{
|
||
|
$q = ParagonIE_Sodium_Core_X25519::crypto_scalarmult_curve25519_ref10($sKey, $pKey);
|
||
|
self::scalarmult_throw_if_zero($q);
|
||
|
return $q;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ECDH over Curve25519, using the basepoint.
|
||
|
* Used to get a secret key from a public key.
|
||
|
*
|
||
|
* @param string $secret
|
||
|
* @return string
|
||
|
*
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function scalarmult_base($secret)
|
||
|
{
|
||
|
$q = ParagonIE_Sodium_Core_X25519::crypto_scalarmult_curve25519_ref10_base($secret);
|
||
|
self::scalarmult_throw_if_zero($q);
|
||
|
return $q;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* This throws an Error if a zero public key was passed to the function.
|
||
|
*
|
||
|
* @param string $q
|
||
|
* @return void
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
protected static function scalarmult_throw_if_zero($q)
|
||
|
{
|
||
|
$d = 0;
|
||
|
for ($i = 0; $i < self::box_curve25519xsalsa20poly1305_SECRETKEYBYTES; ++$i) {
|
||
|
$d |= ParagonIE_Sodium_Core_Util::chrToInt($q[$i]);
|
||
|
}
|
||
|
|
||
|
/* branch-free variant of === 0 */
|
||
|
if (-(1 & (($d - 1) >> 8))) {
|
||
|
throw new SodiumException('Zero public key is not allowed');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* XSalsa20-Poly1305 authenticated symmetric-key encryption.
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $plaintext
|
||
|
* @param string $nonce
|
||
|
* @param string $key
|
||
|
* @return string
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function secretbox($plaintext, $nonce, $key)
|
||
|
{
|
||
|
/** @var string $subkey */
|
||
|
$subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key);
|
||
|
|
||
|
/** @var string $block0 */
|
||
|
$block0 = str_repeat("\x00", 32);
|
||
|
|
||
|
/** @var int $mlen - Length of the plaintext message */
|
||
|
$mlen = ParagonIE_Sodium_Core_Util::strlen($plaintext);
|
||
|
$mlen0 = $mlen;
|
||
|
if ($mlen0 > 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES) {
|
||
|
$mlen0 = 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES;
|
||
|
}
|
||
|
$block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0);
|
||
|
|
||
|
/** @var string $block0 */
|
||
|
$block0 = ParagonIE_Sodium_Core_Salsa20::salsa20_xor(
|
||
|
$block0,
|
||
|
ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
|
||
|
$subkey
|
||
|
);
|
||
|
|
||
|
/** @var string $c */
|
||
|
$c = ParagonIE_Sodium_Core_Util::substr(
|
||
|
$block0,
|
||
|
self::secretbox_xsalsa20poly1305_ZEROBYTES
|
||
|
);
|
||
|
if ($mlen > $mlen0) {
|
||
|
$c .= ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic(
|
||
|
ParagonIE_Sodium_Core_Util::substr(
|
||
|
$plaintext,
|
||
|
self::secretbox_xsalsa20poly1305_ZEROBYTES
|
||
|
),
|
||
|
ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
|
||
|
1,
|
||
|
$subkey
|
||
|
);
|
||
|
}
|
||
|
$state = new ParagonIE_Sodium_Core_Poly1305_State(
|
||
|
ParagonIE_Sodium_Core_Util::substr(
|
||
|
$block0,
|
||
|
0,
|
||
|
self::onetimeauth_poly1305_KEYBYTES
|
||
|
)
|
||
|
);
|
||
|
try {
|
||
|
ParagonIE_Sodium_Compat::memzero($block0);
|
||
|
ParagonIE_Sodium_Compat::memzero($subkey);
|
||
|
} catch (SodiumException $ex) {
|
||
|
$block0 = null;
|
||
|
$subkey = null;
|
||
|
}
|
||
|
|
||
|
$state->update($c);
|
||
|
|
||
|
/** @var string $c - MAC || ciphertext */
|
||
|
$c = $state->finish() . $c;
|
||
|
unset($state);
|
||
|
|
||
|
return $c;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Decrypt a ciphertext generated via secretbox().
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $ciphertext
|
||
|
* @param string $nonce
|
||
|
* @param string $key
|
||
|
* @return string
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function secretbox_open($ciphertext, $nonce, $key)
|
||
|
{
|
||
|
/** @var string $mac */
|
||
|
$mac = ParagonIE_Sodium_Core_Util::substr(
|
||
|
$ciphertext,
|
||
|
0,
|
||
|
self::secretbox_xsalsa20poly1305_MACBYTES
|
||
|
);
|
||
|
|
||
|
/** @var string $c */
|
||
|
$c = ParagonIE_Sodium_Core_Util::substr(
|
||
|
$ciphertext,
|
||
|
self::secretbox_xsalsa20poly1305_MACBYTES
|
||
|
);
|
||
|
|
||
|
/** @var int $clen */
|
||
|
$clen = ParagonIE_Sodium_Core_Util::strlen($c);
|
||
|
|
||
|
/** @var string $subkey */
|
||
|
$subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key);
|
||
|
|
||
|
/** @var string $block0 */
|
||
|
$block0 = ParagonIE_Sodium_Core_Salsa20::salsa20(
|
||
|
64,
|
||
|
ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
|
||
|
$subkey
|
||
|
);
|
||
|
$verified = ParagonIE_Sodium_Core_Poly1305::onetimeauth_verify(
|
||
|
$mac,
|
||
|
$c,
|
||
|
ParagonIE_Sodium_Core_Util::substr($block0, 0, 32)
|
||
|
);
|
||
|
if (!$verified) {
|
||
|
try {
|
||
|
ParagonIE_Sodium_Compat::memzero($subkey);
|
||
|
} catch (SodiumException $ex) {
|
||
|
$subkey = null;
|
||
|
}
|
||
|
throw new SodiumException('Invalid MAC');
|
||
|
}
|
||
|
|
||
|
/** @var string $m - Decrypted message */
|
||
|
$m = ParagonIE_Sodium_Core_Util::xorStrings(
|
||
|
ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xsalsa20poly1305_ZEROBYTES),
|
||
|
ParagonIE_Sodium_Core_Util::substr($c, 0, self::secretbox_xsalsa20poly1305_ZEROBYTES)
|
||
|
);
|
||
|
if ($clen > self::secretbox_xsalsa20poly1305_ZEROBYTES) {
|
||
|
// We had more than 1 block, so let's continue to decrypt the rest.
|
||
|
$m .= ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic(
|
||
|
ParagonIE_Sodium_Core_Util::substr(
|
||
|
$c,
|
||
|
self::secretbox_xsalsa20poly1305_ZEROBYTES
|
||
|
),
|
||
|
ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
|
||
|
1,
|
||
|
(string) $subkey
|
||
|
);
|
||
|
}
|
||
|
return $m;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* XChaCha20-Poly1305 authenticated symmetric-key encryption.
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $plaintext
|
||
|
* @param string $nonce
|
||
|
* @param string $key
|
||
|
* @return string
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function secretbox_xchacha20poly1305($plaintext, $nonce, $key)
|
||
|
{
|
||
|
/** @var string $subkey */
|
||
|
$subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20(
|
||
|
ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16),
|
||
|
$key
|
||
|
);
|
||
|
$nonceLast = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8);
|
||
|
|
||
|
/** @var string $block0 */
|
||
|
$block0 = str_repeat("\x00", 32);
|
||
|
|
||
|
/** @var int $mlen - Length of the plaintext message */
|
||
|
$mlen = ParagonIE_Sodium_Core_Util::strlen($plaintext);
|
||
|
$mlen0 = $mlen;
|
||
|
if ($mlen0 > 64 - self::secretbox_xchacha20poly1305_ZEROBYTES) {
|
||
|
$mlen0 = 64 - self::secretbox_xchacha20poly1305_ZEROBYTES;
|
||
|
}
|
||
|
$block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0);
|
||
|
|
||
|
/** @var string $block0 */
|
||
|
$block0 = ParagonIE_Sodium_Core_ChaCha20::streamXorIc(
|
||
|
$block0,
|
||
|
$nonceLast,
|
||
|
$subkey
|
||
|
);
|
||
|
|
||
|
/** @var string $c */
|
||
|
$c = ParagonIE_Sodium_Core_Util::substr(
|
||
|
$block0,
|
||
|
self::secretbox_xchacha20poly1305_ZEROBYTES
|
||
|
);
|
||
|
if ($mlen > $mlen0) {
|
||
|
$c .= ParagonIE_Sodium_Core_ChaCha20::streamXorIc(
|
||
|
ParagonIE_Sodium_Core_Util::substr(
|
||
|
$plaintext,
|
||
|
self::secretbox_xchacha20poly1305_ZEROBYTES
|
||
|
),
|
||
|
$nonceLast,
|
||
|
$subkey,
|
||
|
ParagonIE_Sodium_Core_Util::store64_le(1)
|
||
|
);
|
||
|
}
|
||
|
$state = new ParagonIE_Sodium_Core_Poly1305_State(
|
||
|
ParagonIE_Sodium_Core_Util::substr(
|
||
|
$block0,
|
||
|
0,
|
||
|
self::onetimeauth_poly1305_KEYBYTES
|
||
|
)
|
||
|
);
|
||
|
try {
|
||
|
ParagonIE_Sodium_Compat::memzero($block0);
|
||
|
ParagonIE_Sodium_Compat::memzero($subkey);
|
||
|
} catch (SodiumException $ex) {
|
||
|
$block0 = null;
|
||
|
$subkey = null;
|
||
|
}
|
||
|
|
||
|
$state->update($c);
|
||
|
|
||
|
/** @var string $c - MAC || ciphertext */
|
||
|
$c = $state->finish() . $c;
|
||
|
unset($state);
|
||
|
|
||
|
return $c;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Decrypt a ciphertext generated via secretbox_xchacha20poly1305().
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $ciphertext
|
||
|
* @param string $nonce
|
||
|
* @param string $key
|
||
|
* @return string
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key)
|
||
|
{
|
||
|
/** @var string $mac */
|
||
|
$mac = ParagonIE_Sodium_Core_Util::substr(
|
||
|
$ciphertext,
|
||
|
0,
|
||
|
self::secretbox_xchacha20poly1305_MACBYTES
|
||
|
);
|
||
|
|
||
|
/** @var string $c */
|
||
|
$c = ParagonIE_Sodium_Core_Util::substr(
|
||
|
$ciphertext,
|
||
|
self::secretbox_xchacha20poly1305_MACBYTES
|
||
|
);
|
||
|
|
||
|
/** @var int $clen */
|
||
|
$clen = ParagonIE_Sodium_Core_Util::strlen($c);
|
||
|
|
||
|
/** @var string $subkey */
|
||
|
$subkey = ParagonIE_Sodium_Core_HChaCha20::hchacha20($nonce, $key);
|
||
|
|
||
|
/** @var string $block0 */
|
||
|
$block0 = ParagonIE_Sodium_Core_ChaCha20::stream(
|
||
|
64,
|
||
|
ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
|
||
|
$subkey
|
||
|
);
|
||
|
$verified = ParagonIE_Sodium_Core_Poly1305::onetimeauth_verify(
|
||
|
$mac,
|
||
|
$c,
|
||
|
ParagonIE_Sodium_Core_Util::substr($block0, 0, 32)
|
||
|
);
|
||
|
|
||
|
if (!$verified) {
|
||
|
try {
|
||
|
ParagonIE_Sodium_Compat::memzero($subkey);
|
||
|
} catch (SodiumException $ex) {
|
||
|
$subkey = null;
|
||
|
}
|
||
|
throw new SodiumException('Invalid MAC');
|
||
|
}
|
||
|
|
||
|
/** @var string $m - Decrypted message */
|
||
|
$m = ParagonIE_Sodium_Core_Util::xorStrings(
|
||
|
ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xchacha20poly1305_ZEROBYTES),
|
||
|
ParagonIE_Sodium_Core_Util::substr($c, 0, self::secretbox_xchacha20poly1305_ZEROBYTES)
|
||
|
);
|
||
|
|
||
|
if ($clen > self::secretbox_xchacha20poly1305_ZEROBYTES) {
|
||
|
// We had more than 1 block, so let's continue to decrypt the rest.
|
||
|
$m .= ParagonIE_Sodium_Core_ChaCha20::streamXorIc(
|
||
|
ParagonIE_Sodium_Core_Util::substr(
|
||
|
$c,
|
||
|
self::secretbox_xchacha20poly1305_ZEROBYTES
|
||
|
),
|
||
|
ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
|
||
|
(string) $subkey,
|
||
|
ParagonIE_Sodium_Core_Util::store64_le(1)
|
||
|
);
|
||
|
}
|
||
|
return $m;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $key
|
||
|
* @return array<int, string> Returns a state and a header.
|
||
|
* @throws Exception
|
||
|
* @throws SodiumException
|
||
|
*/
|
||
|
public static function secretstream_xchacha20poly1305_init_push($key)
|
||
|
{
|
||
|
# randombytes_buf(out, crypto_secretstream_xchacha20poly1305_HEADERBYTES);
|
||
|
$out = random_bytes(24);
|
||
|
|
||
|
# crypto_core_hchacha20(state->k, out, k, NULL);
|
||
|
$subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20($out, $key);
|
||
|
$state = new ParagonIE_Sodium_Core_SecretStream_State(
|
||
|
$subkey,
|
||
|
ParagonIE_Sodium_Core_Util::substr($out, 16, 8) . str_repeat("\0", 4)
|
||
|
);
|
||
|
|
||
|
# _crypto_secretstream_xchacha20poly1305_counter_reset(state);
|
||
|
$state->counterReset();
|
||
|
|
||
|
# memcpy(STATE_INONCE(state), out + crypto_core_hchacha20_INPUTBYTES,
|
||
|
# crypto_secretstream_xchacha20poly1305_INONCEBYTES);
|
||
|
# memset(state->_pad, 0, sizeof state->_pad);
|
||
|
return array(
|
||
|
$state->toString(),
|
||
|
$out
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $key
|
||
|
* @param string $header
|
||
|
* @return string Returns a state.
|
||
|
* @throws Exception
|
||
|
*/
|
||
|
public static function secretstream_xchacha20poly1305_init_pull($key, $header)
|
||
|
{
|
||
|
# crypto_core_hchacha20(state->k, in, k, NULL);
|
||
|
$subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20(
|
||
|
ParagonIE_Sodium_Core_Util::substr($header, 0, 16),
|
||
|
$key
|
||
|
);
|
||
|
$state = new ParagonIE_Sodium_Core_SecretStream_State(
|
||
|
$subkey,
|
||
|
ParagonIE_Sodium_Core_Util::substr($header, 16)
|
||
|
);
|
||
|
$state->counterReset();
|
||
|
# memcpy(STATE_INONCE(state), in + crypto_core_hchacha20_INPUTBYTES,
|
||
|
# crypto_secretstream_xchacha20poly1305_INONCEBYTES);
|
||
|
# memset(state->_pad, 0, sizeof state->_pad);
|
||
|
# return 0;
|
||
|
return $state->toString();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $state
|
||
|
* @param string $msg
|
||
|
* @param string $aad
|
||
|
* @param int $tag
|
||
|
* @return string
|
||
|
* @throws SodiumException
|
||
|
*/
|
||
|
public static function secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0)
|
||
|
{
|
||
|
$st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state);
|
||
|
# crypto_onetimeauth_poly1305_state poly1305_state;
|
||
|
# unsigned char block[64U];
|
||
|
# unsigned char slen[8U];
|
||
|
# unsigned char *c;
|
||
|
# unsigned char *mac;
|
||
|
|
||
|
$msglen = ParagonIE_Sodium_Core_Util::strlen($msg);
|
||
|
$aadlen = ParagonIE_Sodium_Core_Util::strlen($aad);
|
||
|
|
||
|
if ((($msglen + 63) >> 6) > 0xfffffffe) {
|
||
|
throw new SodiumException(
|
||
|
'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes'
|
||
|
);
|
||
|
}
|
||
|
|
||
|
# if (outlen_p != NULL) {
|
||
|
# *outlen_p = 0U;
|
||
|
# }
|
||
|
# if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {
|
||
|
# sodium_misuse();
|
||
|
# }
|
||
|
|
||
|
# crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
|
||
|
# crypto_onetimeauth_poly1305_init(&poly1305_state, block);
|
||
|
# sodium_memzero(block, sizeof block);
|
||
|
$auth = new ParagonIE_Sodium_Core_Poly1305_State(
|
||
|
ParagonIE_Sodium_Core_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey())
|
||
|
);
|
||
|
|
||
|
# crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
|
||
|
$auth->update($aad);
|
||
|
|
||
|
# crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
|
||
|
# (0x10 - adlen) & 0xf);
|
||
|
$auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf)));
|
||
|
|
||
|
# memset(block, 0, sizeof block);
|
||
|
# block[0] = tag;
|
||
|
# crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block,
|
||
|
# state->nonce, 1U, state->k);
|
||
|
$block = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
|
||
|
ParagonIE_Sodium_Core_Util::intToChr($tag) . str_repeat("\0", 63),
|
||
|
$st->getCombinedNonce(),
|
||
|
$st->getKey(),
|
||
|
ParagonIE_Sodium_Core_Util::store64_le(1)
|
||
|
);
|
||
|
|
||
|
# crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
|
||
|
$auth->update($block);
|
||
|
|
||
|
# out[0] = block[0];
|
||
|
$out = $block[0];
|
||
|
# c = out + (sizeof tag);
|
||
|
# crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, state->nonce, 2U, state->k);
|
||
|
$cipher = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
|
||
|
$msg,
|
||
|
$st->getCombinedNonce(),
|
||
|
$st->getKey(),
|
||
|
ParagonIE_Sodium_Core_Util::store64_le(2)
|
||
|
);
|
||
|
|
||
|
# crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
|
||
|
$auth->update($cipher);
|
||
|
|
||
|
$out .= $cipher;
|
||
|
unset($cipher);
|
||
|
|
||
|
# crypto_onetimeauth_poly1305_update
|
||
|
# (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
|
||
|
$auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf)));
|
||
|
|
||
|
# STORE64_LE(slen, (uint64_t) adlen);
|
||
|
$slen = ParagonIE_Sodium_Core_Util::store64_le($aadlen);
|
||
|
|
||
|
# crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
|
||
|
$auth->update($slen);
|
||
|
|
||
|
# STORE64_LE(slen, (sizeof block) + mlen);
|
||
|
$slen = ParagonIE_Sodium_Core_Util::store64_le(64 + $msglen);
|
||
|
|
||
|
# crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
|
||
|
$auth->update($slen);
|
||
|
|
||
|
# mac = c + mlen;
|
||
|
# crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
|
||
|
$mac = $auth->finish();
|
||
|
$out .= $mac;
|
||
|
|
||
|
# sodium_memzero(&poly1305_state, sizeof poly1305_state);
|
||
|
unset($auth);
|
||
|
|
||
|
|
||
|
# XOR_BUF(STATE_INONCE(state), mac,
|
||
|
# crypto_secretstream_xchacha20poly1305_INONCEBYTES);
|
||
|
$st->xorNonce($mac);
|
||
|
|
||
|
# sodium_increment(STATE_COUNTER(state),
|
||
|
# crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
|
||
|
$st->incrementCounter();
|
||
|
// Overwrite by reference:
|
||
|
$state = $st->toString();
|
||
|
|
||
|
/** @var bool $rekey */
|
||
|
$rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0;
|
||
|
# if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||
|
||
|
# sodium_is_zero(STATE_COUNTER(state),
|
||
|
# crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {
|
||
|
# crypto_secretstream_xchacha20poly1305_rekey(state);
|
||
|
# }
|
||
|
if ($rekey || $st->needsRekey()) {
|
||
|
// DO REKEY
|
||
|
self::secretstream_xchacha20poly1305_rekey($state);
|
||
|
}
|
||
|
# if (outlen_p != NULL) {
|
||
|
# *outlen_p = crypto_secretstream_xchacha20poly1305_ABYTES + mlen;
|
||
|
# }
|
||
|
return $out;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $state
|
||
|
* @param string $cipher
|
||
|
* @param string $aad
|
||
|
* @return bool|array{0: string, 1: int}
|
||
|
* @throws SodiumException
|
||
|
*/
|
||
|
public static function secretstream_xchacha20poly1305_pull(&$state, $cipher, $aad = '')
|
||
|
{
|
||
|
$st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state);
|
||
|
|
||
|
$cipherlen = ParagonIE_Sodium_Core_Util::strlen($cipher);
|
||
|
# mlen = inlen - crypto_secretstream_xchacha20poly1305_ABYTES;
|
||
|
$msglen = $cipherlen - ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES;
|
||
|
$aadlen = ParagonIE_Sodium_Core_Util::strlen($aad);
|
||
|
|
||
|
# if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {
|
||
|
# sodium_misuse();
|
||
|
# }
|
||
|
if ((($msglen + 63) >> 6) > 0xfffffffe) {
|
||
|
throw new SodiumException(
|
||
|
'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes'
|
||
|
);
|
||
|
}
|
||
|
|
||
|
# crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
|
||
|
# crypto_onetimeauth_poly1305_init(&poly1305_state, block);
|
||
|
# sodium_memzero(block, sizeof block);
|
||
|
$auth = new ParagonIE_Sodium_Core_Poly1305_State(
|
||
|
ParagonIE_Sodium_Core_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey())
|
||
|
);
|
||
|
|
||
|
# crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
|
||
|
$auth->update($aad);
|
||
|
|
||
|
# crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
|
||
|
# (0x10 - adlen) & 0xf);
|
||
|
$auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf)));
|
||
|
|
||
|
|
||
|
# memset(block, 0, sizeof block);
|
||
|
# block[0] = in[0];
|
||
|
# crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block,
|
||
|
# state->nonce, 1U, state->k);
|
||
|
$block = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
|
||
|
$cipher[0] . str_repeat("\0", 63),
|
||
|
$st->getCombinedNonce(),
|
||
|
$st->getKey(),
|
||
|
ParagonIE_Sodium_Core_Util::store64_le(1)
|
||
|
);
|
||
|
# tag = block[0];
|
||
|
# block[0] = in[0];
|
||
|
# crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
|
||
|
$tag = ParagonIE_Sodium_Core_Util::chrToInt($block[0]);
|
||
|
$block[0] = $cipher[0];
|
||
|
$auth->update($block);
|
||
|
|
||
|
|
||
|
# c = in + (sizeof tag);
|
||
|
# crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
|
||
|
$auth->update(ParagonIE_Sodium_Core_Util::substr($cipher, 1, $msglen));
|
||
|
|
||
|
# crypto_onetimeauth_poly1305_update
|
||
|
# (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
|
||
|
$auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf)));
|
||
|
|
||
|
# STORE64_LE(slen, (uint64_t) adlen);
|
||
|
# crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
|
||
|
$slen = ParagonIE_Sodium_Core_Util::store64_le($aadlen);
|
||
|
$auth->update($slen);
|
||
|
|
||
|
# STORE64_LE(slen, (sizeof block) + mlen);
|
||
|
# crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
|
||
|
$slen = ParagonIE_Sodium_Core_Util::store64_le(64 + $msglen);
|
||
|
$auth->update($slen);
|
||
|
|
||
|
# crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
|
||
|
# sodium_memzero(&poly1305_state, sizeof poly1305_state);
|
||
|
$mac = $auth->finish();
|
||
|
|
||
|
# stored_mac = c + mlen;
|
||
|
# if (sodium_memcmp(mac, stored_mac, sizeof mac) != 0) {
|
||
|
# sodium_memzero(mac, sizeof mac);
|
||
|
# return -1;
|
||
|
# }
|
||
|
|
||
|
$stored = ParagonIE_Sodium_Core_Util::substr($cipher, $msglen + 1, 16);
|
||
|
if (!ParagonIE_Sodium_Core_Util::hashEquals($mac, $stored)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
# crypto_stream_chacha20_ietf_xor_ic(m, c, mlen, state->nonce, 2U, state->k);
|
||
|
$out = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
|
||
|
ParagonIE_Sodium_Core_Util::substr($cipher, 1, $msglen),
|
||
|
$st->getCombinedNonce(),
|
||
|
$st->getKey(),
|
||
|
ParagonIE_Sodium_Core_Util::store64_le(2)
|
||
|
);
|
||
|
|
||
|
# XOR_BUF(STATE_INONCE(state), mac,
|
||
|
# crypto_secretstream_xchacha20poly1305_INONCEBYTES);
|
||
|
$st->xorNonce($mac);
|
||
|
|
||
|
# sodium_increment(STATE_COUNTER(state),
|
||
|
# crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
|
||
|
$st->incrementCounter();
|
||
|
|
||
|
# if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||
|
||
|
# sodium_is_zero(STATE_COUNTER(state),
|
||
|
# crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {
|
||
|
# crypto_secretstream_xchacha20poly1305_rekey(state);
|
||
|
# }
|
||
|
|
||
|
// Overwrite by reference:
|
||
|
$state = $st->toString();
|
||
|
|
||
|
/** @var bool $rekey */
|
||
|
$rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0;
|
||
|
if ($rekey || $st->needsRekey()) {
|
||
|
// DO REKEY
|
||
|
self::secretstream_xchacha20poly1305_rekey($state);
|
||
|
}
|
||
|
return array($out, $tag);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $state
|
||
|
* @return void
|
||
|
* @throws SodiumException
|
||
|
*/
|
||
|
public static function secretstream_xchacha20poly1305_rekey(&$state)
|
||
|
{
|
||
|
$st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state);
|
||
|
# unsigned char new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES +
|
||
|
# crypto_secretstream_xchacha20poly1305_INONCEBYTES];
|
||
|
# size_t i;
|
||
|
# for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) {
|
||
|
# new_key_and_inonce[i] = state->k[i];
|
||
|
# }
|
||
|
$new_key_and_inonce = $st->getKey();
|
||
|
|
||
|
# for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) {
|
||
|
# new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i] =
|
||
|
# STATE_INONCE(state)[i];
|
||
|
# }
|
||
|
$new_key_and_inonce .= ParagonIE_Sodium_Core_Util::substR($st->getNonce(), 0, 8);
|
||
|
|
||
|
# crypto_stream_chacha20_ietf_xor(new_key_and_inonce, new_key_and_inonce,
|
||
|
# sizeof new_key_and_inonce,
|
||
|
# state->nonce, state->k);
|
||
|
|
||
|
$st->rekey(ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
|
||
|
$new_key_and_inonce,
|
||
|
$st->getCombinedNonce(),
|
||
|
$st->getKey(),
|
||
|
ParagonIE_Sodium_Core_Util::store64_le(0)
|
||
|
));
|
||
|
|
||
|
# for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) {
|
||
|
# state->k[i] = new_key_and_inonce[i];
|
||
|
# }
|
||
|
# for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) {
|
||
|
# STATE_INONCE(state)[i] =
|
||
|
# new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i];
|
||
|
# }
|
||
|
# _crypto_secretstream_xchacha20poly1305_counter_reset(state);
|
||
|
$st->counterReset();
|
||
|
|
||
|
$state = $st->toString();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Detached Ed25519 signature.
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $message
|
||
|
* @param string $sk
|
||
|
* @return string
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function sign_detached($message, $sk)
|
||
|
{
|
||
|
return ParagonIE_Sodium_Core_Ed25519::sign_detached($message, $sk);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Attached Ed25519 signature. (Returns a signed message.)
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $message
|
||
|
* @param string $sk
|
||
|
* @return string
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function sign($message, $sk)
|
||
|
{
|
||
|
return ParagonIE_Sodium_Core_Ed25519::sign($message, $sk);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Opens a signed message. If valid, returns the message.
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $signedMessage
|
||
|
* @param string $pk
|
||
|
* @return string
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function sign_open($signedMessage, $pk)
|
||
|
{
|
||
|
return ParagonIE_Sodium_Core_Ed25519::sign_open($signedMessage, $pk);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Verify a detached signature of a given message and public key.
|
||
|
*
|
||
|
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
|
||
|
*
|
||
|
* @param string $signature
|
||
|
* @param string $message
|
||
|
* @param string $pk
|
||
|
* @return bool
|
||
|
* @throws SodiumException
|
||
|
* @throws TypeError
|
||
|
*/
|
||
|
public static function sign_verify_detached($signature, $message, $pk)
|
||
|
{
|
||
|
return ParagonIE_Sodium_Core_Ed25519::verify_detached($signature, $message, $pk);
|
||
|
}
|
||
|
}
|