Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "solidity/lib/forge-std"]
path = solidity/lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "solidity/lib/account-abstraction"]
path = solidity/lib/account-abstraction
url = https://github.com/eth-infinitism/account-abstraction
71 changes: 71 additions & 0 deletions solidity/falcon/deterministic_ecdsa_sign.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// first install npm install ethers
import { Wallet, hashMessage, recoverAddress, SigningKey, sha256 } from "ethers";

// --- Get arguments from command line ---
// Expected usage: node your_script_name.js <32_byte_seed_hex> <message_string>
const args = process.argv.slice(2); // Slice to get only the actual arguments

if (args.length < 2) {
console.error("Usage: node your_script_name.js <32_byte_seed_hex> <message_string>");
process.exit(1);
}

const deterministicSeedInput = args[0];
const message = args[1]; // The message is now taken directly from the second argument

// Validate seed length (must be 32 bytes = 64 hex characters)
if (deterministicSeedInput.length !== 64 || !/^[0-9a-fA-F]{64}$/.test(deterministicSeedInput)) {
console.error("Error: Seed must be a 32-byte (64 hex characters) hexadecimal string.");
process.exit(1);
}

// Prepend "0x" if it's not there, as ethers.sha256 typically expects it or a Uint8Array
const deterministicSeed = deterministicSeedInput.startsWith("0x") ? deterministicSeedInput : "0x" + deterministicSeedInput;

async function signMessage() {
// Derive the private key deterministically from the seed using SHA256
const privateKey = sha256(deterministicSeed);

// Create a Wallet instance from the derived private key
const wallet = new Wallet(privateKey);

const messageHash = hashMessage(message);
const signature = await wallet.signMessage(message);
const recoveredAddress = recoverAddress(messageHash, signature);

// Helper to parse the signature
function parseSignature(signature) {
const sig = signature.startsWith("0x") ? signature.slice(2) : signature;
return {
r: "0x" + sig.slice(0, 64),
s: "0x" + sig.slice(64, 128),
v: parseInt(sig.slice(128, 130), 16),
};
}
const sig = parseSignature(signature);

const signingKey = new SigningKey(wallet.privateKey);
const publicKey = signingKey.publicKey;

// Extract X and Y coordinates from the uncompressed public key
const x = "0x" + publicKey.slice(4, 68); // Slice from index 4 to 68 for X (skipping 0x04 prefix)
const y = "0x" + publicKey.slice(68); // Slice from index 68 to end for Y

console.log("Message: ", message);
console.log("Input Seed (hex):", deterministicSeedInput);
console.log("Derived Private Key:", privateKey);
console.log("Wallet Address: ", wallet.address);
console.log("Message Hash: ", messageHash);
console.log("Signature: ", signature);
console.log("r (signature): ", sig.r);
console.log("s (signature): ", sig.s);
console.log("v (signature): ", sig.v);
console.log("Public Key (uncompressed):", publicKey); // The full 0x04... key
console.log("Public Key X: ", x);
console.log("Public Key Y: ", y);
console.log("Recovered Addr: ", recoveredAddress);
}

signMessage(); // Call the async function

console.log("ending script...");
124 changes: 124 additions & 0 deletions solidity/falcon/deterministic_falcon_sign.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
const fs = require('fs');
const Module = require('./falcon.js');

// --- Get arguments from command line ---
// Expected usage: node test_falcon.js <32_byte_seed_hex> <message_hex_with_0x_prefix>
const args = process.argv.slice(2); // Slice to get only the actual arguments

if (args.length < 2) {
console.error("Usage: node test_falcon.js <32_byte_seed_hex> <message_hex_with_0x_prefix>");
process.exit(1);
}

const seedHexInput = args[0];
const messageHexInput = args[1];

// Validate seed length (must be 32 bytes = 64 hex characters)
if (seedHexInput.length !== 64) {
console.error("Error: Seed must be 32 bytes (64 hex characters).");
process.exit(1);
}

// Convert seed and message hex strings to Buffers
const seed = Buffer.from(seedHexInput, "hex");
const message = Buffer.from(messageHexInput.startsWith("0x") ? messageHexInput.slice(2) : messageHexInput, "hex");

// --- Main FALCON Module execution ---
Module().then((falcon) => {
const pkLen = 897;
const skLen = 1281;
const sigMaxLen = 690;
const seedLen = 32; // This is now enforced by input validation

// Allocate memory
const pkPtr = falcon._malloc(pkLen);
const skPtr = falcon._malloc(skLen);
const msgPtr = falcon._malloc(message.length);
const seedPtr = falcon._malloc(seedLen);

falcon.HEAPU8.set(message, msgPtr);
falcon.HEAPU8.set(seed, seedPtr);

// Generate keypair using the provided seed
falcon.ccall(
'crypto_keypair',
'number',
['number', 'number', 'number'],
[pkPtr, skPtr, seedPtr]
);

console.log("🔑 Message (hex):", message.toString("hex"));

// The secretKey and publicKey are extracted from WASM memory.
// Note: The secretKey buffer here might include padding or other data
// depending on how crypto_keypair lays out data. For actual use,
// ensure you know the exact layout if you need to store/retrieve it.
const secretKey = Buffer.from(falcon.HEAPU8.subarray(skPtr, skPtr + skLen));
console.log("🔑 Secret Key (hex):", secretKey.toString("hex"));

const publicKey = Buffer.from(falcon.HEAPU8.subarray(pkPtr, pkPtr + pkLen));
console.log("🔑 Public Key (base64):", publicKey.toString("base64"));
console.log("🔑 Public Key (hex):", publicKey.toString("hex"));

// Sign the message
const signedMsgMaxLen = message.length + sigMaxLen;
const signedMsgPtr = falcon._malloc(signedMsgMaxLen);
const signedMsgLenPtr = falcon._malloc(8); // 64-bit space

const signRet = falcon._crypto_sign(
signedMsgPtr,
signedMsgLenPtr,
msgPtr,
BigInt(message.length),
skPtr
);

if (signRet !== 0) {
console.error("❌ Signing failed.");
// Free memory before exiting on error
[pkPtr, skPtr, msgPtr, seedPtr, signedMsgPtr, signedMsgLenPtr].forEach(ptr => falcon._free(ptr));
return;
}

// Read 64-bit signature length (low + high)
function readUint64(ptr) {
const low = falcon.HEAPU32[ptr >> 2];
const high = falcon.HEAPU32[(ptr >> 2) + 1];
return BigInt(high) << 32n | BigInt(low);
}

const sigLen = Number(readUint64(signedMsgLenPtr));
const signedMessage = Buffer.from(falcon.HEAPU8.subarray(signedMsgPtr, signedMsgPtr + sigLen));

console.log("✅ Signature generated.");
console.log("🔐 Sig+Msg (base64):", signedMessage.toString("base64"));
console.log("🔐 Sig+Msg (hexa):", signedMessage.toString("hex"));

// Verify the message
const recoveredMsgPtr = falcon._malloc(sigLen); // Max length of recovered message is sigLen (signed message length)
const recoveredLenPtr = falcon._malloc(8);

const verifyRet = falcon._crypto_sign_open(
recoveredMsgPtr,
recoveredLenPtr,
signedMsgPtr,
BigInt(sigLen),
pkPtr
);

if (verifyRet === 0) {
const recLen = Number(readUint64(recoveredLenPtr));
const recoveredMessage = Buffer.from(falcon.HEAPU8.subarray(recoveredMsgPtr, recoveredMsgPtr + recLen));
console.log("✅ Verification success.");
console.log("📦 Recovered message (hex):", recoveredMessage.toString("hex"));
console.log("🧪 Match:", message.equals(recoveredMessage));
} else {
console.error("❌ Signature verification failed.");
}

// Free memory
[pkPtr, skPtr, msgPtr, seedPtr, signedMsgPtr, signedMsgLenPtr, recoveredMsgPtr, recoveredLenPtr]
.forEach(ptr => falcon._free(ptr));
}).catch(error => {
console.error("An error occurred during FALCON module initialization or execution:", error);
});
6 changes: 5 additions & 1 deletion solidity/falcon/test_falcon.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ Module().then((falcon) => {
const sigMaxLen = 690;
const seedLen= 32;

const message = Buffer.from("hello from ZKNOX!");
const seed=Buffer.from("12345678123456781234567812345679")

const hexString = "0x50b2c43fd39106bafbba0da34fc430e1f91e3c96ea2acee2bc34119f92b37750";

// Remove the "0x" prefix before passing to Buffer.from
const message = Buffer.from(hexString.slice(2), "hex");

// Allocate memory
const pkPtr = falcon._malloc(pkLen);
const skPtr = falcon._malloc(skLen);
Expand Down
1 change: 1 addition & 0 deletions solidity/lib/account-abstraction
Submodule account-abstraction added at cc3893
1 change: 1 addition & 0 deletions solidity/lib/kernel
Submodule kernel added at cd697c
3 changes: 2 additions & 1 deletion solidity/script/DelegationExample.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ contract SignDelegationTest is BaseScript {
address iVerifier_algo = address(falcon);
address iPublicKey = DeployPolynomial(salty, pkc);

Verifier = new ZKNOX_Verifier(iAlgoID, iVerifier_algo, iPublicKey);
Verifier = new ZKNOX_Verifier();
Verifier.initialize(iAlgoID, iVerifier_algo, iPublicKey);
console.log("param Verifier:", Verifier.algoID(), Verifier.CoreAddress(), Verifier.authorizedPublicKey());

// Deploy an ERC-20 token contract where Alice is the minter.
Expand Down
Loading