A WASM/JS port of the Falcon deterministic post-quantum signature algorithm using Emscripten & WebAssembly.
Try it live here: Falcon Signatures
The Algorand Falcon repository does only provide the C and GO implementations; therefore, a majority of developers were not able to use Falcon keys and signatures in their JavaScript or TypeScript applications.
This project from GoPlausible aims to fill that gap. We initially use that to make all of our agentic toolins and platforms post-quantum resistant.
Includes A JavaScript CLI and library for the Falcon post-quantum cryptography signature algorithm, compiled to WebAssembly for use in both Node.js and browser environments.
The original library in C and GO is great work of:
[email protected], David Lazar [email protected] Chris Peikert from Algorand, Inc.
This contribution is inspired by amazing work of Giulio Pizzini in Go language by the Algorand Foundation. Many thanks to Giulio for his great work and support!
Many thanks to my good friend Nullun
- Introduction
- Features
- Installation
- Usage
- Building from Source
- Post-Quantum Resistance & Entropy Improvements (v2.0)
- Testing
- API Reference
- Implementation Details
- License
- Results example
Falcon (Fast-Fourier Lattice-based Compact Signatures over NTRU) is a post-quantum cryptographic signature algorithm submitted to NIST's Post-Quantum Cryptography project. It is designed to be resistant against attacks from quantum computers while maintaining efficiency and compact signatures.
This project provides a JavaScript CLI and library for the deterministic variant of Falcon, compiled from the C implementation to WebAssembly using Emscripten. It supports key generation, signing, and verification operations in both Node.js and browser environments.
The implementation uses the deterministic variant of Falcon, which provides reproducible signatures for the same message and key pair. This is particularly important for applications where signature reproducibility is required.
- Post-Quantum Resistance: Implements the deterministic Falcon-1024 signature scheme, resistant to quantum computer attacks
- Deterministic Signatures: Produces reproducible signatures for the same message and key pair
- Dual Signature Formats:
- Compressed Format: Variable length, smaller size (~1,230 bytes)
- Constant-Time Format: Fixed length, better timing attack resistance (1,538 bytes)
- Format Conversion: Easily convert between compressed and constant-time formats
- Salt Version Support: Retrieve the salt version from signatures
- Cross-Platform: Works in Node.js and modern browsers via WebAssembly
- Simple API: Easy-to-use functions for all cryptographic operations
- Command Line Interface: Convenient CLI with automatic signature format detection
- Now uses libsodium’s ChaCha20-based CSPRNG for high-entropy seed generation
- Secure Memory Handling: Sensitive data is wiped after use with
sodium_memzero() - Mnemonic-ready architecture for future human-readable key recovery
npm install falcon-signaturesThe CLI provides commands for key generation, signing, and verification:
node falcon-cli.js keygenThis will output the public and private keys in hexadecimal format and save them to falcon_pk.bin and falcon_sk.bin.
node falcon-cli.js sign "message to sign" <hex_secret_key>This produces a compressed signature format. To sign using a message from a file:
node falcon-cli.js sign "$(cat message.txt)" <hex_secret_key>node falcon-cli.js verify "message to verify" <hex_signature> <hex_public_key>The verify command automatically detects whether the signature is in compressed or constant-time format.
node falcon-cli.js convert <hex_compressed_signature>This converts a compressed signature to constant-time format.
// Using ES modules
import Falcon from 'falcon-signatures';
// Using CommonJS
// const Falcon = require('falcon-signatures');
async function example() {
// Initialize the Falcon instance
const falcon = new Falcon();
// Generate a deterministic keypair
console.log('Generating a Falcon-1024 deterministic keypair...');
const { publicKey, secretKey } = await falcon.keypair();
console.log(`Public key length: ${publicKey.length} bytes`);
console.log(`Secret key length: ${secretKey.length} bytes`);
// Convert to hex for storage or display
const pkHex = Falcon.bytesToHex(publicKey);
const skHex = Falcon.bytesToHex(secretKey);
// Display shortened versions for better readability
const shortenHex = (hex) => {
if (hex.length <= 40) return hex;
return hex.substring(0, 20) + '...' + hex.substring(hex.length - 20);
};
console.log(`Public key: ${shortenHex(pkHex)}`);
console.log(`Secret key: ${shortenHex(skHex)}`);
// Sign a message (produces compressed signature)
const message = 'Hello, Falcon!';
console.log(`Signing message: "${message}"`);
const signature = await falcon.sign(message, secretKey);
console.log(`Compressed signature length: ${signature.length} bytes`);
console.log(`Compressed signature: ${shortenHex(Falcon.bytesToHex(signature))}`);
// Get the salt version
const saltVersion = await falcon.getSaltVersion(signature);
console.log(`Signature salt version: ${saltVersion}`);
// Verify the compressed signature
const isValid = await falcon.verify(message, signature, publicKey);
console.log(`Verification result: ${isValid ? 'Valid ✓' : 'Invalid ✗'}`);
// Convert to constant-time signature format
const ctSignature = await falcon.convertToConstantTime(signature);
console.log(`Constant-time signature length: ${ctSignature.length} bytes`);
console.log(`CT signature: ${shortenHex(Falcon.bytesToHex(ctSignature))}`);
// Verify the constant-time signature
const ctIsValid = await falcon.verifyConstantTime(message, ctSignature, publicKey);
console.log(`CT verification result: ${ctIsValid ? 'Valid ✓' : 'Invalid ✗'}`);
// Demonstrate deterministic property
const sig1 = await falcon.sign(message, secretKey);
const sig2 = await falcon.sign(message, secretKey);
const areIdentical = Falcon.bytesToHex(sig1) === Falcon.bytesToHex(sig2);
console.log(`Signatures for same message are identical: ${areIdentical ? 'Yes ✓' : 'No ✗'}`);
}
example().catch(console.error);If you're building the library from source:
-
Ensure Emscripten is installed
source /path/to/emsdk/emsdk_env.sh -
Clone and initialize the repository
git clone https://github.com/GoPlausible/falcon-signatures-js.git cd falcon-signatures-js git submodule update --init --recursive -
Build libsodium (secure RNG)
chmod +x build_libsodium_wasm.sh ./build_libsodium_wasm.sh
This compiles a WASM-safe static
libsodium.aunder:external/libsodium/dist/ -
Build the WebAssembly module:
emcc -O3 -s MODULARIZE=1 -s EXPORT_ES6=1 -s ENVIRONMENT=web,worker,node \
-Iexternal/libsodium/dist/include \
-Lexternal/libsodium/dist/lib -lsodium \
-s EXPORTED_FUNCTIONS='["_malloc","_free","_falcon_det1024_keygen_wrapper","_falcon_det1024_sign_compressed_wrapper","_falcon_det1024_convert_compressed_to_ct_wrapper","_falcon_det1024_verify_compressed_wrapper","_falcon_det1024_verify_ct_wrapper","_falcon_det1024_get_salt_version_wrapper","_get_sk_size","_get_pk_size","_get_sig_compressed_max_size","_get_sig_ct_size"]' \
-s EXPORTED_RUNTIME_METHODS='["ccall","cwrap","getValue","setValue","HEAPU8"]' \
falcon/common.c falcon/codec.c falcon/deterministic.c falcon/falcon.c falcon/fft.c falcon/fpr.c falcon/keygen.c falcon/rng.c falcon/shake.c falcon/sign.c falcon/vrfy.c falcon_wrapper.c \
-o falcon.jsThis will generate two files:
falcon.js: The JavaScript wrapper for the WebAssembly modulefalcon.wasm: The WebAssembly binary
To run the tests:
node falcon-cli-test.js
node falcon-test.jsThese will:
- Generate a keypair (and check for randomness to work properly using a second keypair generation)
- Sign a test message with compressed format
- Convert the signature to constant-time format
- Verify both compressed and constant-time signatures
- Test the deterministic property of signatures
_get_sk_size(): Returns the size of a secret key in bytes_get_pk_size(): Returns the size of a public key in bytes_get_sig_compressed_max_size(): Returns the maximum size of a compressed signature in bytes_get_sig_ct_size(): Returns the size of a constant-time signature in bytes_falcon_det1024_keygen_wrapper(): Generates a deterministic keypair_falcon_det1024_sign_compressed_wrapper(): Signs a message with compressed format_falcon_det1024_convert_compressed_to_ct_wrapper(): Converts to constant-time format_falcon_det1024_verify_compressed_wrapper(): Verifies a compressed signature_falcon_det1024_verify_ct_wrapper(): Verifies a constant-time signature_falcon_det1024_get_salt_version_wrapper(): Gets the salt version from a signature
keygen: Generates a new deterministic keypairsign <message> <hex_sk>: Signs a message using a secret key (compressed format)verify <message> <hex_sig> <hex_pk>: Verifies a signature (auto-detects format)convert <hex_compressed_sig>: Converts a compressed signature to constant-time format
keypair(): Generates a new deterministic keypairsign(message, secretKey): Signs a message with compressed formatverify(message, signature, publicKey): Verifies a compressed signatureconvertToConstantTime(compressedSignature): Converts to constant-time formatverifyConstantTime(message, signature, publicKey): Verifies a constant-time signaturegetSaltVersion(signature): Gets the salt version from a signature
This implementation is based on the deterministic variant of the Falcon reference implementation in C, compiled to WebAssembly using Emscripten. It uses the Falcon-1024 parameter set, which provides the highest security level.
The Falcon C implementation is included as a Git submodule from the Algorand Falcon repository. The C wrapper functions in falcon_wrapper.c provide a simplified interface to the Falcon implementation, handling memory allocation and parameter passing. The JavaScript CLI and API then interact with these wrapper functions through the WebAssembly module.
Key technical details:
- Uses deterministic Falcon-1024 (logn=10)
- Secret key size: 2,305 bytes
- Public key size: 1,793 bytes
- Compressed signature size: ~1,230 bytes (variable length)
- Constant-time signature size: 1,538 bytes (fixed length)
Memory management is handled carefully to avoid memory leaks and stack overflows in the WebAssembly environment:
- Large temporary buffers are allocated on the heap instead of the stack to avoid stack overflow errors
- All allocated memory is properly freed after use
- Error handling is implemented for all memory allocations
The project is structured as follows:
falcon/: Git submodule containing the Falcon C implementationfalcon_wrapper.c: C wrapper functions for the WebAssembly interfaceindex.js: JavaScript API for the Falcon functionalityfalcon-cli.js: Command-line interfacefalcon-test.js: Test file for the JavaScript APIfalcon-cli-test.js: Test file for the CLIfalcon.html: Browser demofalcon.js: JavaScript wrapper for the WebAssembly module (generated)falcon.wasm: WebAssembly binary (generated)
This implementation now depends on both the Falcon C reference and libsodium:
- Falcon logic remains deterministic and PQC-correct.
- libsodium provides secure entropy and constant-time memory ops.
- Deterministic SHAKE256 internal PRNG ensures repeatable key derivation.
- libsodium’s ChaCha20-based CSPRNG seeds the Falcon PRNG with high-entropy randomness.
🔬 How it works
-
Built around a ChaCha20 stream cipher in counter mode.
-
Initially seeded from the system CSPRNG (/dev/urandom, getrandom(), or platform equivalents).
-
Then it runs in user space with periodic reseeding and internal re-keying.
-
Each process and thread gets an independent DRBG instance.
-
In Emscripten builds, it automatically maps to WebCrypto’s crypto.getRandomValues().
✅ Strengths
-
ChaCha20 is one of the most extensively reviewed modern ciphers (RFC 8439, used in TLS 1.3, WireGuard, OpenSSH).
-
Deterministic and portable — runs identically across Linux, macOS, Windows, WASM, Node.js.
-
High performance: zero syscalls after seeding; constant-time, branch-free.
-
Entropy refresh: automatic reseed after ~1 MB of output or ~10 minutes.
-
Built-in safety mechanisms: crash-safe state, constant-time zeroization, entropy whitening.
This project is licensed under the MIT License.
=== Falcon CLI Test ===
=== Step 1: Generating a new Falcon keypair ===
> node ./falcon-cli.js keygen
🔑 Generating Falcon-1024 deterministic keypair...
PublicKey: 0a5daabb30542b41...24dbfc2058d9ea7a8341
SecretKey: 5a107dc07ba3177...fceffef2edfbfb260e11
Keygen completed successfully
Public key length: 1793 bytes
Secret key length: 2305 bytes
Keys saved to falcon_pk.bin and falcon_sk.bin
=== Step 2: Signing a message (compressed format) ===
> node ./falcon-cli.js sign "Hello, Deterministic Falcon!" 5a107dc07ba3177...260e11
Signature (compressed format): ba008f28282bf9bd45...5b4aee1566f394c73c80
Signature salt version: 0
Signing completed successfully
Compressed signature length: 1230 bytes
Signature saved to falcon_sig_compressed.bin
=== Step 3: Converting signature to constant-time format ===
> node ./falcon-cli.js convert ba008f28282bf9bd45...394c73c80
CT Signature: da00054ff40900b5fb...e990cceb1
Conversion completed successfully
CT Signature length: 1538 bytes
CT Signature saved to falcon_sig_ct.bin
=== Step 4: Verifying the compressed signature ===
> node ./falcon-cli.js verify "Hello, Deterministic Falcon!" ba008f28282bf9bd45...394c73c80 0a5daabb30542b41...ea7a8341
Detected compressed signature format
[falcon_wrapper] Signature verified successfully
✅ Verification success
=== Step 5: Verifying the constant-time signature ===
> node ./falcon-cli.js verify "Hello, Deterministic Falcon!" da00054ff40900b5fb...e990cceb1 0a5daabb30542b41...ea7a8341
Detected constant-time signature format
[falcon_wrapper] Signature verified successfully
✅ Verification success
=== All tests passed successfully! ===