Skip to content

kem: Implement round4 Mceliece - #703

Open
qnfm wants to merge 4 commits into
cloudflare:mainfrom
qnfm:mceliece-round4
Open

qnfm wants to merge 4 commits into
cloudflare:mainfrom
qnfm:mceliece-round4

Conversation

@qnfm

@qnfm qnfm commented Sep 22, 2026 •

Copy link
Copy Markdown

add avx512/avx2 and pure go round4 Classic Mceliece


Devin Review

Add a generated pure-Go implementation of the Classic McEliece Round 4 KEM
parameter sets:

- mceliece348864 / mceliece348864f
- mceliece460896 / mceliece460896f
- mceliece6688128 / mceliece6688128f
- mceliece6960119 / mceliece6960119f
- mceliece8192128 / mceliece8192128f

The parameter-set code is generated from shared templates
(internal/mceliecegen) to keep the variants consistent while preserving the
differences required by the official submission: f/non-f key generation,
unaligned 6960119 public-key packing, and 8192128 support handling. Public
CIRCL KEM wrappers are added and all ten schemes are registered in
kem/schemes.

Tests verify generated keys, ciphertexts, and shared secrets against the
official Round 4 KAT vectors. To keep the package hermetic and small, the
committed testdata archive holds only the first vector per variant (the
coverage the default run checks); set CIRCL_MCELIECE_KAT_ARCHIVE to the full
official gzip archive together with CIRCL_MCELIECE_FULL_KAT to run the
complete set out of tree.

Signed-off-by: qnfm <uzinag@163.com>
…dition

The Gaussian elimination in pkGen / pkGenFromSK dominates key generation
and repeats a single masked GF(2) row addition (dst[i] ^= src[i] & mask)
over PKNRows * SysN/8 bytes.

Factor the operation into internal/matops.AddMasked, which on amd64
selects the widest kernel the CPU supports:

- AVX-512 (preferred): avo-generated kernel over 64-byte ZMM blocks,
  gated on cpu.X86.HasAVX512F && HasAVX512BW.
- AVX2: avo-generated kernel over 32-byte YMM blocks, gated on
  cpu.X86.HasAVX2.
- everything else and the purego build tag: constant-time word-wise Go
  fallback (8 bytes/iter, mask broadcast arithmetically).

A word-wise generic tail finishes any bytes past the last full vector
block. Every path processes the full buffer and derives the per-byte mask
without branching on the secret pivot mask, so no data-dependent timing
is introduced. Both asm kernels are avo-generated by a nested
internal/asm module in line with the keccakf1600/kyber convention,
keeping avo out of the main module dependencies. Dedicated tests drive
each kernel directly so both stay covered regardless of host features.

Signed-off-by: qnfm <uzinag@163.com>

@devin-ai-integration devin-ai-integration Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Newer findings are available below. Devin Review posted a newer report on this PR, in addition to the findings presented here.

Devin Review found 3 potential issues.

Devin Review

Comment thread kem/classicmceliece/internal/mceliecegen/main.go

go 1.26.0

require github.com/mmcloughlin/avo v0.6.0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Assembly dependency lacks allowlist update

The generator adds github.com/mmcloughlin/avo without the required depguard update. Maintainers must approve or exempt this nested build dependency.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

@alanmcanonical

Copy link
Copy Markdown

Same as #378

…mplates

- Ciphertext padding no longer panics (remote DoS).
  Only mceliece6960119 and mceliece6960119f set CheckPadding (PKNRows =
  119*13 = 1547, not a multiple of 8). core.Decapsulate rejected a
  correctly sized ciphertext with non-zero padding bits by returning an
  error, and the higher-level PrivateKey.DecapsulateTo turns any such
  error into panic(err). Scheme.Decapsulate only length-checks before
  calling DecapsulateTo, so an attacker-controlled ciphertext could crash
  any application that decapsulates it.

  Match the reference implementation (operations.c) instead: always run
  the full decapsulation and, when the padding bits are non-zero,
  deterministically mangle the shared secret (OR in the all-ones padding
  mask) rather than returning an error. This is the same constant-time
  implicit-rejection strategy already used for decryption failure, keeps
  Decapsulate error-free for correctly sized ciphertexts, and removes the
  panic path. The padding mask is derived solely from the public
  ciphertext, so no secret-dependent behaviour is introduced.

- Generator now emits the three test templates.
  kat_test.go.tmpl, kem_test.go.tmpl and public_mceliece_test.go.tmpl
  were present but never rendered by mceliecegen, so the committed
  *_test.go files were orphaned "DO NOT EDIT" output that go generate
  could not reproduce. Wire kem_test and public_mceliece_test into
  generateScheme, and restore the KATHashes scheme field (one pinned
  count=0 NIST KAT digest per variant) that kat_test.go.tmpl consumes.
  Regeneration reproduces the existing test files byte-for-byte, so the
  CI generate-diff check now actually covers them.

Signed-off-by: qnfm <uzinag@163.com>
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

Signed-off-by: qnfm <uzinag@163.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants