Security › kem-36 · Constant-time review · Performance: x86_1 · arm_1

TRIKE (kem-36)

CandidateTRIKE
FamilyCode-based
ArchiveTRIKE.zip (SHA-256: 03956a13fde3d402513bfcf9942f2b04fd23e98b01a3dc52b48938b9c89fe4d6)
Specificationkem-36-spec.pdf PDF
Performancex86_1 · arm_1

kem-36-1: The specified TRIKE decoder rejects every tested honest ciphertext

SeverityHigh
Scopedesign
StatusConfirmed
AffectedTRIKE specification; all four implementations use a different rule
DiscoveryTrivial
ExploitationTrivial correctness failure for the specified algorithm
CreditMarkku-Juhani O. Saarinen markku-juhani.saarinen@tuni.fi, with AI assistance
Date2026-09-21

Follow-up source: Xiong and Wang's TRIKE analysis

TRIKE's PDF defines the bit-flipping threshold as max(Tnow,T′), repeats that return value in Algorithm 8, and explains that the decoder uses the larger threshold. Every submitted implementation instead computes min(Tnow,T′).

The difference is decisive. In a paired whole-KEM test where the libraries differed only in this expression, the shipped min decoder recovered 1,000/1,000 honest TRIKE-2 shared secrets. The literal PDF max decoder recovered 0/1,000: the API still returned success, but every derived shared secret was wrong. Hence the specified scheme is nonfunctional, while the implementation and its KATs instantiate a materially different decoder. Any DFR claim must identify and analyze the actual rule.

Follow-up Analysis

Zhenyu Xiong and Mingsheng Wang's 2026-09-30 PKC Forum post identifies an independent defect in the same specified decoder: Algorithm 8 omits the original syndrome term when updating the syndrome, while the source retains it. This gives a second reason that the specified decoder differs from the implemented one; the post does not independently retest the max/min differential above and does not change this finding's classification.

Reproducing

Commands below run in a checkout of the ngcc-harness repository with the candidate built (see its README).

make -C kem-36 lib/libTRIKE-2.so
python3 security/trike_threshold_differential.py --trials 1000

The script builds an isolated copy with the literal PDF expression and compares both complete KEMs on identical deterministic trials.

kem-36-2: Withdrawn — Algorithm 9 already uses binomial collision counts

SeverityInfo
Scopedesign
StatusWithdrawn
AffectedNone; the frozen specification and submitted implementations use the same binomial formula
DiscoveryTrivial
ExploitationNone; the original claim resulted from misreading PDF notation
CreditZhenyu Xiong and Mingsheng Wang, with AI assistance
Date2026-09-30

Original source: Xiong and Wang's TRIKE analysis Follow-up source: TRIKE team's correction

Withdrawn on 2026-10-20. Visual inspection of Appendix Algorithm 9 (frozen PDF, physical p. 29) shows C(cnt_{u,j},2) and C(μ,2), not squared multiplicities. Text extraction rendered the stacked binomial notation misleadingly as cnt² and μ². The original report and its certificate evaluated that nonexistent squared formula, so their rejection conclusion does not apply to TRIKE.

The submitted source also uses binomial collision counts. The team's reply correctly identifies the notation; no fix to this formula is required. This withdrawal concerns only kem-36-2 and does not assess the team's other proposed revisions.

Reproducing

Commands below run in a checkout of the ngcc-harness repository with the candidate built (see its README).

python3 kem-36/reproduce_weak_key_test.py

The updated check confirms the binomial source formula in all eight submitted reference and optimized trees. The PDF notation was checked visually; this script does not parse or certify it.

kem-36-3: Decapsulation fails to free the private h0 buffer

SeverityLow
Scopeimplementation
StatusConfirmed
AffectedAll four reference and four optimized implementations
DiscoveryTrivial
ExploitationRepeated decapsulation exhausts process memory; no secret disclosure or cryptographic break demonstrated
CreditZhenyu Xiong and Mingsheng Wang, with AI assistance
Date2026-09-30

Original source: Xiong and Wang's TRIKE analysis

kem_dec allocates a padded buffer h0, copies the first private parity-check block into it, and never frees it. The function frees its other eleven heap buffers before returning. The omission is present in every submitted parameter set in both implementation families.

The reporters observed an AddressSanitizer direct leak and approximately 2,064 bytes retained per TRIKE-2 call and 16,400 bytes per TRIKE-7 or TRIKE-9 call. This supports resource exhaustion in a long-running decapsulation service, but neither memory disclosure nor control-flow impact is shown.

Proposed fixes

The original post proposes adding free(h0) after free(t0) in each of the eight affected decapsulation functions. This section records the proposal without evaluating it.

Reproducing

Commands below run in a checkout of the ngcc-harness repository with the candidate built (see its README).

python3 kem-36/reproduce_implementation_issues.py

The static certificate isolates kem_dec, confirms the unmatched h0 allocation, and checks that the other temporaries are freed in all eight source trees.

kem-36-4: Boundary weak-key classes are omitted from the DFR bound

SeverityMedium
Scopedesign
StatusProof gap
AffectedTRIKE weak-key rejection and DFR analysis, all parameter sets
DiscoveryModerate
ExploitationThe target-level failure bounds are not established for adjacent unfiltered key classes; no excessive DFR is demonstrated
CreditZhenyu Xiong and Mingsheng Wang, with AI assistance
Date2026-09-30

Original source: Xiong and Wang's TRIKE analysis

Appendix Tables 12–14 derive complete filtering only for weak-key classes at or beyond the selected boundary m0. For the adjacent unfiltered classes m0-1, the specification instead reports zero failures in 10^7 experiments for TRIKE-1/2/3/5 and 10^6 for TRIKE-7/9. Those experiments give only 95% upper confidence limits of about 2^-21.67 and 2^-18.35; they do not establish failure probabilities at the claimed 2^-128 through 2^-512 scales.

This is an evidence gap, not evidence that the true DFR is as high as those confidence limits. In particular, zero observed failures supplies no lower bound on the boundary-class DFR and no concrete failure-oracle or key-recovery attack follows from the reported experiment.

Proposed fixes

The original post proposes fitting the failure model separately for the boundary classes and including their density-weighted DFR in the total bound. This section records the proposal without evaluating it.

Reproducing

Commands below run in a checkout of the ngcc-harness repository with the candidate built (see its README).

python3 kem-36/reproduce_spec_proof_gaps.py

The certificate locates the two unfiltered-boundary statements, their experiment counts, and their confidence limits in the submitted PDF.

kem-36-5: The weak-key reduction assumes the secret parity checks are public

SeverityMedium
Scopedesign
StatusProof gap
AffectedTRIKE Theorem 4 and its claimed weak-key-rejection security loss
DiscoveryModerate
ExploitationThe stated reduction cannot test its required predicate from the actual public key; no IND-CCA attack demonstrated
CreditZhenyu Xiong and Mingsheng Wang, with AI assistance
Date2026-09-30

Original source: Xiong and Wang's TRIKE analysis

Normative KEM.KeyGen (Algorithm 4) publishes pk = (sigma,r2) and retains (h0,h1,h2) in the secret key. Theorem 4 nevertheless begins its reduction with “public key pk = (h0,h1,h2)” and has the reduction decide whether that tuple passes the private weak-key predicate. An IND-CCA instance for the actual scheme does not reveal those parity checks, so the described reduction cannot perform its first step.

Conditioning key generation on a rejection event of probability rho can still be related to the original distribution by a generic statistical-distance bound of order rho. The report does not claim the conditioned scheme is insecure; it records that the specification's exact multiplicative reduction and its quoted negligible bit loss are not proved by the stated argument.

Proposed fixes

The original post proposes dropping Theorem 4 and accounting for weak keys on the DFR side instead. This section records the proposal without evaluating it.

Reproducing

Commands below run in a checkout of the ngcc-harness repository with the candidate built (see its README).

python3 kem-36/reproduce_spec_proof_gaps.py

The certificate checks both the actual public-key assignment in Algorithm 4 and the incompatible public-key assumption in Theorem 4.

kem-36-6: Decapsulation addresses depend on the secret support

SeverityMedium
Scopeside-channel
StatusConfirmed
AffectedAll four reference and four optimized implementations
DiscoveryModerate
ExploitationRepeated fine-grained secret-address dependence in decapsulation; no observation trace or key recovery demonstrated
CreditZhenyu Xiong and Mingsheng Wang, with AI assistance
Date2026-09-30

Original source: Xiong and Wang's TRIKE analysis

Decapsulation copies the sparse secret supports h0_idx, h1_idx, and h2_idx from the secret key into the decoder. The reference calc_upc_block then computes idx_to = (i + idx[k]) mod r and reads s[idx_to/8]; its memory addresses therefore expose the support positions repeatedly throughout decoding. The optimized rotation path likewise derives its load offsets from secret shifts. The early-exit comparison used by some paths adds a smaller timing dependency.

This contradicts the specification's statement that the reference implementation was designed to execute in constant time and that the optimized implementation preserves constant-time behavior (p. 12). The finding confirms the address dependence only; the reporters did not capture a cache or power trace and did not recover a key.

Constant-time fix (moderate, hence Medium): replace secret-indexed rotations with an oblivious rotation, such as a masked barrel shifter or a BIKE-style constant-time decoder, and use a masked ciphertext comparison. This is a substantial decoder rewrite, but published techniques exist.

Proposed fixes

The original post proposes register-level rotation for the secret-support operations and a masked ciphertext comparison. This section records the proposal without evaluating it.

Reproducing

Commands below run in a checkout of the ngcc-harness repository with the candidate built (see its README).

python3 kem-36/reproduce_implementation_issues.py

The certificate traces the index from the secret key into the syndrome address in every submitted source tree.

kem-36-7: TRIKE shares the generic unsalted-FO multi-ciphertext loss, no TRIKE-specific weakness

SeverityInfo
Scopedesign
StatusConfirmed
AffectedTRIKE-2, -5, -7 and -9 specification and implementations, in common with other unsalted deterministic FO KEMs
DiscoveryModerate
ExploitationNone specific to TRIKE; with T observed ciphertexts under one key, one shared secret is found in about 2^l/T public encapsulations, which does not break the required single-challenge IND-CCA2 game
CreditZhenyu Xiong and Mingsheng Wang, with AI assistance
Date2026-10-03

Original source: Xiong and Wang's PKC Forum post

Reference: Andreeva et al., ePrint 2025/343

TRIKE encapsulation samples one l-bit message and deterministically derives both ciphertext and shared secret from it and the public key, without a salt (Algorithm 5, physical p. 7; KEM_AlgorithmInstance.c:242–273). Given T ciphertext fingerprints under one key, public re-encapsulation therefore recovers one message and its shared secret after about 2^l/(T+1) trials; for T=2^32 this is about 2^224 encapsulations for TRIKE-5 and 2^480 for TRIKE-9. The scaled witness confirms the mechanism on the submitted code.

This is recorded for information. The amortization is the generic multi-ciphertext loss of any unsalted deterministic FO KEM whose message length equals its target, a property shared by many submissions and by standardized designs. It is not a break of the single-challenge IND-CCA2 game required by the call, and the 2^80 evaluation budget limits chosen decapsulation queries rather than the number of honest ciphertexts available to such a passive search. TRIKE's specification makes no multi-target claim beyond its IND-CCA2 statement and adds nothing that would make the loss worse than in the generic case. A public salt, or a message longer than the target level, would prevent the amortization.

Reproducing

Commands below run in a checkout of the ngcc-harness repository with the candidate built (see its README).

make -C kem-36 reproduce-multitarget
python3 kem-36/reproduce_multitarget_bounds.py

The native scaled witness links the unmodified submitted encapsulation and decapsulation code, forces only the small model message, checks the single global RNG read, and requires the recovered key to match both parties. The certificate checks the full-size source paths and the 2^l/T accounting; the full searches are extrapolated.

kem-36-8: TRIKE resets the external DRBG for deterministic re-encryption

SeverityLow
Scopeimplementation
StatusConfirmed
AffectedTRIKE-2, -5, -7 and -9 reference implementations
DiscoveryTrivial
ExploitationEncapsulation and FO re-encryption agree only for the particular external RBG stream; the frozen submitted build is internally consistent
CreditMarkku-Juhani O. Saarinen markku-juhani.saarinen@tuni.fi, with AI assistance
Date2026-10-03

generate_error_vector hashes msg || r2, resets a local external DRNG_ctx from that value, and draws the deterministic error vector (sample.c:230–244). Encapsulation and decapsulation both call it to make the FO re-encryption comparison (KEM_AlgorithmInstance.c:252,353). This is XOF/PRG functionality: changing the RBG mapping makes honest ciphertexts fail verification.

The bundled drng.c gives a working frozen implementation. The Low defect is making correctness depend on a nominally external RBG rather than a specified deterministic expander.

Reproducing

Commands below run in a checkout of the ngcc-harness repository with the candidate built (see its README).

python3 security/rbg_protocol_dependency.py --report-id kem-36-8

kem-36-9: Same-key multi-instance decoding puts three TRIKE sets below target

SeverityCritical
Scopedesign
StatusConfirmed
AffectedSubmitted TRIKE-5, -7 and -9 under one reused encapsulation key; TRIKE-2 does not cross in the checked model through 2^80 sessions
DiscoveryNon-trivial
ExploitationConditional 2^255.682, 2^383.990 and 2^511.404 bit-operation estimates; full-size decoding not executed
CreditYongge Wang, with AI assistance
Date2026-11-01

Original source: Wang's PKC Forum publication post

TRIKE publishes u = e0+r1*e1+r2*e2 and v = e0+t1*e1+t2*e2 for fresh total-weight-t errors (specification Algorithm 5). Together these are a public [3r,r] syndrome-decoding instance. One ciphertext gives r cyclic rotations of the target; repeated honest encapsulations under one key give more targets. Those ciphertexts must be independently generated by other parties, with ephemeral errors unknown to the attacker; attacker-generated encapsulations do not supply unknown decoding targets. Recovering the true (e0,e1,e2) exposes m = c2+L(e0,e1,e2) and the public K(m,c) derivation. This route concerns decoding the public ciphertext relation, independently of decoder implementation details.

Submitted set Honest ciphertexts DS-DOOM time Memory, bits Target
TRIKE-5 2^47 2^255.682 2^78.290 256
TRIKE-7 2^63 2^383.990 2^96.178 384
TRIKE-9 2^63 2^511.404 2^97.599 512

These are the first below-target points in May and Sá Diogo's pinned DS-DOOM estimator; the previous exponents remain above target. TRIKE-2 is a control at 2^146.699 after 2^80 sessions. The TRIKE-7 crossing is just 0.010 bits below target, so modest alternative cost conventions could erase it. The estimator counts cyclic rotations of each ciphertext as related targets. The paper's much earlier TRIKE-1 crossing concerns a specification-only set that was not submitted. The pinned estimator, controls, and public ciphertext-to-key equations establish the model crossings, hence Critical / Confirmed. No full-size decoding or end-to-end key recovery was performed.

Reproducing

Commands below run in a checkout of the ngcc-harness repository with the candidate built (see its README).

NGCC_ESTIMATOR_PYTHON=/path/to/python-with-numpy-scipy python3 security/reproduce_code_multi_instance.py kem-36

The witness checks all three first crossings, preceding controls and the TRIKE-2 control against the hash-pinned estimator.