78 lines
2.6 KiB
PHP
78 lines
2.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Contractless\Rpc\Crypto;
|
|
|
|
use Contractless\Rpc\Exception\ContractlessException;
|
|
use Contractless\Rpc\Protocol\Binary;
|
|
|
|
final class NativeCrypto implements CryptoInterface
|
|
{
|
|
private const ALGORITHM = 'Falcon-padded-512';
|
|
|
|
/** @var \OQS_SIGNATURE */
|
|
private readonly object $falcon;
|
|
|
|
public function __construct()
|
|
{
|
|
if (!function_exists('skein_hash')) {
|
|
throw new ContractlessException('The PHP Skein extension is not loaded.');
|
|
}
|
|
if (!function_exists('contractless_shake256')) {
|
|
throw new ContractlessException(
|
|
'The Contractless liboqs-php SHAKE256 helper is not loaded.',
|
|
);
|
|
}
|
|
if (!class_exists('OQS_SIGNATURE')) {
|
|
throw new ContractlessException('The liboqs-php extension is not loaded.');
|
|
}
|
|
|
|
$this->falcon = new \OQS_SIGNATURE(self::ALGORITHM);
|
|
}
|
|
|
|
public function skein256(string $message): string
|
|
{
|
|
$digest = skein_hash($message, 256);
|
|
if (!is_string($digest) || strlen($digest) !== 32) {
|
|
throw new ContractlessException('Skein-256 hashing failed.');
|
|
}
|
|
return $digest;
|
|
}
|
|
|
|
public function sign(string $message, string $privateKey, string $publicKey): string
|
|
{
|
|
Binary::length($privateKey, 1281, 'Falcon private key');
|
|
Binary::length($publicKey, 897, 'Falcon public key');
|
|
$adapted = $this->adaptMessage($message, $publicKey);
|
|
$signature = '';
|
|
$status = $this->falcon->sign($signature, $adapted, $privateKey);
|
|
if ($status !== 0 || strlen($signature) !== 666) {
|
|
throw new ContractlessException('Falcon-padded-512 signing failed.');
|
|
}
|
|
return $signature;
|
|
}
|
|
|
|
public function verify(string $message, string $signature, string $publicKey): bool
|
|
{
|
|
Binary::length($signature, 666, 'Falcon signature');
|
|
Binary::length($publicKey, 897, 'Falcon public key');
|
|
return $this->falcon->verify(
|
|
$this->adaptMessage($message, $publicKey),
|
|
$signature,
|
|
$publicKey,
|
|
) === 0;
|
|
}
|
|
|
|
private function adaptMessage(string $message, string $publicKey): string
|
|
{
|
|
$publicKeyHash = contractless_shake256($publicKey, 64);
|
|
if (!is_string($publicKeyHash) || strlen($publicKeyHash) !== 64) {
|
|
throw new ContractlessException('SHAKE256 public-key hashing failed.');
|
|
}
|
|
|
|
// fn-dsa uses DOMAIN_NONE and HASH_ID_RAW before the original message.
|
|
return $publicKeyHash . "\0\0" . $message;
|
|
}
|
|
}
|