locality_falcon 1.0.0
locality_falcon: ^1.0.0 copied to clipboard
Falcon asymmetric signature algorithm implementation in pure dart.
locality_falcon #
A complete, web-safe Dart/Flutter implementation of Falcon — the NIST-standardized, lattice-based post-quantum signature scheme — designed to run signing and verification on end-user devices (mobile, desktop, and Flutter Web).
It is a faithful port of the reference implementation
tprest/falcon.py: the Dart output is
byte-for-byte identical to the reference (same keys, same signatures for the
same seed), and the two interoperate — a signature produced by one verifies under
the other.
Why Falcon #
Small payloads, frequent signing/verification, and tiny public keys (≈900 B for Falcon-512, ≈1.8 KB for Falcon-1024) make Falcon the right fit for messaging-style workloads where keys are shared often. See the design rationale.
Usage #
import 'dart:convert';
import 'package:locality_falcon/falcon.dart';
void main() {
final falcon = Falcon(512); // 2..1024, powers of two
final (sk, vk) = falcon.keygen(); // secret key + verification key (bytes)
final message = utf8.encode('hello quantum world');
final signature = falcon.sign(sk, message); // Uint8List, 666 B for Falcon-512
assert(falcon.verify(vk, message, signature) == true);
assert(falcon.verify(vk, utf8.encode('tampered'), signature) == false);
}
Reproducible / deterministic mode (e.g. for tests or KATs) — inject a seeded ChaCha20 stream (the same PRNG the reference uses):
final seed = Uint8List(56); // your 56-byte seed
final sig = falcon.sign(sk, message, randombytes: ChaCha20(seed).randombytes);
Parameter sets #
| Set | n | public key | signature | NIST level |
|---|---|---|---|---|
| Falcon-512 | 512 | 897 B | 666 B | 1 |
| Falcon-1024 | 1024 | 1793 B | 1280 B | 5 |
Smaller degrees (2…256) are supported for testing.
Web safety #
The whole stack avoids 64-bit-int assumptions so it behaves identically on
native and on dart2js/Wasm (where int is a 53-bit double):
- SHAKE-256 via PointyCastle (64-bit register emulation).
- The Gaussian sampler's FACCT
approxExpand all NTRU-solve arithmetic useBigIntwhere exact integers exceed 2⁵³. - The NTT keeps every intermediate below
q² ≈ 1.5·10⁸ < 2⁵³.
Architecture #
Single-responsibility modules, none over 300 lines (see
lib/sign/ARCHITECTURE.md):
lib/sign/
complex.dart, fft.dart, phi.dart, constants/ # FFT over R[x]/(x^n+1)
ntt/ntt.dart # negacyclic NTT mod q
sampler/gaussian_sampler.dart # discrete Gaussian (FACCT)
rng/{random_bytes,chacha20}.dart # secure + deterministic RNG
hash/{shake256,hash_to_point}.dart # SHAKE-256 XOF, hash-to-point
encoding/{poly_codec,signature_codec}.dart # key/signature (de)serialization
ffsampling/{ldl,ffldl,ff_sampling,ff_tree}.dart# fast Fourier sampling
ntrugen/{bigint_poly,reduce,ntru_solve,ntru_gen}.dart # NTRU key generation
api/{falcon,falcon_keygen,falcon_sign,falcon_verify,...}.dart # public API
Testing #
122 tests, all cross-validated against the Python reference:
flutter test
Component tests check each primitive (SHAKE-256, ChaCha20, samplerZ, NTT,
hash-to-point, encoding) byte-for-byte against vectors in test/vectors/;
the end-to-end tests check the NTRU equation f·G − g·F = q, byte-identical
signature reproduction, cross-verification with the reference, and tamper
rejection at n = 8…512.
Regenerate the vectors (requires Python + pycryptodome and a checkout of the
reference) with python3 tool/gen_vectors.py.
Security notes #
This is a clean, spec-faithful implementation intended for correctness and portability. It has not been independently audited, and the port prioritizes matching the reference over constant-time guarantees — review side-channel resistance before production use. Always sign with the default secure RNG; the seeded ChaCha20 path exists only for reproducible tests.
Credits #
Thanks to Shindeyu aka 0xpingu for significant development contributions.