RFC: Add incremental encaps API to support ML-KEM Braid#1619
RFC: Add incremental encaps API to support ML-KEM Braid#1619mkannwischer wants to merge 12 commits intomainfrom
Conversation
325ab51 to
285fc8a
Compare
There was a problem hiding this comment.
Intel Xeon 4th gen (c7i)
Details
| Benchmark suite | Current: 0a0a167 | Previous: 712709d | Ratio |
|---|---|---|---|
ML-KEM-512 keypair |
11989 cycles |
11971 cycles |
1.00 |
ML-KEM-512 encaps |
13728 cycles |
13745 cycles |
1.00 |
ML-KEM-512 decaps |
17966 cycles |
17771 cycles |
1.01 |
ML-KEM-768 keypair |
21321 cycles |
21010 cycles |
1.01 |
ML-KEM-768 encaps |
22292 cycles |
22095 cycles |
1.01 |
ML-KEM-768 decaps |
28060 cycles |
28300 cycles |
0.99 |
ML-KEM-1024 keypair |
29955 cycles |
29866 cycles |
1.00 |
ML-KEM-1024 encaps |
31948 cycles |
31758 cycles |
1.01 |
ML-KEM-1024 decaps |
40366 cycles |
39591 cycles |
1.02 |
This comment was automatically generated by workflow using github-action-benchmark.
There was a problem hiding this comment.
ppc64le (POWER10) benchmarks
Details
| Benchmark suite | Current: 0a0a167 | Previous: 712709d | Ratio |
|---|---|---|---|
ML-KEM-512 keypair |
59214 cycles |
60047 cycles |
0.99 |
ML-KEM-512 encaps |
72322 cycles |
72930 cycles |
0.99 |
ML-KEM-512 decaps |
91754 cycles |
92987 cycles |
0.99 |
ML-KEM-768 keypair |
98445 cycles |
98984 cycles |
0.99 |
ML-KEM-768 encaps |
115372 cycles |
115469 cycles |
1.00 |
ML-KEM-768 decaps |
141100 cycles |
141322 cycles |
1.00 |
ML-KEM-1024 keypair |
148781 cycles |
149075 cycles |
1.00 |
ML-KEM-1024 encaps |
167999 cycles |
167651 cycles |
1.00 |
ML-KEM-1024 decaps |
199548 cycles |
198842 cycles |
1.00 |
This comment was automatically generated by workflow using github-action-benchmark.
There was a problem hiding this comment.
AMD EPYC 3rd gen (c6a)
Details
| Benchmark suite | Current: 0a0a167 | Previous: 712709d | Ratio |
|---|---|---|---|
ML-KEM-512 keypair |
14284 cycles |
14291 cycles |
1.00 |
ML-KEM-512 encaps |
16843 cycles |
16019 cycles |
1.05 |
ML-KEM-512 decaps |
21421 cycles |
21507 cycles |
1.00 |
ML-KEM-768 keypair |
24344 cycles |
24715 cycles |
0.98 |
ML-KEM-768 encaps |
25386 cycles |
25491 cycles |
1.00 |
ML-KEM-768 decaps |
34652 cycles |
33275 cycles |
1.04 |
ML-KEM-1024 keypair |
37236 cycles |
37264 cycles |
1.00 |
ML-KEM-1024 encaps |
36923 cycles |
36892 cycles |
1.00 |
ML-KEM-1024 decaps |
49514 cycles |
46772 cycles |
1.06 |
This comment was automatically generated by workflow using github-action-benchmark.
There was a problem hiding this comment.
⚠️ Performance Alert ⚠️
Possible performance regression was detected for benchmark 'AMD EPYC 3rd gen (c6a)'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.03.
| Benchmark suite | Current: 0a0a167 | Previous: 712709d | Ratio |
|---|---|---|---|
ML-KEM-512 encaps |
16843 cycles |
16019 cycles |
1.05 |
ML-KEM-768 decaps |
34652 cycles |
33275 cycles |
1.04 |
ML-KEM-1024 decaps |
49514 cycles |
46772 cycles |
1.06 |
This comment was automatically generated by workflow using github-action-benchmark.
There was a problem hiding this comment.
Intel Xeon 4th gen (c7i) (no-opt)
Details
| Benchmark suite | Current: 0a0a167 | Previous: 712709d | Ratio |
|---|---|---|---|
ML-KEM-512 keypair |
28980 cycles |
28131 cycles |
1.03 |
ML-KEM-512 encaps |
35927 cycles |
36568 cycles |
0.98 |
ML-KEM-512 decaps |
45329 cycles |
45119 cycles |
1.00 |
ML-KEM-768 keypair |
46748 cycles |
46297 cycles |
1.01 |
ML-KEM-768 encaps |
55800 cycles |
55658 cycles |
1.00 |
ML-KEM-768 decaps |
69061 cycles |
69941 cycles |
0.99 |
ML-KEM-1024 keypair |
70953 cycles |
70194 cycles |
1.01 |
ML-KEM-1024 encaps |
84236 cycles |
82475 cycles |
1.02 |
ML-KEM-1024 decaps |
99959 cycles |
98796 cycles |
1.01 |
This comment was automatically generated by workflow using github-action-benchmark.
There was a problem hiding this comment.
AMD EPYC 4th gen (c7a)
Details
| Benchmark suite | Current: 0a0a167 | Previous: 712709d | Ratio |
|---|---|---|---|
ML-KEM-512 keypair |
13155 cycles |
12768 cycles |
1.03 |
ML-KEM-512 encaps |
15653 cycles |
14254 cycles |
1.10 |
ML-KEM-512 decaps |
19144 cycles |
19131 cycles |
1.00 |
ML-KEM-768 keypair |
22752 cycles |
22414 cycles |
1.02 |
ML-KEM-768 encaps |
23130 cycles |
23041 cycles |
1.00 |
ML-KEM-768 decaps |
30221 cycles |
30089 cycles |
1.00 |
ML-KEM-1024 keypair |
34312 cycles |
33024 cycles |
1.04 |
ML-KEM-1024 encaps |
33636 cycles |
33006 cycles |
1.02 |
ML-KEM-1024 decaps |
49127 cycles |
42430 cycles |
1.16 |
This comment was automatically generated by workflow using github-action-benchmark.
There was a problem hiding this comment.
⚠️ Performance Alert ⚠️
Possible performance regression was detected for benchmark 'AMD EPYC 4th gen (c7a)'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.03.
| Benchmark suite | Current: 0a0a167 | Previous: 712709d | Ratio |
|---|---|---|---|
ML-KEM-512 keypair |
13155 cycles |
12768 cycles |
1.03 |
ML-KEM-512 encaps |
15653 cycles |
14254 cycles |
1.10 |
ML-KEM-1024 keypair |
34312 cycles |
33024 cycles |
1.04 |
ML-KEM-1024 decaps |
49127 cycles |
42430 cycles |
1.16 |
This comment was automatically generated by workflow using github-action-benchmark.
There was a problem hiding this comment.
Intel Xeon 3rd gen (c6i)
Details
| Benchmark suite | Current: 0a0a167 | Previous: 712709d | Ratio |
|---|---|---|---|
ML-KEM-512 keypair |
17486 cycles |
17485 cycles |
1.00 |
ML-KEM-512 encaps |
20539 cycles |
19875 cycles |
1.03 |
ML-KEM-512 decaps |
27163 cycles |
26415 cycles |
1.03 |
ML-KEM-768 keypair |
31001 cycles |
31874 cycles |
0.97 |
ML-KEM-768 encaps |
32072 cycles |
31109 cycles |
1.03 |
ML-KEM-768 decaps |
42651 cycles |
41545 cycles |
1.03 |
ML-KEM-1024 keypair |
44383 cycles |
46137 cycles |
0.96 |
ML-KEM-1024 encaps |
45973 cycles |
45137 cycles |
1.02 |
ML-KEM-1024 decaps |
61116 cycles |
58253 cycles |
1.05 |
This comment was automatically generated by workflow using github-action-benchmark.
There was a problem hiding this comment.
⚠️ Performance Alert ⚠️
Possible performance regression was detected for benchmark 'Intel Xeon 3rd gen (c6i)'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.03.
| Benchmark suite | Current: 0a0a167 | Previous: 712709d | Ratio |
|---|---|---|---|
ML-KEM-512 encaps |
20539 cycles |
19875 cycles |
1.03 |
ML-KEM-768 encaps |
32072 cycles |
31109 cycles |
1.03 |
ML-KEM-1024 decaps |
61116 cycles |
58253 cycles |
1.05 |
This comment was automatically generated by workflow using github-action-benchmark.
There was a problem hiding this comment.
AMD EPYC 3rd gen (c6a) (no-opt)
Details
| Benchmark suite | Current: 0a0a167 | Previous: 712709d | Ratio |
|---|---|---|---|
ML-KEM-512 keypair |
40146 cycles |
40253 cycles |
1.00 |
ML-KEM-512 encaps |
48727 cycles |
48411 cycles |
1.01 |
ML-KEM-512 decaps |
62931 cycles |
62600 cycles |
1.01 |
ML-KEM-768 keypair |
64509 cycles |
63756 cycles |
1.01 |
ML-KEM-768 encaps |
75410 cycles |
74947 cycles |
1.01 |
ML-KEM-768 decaps |
93707 cycles |
93618 cycles |
1.00 |
ML-KEM-1024 keypair |
95205 cycles |
94982 cycles |
1.00 |
ML-KEM-1024 encaps |
109859 cycles |
109167 cycles |
1.01 |
ML-KEM-1024 decaps |
132630 cycles |
131931 cycles |
1.01 |
This comment was automatically generated by workflow using github-action-benchmark.
There was a problem hiding this comment.
AMD EPYC 4th gen (c7a) (no-opt)
Details
| Benchmark suite | Current: 0a0a167 | Previous: 712709d | Ratio |
|---|---|---|---|
ML-KEM-512 keypair |
36601 cycles |
36584 cycles |
1.00 |
ML-KEM-512 encaps |
43203 cycles |
43043 cycles |
1.00 |
ML-KEM-512 decaps |
55847 cycles |
55694 cycles |
1.00 |
ML-KEM-768 keypair |
58695 cycles |
58618 cycles |
1.00 |
ML-KEM-768 encaps |
67803 cycles |
67618 cycles |
1.00 |
ML-KEM-768 decaps |
84624 cycles |
84427 cycles |
1.00 |
ML-KEM-1024 keypair |
89078 cycles |
88963 cycles |
1.00 |
ML-KEM-1024 encaps |
99677 cycles |
99133 cycles |
1.01 |
ML-KEM-1024 decaps |
121227 cycles |
120720 cycles |
1.00 |
This comment was automatically generated by workflow using github-action-benchmark.
There was a problem hiding this comment.
Arm Cortex-A76 (Raspberry Pi 5) benchmarks
Details
| Benchmark suite | Current: 0a0a167 | Previous: 712709d | Ratio |
|---|---|---|---|
ML-KEM-512 keypair |
28272 cycles |
28233 cycles |
1.00 |
ML-KEM-512 encaps |
34307 cycles |
34105 cycles |
1.01 |
ML-KEM-512 decaps |
44554 cycles |
44329 cycles |
1.01 |
ML-KEM-768 keypair |
47644 cycles |
47627 cycles |
1.00 |
ML-KEM-768 encaps |
54197 cycles |
53956 cycles |
1.00 |
ML-KEM-768 decaps |
68663 cycles |
68377 cycles |
1.00 |
ML-KEM-1024 keypair |
70252 cycles |
70255 cycles |
1.00 |
ML-KEM-1024 encaps |
79150 cycles |
78806 cycles |
1.00 |
ML-KEM-1024 decaps |
98773 cycles |
98442 cycles |
1.00 |
This comment was automatically generated by workflow using github-action-benchmark.
There was a problem hiding this comment.
Graviton4
Details
| Benchmark suite | Current: 0a0a167 | Previous: 712709d | Ratio |
|---|---|---|---|
ML-KEM-512 keypair |
17678 cycles |
17650 cycles |
1.00 |
ML-KEM-512 encaps |
20697 cycles |
20601 cycles |
1.00 |
ML-KEM-512 decaps |
27162 cycles |
27068 cycles |
1.00 |
ML-KEM-768 keypair |
29932 cycles |
29899 cycles |
1.00 |
ML-KEM-768 encaps |
32943 cycles |
32776 cycles |
1.01 |
ML-KEM-768 decaps |
42137 cycles |
41967 cycles |
1.00 |
ML-KEM-1024 keypair |
43751 cycles |
43750 cycles |
1.00 |
ML-KEM-1024 encaps |
48899 cycles |
48727 cycles |
1.00 |
ML-KEM-1024 decaps |
61543 cycles |
61390 cycles |
1.00 |
This comment was automatically generated by workflow using github-action-benchmark.
There was a problem hiding this comment.
Intel Xeon 3rd gen (c6i) (no-opt)
Details
| Benchmark suite | Current: 0a0a167 | Previous: 712709d | Ratio |
|---|---|---|---|
ML-KEM-512 keypair |
46126 cycles |
45656 cycles |
1.01 |
ML-KEM-512 encaps |
55040 cycles |
54451 cycles |
1.01 |
ML-KEM-512 decaps |
70160 cycles |
69753 cycles |
1.01 |
ML-KEM-768 keypair |
73388 cycles |
74171 cycles |
0.99 |
ML-KEM-768 encaps |
86586 cycles |
85948 cycles |
1.01 |
ML-KEM-768 decaps |
106701 cycles |
106520 cycles |
1.00 |
ML-KEM-1024 keypair |
111782 cycles |
112098 cycles |
1.00 |
ML-KEM-1024 encaps |
126133 cycles |
124601 cycles |
1.01 |
ML-KEM-1024 decaps |
151974 cycles |
150531 cycles |
1.01 |
This comment was automatically generated by workflow using github-action-benchmark.
There was a problem hiding this comment.
Graviton4 (no-opt)
Details
| Benchmark suite | Current: 0a0a167 | Previous: 712709d | Ratio |
|---|---|---|---|
ML-KEM-512 keypair |
35450 cycles |
35416 cycles |
1.00 |
ML-KEM-512 encaps |
41585 cycles |
40122 cycles |
1.04 |
ML-KEM-512 decaps |
51571 cycles |
51145 cycles |
1.01 |
ML-KEM-768 keypair |
56738 cycles |
56670 cycles |
1.00 |
ML-KEM-768 encaps |
65173 cycles |
65151 cycles |
1.00 |
ML-KEM-768 decaps |
79386 cycles |
79295 cycles |
1.00 |
ML-KEM-1024 keypair |
88008 cycles |
87866 cycles |
1.00 |
ML-KEM-1024 encaps |
97739 cycles |
96879 cycles |
1.01 |
ML-KEM-1024 decaps |
116607 cycles |
115822 cycles |
1.01 |
This comment was automatically generated by workflow using github-action-benchmark.
There was a problem hiding this comment.
Graviton3
Details
| Benchmark suite | Current: 0a0a167 | Previous: 712709d | Ratio |
|---|---|---|---|
ML-KEM-512 keypair |
18670 cycles |
18639 cycles |
1.00 |
ML-KEM-512 encaps |
21918 cycles |
21876 cycles |
1.00 |
ML-KEM-512 decaps |
28893 cycles |
28861 cycles |
1.00 |
ML-KEM-768 keypair |
31593 cycles |
31540 cycles |
1.00 |
ML-KEM-768 encaps |
34945 cycles |
34771 cycles |
1.01 |
ML-KEM-768 decaps |
44893 cycles |
44775 cycles |
1.00 |
ML-KEM-1024 keypair |
46070 cycles |
46082 cycles |
1.00 |
ML-KEM-1024 encaps |
51716 cycles |
51501 cycles |
1.00 |
ML-KEM-1024 decaps |
65317 cycles |
65036 cycles |
1.00 |
This comment was automatically generated by workflow using github-action-benchmark.
There was a problem hiding this comment.
Graviton2
Details
| Benchmark suite | Current: 0a0a167 | Previous: 712709d | Ratio |
|---|---|---|---|
ML-KEM-512 keypair |
28281 cycles |
28256 cycles |
1.00 |
ML-KEM-512 encaps |
34335 cycles |
34110 cycles |
1.01 |
ML-KEM-512 decaps |
44593 cycles |
44398 cycles |
1.00 |
ML-KEM-768 keypair |
47645 cycles |
47665 cycles |
1.00 |
ML-KEM-768 encaps |
54337 cycles |
53940 cycles |
1.01 |
ML-KEM-768 decaps |
68766 cycles |
68363 cycles |
1.01 |
ML-KEM-1024 keypair |
70355 cycles |
70328 cycles |
1.00 |
ML-KEM-1024 encaps |
79143 cycles |
78757 cycles |
1.00 |
ML-KEM-1024 decaps |
98854 cycles |
98529 cycles |
1.00 |
This comment was automatically generated by workflow using github-action-benchmark.
There was a problem hiding this comment.
Graviton3 (no-opt)
Details
| Benchmark suite | Current: 0a0a167 | Previous: 712709d | Ratio |
|---|---|---|---|
ML-KEM-512 keypair |
38936 cycles |
38887 cycles |
1.00 |
ML-KEM-512 encaps |
47075 cycles |
44596 cycles |
1.06 |
ML-KEM-512 decaps |
57101 cycles |
56673 cycles |
1.01 |
ML-KEM-768 keypair |
62374 cycles |
62294 cycles |
1.00 |
ML-KEM-768 encaps |
71641 cycles |
72330 cycles |
0.99 |
ML-KEM-768 decaps |
87319 cycles |
87696 cycles |
1.00 |
ML-KEM-1024 keypair |
96361 cycles |
96160 cycles |
1.00 |
ML-KEM-1024 encaps |
107186 cycles |
106135 cycles |
1.01 |
ML-KEM-1024 decaps |
127584 cycles |
126583 cycles |
1.01 |
This comment was automatically generated by workflow using github-action-benchmark.
There was a problem hiding this comment.
Graviton2 (no-opt)
Details
| Benchmark suite | Current: 0a0a167 | Previous: 712709d | Ratio |
|---|---|---|---|
ML-KEM-512 keypair |
59167 cycles |
59049 cycles |
1.00 |
ML-KEM-512 encaps |
69657 cycles |
68578 cycles |
1.02 |
ML-KEM-512 decaps |
87765 cycles |
87314 cycles |
1.01 |
ML-KEM-768 keypair |
95627 cycles |
95479 cycles |
1.00 |
ML-KEM-768 encaps |
111277 cycles |
109908 cycles |
1.01 |
ML-KEM-768 decaps |
135008 cycles |
134361 cycles |
1.00 |
ML-KEM-1024 keypair |
147782 cycles |
147876 cycles |
1.00 |
ML-KEM-1024 encaps |
164356 cycles |
163805 cycles |
1.00 |
ML-KEM-1024 decaps |
196183 cycles |
195456 cycles |
1.00 |
This comment was automatically generated by workflow using github-action-benchmark.
CBMC Results (ML-KEM-512)
Full Results (191 proofs)
|
CBMC Results (ML-KEM-768)
Full Results (191 proofs)
|
CBMC Results (ML-KEM-1024)
Full Results (191 proofs)
|
hanno-becker
left a comment
There was a problem hiding this comment.
What's the purpose of 0a01cc4? Tests also serve as documentation, and using internal constants rather than public ones sets a wrong example.
If this is needed, can it be done in a preparatory PR? It seems unrelated to this PR.
The main question here is if we want to add the new API in mlkem_native.h or not. If we don't, we can't test the API in the standard test_mlkem.c, but we could add it in a separate test that includes kem.h, but not mlkem_native.h. I agree with you that we don't want to keep it as is right now. |
|
Seeing that you also observed a slowdown on x86, I wonder if we should treat the incremental API as internal by default and only expose it in the public API if some new option |
30af7b8 to
22448d5
Compare
22448d5 to
d2e3021
Compare
d2e3021 to
603c364
Compare
603c364 to
907613d
Compare
Split K-PKE.Encrypt and ML-KEM.Encaps into two phases (u and v) to support protocols like MLKEMBraid that transmit large KEM components in parallel over bandwidth-constrained channels. CPA level (indcpa): - mlk_indcpa_enc_u: computes ct_u from ek_seed, outputs intermediate state (sp, epp) - mlk_indcpa_enc_v: computes ct_v from ek_vector using intermediate state from enc_u CCA KEM level (kem): - mlk_kem_enc_derand_u: FO transform + enc_u, outputs shared secret and intermediate state; only needs ek_seed and H(pk) - mlk_kem_enc_v: modulus check on ek_vector + enc_v; only needs ek_vector The test verifies that the incremental API produces identical ciphertexts and shared secrets as the standard API across all three parameter sets. Signed-off-by: Matthias J. Kannwischer <matthias@kannwischer.eu>
Use mlk_kem_enc_derand_u + mlk_kem_enc_v as the single implementation for both the standard and incremental encapsulation API. Serialize the intermediate state (sp, epp) via 16-bit little-endian encoding into separate buffers sp_serial[MLKEM_POLYVEC16_BYTES] and epp_serial[MLKEM_POLY16_BYTES]. Signed-off-by: Matthias J. Kannwischer <matthias@kannwischer.eu>
Add CBMC contracts for mlk_indcpa_enc_u and mlk_indcpa_enc_v, including an epp coefficient bound postcondition on enc_u (array_abs_bound ETA2+1) and a matching precondition on enc_v (array_abs_bound 16). Serialize epp as 4-bit nibbles (ETA2 - x) in 128 bytes instead of 16-bit LE (512 bytes), providing a natural coefficient bound on deserialization. Revert mlk_kem_enc_derand to call mlk_indcpa_enc directly, avoiding unnecessary serialization overhead. Add CBMC proofs for indcpa_enc_u, indcpa_enc_v, kem_enc_derand_u, and kem_enc_v. Update the indcpa_enc proof to compose enc_u and enc_v. Signed-off-by: Matthias J. Kannwischer <matthias@kannwischer.eu>
Signed-off-by: Matthias J. Kannwischer <matthias@kannwischer.eu>
Signed-off-by: Matthias J. Kannwischer <matthias@kannwischer.eu>
Signed-off-by: Matthias J. Kannwischer <matthias@kannwischer.eu>
Change mlk_kem_enc_derand_u and mlk_kem_enc_v from MLK_INTERNAL_API to MLK_EXTERNAL_API so they are not static in monolithic builds. Add -Wno-unused-function to the monolithic_build_multilevel_native example (matching mldsa-native) since those examples don't exercise the incremental API. Signed-off-by: Matthias J. Kannwischer <matthias@kannwischer.eu>
Signed-off-by: Matthias J. Kannwischer <matthias@kannwischer.eu>
Signed-off-by: Matthias J. Kannwischer <matthias@kannwischer.eu>
Signed-off-by: Matthias J. Kannwischer <matthias@kannwischer.eu>
Signed-off-by: Hanno Becker <beckphan@amazon.co.uk>
Signed-off-by: Hanno Becker <beckphan@amazon.co.uk>
907613d to
0a0a167
Compare
oqs-bot
left a comment
There was a problem hiding this comment.
⚠️ Performance Alert ⚠️
Possible performance regression was detected for benchmark 'Intel Xeon 4th gen (c7i) (no-opt)'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.03.
| Benchmark suite | Current: 0a0a167 | Previous: 712709d | Ratio |
|---|---|---|---|
ML-KEM-512 keypair |
28980 cycles |
28131 cycles |
1.03 |
This comment was automatically generated by workflow using github-action-benchmark.
oqs-bot
left a comment
There was a problem hiding this comment.
⚠️ Performance Alert ⚠️
Possible performance regression was detected for benchmark 'Graviton4 (no-opt)'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.03.
| Benchmark suite | Current: 0a0a167 | Previous: 712709d | Ratio |
|---|---|---|---|
ML-KEM-512 encaps |
41585 cycles |
40122 cycles |
1.04 |
This comment was automatically generated by workflow using github-action-benchmark.
oqs-bot
left a comment
There was a problem hiding this comment.
⚠️ Performance Alert ⚠️
Possible performance regression was detected for benchmark 'Graviton3 (no-opt)'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.03.
| Benchmark suite | Current: 0a0a167 | Previous: 712709d | Ratio |
|---|---|---|---|
ML-KEM-512 encaps |
47075 cycles |
44596 cycles |
1.06 |
This comment was automatically generated by workflow using github-action-benchmark.
Split ML-KEM encapsulation into two phases (mlk_kem_enc_derand_u / mlk_kem_enc_v) to support protocols like Braid that need to interleave encapsulation with other operations between computing the u- and v-components of the ciphertext. The first phase only requires the public seed and H(pk), not the full public key vector. Internally, K-PKE.Encrypt is refactored into mlk_indcpa_enc_u + mlk_indcpa_enc_v. The non-incremental KEM path calls mlk_indcpa_enc directly to avoid serialization overhead. The intermediate noise polynomial epp is serialized as 4-bit nibbles (128 bytes) - this is primarily done to not require a pre-condition on the allowed values.