Skip to content

Update HQC implementation to 2025-08-22 version#2407

Open
xuganyu96 wants to merge 41 commits into
mainfrom
gyx-hqc-20250822
Open

Update HQC implementation to 2025-08-22 version#2407
xuganyu96 wants to merge 41 commits into
mainfrom
gyx-hqc-20250822

Conversation

@xuganyu96
Copy link
Copy Markdown
Contributor

@xuganyu96 xuganyu96 commented Apr 14, 2026

This pull request replaces the PQClean implementation of HQC with the reference implementation, originally hosted on GitLab.

All code change is developed on a personal fork. After reaching sufficient maturity, the code change is git-diff'ed into a patch file. This admittedly will make future development more tedious, so let me know if we should continue using a personal fork. We can also reach out to the team to merge our code change.

# from xuganyu96/pqc-hqc at branch liboqs-integration
# 161cd4f is the base commit
git diff 161cd4f --patch > $LIBOQS_DIR/scripts/copy_from_upstream/patches/pqchqc-liboqs-integration.patch

Code change

There are several significant changes to the original implementation:

Namespacing was added to the original implementation. Every non-static symbol has a PQCHQC_NAMESPACE macro applied so that a user-defined prefix will be attached (e.g. PQCHQC_HQC1_C_ for the portable implementation of HQC-1). Several symbols that were not used across different source files were additionally declared static.

OQS common primitives replaced the included implementation of SHA3/Keccak and PRNG.

Name of algorithms have changed from HQC-128/192/256 to HQC-1/3/5.

Other:

  • Some debug logging facility added to src/common
  • Reorganized the test_kat.py test so that on initial failure, the test binary is re-run to capture debugging output

Known limitations

  • x86_64 optimized implementations were not included
  • Branching-on-secret was not validated against valgrind
  • On GitHub Actions, some HQC unit tests intermittently fail, but not consistently

No LLM/AI was used in this pull request

  • [YES] Does this PR change the input/output behaviour of a cryptographic algorithm (i.e., does it change known answer test values)? (If so, a version bump will be required from x.y.z to x.(y+1).0.)
  • [YES] Does this PR change the list of algorithms available -- either adding, removing, or renaming? Does this PR otherwise change an API? (If so, PRs in fully supported downstream projects dependent on these, i.e., oqs-provider will also need to be ready for review and merge by the time this is merged. Also, make sure to update the list of algorithms in the continuous benchmarking files: .github/workflows/kem-bench.yml and sig-bench.yml)

@coveralls
Copy link
Copy Markdown

coveralls commented Apr 14, 2026

Coverage Status

coverage: 82.37% (+0.1%) from 82.267% — gyx-hqc-20250822 into main

@dstebila dstebila moved this from Backlog to In progress in 0.16.0 prioritization Apr 15, 2026
@dstebila dstebila added this to the 0.16.0 milestone Apr 15, 2026
@dstebila
Copy link
Copy Markdown
Member

Analysis by Claude about Windows failure:

Root cause is in the BITMASK macro.

The bug

src/kem/hqc/xuganyu96-hqc_hqc-*_ref/parameters.h:12:
#define BITMASK(a, size) ((1UL << (a % size)) - 1)

On Windows x86_64 (LLP64) unsigned long is 32 bits. On Linux/macOS x86_64 (LP64) it's 64 bits. So 1UL << 37 is fine everywhere except Windows, where it's undefined behavior (shift ≥ operand width) and MSVC typically emits an x86 shl that masks the
count to 5 bits, giving 1<<5 = 32 → BITMASK = 31 instead of 0x1FFFFFFFFF.

Why only HQC-5:

┌─────────┬─────────┬──────────────┬─────────────┐
│ Variant │ PARAM_N │ PARAM_N % 64 │ Windows OK? │
├─────────┼─────────┼──────────────┼─────────────┤
│ HQC-1 │ 17669 │ 5 │ yes │
├─────────┼─────────┼──────────────┼─────────────┤
│ HQC-3 │ 35851 │ 11 │ yes │
├─────────┼─────────┼──────────────┼─────────────┤
│ HQC-5 │ 57637 │ 37 │ no │
└─────────┴─────────┴──────────────┴─────────────┘

The mask is applied at:

  • gf2x.c:139 inside reduce() — called from vect_mul(), which runs in keygen/encaps
  • vector.c:205 in vect_set_random() — generates h from the seed

So every vect_mul result and every sampled h has its top 37 bits zeroed on Windows, producing corrupted keys/ciphertexts — exactly what your debug fprintf of s, y, h in hqc_pke_keygen should expose (look at s[VEC_N_SIZE_64 - 1] and h[VEC_N_SIZE_64 -
1]).

Why "when SLH-DSA is disabled" is a red herring

.github/workflows/windows.yml:19,52 unconditionally passes -DOQS_ENABLE_SIG_SLH_DSA=OFF. That's just the Windows CI config — not causal. The bug fires on Windows regardless of SLH-DSA.

Fix

Change 1UL → 1ULL (or UINT64_C(1)) in all three parameters.h files. Also consider (a) % (size) parens, but the UL→ULL is the actual fix.

@xuganyu96 xuganyu96 force-pushed the gyx-hqc-20250822 branch 6 times, most recently from 37f3f99 to 627be2b Compare April 22, 2026 17:50
@xuganyu96 xuganyu96 marked this pull request as ready for review April 22, 2026 19:50
@xuganyu96 xuganyu96 requested review from a team, RodriM11 and vsoftco April 22, 2026 19:51
@RodriM11
Copy link
Copy Markdown
Member

Hi @xuganyu96 ! Thank you for the immense amount of work in order to see this PR through, I greatly appreciate it. I have a general question:

The source implementation maintains a certain amount of "common" code (e.g. code.c, gf.c and so on), while this PR includes each file (at first sight, being the same) on each of the parametrizations. Is there any reason why this was done with the files appearing on each folder?

@xuganyu96
Copy link
Copy Markdown
Contributor Author

Is there any reason why this was done with the files appearing on each folder?

The short reason is simplicity of configuration files and ease of development.

I am aware that only recently I have pushed the MQOM contributor to de-duplicate files, but I am currently prioritizing pushing HQC across the finish line so we can release 0.16. I am happy to come back to this.

@RodriM11
Copy link
Copy Markdown
Member

I am aware that only recently I have pushed the #2361 (review) to de-duplicate files, but I am currently prioritizing pushing HQC across the finish line so we can release 0.16. I am happy to come back to this.

That decision seems sensible to me, seeing as we are gearing towards a release soon and HQC is, IMO, one of the top priorities. So the PR LGTM if we ensure we handle de-duplicating the code once 0.16.0 lands.

Thanks for the work and for seeing this through @xuganyu96 !

@xuganyu96
Copy link
Copy Markdown
Contributor Author

It would be ideal to reach out to the HQC team for an "official" liboqs integration effort.

In case that is not feasible, I think it makes sense to move the fork of official repository under open-quantum-safe organization to facilitate future contributions.

@baentsch
Copy link
Copy Markdown
Member

It would be ideal to reach out to the HQC team for an "official" liboqs integration effort.

In case that is not feasible, I think it makes sense to move the fork of official repository under open-quantum-safe organization to facilitate future contributions.

In my opinion, it would not only be ideal, but necessary -- unless of course you now consider yourself an expert in the HQC code @xuganyu96 : Do you? The discussion above (relying on Claude "knowledge/advice") doesn't quite create that impression, but I may be jumping to conclusions here.

Finally, moving a fork into the oqs namespace in my opinion creates the impression and expectation that OQS becomes/is the final "source of truth" and maintainer of last resort of an algorithm. I would think we should do that only if we have absolute certainty we understand the code and logic inside-out and can maintain it going forward under all circumstances (even if single members leave the project). I'm not sure this applies here and suggest having a TSC discussion about this.

xuganyu96 added 29 commits May 22, 2026 15:00
However, incorrect namespacing may have caused some symbols from HQC-1
to pollute symbols that should have belonged to HQC-3 and HQC-5

Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
…ded tests]

Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
…xtended tests]

Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
On certain platforms, "unsigned long" is 32-bit wide, so (1UL << x) can
overflow if x is greater than 31, such as with HQC-5.

Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
WHY WHY WHY what is even the difference????

Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
For liboqs integration, hash_g implementation has been modified
There are some unexpected dispatch errors under liboqs/src/common/sha3
that causes intermittent GitHub Action test failures. Due to the
difficulty of debugging on GitHub action, I chose this shortcut of
replacing incremental API with a single hash over a continuous strip of
memory. There might be performance and stack memory usage penalty
associated with this approach, but I deemed it acceptable.

we need to eventually figure out what happened with Keccak dispatch
and restore the usage of incremental API

Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
`debuglogging` was a convenient debugging tool I added during the
development processs. Though it was helpful, it is not in the scope of
HQC integration, so it is removed in this branch. I will gauge interest
and implement the debuglogging feature separately.

Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
Signed-off-by: Ganyu (Bruce) Xu <g66xu@uwaterloo.ca>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

focus Reserved for a small number of topics that have been identified as focus issues for the current week needs review Looking for a(nother) review

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

5 participants