[CFRG] Nailing down non-contributory behaviour in the hybrid-kems drafts

Jack Grigg <ietf@jackgrigg.com> Wed, 15 July 2026 13:43 UTC

Return-Path: <me@jackgrigg.com>
X-Original-To: cfrg@mail2.ietf.org
Delivered-To: cfrg@mail2.ietf.org
Received: from localhost (localhost [127.0.0.1]) by mail2.ietf.org (Postfix) with ESMTP id 6FBE51173423A for <cfrg@mail2.ietf.org>; Wed, 15 Jul 2026 06:43:35 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ietf.org; s=ietf1; t=1784123015; bh=kYzYz1acjllA7bmc8qwVBEQLy6KhnGIyR8R+F+XcyoQ=; h=Reply-To:From:Date:Subject:To; b=Jh0pctTNVhvZHNdjh99HdL5pOLp/IgEqoN4NQ1PsrswGwo7w2pRWQkuE/4xN+/8ID G36HgpoYAeM36OMtnIg3D5j712pxBJzG2wWmTROdU+kvRVG9evdpvobpyklQ8uM/Je toBHhoL/TbwbpLZaF44sZ1UmTvqh+i+qhnDsYjfM=
X-Virus-Scanned: amavisd-new at ietf.org
X-Spam-Flag: NO
X-Spam-Score: -1.895
X-Spam-Level:
X-Spam-Status: No, score=-1.895 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001] autolearn=ham autolearn_force=no
Received: from mail2.ietf.org ([166.84.6.31]) by localhost (mail2.ietf.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id UyvBfFNCzIuN for <cfrg@mail2.ietf.org>; Wed, 15 Jul 2026 06:43:34 -0700 (PDT)
Received: from mail-ej1-f54.google.com (mail-ej1-f54.google.com [209.85.218.54]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature ECDSA (P-256) server-digest SHA256) (No client certificate requested) by mail2.ietf.org (Postfix) with ESMTPS id A576011734233 for <cfrg@irtf.org>; Wed, 15 Jul 2026 06:43:34 -0700 (PDT)
Received: by mail-ej1-f54.google.com with SMTP id a640c23a62f3a-c15b509c323so711063766b.0 for <cfrg@irtf.org>; Wed, 15 Jul 2026 06:43:34 -0700 (PDT)
ARC-Seal: i=1; a=rsa-sha256; t=1784123008; cv=none; d=google.com; s=arc-20260327; b=A0oyornJpAoInJ/51u6BzQJpyTudN/QQbq8PHp9VYYPk95+EgjlsudTbdqnCvjWaK5 YdpbNhwZQgQwFrqesdg2TxOOIi07rjrXItwvoArgL/gR2NsUFkOw++jj94eBf+tk7MjR asT8GxGCl5Xfgg0Crh4g+nvyFNAcD70dtGJv0+GDgr3Od1YosLpGx8F58aVA5XrAyAQg tDNjCIViBvpiE+Qdijxj+pSuzN/EZNluP3I/USvIGHMPalLLkH85PowBnxHT3IZd8s8t mcb68OPkicNL2aWenUnCo7l6AtOqvFO2Je58CT8zw+q081VbxSmouhc+82IHKcV0d4Ij rWVA==
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=to:subject:message-id:date:from:reply-to:mime-version; bh=VJaMLeBvjIMWOT0koD5qsm4JsusnnzlA57HwdcyDhFg=; fh=PtPfqTVe+9RcQClHmar7JeBMB314Y31yhoSfdH8nv9s=; b=Zv2rP3v3hGc58nZ1MhESxbvuy1EDVZtTSjaqYxOdGwiLE8yJgaEuTjvq85h4IAgJ9N qSLBhKrlLq97RoGnkqofvyJrig1FzwIjAOdrvaeM7nUPJef72WrQL+ncqelMhMLKbGIg bewYld1nxkfiBEcbHS7oMxd7lSaVBHuYe5KwTePaSIedf0PhXXquOiWxJ73ogV40luqa GsEn5k/Fb2GIuCyVpz567Qp/L2kkJO6eQmEPGNV+TxQX1hX9KTftQV8f8me9QOOqfWZc zFnKUMe81ks7OaHraguO6zt+Zp9Ii1b7ZbyhSHu52M+M/jBaAJFSaJXjQxG1p5r3wv+D T2Qw==; darn=irtf.org
ARC-Authentication-Results: i=1; mx.google.com; arc=none
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784123008; x=1784727808; h=content-type:to:subject:message-id:date:from:reply-to:mime-version :x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to:content-type; bh=VJaMLeBvjIMWOT0koD5qsm4JsusnnzlA57HwdcyDhFg=; b=N/FnfLd4I3nmCvR0mVdgyVqaSLt3Xgg4XJ6xxcXKua8fENevCTWlUc3f43QTG5gAsz 3MqZu2ag4tutxabfmyOomfWZ7zXaE/o1tQ5g+SCKa1s9KxmY+1aUqfjNDaY+VSPAzCy4 wPSv3g+GaoM5aiedbUa6Qv6X3MabDG4zWoySVmBN+nPjoc62lYZqU7kwxhTNrp9JGobd gZVKuB2rc2YMPXTN4qSXArkgGwqO+jWVTj0W3zHvVNYlbSUiR+67H++sZZmsAy7J3kyT 8+xHmbfK1I09LJtpkrVcDfX+QnLRuqrx/sfOqKgj19Cj3laBy/D7+/2YI4DfMnn6psVS 2nGQ==
X-Gm-Message-State: AOJu0Yy6uoS816uda8lC/mk+R87L0ZnMoPdz4568nZDX4cykid+v4J94 9LGGC+Z5LSiOoCdGnJMbIDIoB+yOMyjNsrp0xyEgpyZ2UckYtKopHYS9/xJM0Fc66e5crIQvJdX HMGbBLmavOj0c0xs+CaExXyhWrUhCWxYKYPAxgcq0U+PAyGraupwBRXYpr52UcEo=
X-Gm-Gg: AfdE7cnYY4yxsXr+Xe6ily96xXIFmhANHsjfubDERY3cBdM5XMMGq5YPhKPpC+cMhTx Zl2MhxRNcrqFIi5bR553raQM2mTqYdNNpL4bfJLesXhpSpFsHbUzxo81oufZcfpYS7r/A53nbkY iONCk5DavYBXPEcJv1NIkvhkPvYR+Mr4bHsVk/X+WvJ6gsCFlc9QJkyKhQqiu/83aPm8YoWzshN t+cnpdFVCl9dRQ3yV7g9EV0Q72w6JpZer6/uurVUrB7uQhO547bKZpJbaq7m9iHqX+I3Qt8
X-Received: by 2002:a17:906:730f:b0:c12:8cbe:b897 with SMTP id a640c23a62f3a-c1667935e07mr411151066b.15.1784123007299; Wed, 15 Jul 2026 06:43:27 -0700 (PDT)
MIME-Version: 1.0
From: Jack Grigg <ietf@jackgrigg.com>
Date: Wed, 15 Jul 2026 14:43:15 +0100
X-Gm-Features: AUfX_mw-YWrFGlrab7eBXhbQ5Ai5Re2uCYoWU3bnwQ6y38kSEdk_9PP2tioSNOc
Message-ID: <CAPC=aNXyp1qpsbMiTYjqc=RX8tmWmKCasTS8sKh4Q==UOi+=sQ@mail.gmail.com>
To: IRTF CFRG <cfrg@irtf.org>
Content-Type: multipart/alternative; boundary="000000000000163f270656a682c3"
Message-ID-Hash: 6FGMYA3ZSPUFNZ45XO3VBQY2KJ35FJXP
X-Message-ID-Hash: 6FGMYA3ZSPUFNZ45XO3VBQY2KJ35FJXP
X-MailFrom: me@jackgrigg.com
X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-cfrg.irtf.org-0; header-match-cfrg.irtf.org-1; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header
X-Mailman-Version: 3.3.9rc6
Precedence: list
Reply-To: ietf@jackgrigg.com
Subject: [CFRG] Nailing down non-contributory behaviour in the hybrid-kems drafts
List-Id: Crypto Forum Research Group <cfrg.irtf.org>
Archived-At: <https://mailarchive.ietf.org/arch/msg/cfrg/t2nIVd88lzBpJjw_XF1hrBqiR9A>
List-Archive: <https://mailarchive.ietf.org/arch/browse/cfrg>
List-Help: <mailto:cfrg-request@irtf.org?subject=help>
List-Owner: <mailto:cfrg-owner@irtf.org>
List-Post: <mailto:cfrg@irtf.org>
List-Subscribe: <mailto:cfrg-join@irtf.org>
List-Unsubscribe: <mailto:cfrg-leave@irtf.org>

 Hi all,

While implementing a protocol using X-Wing (specifically post-quantum
support in age encryption [0] using HPKE), I encountered an incompatibility
between two different X-Wing implementations that led me down the
hybrid-kems rabbit hole, and I uncovered several inconsistencies in the
drafts that should be resolved.

The core problem stems from Section 6.1 of RFC 7748 [1], which placed a MAY
on checking for non-contributory behaviour:

> Both now share K = X25519(a, X25519(b, 9)) = X25519(b, X25519(a, 9)) as a
shared secret.  Both MAY check, without leaking extra
> information about the value of K, whether K is the all-zero value and
abort if so (see below).
> [...]
> The check for the all-zero value results from the fact that the X25519
function produces that value if it operates on an input corresponding to a
point with small order, where the order divides the cofactor of the curve
(see [Section 7](https://www.rfc-editor.org/info/rfc7748/#section-7))

Neither the original X-Wing draft [2], nor the ePrint it is based on [3]
mentioned this, and thus inherently embedded the MAY in its specification.
This has led to different X-Wing implementations implementing different
behaviour, likely based on whatever their existing X25519 implementation
made most convenient to do.

However, this is seemingly incompatible with the current draft
specification for X-Wing (aka MLKEM768-X25519) in
draft-irtf-cfrg-concrete-hybrid-kems-04 [4]. Specifically, Section 4.2 of
draft-irtf-cfrg-hybrid-kems-12 [5] states:

> A scalar value of zero MUST NOT be used as a private key: it does not
correspond to a meaningful Diffie-Hellman exchange and, for some curves
such as P-256, is explicitly forbidden by the relevant standards and would
yield a point-at-infinity encoding that departs from the fixed-size model
used here. RandomScalar MUST NOT return a zero scalar; for the groups of
interest this occurs only with negligible probability.

A strict reading of the above could be interpreted as allowing
non-contributory behaviour, because AFAICT there's nothing in the current
text that actually does the identity check on the nominal group shared
secret. However, I think these MUST NOTs imply it was the authors'
intentions to require rejection of non-contributory behaviour. Indeed, if
you read the specifications of expandDecapsKeyG and prepareEncapsG [6] with
those MUST NOTs enforced, then it is impossible to produce a ciphertext
that would have non-contributory behaviour, and thus an implementation
could quite justifiably reject those that do.

Further supporting evidence for rejecting non-contributory behaviour in
IETF KEMs is found in RFC 9180 [7], which makes this a MUST for the X25519
DHKEM (which its update draft [8] preserves).

Now, for an honestly-generated key and ciphertext, the chance of
encountering this divergence is cryptographically negligible:
- The Curve25519 scalar within a decapsulation key is a subslice of the
output of PRG(seed) (via GenerateKeyPair [9] and expandDecapsKeyG).
- The ephemeral Curve25519 scalar used within encapsulation is the output
of RandomScalar(random(32)) (via prepareEncapsG), which is constrained by
the MUST NOT on the output of RandomScalar.

However, for an adversarial ciphertext, there is nothing preventing the
adversary from using zero as the ephemeral Curve25519 scalar, or a point of
small order as the ephemeral public key. Indeed, I encountered this issue
due to this specific edge case being exercised in age test vectors. So the
probability of encountering this divergence is non-negligible, and the
specs should handle it.

My suggested fix is to alter the definition of ElementToSharedSecret [10]
to something like:

>  ElementToSharedSecret(P) -> ss: Extract a shared secret from an element
of the group (e.g., by taking the X coordinate of an elliptic curve point),
aborting if a valid shared secret cannot be extracted.

and then for the Curve25519 Nominal Group defined in Section 3.1.2
of draft-irtf-cfrg-concrete-hybrid-kems-04 [11], change its
ElementToSharedSecret specification to:

> ElementToSharedSecret(p) -> ss: Aborts if P is the all-zero value,
otherwise outputs P.

However, that does then raise the question of whether X-Wing is being
assumed as purely an implicitly-rejecting KEM (due to how its ML-KEM
component behaves). I don't believe it was ever valid to assume this, due
to the MAY in the X25519 component meaning that X25519 implementations can
explicitly abort, and thus so might X-Wing. However, if this is a
widely-held and relied-upon assumption, then it would imply that a
compatible X-Wing spec would instead need to require that
expandDecapsKeyG MUST NOT check whether the shared secret is the all-zero
value, in order to prevent the abort. I myself would prefer that X-Wing
follow the RFC 9180 precedent and reject non-contributory behaviour.

---

Regardless of how the above is addressed, at least one other fix is
necessary. The Curve25519 Nominal Group specifies its RandomScalar as:

> RandomScalar(seed) -> k: Implemented by the identity function, i.e., by
returning the seed.

This trivially violates the MUST NOT from draft-irtf-cfrg-hybrid-kems-12;
the all-zero seed produces the forbidden zero scalar. This should be
similarly updated to:

> RandomScalar(seed) -> k: Aborts if seed is the all-zero value, otherwise
returns the seed.

Cheers,
Jack

[0] https://c2sp.org/age#mlkem768x25519-recipient-stanza
[1] https://www.rfc-editor.org/info/rfc7748/#section-6.1
[2] https://datatracker.ietf.org/doc/html/draft-connolly-cfrg-xwing-kem-10
[3] https://eprint.iacr.org/2024/039
[4]
https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-concrete-hybrid-kems-04#name-mlkem768-x25519
[5]
https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hybrid-kems-12#section-4.2-4
[6]
https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hybrid-kems-12#name-using-a-nominal-group
[7] https://www.rfc-editor.org/info/rfc9180/#section-7.1.4
[8]
https://datatracker.ietf.org/doc/html/draft-ietf-hpke-hpke-04#name-validation-of-inputs-and-ou
[9]
https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hybrid-kems-12#name-key-generation
[10]
https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hybrid-kems-12#section-4.2-3.3.1
[11]
https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-concrete-hybrid-kems-04#name-curve25519-nominal-group