Skip to content

Commit

Permalink
Merge pull request #18 from eum602/falcon_precompiled
Browse files Browse the repository at this point in the history
add falcon interface to test both, precompiled and solidity implement…
  • Loading branch information
eum602 authored Oct 26, 2023
2 parents ae60f2f + af73b8a commit 9c76030
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 101 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
services:
besu:
image: hyperledger/besu:23.1.0
image: ghcr.io/lacchain/lacchain-besu:23.7.3.0-rc2-amd64
env:
BESU_NETWORK: dev
BESU_MIN_GAS_PRICE: 0
Expand Down
16 changes: 8 additions & 8 deletions contracts/Falcon.sol
Original file line number Diff line number Diff line change
Expand Up @@ -977,7 +977,7 @@ contract Falcon
////////////////////////////////////////
//
////////////////////////////////////////
function keccak_inc_absorb(uint32 r, uint8[] memory pInput, uint32 cbInput) public payable
function keccak_inc_absorb(uint32 r, bytes memory pInput, uint32 cbInput) public payable
{
uint32 i;
uint32 msg_offset;
Expand Down Expand Up @@ -1092,7 +1092,7 @@ contract Falcon
////////////////////////////////////////
//
////////////////////////////////////////
function OQS_SHA3_shake256_inc_absorb(uint8[] memory input, uint32 inlen) public payable
function OQS_SHA3_shake256_inc_absorb(bytes memory input, uint32 inlen) public payable
{
keccak_inc_absorb(SHAKE256_RATE, input, inlen);
}
Expand Down Expand Up @@ -1262,7 +1262,7 @@ contract Falcon
////////////////////////////////////////
//
////////////////////////////////////////
function PQCLEAN_FALCON512_CLEAN_modq_decode(uint16[] memory pX, uint16 logn, uint8[] memory pInput, uint16 In_offset, uint16 cbInputMax) public pure returns (uint16)
function PQCLEAN_FALCON512_CLEAN_modq_decode(uint16[] memory pX, uint16 logn, bytes memory pInput, uint16 In_offset, uint16 cbInputMax) public pure returns (uint16)
{
uint16 n;
uint16 In_len;
Expand Down Expand Up @@ -1441,11 +1441,11 @@ contract Falcon
// const uint8_t* pPublicKey)
////////////////////////////////////////
function verify (uint8 signatureType,
uint8[] memory pSignatureBuf,
bytes calldata pSignatureBuf,
uint16 cbSignatureBuf,
uint8[] memory pMessage,
bytes memory pMessage,
uint16 cbMessage,
uint8[] memory pPublicKey,
bytes memory pPublicKey,
uint16 cbPublicKey) public payable returns (int16)
{

Expand Down Expand Up @@ -1554,7 +1554,7 @@ contract Falcon
// Start of Verification Proper
////////////////////////////////////////////////

uint8[] memory pNonce = new uint8[](NONCELEN); // uint8[NONCELEN] memory pNonce;
bytes memory pNonce = new bytes(NONCELEN); // uint8[NONCELEN] memory pNonce;
uint16[] memory pWordArrayH; // uint16[512]
int16[] memory pSignedWordArraySig; // int16[512]

Expand All @@ -1577,7 +1577,7 @@ contract Falcon
uint sourceOffset = 1;
for (ii=0; ii<NONCELEN; ii++)
{
pNonce[ii] = uint8(pSignatureBuf[sourceOffset + ii]);
pNonce[ii] = pSignatureBuf[sourceOffset + ii];
}

sourceOffset = 1 + NONCELEN;
Expand Down
24 changes: 24 additions & 0 deletions contracts/FalconWrap.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;

import "./Falcon.sol";

contract FalconWrap is Falcon {
function verify(
bytes calldata signature,
bytes calldata publicKey,
bytes calldata message
) public returns (int16) {
uint8 signatureType = (uint8(signature[0]) >> 5) & 0x03;
return
verify(
signatureType,
signature,
uint16(signature.length),
message,
uint16(message.length),
publicKey,
uint16(publicKey.length)
);
}
}
22 changes: 22 additions & 0 deletions contracts/Interface.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//SPDX-License-Identifier: APACHE2
pragma solidity ^0.7.0;

contract FalconInterface {
function verify(
bytes calldata signature,
bytes calldata publicKey,
bytes calldata message,
address falconVerifier
) public returns (bool isValid) {
(bool success, bytes memory verifies) = address(falconVerifier).call(
abi.encodeWithSignature(
"verify(bytes,bytes,bytes)",
signature,
publicKey,
message
)
);
require(success && verifies.length == 32, "Invalid signature");
return verifies[31] == 0;
}
}
167 changes: 76 additions & 91 deletions test/falcon512-KAT.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,92 +6,106 @@ const Test = require('mocha/lib/test');

const falcConsts = require('./falcon_constants.js');

async function callAndCheck(
signature_array,
pubKey_array,
message_array,
contractAddress,
isValid,
falconCommonInterface
) {
let verifyArgs = [
signature_array,
pubKey_array,
message_array,
contractAddress,
];

let ret = await falconCommonInterface.callStatic.verify.apply(
null,
verifyArgs
);
assert.equal(ret, isValid);
}

async function verifyKat(kat, falconCommonInterface, targetImplementationContractAddress) {
let signatureType = falcConsts.FALCON_SIG_1_COMPRESSED; // FALCON_SIG_0_INFERRED, FALCON_SIG_1_COMPRESSED, FALCON_SIG_2_PADDED, FALCON_SIG_3_CT
const msg = Buffer.from(kat.msg, 'hex');
const mlen = parseInt(kat.mlen);
const pk = Buffer.from(kat.pk, 'hex');
const sm = Buffer.from(kat.sm, 'hex');
const smlen = parseInt(kat.smlen);

// Deconstruct KAT sm field which is in "3.11.6 NIST API" format
const smSigLen = sm.readInt16BE(0); // Convert a 2 byte BigEndian value to a number
const smNonce = sm.slice(2, 42); // Skip over the 2 bytes for the smSigLen
const smMsg = sm.slice(2 + 40, smlen-2-40-smSigLen); // Not used - Should be identical to msg
const smSig = sm.slice(2 + 40 + mlen); // Skip 2 bytes for signatureSize + 40 bytes for nonce + mlen bytes for message.
const smSigHdrByte = smSig.slice(0, 1); // Header Byte
const smSigRaw = smSig.slice(1); // Raw Signature

// Construct Signature field to send to the contract which expects it in "3.11.3 Signatures" format
var argSigHdrBuf = Buffer.from([0x39]);
const argSig = Buffer.concat([argSigHdrBuf, smNonce, smSigRaw]);

// calling both precompiled and solidity through a common interface.
const signature_array = Array.from(argSig);
const message_array = Array.from(msg);
const pubKey_array = Array.from(pk);
await callAndCheck(
signature_array,
pubKey_array,
message_array,
targetImplementationContractAddress,
true,
falconCommonInterface
);
}

describe("Falcon", async () =>
{
const suite = describe("falcon512-KAT - Known Answer Tests", async () =>
{
let falconSolidityImpl;
let falconCommonInterface;
let falconInstance;
let precompiledContractAddress = falcConsts.FALCON_PRECOMPILED_ADDRESS;

before(async () =>
{
const kats = await parseKats(fs.createReadStream('test/falcon512-KAT.rsp'));
const Falcon = await hre.ethers.getContractFactory('Falcon')
const FalconSolidityImpl = await hre.ethers.getContractFactory(
"FalconWrap"
);
const FalconCommonInterface = await hre.ethers.getContractFactory(
"FalconInterface"
);
falconInstance = await Falcon.deploy();
falconSolidityImpl = await FalconSolidityImpl.deploy();
const solidityFalconAddress = falconSolidityImpl.address;
falconCommonInterface = await FalconCommonInterface.deploy();

//kats.slice(0, 5).forEach(kat => // Tests 0,1,2,3,4
kats.forEach(kat => // All Tests
{
suite.addTest(new Test(`KAT test ${kat.count}`, async() =>
suite.addTest(new Test(`KAT test ${kat.count} (solidity)`, async() =>
{
let signatureType = falcConsts.FALCON_SIG_1_COMPRESSED; // FALCON_SIG_0_INFERRED, FALCON_SIG_1_COMPRESSED, FALCON_SIG_2_PADDED, FALCON_SIG_3_CT
const msg = Buffer.from(kat.msg, 'hex');
const mlen = parseInt(kat.mlen);
const pk = Buffer.from(kat.pk, 'hex');
const pklen = pk.length;
const sm = Buffer.from(kat.sm, 'hex');
const smlen = parseInt(kat.smlen);

// Deconstruct KAT sm field which is in "3.11.6 NIST API" format
const smSigLen = sm.readInt16BE(0); // Convert a 2 byte BigEndian value to a number
const smNonce = sm.slice(2, 42); // Skip over the 2 bytes for the smSigLen
const smMsg = sm.slice(2 + 40, smlen-2-40-smSigLen); // Not used - Should be identical to msg
const smSig = sm.slice(2 + 40 + mlen); // Skip 2 bytes for signatureSize + 40 bytes for nonce + mlen bytes for message.
const smSigHdrByte = smSig.slice(0, 1); // Header Byte
const smSigRaw = smSig.slice(1); // Raw Signature

// Construct Signature field to send to the contract which expects it in "3.11.3 Signatures" format
var argSigHdrBuf = Buffer.from([0x39]);
const argSig = Buffer.concat([argSigHdrBuf, smNonce, smSigRaw]);
const argSigLen = argSigHdrBuf.length + smNonce.length + smSigRaw.length;

//console.log("==> argSigHdrBuf[%d]: 0x%s", argSigHdrBuf.length, argSigHdrBuf.toString('hex'));
//console.log("==> smSigRawLen = smlen(%d) - smSigLen.len(2) - nonceNen(40) - mlen(%d) - sigHdrByte(1) = %d [%d]",
// smlen, mlen,
// (smlen - 2 - 40 - mlen - 1),
// smSigRaw.length );
//console.log("==> argSig[%d]: 0x%s", argSigLen, argSig.toString('hex'));

if (1)
{
const contractReturn = await falconInstance.callStatic.verify(signatureType, Array.from(argSig), argSigLen, Array.from(msg), mlen, Array.from(pk), pklen);
if (contractReturn != falcConsts.FALCON_ERR_SUCCESS)
{
let ret = getFalconReturnValue(contractReturn);
let errorReasonCode = getReasonCode(contractReturn);
let errorStr = "ERROR: falcon.verify returned " + ret + " (" + falcConsts.FALCON_ERR_Description[Math.abs(ret)] + ") [reason: " + errorReasonCode + "]";
console.log(errorStr);
}
const falconReturn = getFalconReturnValue(contractReturn);
assert.equal(falconReturn, falcConsts.FALCON_ERR_SUCCESS, `${falcConsts.FALCON_ERR_LongDescription[Math.abs(falconReturn)]}`);
}
else
{
let expectedRet = falcConsts.FALCON_ERR_SUCCESS;
const signature_array = argSig.toJSON().data;
const message_array = msg.toJSON().data;
const pubKey_array = pk.toJSON().data;
let verifyArgs = [signatureType, signature_array, argSigLen, message_array, mlen, pubKey_array, pklen];
let ret = await falconInstance.callStatic.verify.apply(null, verifyArgs);
let errorReasonCode = getReasonCode(ret);
ret = getFalconReturnValue(ret);
let errorStr = "ERROR: falcon.verify expected " + expectedRet + " (" + falcConsts.FALCON_ERR_Description[Math.abs(expectedRet)] + "), but got " + ret + " (" + falcConsts.FALCON_ERR_Description[Math.abs(ret)] + ") [reason: " + errorReasonCode + "]";
if (ret != expectedRet) console.log(errorStr);
assert.equal(ret, expectedRet, errorStr);
let tx = await falconInstance.verify.apply(null, verifyArgs);
let receipt = await tx.wait();
assert.equal(receipt.status, true);
}
await verifyKat(kat, falconCommonInterface, solidityFalconAddress);
}));
});


suite.addTest(new Test(`KAT test ${kat.count} (precompiled)`, async() =>
{
await verifyKat(kat, falconCommonInterface, precompiledContractAddress);
}));
});
});

it.skip("dummy", () =>
{
// Mocha needs at least one explicit declaration otherwise it ignores the whole suite.
});


const parseKats = async (katStream) =>
{
const rl = readline.createInterface( {input: katStream, crlfDelay: Infinity} );
Expand All @@ -117,35 +131,6 @@ describe("Falcon", async () =>
}
return kats;
}

const getFalconReturnValue = (ret) =>
{
var isNegative = false;
if (ret < 0)
{
isNegative = true;
ret = Math.abs(ret);
}
var remainder = ret % 10;
if (isNegative)
remainder = -remainder;
return remainder;
}

const getReasonCode = (ret) =>
{
var isNegative = false;
if (ret < 0)
{
isNegative = true;
ret = Math.abs(ret);
}
var remainder = ret % 10;
var quotient = ret - remainder; // or maybe Math.floor(ret/10) or trunc(ret/10)
if (isNegative)
quotient = -quotient;
return quotient;
}
});
});

Expand Down
5 changes: 4 additions & 1 deletion test/falcon_constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ const FALCON_SIG_3_CT = 3; // Fixed-size format amenable to constant-tim
// the 'CT' format also prevents information about the signature value and the signed data hash to leak through timing-based side channels (this feature is rarely needed).
const FALCON_SIG_4_INVALID = 4;

const FALCON_PRECOMPILED_ADDRESS = "0x0000000000000000000000000000000000000014";

module.exports = {
FALCON_ERR_SUCCESS,
FALCON_ERR_RANDOM,
Expand All @@ -56,7 +58,8 @@ module.exports = {
FALCON_SIG_1_COMPRESSED,
FALCON_SIG_2_PADDED,
FALCON_SIG_3_CT,
FALCON_SIG_4_INVALID
FALCON_SIG_4_INVALID,
FALCON_PRECOMPILED_ADDRESS
};


Expand Down

0 comments on commit 9c76030

Please sign in to comment.