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; } }