Skip to content

add ML-KEM as an option for base OT#48

Open
dartdart26 wants to merge 1 commit intorobinhundt:mainfrom
dartdart26:petar/ml-kem
Open

add ML-KEM as an option for base OT#48
dartdart26 wants to merge 1 commit intorobinhundt:mainfrom
dartdart26:petar/ml-kem

Conversation

@dartdart26
Copy link

@dartdart26 dartdart26 commented Jan 30, 2026

This makes the base OT post-quantum secure by utilizing ML-KEM key encapsulation via https://crates.io/crates/ml-kem.

We keep the Simplest OT as default base OT and make ML-KEM optional by adding compile-time features for different variants (k = 512/768/1024).

References:

Copy link
Owner

@robinhundt robinhundt left a comment

Choose a reason for hiding this comment

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

Hi, thank you so much for your interest in my crate and this PR!

I've had a preliminary look at the paper and the code and left a few comments. I also enabled the pipelines to run for you.

  • the lint and docs checks are currently failing. Note that I'm using nightly rustfmt (see CI here) so that I can enable some extra options.
  • What is really weird, is that on macos, the silent_ot::tests::random_silent_ot test consistently fails. It doesn't fail on Ubuntu and doesn't fail on the main branch. I think this failure has something to do with the mlkem-ot implementation, as the tests are run with --all-features so it will be used, but I'm really stumped why exactly this test fails with a communication error in the PPRF phase and only on macos 🤔

Regarding security:

When used in an OT extension protocol, semi-honest base OT is sufficient for the whole protocol to have malicious security

I'm not sure this holds. E.g. if an actively malicious adversary is able to recover information about the choice bits used for the base OTs of the KOS15 protocol, the whole protocol should be broken.

I'm also a bit unsure about the security of the ML-KEM OT. On the one hand, it offers stronger security by being PQ secure, on other hand, it only offers endemic security whereas the current implementation of Simplest OT offers uniform message security (I think). While the uniform security is currently not documented, it could still be a hazard if the ML-KEM OT offers weaker message security.

I think this is a good opportunity to better document the actual security guarantees of the OT implementations and I like the definitions of the MR19 paper. But I'll need to look a bit closer into that.

I haven't yet looked in-depth into the code itself and potential differences to the libOTe implementation. I'll do that in the coming days.

@dartdart26
Copy link
Author

dartdart26 commented Jan 31, 2026

Thanks for looking the PR! I will fix the small things.

Re malicious security, I was coming from what libOTe + Kyber is doing, but I cannot prove it is secure myself, at least not yet. Even if it isn't, I guess there is still value in using ML-KEM, right?

I agree it would be good to document the requirements and what the extensions end up with given what base OT is used, but I'd need more time. But more importantly, maybe I am not the right person for that as I am not a cryptographer, I hope you can help 🙂

@dartdart26
Copy link
Author

dartdart26 commented Jan 31, 2026

Re MacOS and Linux, maybe could be endianness (ARM vs AMD64) and not the OS.

Or maybe OS-related - socket buffers when using much bigger ciphertexts.

Just speculating, will have a look.

Copy link
Owner

@robinhundt robinhundt left a comment

Choose a reason for hiding this comment

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

Thanks for the changes! It seems that limiting the threads on macos "fixed" the issue. I think there is likely an issue with my network library which caused this issue in the test and also the spurious benchmarking failures. It's annoying that I can't seem to replicate it locally.

I've had a closer look at the implementation now and it does not seem to correctly implement the MR19 protocol, see the comment below.

Comment on lines +184 to +205
let (dk, ek) = MlKem::generate(&mut RngCompat(&mut self.rng));
let real_ek = EncapKeyBytes(
ek.as_bytes()
.as_slice()
.try_into()
.expect("incorrect encapsulation key size"),
);
let fake_ek = EncapKeyBytes(self.rng.random());

let ek0 = EncapKeyBytes::conditional_select(&real_ek, &fake_ek, *choice);
let ek1 = EncapKeyBytes::conditional_select(&fake_ek, &real_ek, *choice);

decap_keys.push(dk);
eks0.push(ek0);
eks1.push(ek1);
}

let receiver_msg = EncapsulationKeysMessage { eks0, eks1 };
{
let mut send_stream = send.as_stream();
send_stream.send(receiver_msg).await?;
}
Copy link
Owner

Choose a reason for hiding this comment

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

This does not follow the MR19 protocol Figure 8. One message needs to be a real ek corresponding to the dk, and the other one needs to be the real ek minus a hash of the random ek. See the libOTe implementation here.

Crucially, both messages need to look like actual group elements of the UKA protocol. In the current version, ek_c is an actual key generated by the MlKem library, whereas the other one are just random bytes. The problem is, that the actual ek is distinguishable from the fake_ek as it has a specific structure which the random bytes do not have with high likelihood.

Sadly, it seems the ml-kem crate does not directly expose the necessary internals required to implement the MR19 protocol (e.g. adding/subtracting of group elements, hashing to the group)... Ohh, I just saw that the ML-KEM crate uses the module-lattice crate for the arithmetic. Maybe that can be used.

Copy link
Author

Choose a reason for hiding this comment

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

Thank you, let me look into that!

And maybe a dumb question - should the reference be the MR19 paper if we are using ML-KEM and not Kyber?

Also, what about the receiver being able to keep the fake key? I will dig into that more, but have you had a chance to look?

Copy link
Author

@dartdart26 dartdart26 Feb 1, 2026

Choose a reason for hiding this comment

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

Actually, ignore these questions. I've realized I've misunderstood the endemic thing and assumed quite a lot. Sorry for wasting your time on that. Let me get back at it and will try to make it correctly in this PR. Thank you!

Copy link
Owner

Choose a reason for hiding this comment

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

No worries, the MR19 paper is actually very interesting and clarified some relations between key exchange and OT for me. It is quite long though and I initially also had some trouble understanding how the generic OT protocol presented in the paper can be used with ML-KEM.

@robinhundt
Copy link
Owner

I've had a closer look at the ml-kem and module-lattice crate and I think they can be used to implement the endemic OT protocol. The module-lattice crate has the NttVector type, which as I understand it corresponds to the group G in Figure 8 of MR19 and is the domain of the encapsulation keys in ML-KEM. This type already implements addition and subtraction which will be needed for the OT protcol and can be encoded/decoded to a byte array. In the ml-kem crate, we can see how the module-lattice crate is used. Note that first the define_field macro is used to define the prime order field.
The file also contains the smple_ntt which we could copy for the secure sampling a random encapsulation key. I think this implements algorithm 7 from FIPS 203.

This leaves hashing to the group which I'm about unsure how to implement. It seems libOTe hashes the pk to a 32-byte seed and then from that seed samples a new random pk (see here). I think this operation is described section D.3 of the MR19 paper but it is a bit hard for me to understand.

@robinhundt
Copy link
Owner

As it seems that the module lattice crate was recently extracted from ML-KEM, it probably makes sense to use the newest pre-release of ml-kem which uses the module-lattice crate to hopefully reduce the amount of duplicated code.

robinhundt added a commit that referenced this pull request Feb 2, 2026
Based on the discussion in #48 this clarifies the message security of
the CO15 base OT implementation using the terminology of MR19.
robinhundt added a commit that referenced this pull request Feb 2, 2026
Based on the discussion in #48 this clarifies the message security of
the CO15 base OT implementation using the terminology of MR19.
robinhundt added a commit that referenced this pull request Feb 2, 2026
Based on the discussion in #48 this clarifies the message security of
the CO15 base OT implementation using the terminology of MR19.
@dartdart26
Copy link
Author

I am back to it and looking at how to implement.

@dartdart26 dartdart26 marked this pull request as draft March 15, 2026 10:11
@dartdart26
Copy link
Author

I've pushed something, but please don't review it yet, I am still working on it. I put the PR as a draft for now.

Will ping you once ready to review.

dartdart26 pushed a commit to dartdart26/CryProt that referenced this pull request Mar 18, 2026
Based on the discussion in robinhundt#48 this clarifies the message security of
the CO15 base OT implementation using the terminology of MR19.
@dartdart26 dartdart26 force-pushed the petar/ml-kem branch 3 times, most recently from 7070fff to 22cda7a Compare March 18, 2026 23:25
@dartdart26 dartdart26 marked this pull request as ready for review March 18, 2026 23:27
@dartdart26
Copy link
Author

Hey @robinhundt, could you please have a look? I now generate the encapsulation keys as you mentioned in the comments above, following libOTe's implementation. I also put an .md file that describes the approach - essentially, I've tried to stick to what you suggested.

This makes the base OT post-quantum secure by utilizing ML-KEM key
encapsulation via https://crates.io/crates/ml-kem.

We keep the Simplest OT as default base OT and make ML-KEM optional by
adding compile-time features for different variants (k = 512/768/1024).

References:

MR19: https://eprint.iacr.org/2019/706
FIPS 203: https://csrc.nist.gov/pubs/fips/203/final
libOTe: https://github.com/osu-crypto/libOTe/blob/d0e499206d1d4d16c6b4ca6c0e712490e0632f80/thirdparty/KyberOT/KyberOT.c#L40-L41
ML-KEM implementation: https://github.com/RustCrypto/KEMs/blob/5a7f3ab7af5420cacca9befc9212532e4c7f6ca1/ml-kem/src/
ml-kem crate: https://crates.io/crates/ml-kem
module-lattice crate: https://crates.io/crates/module-lattice
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