|
<?php |
|
|
|
if (class_exists('ParagonIE_Sodium_Crypto', false)) { |
|
return; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function aead_chacha20poly1305_decrypt( |
|
$message = '', |
|
$ad = '', |
|
$nonce = '', |
|
$key = '' |
|
) { |
|
|
|
$len = ParagonIE_Sodium_Core_Util::strlen($message); |
|
|
|
|
|
$clen = $len - self::aead_chacha20poly1305_ABYTES; |
|
|
|
|
|
$adlen = ParagonIE_Sodium_Core_Util::strlen($ad); |
|
|
|
|
|
$mac = ParagonIE_Sodium_Core_Util::substr( |
|
$message, |
|
$clen, |
|
self::aead_chacha20poly1305_ABYTES |
|
); |
|
|
|
|
|
$ciphertext = ParagonIE_Sodium_Core_Util::substr($message, 0, $clen); |
|
|
|
|
|
$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; |
|
} |
|
$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(); |
|
|
|
|
|
if (!ParagonIE_Sodium_Core_Util::verify_16($computed_mac, $mac)) { |
|
throw new SodiumException('Invalid MAC'); |
|
} |
|
|
|
|
|
return ParagonIE_Sodium_Core_ChaCha20::streamXorIc( |
|
$ciphertext, |
|
$nonce, |
|
$key, |
|
ParagonIE_Sodium_Core_Util::store64_le(1) |
|
); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function aead_chacha20poly1305_encrypt( |
|
$message = '', |
|
$ad = '', |
|
$nonce = '', |
|
$key = '' |
|
) { |
|
|
|
$len = ParagonIE_Sodium_Core_Util::strlen($message); |
|
|
|
|
|
$adlen = ParagonIE_Sodium_Core_Util::strlen($ad); |
|
|
|
|
|
$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; |
|
} |
|
|
|
|
|
$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(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function aead_chacha20poly1305_ietf_decrypt( |
|
$message = '', |
|
$ad = '', |
|
$nonce = '', |
|
$key = '' |
|
) { |
|
|
|
$adlen = ParagonIE_Sodium_Core_Util::strlen($ad); |
|
|
|
|
|
$len = ParagonIE_Sodium_Core_Util::strlen($message); |
|
|
|
|
|
$clen = $len - self::aead_chacha20poly1305_IETF_ABYTES; |
|
|
|
|
|
$block0 = ParagonIE_Sodium_Core_ChaCha20::ietfStream( |
|
32, |
|
$nonce, |
|
$key |
|
); |
|
|
|
|
|
$mac = ParagonIE_Sodium_Core_Util::substr( |
|
$message, |
|
$len - self::aead_chacha20poly1305_IETF_ABYTES, |
|
self::aead_chacha20poly1305_IETF_ABYTES |
|
); |
|
|
|
|
|
$ciphertext = ParagonIE_Sodium_Core_Util::substr( |
|
$message, |
|
0, |
|
$len - self::aead_chacha20poly1305_IETF_ABYTES |
|
); |
|
|
|
|
|
$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(); |
|
|
|
|
|
if (!ParagonIE_Sodium_Core_Util::verify_16($computed_mac, $mac)) { |
|
throw new SodiumException('Invalid MAC'); |
|
} |
|
|
|
|
|
return ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc( |
|
$ciphertext, |
|
$nonce, |
|
$key, |
|
ParagonIE_Sodium_Core_Util::store64_le(1) |
|
); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function aead_chacha20poly1305_ietf_encrypt( |
|
$message = '', |
|
$ad = '', |
|
$nonce = '', |
|
$key = '' |
|
) { |
|
|
|
$len = ParagonIE_Sodium_Core_Util::strlen($message); |
|
|
|
|
|
$adlen = ParagonIE_Sodium_Core_Util::strlen($ad); |
|
|
|
|
|
$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; |
|
} |
|
|
|
|
|
$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(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function auth($message, $key) |
|
{ |
|
return ParagonIE_Sodium_Core_Util::substr( |
|
hash_hmac('sha512', $message, $key, true), |
|
0, |
|
32 |
|
); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function auth_verify($mac, $message, $key) |
|
{ |
|
return ParagonIE_Sodium_Core_Util::hashEquals( |
|
$mac, |
|
self::auth($message, $key) |
|
); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function box_seal($message, $publicKey) |
|
{ |
|
|
|
$ephemeralKeypair = self::box_keypair(); |
|
|
|
|
|
$ephemeralSK = self::box_secretkey($ephemeralKeypair); |
|
|
|
|
|
$ephemeralPK = self::box_publickey($ephemeralKeypair); |
|
|
|
|
|
$nonce = self::generichash( |
|
$ephemeralPK . $publicKey, |
|
'', |
|
24 |
|
); |
|
|
|
|
|
$keypair = self::box_keypair_from_secretkey_and_publickey($ephemeralSK, $publicKey); |
|
|
|
|
|
$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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function box_seal_open($message, $keypair) |
|
{ |
|
|
|
$ephemeralPK = ParagonIE_Sodium_Core_Util::substr($message, 0, 32); |
|
|
|
|
|
$ciphertext = ParagonIE_Sodium_Core_Util::substr($message, 32); |
|
|
|
|
|
$secretKey = self::box_secretkey($keypair); |
|
|
|
|
|
$publicKey = self::box_publickey($keypair); |
|
|
|
|
|
$nonce = self::generichash( |
|
$ephemeralPK . $publicKey, |
|
'', |
|
24 |
|
); |
|
|
|
|
|
$keypair = self::box_keypair_from_secretkey_and_publickey($secretKey, $ephemeralPK); |
|
|
|
|
|
$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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function box_beforenm($sk, $pk) |
|
{ |
|
return ParagonIE_Sodium_Core_HSalsa20::hsalsa20( |
|
str_repeat("\x00", 16), |
|
self::scalarmult($sk, $pk) |
|
); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function box_keypair() |
|
{ |
|
$sKey = random_bytes(32); |
|
$pKey = self::scalarmult_base($sKey); |
|
return $sKey . $pKey; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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) |
|
) |
|
); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function generichash($message, $key = '', $outlen = 32) |
|
{ |
|
|
|
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'); |
|
} |
|
} |
|
|
|
|
|
$in = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($message); |
|
|
|
|
|
$ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outlen); |
|
ParagonIE_Sodium_Core_BLAKE2b::update($ctx, $in, $in->count()); |
|
|
|
|
|
$out = new SplFixedArray($outlen); |
|
$out = ParagonIE_Sodium_Core_BLAKE2b::finish($ctx, $out); |
|
|
|
|
|
$outArray = $out->toArray(); |
|
return ParagonIE_Sodium_Core_Util::intArrayToString($outArray); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function generichash_final($ctx, $outlen = 32) |
|
{ |
|
if (!is_string($ctx)) { |
|
throw new TypeError('Context must be a string'); |
|
} |
|
$out = new SplFixedArray($outlen); |
|
|
|
|
|
$context = ParagonIE_Sodium_Core_BLAKE2b::stringToContext($ctx); |
|
|
|
|
|
$out = ParagonIE_Sodium_Core_BLAKE2b::finish($context, $out); |
|
|
|
|
|
$outArray = $out->toArray(); |
|
return ParagonIE_Sodium_Core_Util::intArrayToString($outArray); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function generichash_init($key = '', $outputLength = 32) |
|
{ |
|
|
|
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'); |
|
} |
|
} |
|
|
|
|
|
$ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outputLength); |
|
|
|
return ParagonIE_Sodium_Core_BLAKE2b::contextToString($ctx); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function generichash_init_salt_personal( |
|
$key = '', |
|
$outputLength = 32, |
|
$salt = '', |
|
$personal = '' |
|
) { |
|
|
|
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; |
|
} |
|
|
|
|
|
$ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outputLength, $s, $p); |
|
|
|
return ParagonIE_Sodium_Core_BLAKE2b::contextToString($ctx); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function generichash_update($ctx, $message) |
|
{ |
|
|
|
ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor(); |
|
|
|
|
|
$context = ParagonIE_Sodium_Core_BLAKE2b::stringToContext($ctx); |
|
|
|
|
|
$in = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($message); |
|
|
|
ParagonIE_Sodium_Core_BLAKE2b::update($context, $in, $in->count()); |
|
|
|
return ParagonIE_Sodium_Core_BLAKE2b::contextToString($context); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
|
); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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]); |
|
} |
|
|
|
|
|
if (-(1 & (($d - 1) >> 8))) { |
|
throw new SodiumException('Zero public key is not allowed'); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function secretbox($plaintext, $nonce, $key) |
|
{ |
|
|
|
$subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key); |
|
|
|
|
|
$block0 = str_repeat("\x00", 32); |
|
|
|
|
|
$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); |
|
|
|
|
|
$block0 = ParagonIE_Sodium_Core_Salsa20::salsa20_xor( |
|
$block0, |
|
ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), |
|
$subkey |
|
); |
|
|
|
|
|
$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); |
|
|
|
|
|
$c = $state->finish() . $c; |
|
unset($state); |
|
|
|
return $c; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function secretbox_open($ciphertext, $nonce, $key) |
|
{ |
|
|
|
$mac = ParagonIE_Sodium_Core_Util::substr( |
|
$ciphertext, |
|
0, |
|
self::secretbox_xsalsa20poly1305_MACBYTES |
|
); |
|
|
|
|
|
$c = ParagonIE_Sodium_Core_Util::substr( |
|
$ciphertext, |
|
self::secretbox_xsalsa20poly1305_MACBYTES |
|
); |
|
|
|
|
|
$clen = ParagonIE_Sodium_Core_Util::strlen($c); |
|
|
|
|
|
$subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key); |
|
|
|
|
|
$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'); |
|
} |
|
|
|
|
|
$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) { |
|
|
|
$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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function secretbox_xchacha20poly1305($plaintext, $nonce, $key) |
|
{ |
|
|
|
$subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20( |
|
ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16), |
|
$key |
|
); |
|
$nonceLast = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8); |
|
|
|
|
|
$block0 = str_repeat("\x00", 32); |
|
|
|
|
|
$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); |
|
|
|
|
|
$block0 = ParagonIE_Sodium_Core_ChaCha20::streamXorIc( |
|
$block0, |
|
$nonceLast, |
|
$subkey |
|
); |
|
|
|
|
|
$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); |
|
|
|
|
|
$c = $state->finish() . $c; |
|
unset($state); |
|
|
|
return $c; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key) |
|
{ |
|
|
|
$mac = ParagonIE_Sodium_Core_Util::substr( |
|
$ciphertext, |
|
0, |
|
self::secretbox_xchacha20poly1305_MACBYTES |
|
); |
|
|
|
|
|
$c = ParagonIE_Sodium_Core_Util::substr( |
|
$ciphertext, |
|
self::secretbox_xchacha20poly1305_MACBYTES |
|
); |
|
|
|
|
|
$clen = ParagonIE_Sodium_Core_Util::strlen($c); |
|
|
|
|
|
$subkey = ParagonIE_Sodium_Core_HChaCha20::hchacha20($nonce, $key); |
|
|
|
|
|
$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'); |
|
} |
|
|
|
|
|
$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) { |
|
|
|
$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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function secretstream_xchacha20poly1305_init_push($key) |
|
{ |
|
|
|
$out = random_bytes(24); |
|
|
|
|
|
$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) |
|
); |
|
|
|
|
|
$state->counterReset(); |
|
|
|
|
|
|
|
|
|
return array( |
|
$state->toString(), |
|
$out |
|
); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function secretstream_xchacha20poly1305_init_pull($key, $header) |
|
{ |
|
|
|
$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(); |
|
|
|
|
|
|
|
|
|
return $state->toString(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0) |
|
{ |
|
$st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state); |
|
|
|
|
|
|
|
|
|
|
|
|
|
$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' |
|
); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$auth = new ParagonIE_Sodium_Core_Poly1305_State( |
|
ParagonIE_Sodium_Core_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey()) |
|
); |
|
|
|
|
|
$auth->update($aad); |
|
|
|
|
|
|
|
$auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf))); |
|
|
|
|
|
|
|
|
|
|
|
$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) |
|
); |
|
|
|
|
|
$auth->update($block); |
|
|
|
|
|
$out = $block[0]; |
|
|
|
|
|
$cipher = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc( |
|
$msg, |
|
$st->getCombinedNonce(), |
|
$st->getKey(), |
|
ParagonIE_Sodium_Core_Util::store64_le(2) |
|
); |
|
|
|
|
|
$auth->update($cipher); |
|
|
|
$out .= $cipher; |
|
unset($cipher); |
|
|
|
|
|
|
|
$auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf))); |
|
|
|
|
|
$slen = ParagonIE_Sodium_Core_Util::store64_le($aadlen); |
|
|
|
|
|
$auth->update($slen); |
|
|
|
|
|
$slen = ParagonIE_Sodium_Core_Util::store64_le(64 + $msglen); |
|
|
|
|
|
$auth->update($slen); |
|
|
|
|
|
|
|
$mac = $auth->finish(); |
|
$out .= $mac; |
|
|
|
|
|
unset($auth); |
|
|
|
|
|
|
|
|
|
$st->xorNonce($mac); |
|
|
|
|
|
|
|
$st->incrementCounter(); |
|
|
|
$state = $st->toString(); |
|
|
|
|
|
$rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0; |
|
|
|
|
|
|
|
|
|
|
|
if ($rekey || $st->needsRekey()) { |
|
|
|
self::secretstream_xchacha20poly1305_rekey($state); |
|
} |
|
|
|
|
|
|
|
return $out; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function secretstream_xchacha20poly1305_pull(&$state, $cipher, $aad = '') |
|
{ |
|
$st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state); |
|
|
|
$cipherlen = ParagonIE_Sodium_Core_Util::strlen($cipher); |
|
|
|
$msglen = $cipherlen - ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES; |
|
$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' |
|
); |
|
} |
|
|
|
|
|
|
|
|
|
$auth = new ParagonIE_Sodium_Core_Poly1305_State( |
|
ParagonIE_Sodium_Core_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey()) |
|
); |
|
|
|
|
|
$auth->update($aad); |
|
|
|
|
|
|
|
$auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf))); |
|
|
|
|
|
|
|
|
|
|
|
|
|
$block = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc( |
|
$cipher[0] . str_repeat("\0", 63), |
|
$st->getCombinedNonce(), |
|
$st->getKey(), |
|
ParagonIE_Sodium_Core_Util::store64_le(1) |
|
); |
|
|
|
|
|
|
|
$tag = ParagonIE_Sodium_Core_Util::chrToInt($block[0]); |
|
$block[0] = $cipher[0]; |
|
$auth->update($block); |
|
|
|
|
|
|
|
|
|
$auth->update(ParagonIE_Sodium_Core_Util::substr($cipher, 1, $msglen)); |
|
|
|
|
|
|
|
$auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf))); |
|
|
|
|
|
|
|
$slen = ParagonIE_Sodium_Core_Util::store64_le($aadlen); |
|
$auth->update($slen); |
|
|
|
|
|
|
|
$slen = ParagonIE_Sodium_Core_Util::store64_le(64 + $msglen); |
|
$auth->update($slen); |
|
|
|
|
|
|
|
$mac = $auth->finish(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$stored = ParagonIE_Sodium_Core_Util::substr($cipher, $msglen + 1, 16); |
|
if (!ParagonIE_Sodium_Core_Util::hashEquals($mac, $stored)) { |
|
return false; |
|
} |
|
|
|
|
|
$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) |
|
); |
|
|
|
|
|
|
|
$st->xorNonce($mac); |
|
|
|
|
|
|
|
$st->incrementCounter(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$state = $st->toString(); |
|
|
|
|
|
$rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0; |
|
if ($rekey || $st->needsRekey()) { |
|
|
|
self::secretstream_xchacha20poly1305_rekey($state); |
|
} |
|
return array($out, $tag); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function secretstream_xchacha20poly1305_rekey(&$state) |
|
{ |
|
$st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state); |
|
|
|
|
|
|
|
|
|
|
|
|
|
$new_key_and_inonce = $st->getKey(); |
|
|
|
|
|
|
|
|
|
|
|
$new_key_and_inonce .= ParagonIE_Sodium_Core_Util::substR($st->getNonce(), 0, 8); |
|
|
|
|
|
|
|
|
|
|
|
$st->rekey(ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc( |
|
$new_key_and_inonce, |
|
$st->getCombinedNonce(), |
|
$st->getKey(), |
|
ParagonIE_Sodium_Core_Util::store64_le(0) |
|
)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$st->counterReset(); |
|
|
|
$state = $st->toString(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function sign_detached($message, $sk) |
|
{ |
|
return ParagonIE_Sodium_Core_Ed25519::sign_detached($message, $sk); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function sign($message, $sk) |
|
{ |
|
return ParagonIE_Sodium_Core_Ed25519::sign($message, $sk); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function sign_open($signedMessage, $pk) |
|
{ |
|
return ParagonIE_Sodium_Core_Ed25519::sign_open($signedMessage, $pk); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function sign_verify_detached($signature, $message, $pk) |
|
{ |
|
return ParagonIE_Sodium_Core_Ed25519::verify_detached($signature, $message, $pk); |
|
} |
|
} |
|
|