<?php
declare(strict_types=1);
define('SODIUM_CRYPTO_RSIGN_SIGBYTES', 96);
function sodium_crypto_rsign(string $message, string $sk): string
{
return sodium_crypto_sign(random_bytes(32) . $message, $sk);
}
function sodium_crypto_rsign_open(string $sm, string $pk): string
{
$opened = sodium_crypto_sign_open($sm, $pk);
return mb_substr($opened, 32, null, '8bit');
}
function sodium_crypto_rsign_detached(string $message, string $sk): string
{
$random = random_bytes(32);
return sodium_crypto_sign_detached($random . $message, $sk) . $random;
}
function sodium_crypto_rsign_verify_detached(string $signature, string $message, string $pk): bool
{
$sig = mb_substr($signature, 0, 64, '8bit');
$random = mb_substr($signature, 64, 32, '8bit');
return sodium_crypto_sign_verify_detached($sig, $random . $message, $pk);
}
/*** TESTS ***/
$kp = sodium_crypto_sign_keypair();
$sk = sodium_crypto_sign_secretkey($kp);
$pk = sodium_crypto_sign_publickey($kp);
# First test: Does it work?
$message = 'This is a test message.';
$signed = sodium_crypto_rsign($message, $sk);
$ropen = sodium_crypto_rsign_open($signed, $pk);
if (!hash_equals($message, $ropen)) {
echo bin2hex($signed), PHP_EOL;
echo bin2hex($ropen), PHP_EOL;
exit(255);
}
# Second test: Is it backwards compatible?
$random = mb_substr($signed, 64, 32, '8bit');
$open = sodium_crypto_sign_open($signed, $pk);
if (!hash_equals($random . $message, $open)) {
echo bin2hex($signed), PHP_EOL;
echo bin2hex($open), PHP_EOL;
exit(255);
}
# Third test: Detached API
$msg2 = 'Cryptography nerds in the kitchen have too much thyme on their hands.'; // Do you feel timing attacked?
$sig = sodium_crypto_rsign_detached($msg2, $sk);
$len = mb_strlen($sig, '8bit');
if ($len !== SODIUM_CRYPTO_RSIGN_SIGBYTES) {
echo 'Expected ', SODIUM_CRYPTO_RSIGN_SIGBYTES, '; got ', $len, '.', PHP_EOL;
exit(255);
}
if (!sodium_crypto_rsign_verify_detached($sig, $msg2, $pk)) {
echo 'Invalid signature.', PHP_EOL;
exit(255);
}
$sig2 = sodium_crypto_rsign_detached($msg2, $sk);
if (hash_equals($sig, $sig2)) {
echo 'RNG Failure.', PHP_EOL;
echo bin2hex($sig), PHP_EOL;
echo bin2hex($sig2), PHP_EOL;
exit(255);
}
$sigLeft = mb_substr($sig, 0, 64, '8bit');
$sigRight = mb_substr($sig, 64, 32, '8bit');
if (!sodium_crypto_rsign_verify_detached($sigLeft, $sigRight . $msg2, $pk)) {
echo 'Invalid signature.', PHP_EOL;
exit(255);
}
echo 'All tests pass!', PHP_EOL;
// Two signatures of the same message:
var_dump(bin2hex($sig), bin2hex($sig2));
preferences:
32.64 ms | 410 KiB | 5 Q