Crypt-Liboqs-Sign
view release on metacpan or search on metacpan
t/compat_pqclean.t view on Meta::CPAN
use strict;
use warnings;
use Test::More;
# Cross-compatibility test: verify that liboqs can work with keys/signatures
# generated by Crypt::PQClean::Sign (they use the same underlying code)
use Crypt::Liboqs::Sign qw(falcon512_keypair falcon512_sign falcon512_verify);
use Digest::SHA qw(sha256);
# hash256 = double SHA-256, same as used in qbitcoin
sub hash256 { sha256(sha256($_[0])) }
# Test vector from qbitcoin-perl test suite (generated with Crypt::PQClean::Sign)
my $sign_data = "\x55\xaa" x 700;
# The signature hex includes a 1-byte algo prefix (0x81 = CRYPT_ALGO_FALCON = 129)
# followed by the raw Falcon-512 signature of hash256(sign_data)
my $sign_hex = "813961c32c0fcc6132c89b74f34894091d4d598c53d284597e7fcf2bace22a41460fb99436f82c151b73836a237a164acde34ff0080eee10be36bcc8545168c22d99deca8d0e25c8e7732f51c419679e05c19670c8497a9760670e3b07f88ad2f850abe77243c0d495f5bf9265a5d887af8da87057...
# The private key from the test contains both private (1281 bytes) and public (897 bytes) key
# We only need the public key (last 897 bytes) for verification
my $private_key_wif = "2Ha9HXWeaemt4enETPZ8WJnH8GqJg1DvbUdoj6g8mLrKZu6aF94UT93nr3VrcaVSyUcFagmrg6SPPGazuPJPKyw4SoFxEjQxMJKzSi3Pawb8NC2cW69eBzaxMU1H7qrKPkGJ7Nmkb4hbxgLdGGHVhyHgshq4nrvAy8v8C5JvYmoSC9bN6cSL7DAk3gxLYyJWtcN5M6U7JpnFTFuW7PSNMD3hzJQomGgaxwL...
# Decode the WIF-encoded private key (base58check)
use Math::BigInt;
my @B58_CHARS = split //, '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
my %b58_val;
$b58_val{$B58_CHARS[$_]} = $_ for 0..$#B58_CHARS;
sub decode_base58 {
my ($str) = @_;
my $num = Math::BigInt->new(0);
for my $c (split //, $str) {
$num->bmul(58)->badd($b58_val{$c});
}
my $hex = $num->as_hex();
$hex =~ s/^0x//;
$hex = "0$hex" if length($hex) % 2;
my $bytes = pack("H*", $hex);
# Count leading '1's for leading zero bytes
for my $c (split //, $str) {
last if $c ne '1';
$bytes = "\x00" . $bytes;
}
return $bytes;
}
# Decode private key from WIF format
my $raw = decode_base58($private_key_wif);
# WIF format: version_byte(1) + payload + checksum(4)
# Strip version byte and checksum
my $pk_data = substr($raw, 1, length($raw) - 5);
# pk_data should be 2178 bytes (1281 private + 897 public)
is(length($pk_data), 2178, "Decoded private key is 2178 bytes (1281 + 897)");
my $pubkey = substr($pk_data, 1281, 897);
is(length($pubkey), 897, "Public key is 897 bytes");
my $full_sig = pack("H*", $sign_hex);
# First byte is algo identifier (0x81 = 129 = CRYPT_ALGO_FALCON), strip it
is(unpack("C", $full_sig), 0x81, "Signature starts with CRYPT_ALGO_FALCON byte");
my $raw_signature = substr($full_sig, 1);
# The signature was created over hash256(sign_data), not sign_data directly
my $hashed_data = hash256($sign_data);
# Verify PQClean-generated signature using liboqs
my $valid = falcon512_verify($raw_signature, $hashed_data, $pubkey);
ok($valid, "PQClean-generated signature verified by liboqs - cross-compatible!");
# Verify rejection of wrong data
my $invalid = falcon512_verify($raw_signature, "wrong data", $pubkey);
ok(!$invalid, "Correctly rejects wrong data");
# Test round-trip: sign with liboqs using PQClean-generated key
my $sk = substr($pk_data, 0, 1281);
my $new_sig = falcon512_sign($hashed_data, $sk);
ok(defined $new_sig, "Signed with liboqs using PQClean-generated key");
my $new_valid = falcon512_verify($new_sig, $hashed_data, $pubkey);
ok($new_valid, "liboqs-generated signature verified with PQClean-generated key");
done_testing();
( run in 1.840 second using v1.01-cache-2.11-cpan-39bf76dae61 )