Merge the nine wc_falcon_*.c cores into wolfcrypt/src/falcon.c, matching the
one-file-per-algorithm convention (ed25519.c, wc_mldsa.c). Sign and keygen are
gated internally on WOLFSSL_FALCON_VERIFY_ONLY and the internal helpers become
static. The fpr/fft/poly primitives stay external (shared with the separate
AVX2/NEON FFT backends) so their three headers are retained; the other five
internal headers are removed.
- wc_falcon.c: replace the stale "Phase 1: verification only" file banner
(the file now holds keygen/sign/verify cores).
- falcon.c: fix the garbled wc_falcon_verify_msg doc comment (removed a
non-existent contextLen parameter; state the level-dependent BUFFER_E
bound and the *res convention).
- wc_falcon.c: name the sign compression-fit retry bound
FALCON_SIGN_MAX_ENCODE_RETRIES (was a bare 32) and document why the
bound is safe, mirroring FALCON_SIGN_MAX_RESTARTS in wc_falcon_sign.c.
- test_falcon.c: add a direct wc_falcon_import_private_only concat(priv,pub)
test that recovers the public key and signs+verifies from that single
import, covering the recover-pub-from-concat path end to end.
test_wc_falcon_sign_verify in tests/api/test_signature.c was merged to
master gated on HAVE_FALCON && HAVE_LIBOQS. With liboqs removed the
macro is defined nowhere, so the test was dead code and check-source-text
failed with 'unrecognized macros used: HAVE_LIBOQS'.
Gate it on WC_FALCON_HAVE_NATIVE_SIGN like the rest of the native
signing tests, and replace the obsolete liboqs-RNG comment. The tree
now has zero HAVE_LIBOQS references, so no .wolfssl_known_macro_extras
entry is needed.
Regenerated from wolfssl-scripts falcon/x86_64/falcon.rb (commit
b680440), which documents in the file header that the backend is
SysV AMD64 ABI only and requires the default MXCSR state. No code
change, comment only.
Remaining fixes from the second review round:
- keygen: falcon_compute_public's scratch buffer holds NTT(f) (private-key
material) in its tail; wc_ForceZero it before both frees (the
f-not-invertible reject path and the success path). Also zeroize the
internally allocated hwork for consistency with the tmpbuf hardening.
- sampler: falcon_sampler_z's rejection loop never consulted the sticky
PRNG error flag, so a mid-signature SHAKE256 squeeze failure could make
berexp deterministically reject and the loop spin forever. Check p.err
each iteration and bail out; the returned value is discarded since
falcon_sign_core rejects the whole signature once p.err is set.
falcon_prng_init now frees the SHAKE256 context when a later init step
fails (plugs a device-context leak in WOLFSSL_ASYNC_CRYPT builds), and
falcon_prng_refill early-returns once the error is latched instead of
re-issuing failing squeezes.
- codec: guard the bits-dependent shifts in falcon_trim_i8_encode/decode
against out-of-range widths (defense in depth; callers only pass 5..8).
- check_key: implement the cryptographic private/public cross-check that
91ebd89d7 documented as a follow-up. New falcon_native_check_key decodes
(f, g) from the private key and h from the public key and verifies the
defining relation h*f == g (mod q, mod X^n + 1) slot-wise in the NTT
domain (falcon_ntt keeps values canonical in [0, q)); a slot with
NTT(f) == 0 is rejected too, as keygen only emits invertible f.
wc_falcon_check_key dispatches to it whenever the native signing core is
compiled in, and falls back to the presence check in verify-only /
callback-only builds. Doxygen updated to the actual contract, and a unit
test added: a mismatched pair (public half from a different key) must
fail with PUBLIC_KEY_E. This also strengthens the keypair validation
done via wc_falcon_check_key in asn.c.
wolfSSL C style forbids recursion. The reference implementation expresses
the ffLDL tree construction, tree normalization and Fast Fourier sampling
as self-recursions on logn; flatten all three into iterations over small
fixed-size frame stacks (depth bounded by logn <= 10):
- ffLDL_fft_inner and ffLDL_binary_normalize do all their work before
their two half-degree sub-invocations, so a LIFO work list of pending
jobs suffices (at most logn + 1 <= 11 frames).
- falcon_ffSampling_fft has work interleaved between its sub-invocations,
so it becomes a three-stage state machine over at most 8 frames. The
reference's inlined base cases are split out into WC_INLINE helpers
(ffSampling_fft_deg4 / ffSampling_fft_deg2), and a bounds check on logn
keeps the frame stack safe even for invalid callers.
Sub-invocations run in exactly the reference order; for ffSampling this is
a hard requirement since the discrete Gaussian sampler consumes a PRNG
stream, and any reordering would change the produced signatures.
Verified with a differential harness (old recursive code from git vs the
new build): expanded keys and ffSampling outputs are bit-identical for
logn 1..10, with identical sampler invocation counts and an identical
order-sensitive hash of every (mu, isigma) sampler argument pair.
Performance is unchanged (within noise at logn 9 and 10).
The DSP-NTT gate in wc_falcon.c keys off the compiler-predefined
__ARM_FEATURE_DSP, and the FPR_DOUBLE soft-double guard in wc_falcon_fpr.h
keys off __ARM_FP; neither was listed in .wolfssl_known_macro_extras, so
the source-text unknown-macro check flags them. Add both in sorted order.
Four fixes from PR review:
- zephyr/CMakeLists.txt: the native port split falcon.c into wc_falcon_*.c
translation units; add the portable sources so a Zephyr build with Falcon
links. x86-64 asm/AVX2 and the NEON backend are left out (not selected by any
Zephyr config).
- configure.ac: fold the standalone --enable-falcon-{asm,double,avx2,neon}
switches into comma-separated sub-options of --enable-falcon
(e.g. --enable-falcon=avx2), matching the common wolfSSL idiom. avx2/neon
imply the double backend after arch-gating so ignoring an unsupported vector
backend does not clobber an explicit 'double'. Sweep the qemu-falcon-neon doc
to the new spelling.
- configure.ac: align the Falcon line in the two feature summaries.
- falcon.c/falcon.h: drop the duplicate public-key copy kept behind the private
key. Its only remaining reader was wc_falcon_check_key, whose compare was
against a copy of the same bytes and so could never detect a real mismatch;
wc_falcon_export_private already rebuilds the concat layout on demand. Shrink
key->k from FALCON_MAX_PRV_KEY_SIZE to FALCON_MAX_KEY_SIZE (saves 1793 bytes
per key at level 5). check_key now verifies both halves are present and
documents a full cryptographic cross-check as a follow-up. Update the unit
test that relied on the old in-memory-copy compare.
On 32-bit ARM targets lacking a double-precision FPU (Cortex-M0/M3/M4/
M23/M33), C double maps onto slow libgcc soft-double, making the opt-in
WOLFSSL_FALCON_FPR_DOUBLE backend a pessimization versus the default
integer fpr backend. Emit a #warning to steer users away. Detect via
__ARM_FP bit 3; AArch64 does not define __arm__ so it is unaffected.
The keygen/sign/verify fuzzer now lives in the testing repo
(fuzzers/falcon); it is not part of the library or its CI. Removing it
here also drops tests/falcon/run.sh from the PR's changed-shell set.
tests/falcon/ hammers make_key -> sign -> verify across many keys and
messages to catch intermittent non-verifying signatures, dumping a
replayable artifact on any mismatch. Includes Makefile and run.sh.
The sign retry loop capped the compressed signature at the caller-supplied
*outLen instead of the level's fixed size, so a buffer larger than the max
let an over-length (e.g. 667-byte Falcon-512) signature escape without a
restart, and verification then rejected it. Cap at sigMax instead.
The check-source-text unknown-macro check (subtest K) flags preprocessor
symbols used in the sources that are neither #defined in a header nor listed
in .wolfssl_known_macro_extras. The native Falcon backends add several
configure/-D build gates that are not #defined anywhere, so a build that
scans the Falcon sources without those options set (e.g. --enable-all
without the sub-backends) reported them as unknown.
Register the nine new gates (LC_ALL=C sorted):
WOLF_CRYPTO_CB_ONLY_FALCON
WOLFSSL_FALCON_FFT_AVX2, WOLFSSL_FALCON_FFT_NEON
WOLFSSL_FALCON_FPR_ASM, WOLFSSL_FALCON_FPR_DOUBLE
WOLFSSL_FALCON_NTT_DSP, WOLFSSL_FALCON_NO_NTT_DSP
WOLFSSL_FALCON_SIGN_STATS, WOLFSSL_FALCON_VERIFY_ONLY
HAVE_FALCON and WC_FALCON_HAVE_NATIVE_SIGN are already known (configure /
falcon.h), so they are not added.
configure.ac sets enable_option_checking=fatal, so removing the
--with-liboqs option turned it into a hard "unrecognized options" error.
Test matrices / CI (e.g. the Jenkins PRB liboqs config branch) and user
scripts that still pass --with-liboqs then fail at the configuration phase
before any build runs.
Re-add --with-liboqs as a recognised, deprecated no-op that warns it has no
effect and points at --enable-falcon. It links no liboqs and does not change
the build (SBOM unaffected); it only prevents the fatal unknown-option abort.
Mirrors the existing --enable-cryptodev deprecation shim.
- falcon.c (wc_falcon_import_private_only): call falcon_store_pub_behind_priv
unconditionally after setting prvKeySet. The raw-size branch previously only
synced the behind-private public copy in the concat layout, so importing a
public key first and then a raw private key left key->k + KEY_SIZE zero and
made wc_falcon_check_key return a false PUBLIC_KEY_E. Added a regression case
to test_wc_falcon_check_key covering the public-then-raw-private ordering.
- wc_falcon.c (native sign cleanup): free the sampler's SHAKE256 context with
wc_Shake256_Free(&spc.p.shake) when it was initialized, before ForceZero.
Without it, WOLFSSL_ASYNC_CRYPT + WC_ASYNC_ENABLE_SHA3 builds leaked the
async device context allocated by wc_InitShake256 on every sign, unlike the
keygen and hash-to-point paths which already free their SHAKE contexts.
- wc_falcon.c: replace the lazily-initialised, mutable NTT twiddle-table
cache with precomputed read-only const tables (falcon_zetas/izetas_l1/l5).
This removes the data race on the shared cache (a reader could see the
init flag set before all table entries were visible) and also drops the
now-unused falcon_brv / falcon_build_tables helpers. Verify stays fast
(const reads, no per-call rebuild); KAT confirms the values.
- falcon.c: correct the wc_falcon_sign_msg doc comment (signing needs the
*private* key; required buffer size is the active level's signature size,
not always FALCON_LEVEL1_SIG_SIZE) and validate rng != NULL in the
software path so the error is reported at the API boundary.
- wc_falcon_sign.c / wc_falcon_sign.h: route key->heap into
falcon_complete_private and falcon_expand_privkey (their large fpr scratch
allocations previously used a NULL heap hint, bypassing custom heap
routing); and fail fast in falcon_do_sign_tree on the sampler's sticky
PRNG error (passed in as samplerErr) instead of running to the restart
bound.
- wc_falcon_codec.h: use the project-standard WOLF_CRYPT_WC_FALCON_CODEC_H
include guard instead of the collision-prone generic FALCON_CODEC_H.
- doc/dox_comments (ssl.h + -ja): drop the stale "or HAVE_LIBOQS" from the
ML-KEM hybrid group requirements; liboqs is no longer supported.
The new tests/api/test_falcon.c was wired into the automake unit test
(tests/api/include.am) but not the CMake one, so CMake-based CI builds
failed to link the unit test with undefined references to
test_wc_falcon_*. Add it to the CMake unit_test source list alongside the
other tests/api/test_*.c entries.
Falcon had crypto-level coverage (KAT + native round-trip in
wolfcrypt/test/test.c) but, unlike ML-DSA and SLH-DSA, no dedicated
tests/api/ unit test exercising the public wc_falcon_* / wc_Falcon_* API
surface. This adds one, wired into the unit test runner as the "falcon"
group.
Coverage (both Falcon-512 / L1 and Falcon-1024 / L5, which are always
compiled together):
- sizes: size/priv_size/pub_size/sig_size vs the spec constants,
get_level round-trip, and NULL / unset-level rejection.
- make_key: NULL and unset-level rejection; real keygen -> check_key.
- sign_vfy: sign -> verify; wrong-message and one-byte tamper rejected;
too-small buffer -> BUFFER_E with the required length set;
verify with no public key -> BAD_FUNC_ARG.
- import_export: public / private-only (raw) / private (concat) / export_key
round-trips, each re-signed or verified, plus too-small
(BUFFER_E) and wrong-size (BAD_FUNC_ARG) paths.
- check_key: valid pass; corrupted public copy, public-only and
private-only keys all fail (PUBLIC_KEY_E); NULL rejected.
- der: KeyToDer / PrivateKeyToDer / PublicKeyToDer round-trips via
PrivateKeyDecode / PublicKeyDecode, size-query (NULL output),
and the SetAsymKeyDer too-small contract (BAD_FUNC_ARG).
- error_paths: exhaustive NULL / bad-level / wrong-size / no-key-set
argument sanitising for every public entry point.
Tests requiring key generation or signing are gated on
WC_FALCON_HAVE_NATIVE_SIGN so the file also builds in
WOLFSSL_FALCON_VERIFY_ONLY and WOLF_CRYPTO_CB_ONLY_FALCON configurations;
size and argument-sanitising tests run in every HAVE_FALCON build.
Verified: 7/7 pass under both --enable-falcon-avx2 and the default
constant-time build; compiles clean with WOLFSSL_FALCON_VERIFY_ONLY.
The falcon_interop job restored/untarred liboqs into the workspace and only
then ran actions/checkout, whose "git clean -ffdx" deleted the untracked
oqs-install/ directory. The interop harness build then failed with
"oqs/oqs.h: No such file or directory".
Move the wolfSSL checkout ahead of the cache-restore / artifact-download /
untar steps so the liboqs install lands after the clean and survives.
The harness itself is fine: built locally against liboqs it passes all eight
interop cells (liboqs<->native, both directions, levels 1 and 5).
The earlier codespell fix reworded the same phrasing in wc_falcon_codec.c
but missed the mirrored comment in the header; codespell flagged "statics"
(==> statistics) in both. Reword to "static functions".
- cmake/functions.cmake: remove duplicated BUILD_FALCON conditional block;
BUILD_FALCON is now set in one place.
- wolfssl/wolfcrypt/include.am: list the internal wc_falcon_*.h headers under
an "if BUILD_FALCON" block so they ship in "make dist" tarballs and install
when Falcon is enabled (the public falcon.h was already listed). Automake
distributes both branches of the conditional, so a Falcon-disabled dist
still contains them.
- wc_falcon_fft_neon.c: fix strict-aliasing UB in falcon_FFT/falcon_iFFT.
fpr is a word64 bit pattern; casting fpr* to double* and using
vld1q_f64/vst1q_f64 accesses the store through the wrong type and can
miscompile at -O2/-O3. Load/store through uint64_t (the real object type)
and reinterpret the vector register to/from f64 via new FALCON_VLD/FALCON_VST
macros. Verified under qemu (NEON_FFT_PASS) with fmla v.2d still emitted.
- wc_falcon_sampler.c / wc_falcon_sampler.h: falcon_prng_get_u8/get_u64 have
no error return, so a SHAKE256 squeeze failure previously went unnoticed and
stale buffer bytes could feed the signer. Add a sticky "err" field latched on
the first refill failure.
- wc_falcon_sign.c: falcon_sign_core now rejects the signature when the PRNG
latched an error, and falcon_do_sign_tree bounds its restart loop
(FALCON_SIGN_MAX_RESTARTS) so a wedged sampler terminates instead of
spinning forever. The bound is far beyond any legitimate restart count.
- codespell.yml: add "fpr"/"FPR" to ignore_words_list. "fpr" is the Falcon
reference's canonical name for the floating-point primitive seam and
appears hundreds of times across the sources; it is not a typo.
- wc_falcon.c: rename local "clen" -> "compLen" (codespell flagged clen)
and replace a non-ASCII em-dash in a comment with "--" (check-source-text
8-bit byte violation).
- wc_falcon_bigint.c: fix typos "morever" -> "moreover", "Mutiply" ->
"Multiply".
- wc_falcon_codec.c: reword "are statics in" -> "are static functions in"
(codespell flagged "statics").
Scrub the temporary "FN-DSA" name and the "FIPS 206" designation from all
in-tree comments, build text, and message strings, leaving the algorithm
named only as "Falcon". The eventual standardized name is not announced.
The differential known-answer test message ("wolfSSL FN-DSA differential
KAT") is a signed input, so the Falcon-512/1024 public keys and signatures
in wolfcrypt/test/test.c (and the mirrored Falcon-512 vector in
IDE/m33mu-falcon-verify/kat.h) were regenerated with liboqs over the new
message "wolfSSL Falcon differential KAT", preserving the differential
property (liboqs-produced signatures verified by the native verifier).
Verified: testwolfcrypt Falcon test passes; the m33mu verify-only harness
passes (BKPT 0x7f) with the regenerated vector.
Add doc/dox_comments/header_files/falcon.h covering every public wc_falcon_* /
wc_Falcon_* function (init/init_ex/init_id/init_label, set/get level, make_key,
sign/verify, import/export public+private, check_key, sizes, and the DER
encode/decode helpers), plus a Falcon \defgroup in doxygen_groups.h. List
Falcon in the INSTALL algorithm summary.
Docs refer to the algorithm only as "Falcon" (it is not standardized yet); the
temporary-name note and the configure --help / summary text are reworded to not
name a specific future standard.
Add wc_falcon_fft_neon.c, the 2-wide-double counterpart of the AVX2 FFT
backend: it processes two doubles per 128-bit vector with fused multiply-add
for the complex butterflies (falcon_FFT / falcon_iFFT), over the inline-double
fpr backend. Enabled with --enable-falcon-neon (AArch64; implies
--enable-falcon-double). Advanced SIMD is part of the ARMv8-A baseline, so no
special -march is needed; the scalar FFT/iFFT in wc_falcon_fft.c are excluded
under WOLFSSL_FALCON_FFT_NEON, and only the tail level (ht/t == 1) falls back to
scalar. Like the AVX2 backend, it does not promise bit-identical (no-FMA)
results; that is safe for the signing FFT (the sampler's determinism and
verification are unaffected).
Tested under qemu-system-aarch64 -machine virt (cortex-a53) via the new
IDE/qemu-falcon-neon bare-metal harness: a full keygen -> sign -> verify
round-trip at levels 1 and 5 accepts genuine signatures and rejects tampered
ones (NEON_FFT_PASS), with vector fmla v.2d confirmed in the FFT.
Add an optional Cortex-M (ARMv7E-M / ARMv8-M) DSP-accelerated verify path,
auto-enabled on cores with the DSP extension (__ARM_FEATURE_DSP; Cortex-M4/M7/
M33). It is bit-identical to the scalar Barrett path (validated over 40k random
polynomials per level) and controlled by WOLFSSL_FALCON_NTT_DSP /
WOLFSSL_FALCON_NO_NTT_DSP.
- NTT/iNTT butterflies process two packed 16-bit coefficients per iteration:
SMLABB/SMLATB 16x16 twiddle multiplies (values are < q < 2^14), SADD16/
SSUB16 packed adds, and a USUB16+SEL packed conditional subtract of q.
- The pointwise multiply packs two coefficients per iteration (SMLABB/SMLATT).
- The squared l2-norm accumulates two coefficients per SMUAD (a.lo^2+a.hi^2).
Tested on the STM32H563 (Cortex-M33) emulator m33mu via the new
IDE/m33mu-falcon-verify harness: the DSP path accepts a genuine Falcon-512 KAT
signature and rejects a tampered one (BKPT 0x7f), with SMUAD/USUB16/SEL/SMLABB
confirmed present in the image.
Also fixes three latent verify-only (WOLFSSL_FALCON_VERIFY_ONLY) build issues
surfaced by the embedded target:
- falcon.h: include random.h unconditionally so WC_RNG is visible for the
always-declared wc_falcon_sign_msg prototype.
- falcon.c: define falcon_store_pub_behind_priv unconditionally
(wc_falcon_import_public, a verify-only op, calls it).
- falcon.c: silence unused inLen/rng in the verify-only sign stub.
Wire Falcon into the crypto callback framework like the other algorithms:
- wc_falcon_make_key now dispatches to wc_CryptoCb_MakePqcSignatureKey
(WC_PQC_SIG_TYPE_FALCON); wc_falcon_sign_msg / wc_falcon_verify_msg already
dispatched to wc_CryptoCb_PqcSign / PqcVerify. All three fall through to the
software implementation when the callback is unavailable.
- Add WOLF_CRYPTO_CB_ONLY_FALCON (mirrors WOLF_CRYPTO_CB_ONLY_RSA/ECC): the
callback becomes authoritative (no software fallback; returns NO_VALID_DEVID
when no device is registered) and the native core (wc_falcon*.c) is compiled
out entirely. WC_FALCON_HAVE_NATIVE_SIGN and the falcon_native_* prototypes
are gated off in that build.
Tests (test.c):
- myCryptoDevCb gains a Falcon branch for PQC keygen/sign/verify.
- falcon_test / falcon_verify_kat now use the global test devId, so
cryptocb_test drives every Falcon operation through the callback and asserts
(via the exampleVar hit counter) that the cb path was actually taken.
- Under WOLF_CRYPTO_CB_ONLY_FALCON, falcon_test instead confirms the API
returns NO_VALID_DEVID with no device registered, and the KAT data/verifier
(software-only) are compiled out.
Verified: default (no cryptocb), --enable-cryptocb, and
-DWOLF_CRYPTO_CB_ONLY_FALCON all build and pass testwolfcrypt (falcon_test +
crypto callback test); the CB_ONLY library contains no falcon_native_* symbols.
Falcon was the last algorithm backed by liboqs; now that wolfCrypt has a
native Falcon implementation, liboqs is no longer needed. Remove the
integration entirely so liboqs does not appear as a build or SBOM dependency:
- configure: drop --with-liboqs (and the -loqs link), the BUILD_LIBOQS
conditional and the summary line.
- CMake: drop WOLFSSL_OQS, the duplicate liboqs-backed WOLFSSL_FALCON
option, the OQS cross-validation / find_package(OQS) block, the
FindOQS.cmake module, BUILD_OQS_HELPER, and HAVE_LIBOQS from options.h.in.
- Remove the wolfcrypt/src/port/liboqs port layer (liboqs.c/.h) and its
wolfSSL_liboqsInit/Close calls in wc_port.c.
- settings.h: drop HAVE_LIBOQS from the asym key import/export aggregates
(HAVE_FALCON already covers them) and from the experimental gate; add
HAVE_FALCON to the experimental gate so the unstandardized Falcon requires
WOLFSSL_EXPERIMENTAL_SETTINGS in every build system.
- Drop liboqs.c from the VS/Zephyr/INTIME project files, remove the liboqs
install from Docker, and update INSTALL/tls.c text (Falcon is native now).
No functional change to non-Falcon builds; the library links no liboqs.
Add a complete native Falcon post-quantum lattice signature implementation to
wolfCrypt, replacing the liboqs wrapper. Full key generation, signing and
verification for Falcon-512 (level 1) and Falcon-1024 (level 5).
- Public API wc_falcon_* / falcon_key in falcon.c wraps the native core
(falcon_native_* in wc_falcon.c) plus wc_falcon_{fpr,fft,poly,sampler,
codec,keygen,sign,bigint}.c. No liboqs dependency.
- Portable, constant-time integer-emulated floating-point (fpr) backend is
the default; opt-in per-architecture acceleration:
--enable-falcon-double inline native double
--enable-falcon-asm x86-64 SSE2 out-of-line fpr asm
--enable-falcon-avx2 x86-64 AVX2 (4-wide) FFT
- Division-free (Barrett) integer NTT on the verify path, so no hardware
divide is required on Cortex-M / embedded targets.
- Verify uses a cached twiddle-factor NTT; signing uses the FFT / ffLDL tree
and discrete Gaussian sampler over the abstract fpr seam.
- test.c falcon_test (KAT verify + native keygen/sign/verify roundtrip);
scripts/falcon-interop.c and a CI workflow cross-check native<->liboqs in
both directions.
* Fix vector register restore on error paths in Sha3Update().
* Add SP 800-185 check against KMAC_FIPS_MIN_KEY in KmacInit() and
KMAC_FIPS_MIN_OUTPUT in KmacFinal(), returning KMAC_MIN_KEYLEN_E and
BAD_LENGTH_E respectively on failure.
* Use word32 rather than byte for wc_Sha3.i, wc_Cshake.count, wc_Kmac.count, and
related, and add explicit range checking where needed, to fix a -Wconversion,
fix possible overruns, obviate 14 casts, and eliminate (negligible) runtime
overhead from masking and promotions.