Subject: [CFRG] Re: [lamps] Re: Re: Cross-testing update on LAMPS Composite and cfrg-concrete-hybrid-kems
From: Martin Schanzenbach <[email-masked]>
To: Thad Thompson <[email-masked]>, Filippo Valsorda <[email-masked]>
Cc: CFRG <[email-masked]>, [email-masked], LAMPS WG <[email-masked]>
Date: Mon, 27 Oct 2025 11:04:05 +0000
List-Id: Crypto Forum Research Group <cfrg.irtf.org>

Am Mittwoch, dem 22.10.2025 um 07:46 -0500 schrieb Thad Thompson:
> > Implementations can obviously always go off-spec for keygen.
> 
> This seems like the heart of the issue. 
> 
> Let's say I get my PQ-HPKE library implemented and tested and it
> works fine with all the test vectors. But in prod we're only allowed
> to use EC private keys forged in the fires of Mount Doom. One does
> not simply tell their boss (and customers) "no problem, we'll just go
> off spec." 
> 
> It would go a long way if there were a section clearly explaining the
> security impact of using EC keys generated elsewhere, making it an
> 'in-spec' option.
> 
> Best
> -Thad

+1

I have to admit, that in my HPKE implementation I did not even notice
(or have enforced) that X25519 keys generated through other means
cannot be used.
Is this a MUST requirement? Where is the reasoning in particlar for
X25519 and X448? If it is a MUST, somewhere in the document should be
an explanation on why it is done such that other KEMs can make an
informed decision how to place limitations on their (private) keys in a
similar fashion.

BR

> 
> 
> On Tue, Oct 21, 2025 at 6:06 PM Filippo Valsorda
> <[email-masked]> wrote:
> > Hi Mike,
> > 
> > Since this thread is about hybrid KEMs and not HPKE DHKEM, I think
> > you are confusing the DeriveKeyPair function of RFC 9180 with the
> > CG framework DeriveKeyPair function of draft-irtf-cfrg-hybrid-kems-
> > 06, Section 5.5, which references the RandomScalar function
> > of draft-irtf-cfrg-concrete-hybrid-kems-01, Section 3.1.1. (I also
> > find it hard to navigate the split in documents.)
> > 
> > I have reproduced them below for you. I am certain you can
> > implement them with any major cryptography library. There is
> > nothing HPKE-specific about them. SP 800-56A, Section 5.6.1.2, only
> > specifies a random key generation algorithm (a pretty silly one,
> > IMHO, with the needless "d = c + 1" instead of checking "c > 0"),
> > so I am not sure what relevance it has here: there is no such thing
> > as a standard library function for SP 800-56A, Section
> > 5.6.1.2.{1,2} key derivation. If the argument is that no one should
> > be allowed to derive ECDH keys from a KDF because NIST doesn't like
> > that, I doubt many folks will find that argument persuasive.
> > 
> > Implementations can obviously always go off-spec for keygen if they
> > need to appease a regulator (or e.g. use hardware keys), though!
> > They will still interoperate.
> > 
> > Cheers,
> > Filippo
> > 
> > def DeriveKeyPair(seed):
> >     (ek_PQ, ek_T, dk_PQ, dk_T) = expandDecapsKeyG(seed)
> >     return (concat(ek_PQ, ek_T), seed)
> > def expandDecapsKeyG(seed):
> >     seed_full = PRG(seed)
> >     (seed_PQ, seed_T) = split(KEM_PQ.Nseed, Group_T.Nseed, seed_full)
> > 
> >     (ek_PQ, dk_PQ) = KEM_PQ.DeriveKeyPair(seed_PQ)
> >     dk_T = Group_T.RandomScalar(seed_T)
> >     ek_T = Group_T.Exp(Group_T.g, dk_T)
> > 
> >     return (ek_PQ, ek_T, dk_PQ, dk_T)
> > def RandomScalar(seed):
> >   state = XOF.Init(seed)
> >   sk = OS2IP(XOF.Read(state, Nscalar))
> >   while sk == 0 || sk >= order:
> >     sk = OS2IP(XOF.Read(state, Nscalar))
> >   return (sk, pk(sk))
> > 
> > (The XOF is SHAKE256, Nseed/Nscalar is 32 for P-256, and 48 for P-
> > 384.)
> > 
> > 2025-10-22 00:31 GMT+02:00 Mike Ounsworth
> > <[email-masked]>:
> > > Hi Watson,
> > > 
> > > SP 800-56A section 5.6.1 defines the FIPS-approved ways of
> > > deriving a ECDH key. You can't derive your keys as described in
> > > rfc9180 7.1.3 and claim that it is "ECDH" as defined in SP 800-
> > > 56A, because it isn't. That's not a FIPS-allowed way to derive EC
> > > keys, and that's the whole story.
> > > 
> > > On Tue, 21 Oct 2025 at 14:23, Watson Ladd <[email-masked]>
> > > wrote:
> > > > On Tue, Oct 21, 2025 at 12:14 PM Mike Ounsworth
> > > > <[email-masked]> wrote:
> > > > >
> > > > > Follow-up: I see that DHKEM.DeriveKeyPair(ikm) is defined in
> > > > RFC9180:
> > > > > https://datatracker.ietf.org/doc/html/rfc9180#derive-key-pair
> > > > >
> > > > > That makes liberal use of HPKE's LabelledExtract(), which
> > > > fully explains why this is not compatible with the python API.
> > > > >
> > > > > That means that a seed-based key derivation is defined for P-
> > > > 256, P-384, and P-521, but only within the confines of the
> > > > DHKEM primitive within the HPKE protocol. It is still the case
> > > > that a seed-based key derivation is not defined for ECDH in
> > > > general.
> > > > >
> > > > > So, on that grounds, I object to draft-irtf-cfrg-concrete-
> > > > hybrid-kems RECOMMENDING a seed-based priv on exactly the same
> > > > grounds that anyone else would object to it RECOMMENDING an
> > > > ASN.1 based encoding: this is a CFRG document that normatively
> > > > depends on a building block that only exists in one single IETF
> > > > protocol, and most importantly to me, does not exist in FIPS or
> > > > current cryptographic libraries.
> > > >  
> > > > I don't understand: HKDF (or the underlying hash function worst
> > > > case)
> > > > absolutely does exist in current cryptographic libraries. If
> > > > the
> > > > argument is that a private key generated one way won't work
> > > > when moved
> > > > to a different system, that's a slightly different matter.
> > > >  
> > > > >
> > > > > On Tue, 21 Oct 2025 at 13:34, Mike Ounsworth
> > > > <[email-masked]> wrote:
> > > > >>
> > > > >> Hi CFRG and HPKE,
> > > > >>
> > > > >> I just want to give an update on this. Richard and I did
> > > > some more cross-testing this week, and we are at an impasse on
> > > > the SEC-P curves, and it has to do with the seeds. The CFRG
> > > > /HPKE draft stores the hybrid private keys as a seed, so you
> > > > have to re-derive the P256 half from a seed. My problem is that
> > > > ECDH is a thing that has been standardized for like 20 years in
> > > > [SEC1] and [SP.800-56A], and those two documents do not specify
> > > > or allow a seed-based EC keygen, and as a consequence, many
> > > > crypto libraries don't support a seed-based EC keygen API. For
> > > > example, I'm doing my reference implementation for Composites
> > > > on top of the core python cryptography library, and I cannot
> > > > implement the CRFG / HPKE draft that way. Python crypto has
> > > > this:
> > > > >>
> > > > https://cryptography.io/en/latest/hazmat/primitives/asymmetric/ec/#cryptography.hazmat.primitives.asymmetric.ec.derive_private_key
> > > > >> but that appears to not derive the same keys as Richard's
> > > > implementation.
> > > > >>
> > > > >> The issue is that EC.derive_priv_from_seed() is not a
> > > > standardized function. You can probably infer from a careful
> > > > read of [SEC1] or [SP.800-56A] what it needs to be, but it is
> > > > not actually standardized or FIPS-allowed. It requires mucking
> > > > around with EC point multiplication. The fact that it's not a
> > > > standardized thing means that I will likely not be the last
> > > > person to have interop problems with this. Maybe my issues are
> > > > as simple as not using that python ec.derive_private_key()
> > > > properly, maybe his scalar is 'big' rather than 'little' or
> > > > something. If that's the case, I'd be happy -- but this is the
> > > > kind of mess you get into when you use non-standardized APIs
> > > > for existing 20 year-old primitives.
> > > > >>
> > > > >> So at this point, I think we have to declare non-
> > > > compatibility between LAMPS and HPKE Hybrid KEMs. Since our X-
> > > > Wings are compatible, we know that our QSF frameworks are
> > > > identical, so in principle our P256's should work too, but
> > > > we're been incapable of testing it because this off-spec seed
> > > > stuff is getting in the way.
> > > > >>
> > > > >> On Thu, 16 Oct 2025 at 17:40, Tushar Patel
> > > > <[email-masked]> wrote:
> > > > >>>
> > > > >>> Sorry, jumping in a running train and
> > > > >>> many years ago while working through JSON bindings for the
> > > > tests, I ran into a sign bit conversion error on big numbers
> > > > and had to rewrite part of the library, the fixed code is with
> > > > a previous employer, 64-bit and 128-bit in JSON libs are a mess
> > > > or sign bit issues in general.
> > > > >>>
> > > > >>> Also, please make sure that you are parsing the compressed
> > > > notations for ECC correctly, some libraries pack those
> > > > incorrectly and also wrote my own ASN.1 parsers for some
> > > > elements, you might be surprised where I hit these areas, can’t
> > > > disclose, though both were from reputed organizations.
> > > > >>>
> > > > >>> Last, please make sure you have matching generators and
> > > > incorporate the affine and infinity checks, these may translate
> > > > to all sorts of weird issues that are impossible to debug
> > > > through OSS macros in most cases.
> > > > >>>
> > > > >>> There is substandard ECC, ECDSA and ECDHE at many places in
> > > > OSS libs and you may have to debug these.
> > > > >>>
> > > > >>> Personally, I think NIST should mandate pristine
> > > > implementations as companies spend a lot more time fixing other
> > > > implementations than their own.
> > > > >>>
> > > > >>> Hope it’s Useful,
> > > > >>> Tushar
> > > > >>>
> > > > >>> On Thu, Oct 16, 2025 at 8:45 AM Mike Ounsworth
> > > > <[email-masked]> wrote:
> > > > >>>>
> > > > >>>> Thanks Filippo,
> > > > >>>>
> > > > >>>> Richard and I did some debugging last night. He gave me
> > > > new test vectors that include that bug fix to the P256 / P384
> > > > constants.
> > > > >>>>
> > > > >>>> My current theory is that the code I quickly whipped up to
> > > > do a P256 private key expansion from seed does not match
> > > > Richard's. Since this is wholly out of scope for LAMPS
> > > > Composite (we store the full P256 private key), I'm hoping that
> > > > Richard can give me a dump of intermediate values that includes
> > > > the expanded P256 / P384 private key so that we don't need to
> > > > debug through the P256-from-seed stuff. Anyway, this is all
> > > > good validation of both our test vectors.
> > > > >>>>
> > > > >>>> Anyway, making progress!
> > > > >>>>
> > > > >>>> On Thu, 16 Oct 2025 at 10:29, Filippo Valsorda
> > > > <[email-masked]> wrote:
> > > > >>>>>
> > > > >>>>> 2025-10-16 03:55 GMT+02:00 Mike Ounsworth
> > > > <[email-masked]>:
> > > > >>>>>
> > > > >>>>> Results: we are cross-compatible on X-Wing, but not on
> > > > P256 (and that's after hacking my script to use the CFRG label
> > > > not the LAMPS label, so there's clearly some other bug at play,
> > > > but I have no idea what, so of course I assume it's on the cfrg
> > > > reference impl side 😋).
> > > > >>>>>
> > > > >>>>>
> > > > >>>>> Could be
> > > > https://github.com/cfrg/draft-irtf-cfrg-concrete-hybrid-kems/pull/21
> > > > .
> > > > >>>>
> > > > >>>> _______________________________________________
> > > > >>>> CFRG mailing list -- [email-masked]
> > > > >>>> To unsubscribe send an email to [email-masked]
> > > > >
> > > > > _______________________________________________
> > > > > Spasm mailing list -- [email-masked]
> > > > > To unsubscribe send an email to [email-masked]
> > > >  
> > > >  
> > > >  
> > > > -- 
> > > > Astra mortemque praestare gradatim
> > 
> > 
> > _______________________________________________
> > CFRG mailing list -- [email-masked]
> > To unsubscribe send an email to [email-masked]
> _______________________________________________
> CFRG mailing list -- [email-masked]
> To unsubscribe send an email to [email-masked]