CVE-2024-36405: Insufficient Information (NVD-CWE-noinfo)
value: liboqs is a C-language cryptographic library that provides implementations of post-quantum cryptography algorithms. A control-flow timing lean has been identified in the reference implementation of the Kyber key encapsulation mechanism when it is compiled with Clang 15-18 for `-Os`, `-O1`, and other compilation options. A proof-of-concept local attack on the reference implementation leaks the entire ML-KEM 512 secret key in ~10 minutes using end-to-end decapsulation timing measurements. The issue has been fixed in version 0.10.1. As a possible workaround, some compiler options may produce vectorized code that does not leak secret information, however relying on these compiler options as a workaround may not be reliable.
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
Published: 2024-06-10T13:15Z
Repository: https://github.com/open-quantum-safe/liboqs
Language: C
Files changed: docs/algorithms/kem/kyber.md, docs/algorithms/kem/kyber.yml, docs/algorithms/kem/ml_kem.md, docs/algorithms/kem/ml_kem.yml, scripts/copy_from_upstream/copy_from_upstream.yml, src/kem/kyber/pqcrystals-kyber_kyber1024_avx2/verify.h, src/kem/kyber/pqcrystals-kyber_kyber1024_ref/poly.c, src/kem/kyber/pqcrystals-kyber_kyber1024_ref/verify.c, src/kem/kyber/pqcrystals-kyber_kyber1024_ref/verify.h, src/kem/kyber/pqcrystals-kyber_kyber512_avx2/verify.h, src/kem/kyber/pqcrystals-kyber_kyber512_ref/poly.c, src/kem/kyber/pqcrystals-kyber_kyber512_ref/verify.c, src/kem/kyber/pqcrystals-kyber_kyber512_ref/verify.h, src/kem/kyber/pqcrystals-kyber_kyber768_avx2/verify.h, src/kem/kyber/pqcrystals-kyber_kyber768_ref/poly.c, src/kem/kyber/pqcrystals-kyber_kyber768_ref/verify.c, src/kem/kyber/pqcrystals-kyber_kyber768_ref/verify.h, src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-1024-ipd_avx2/verify.h, src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-1024-ipd_ref/poly.c, src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-1024-ipd_ref/verify.c, src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-1024-ipd_ref/verify.h, src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-512-ipd_avx2/verify.h, src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-512-ipd_ref/poly.c, src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-512-ipd_ref/verify.c, src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-512-ipd_ref/verify.h, src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-768-ipd_avx2/verify.h, src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-768-ipd_ref/poly.c, src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-768-ipd_ref/verify.c, src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-768-ipd_ref/verify.h
Commit date: 2024-06-05 12:02:05 +0200
Commit message: Pull Kyber/ML-KEM CT-Fix from upstream

Signed-off-by: Basil Hess <bhe@zurich.ibm.com>

Diff: diff --git a/docs/algorithms/kem/kyber.md b/docs/algorithms/kem/kyber.md
index a75c144a..0ae1cf31 100644
--- a/docs/algorithms/kem/kyber.md
+++ b/docs/algorithms/kem/kyber.md
@@ -5,13 +5,13 @@
 - **Principal submitters**: Peter Schwabe.
 - **Auxiliary submitters**: Roberto Avanzi, Joppe Bos, Léo Ducas, Eike Kiltz, Tancrède Lepoint, Vadim Lyubashevsky, John M. Schanck, Gregor Seiler, Damien Stehlé.
 - **Authors' website**: https://pq-crystals.org/
 - **Specification version**: NIST Round 3 submission.
 - **Primary Source**<a name="primary-source"></a>:
-  - **Source**: https://github.com/pq-crystals/kyber/commit/b628ba78711bc28327dc7d2d5c074a00f061884e with copy_from_upstream patches
+  - **Source**: https://github.com/pq-crystals/kyber/commit/441c0519a07e8b86c8d079954a6b10bd31d29efc with copy_from_upstream patches
   - **Implementation license (SPDX-Identifier)**: CC0-1.0 or Apache-2.0
-- **Optimized Implementation sources**: https://github.com/pq-crystals/kyber/commit/b628ba78711bc28327dc7d2d5c074a00f061884e with copy_from_upstream patches
+- **Optimized Implementation sources**: https://github.com/pq-crystals/kyber/commit/441c0519a07e8b86c8d079954a6b10bd31d29efc with copy_from_upstream patches
   - **oldpqclean-aarch64**:<a name="oldpqclean-aarch64"></a>
       - **Source**: https://github.com/PQClean/PQClean/commit/8e220a87308154d48fdfac40abbb191ac7fce06a with copy_from_upstream patches
       - **Implementation license (SPDX-Identifier)**: CC0-1.0 and (CC0-1.0 or Apache-2.0) and (CC0-1.0 or MIT) and MIT
 
 
diff --git a/docs/algorithms/kem/kyber.yml b/docs/algorithms/kem/kyber.yml
index cde870c1..c3561273 100644
--- a/docs/algorithms/kem/kyber.yml
+++ b/docs/algorithms/kem/kyber.yml
@@ -15,11 +15,11 @@ auxiliary-submitters:
 crypto-assumption: Module LWE+R with base ring Z[x]/(3329, x^256+1)
 website: https://pq-crystals.org/
 nist-round: 3
 spec-version: NIST Round 3 submission
 primary-upstream:
-  source: https://github.com/pq-crystals/kyber/commit/b628ba78711bc28327dc7d2d5c074a00f061884e
+  source: https://github.com/pq-crystals/kyber/commit/441c0519a07e8b86c8d079954a6b10bd31d29efc
     with copy_from_upstream patches
   spdx-license-identifier: CC0-1.0 or Apache-2.0
 optimized-upstreams:
   oldpqclean-aarch64:
     source: https://github.com/PQClean/PQClean/commit/8e220a87308154d48fdfac40abbb191ac7fce06a
diff --git a/docs/algorithms/kem/ml_kem.md b/docs/algorithms/kem/ml_kem.md
index 92d1a5b4..7d5e0561 100644
--- a/docs/algorithms/kem/ml_kem.md
+++ b/docs/algorithms/kem/ml_kem.md
@@ -5,11 +5,11 @@
 - **Principal submitters**: Peter Schwabe.
 - **Auxiliary submitters**: Roberto Avanzi, Joppe Bos, Léo Ducas, Eike Kiltz, Tancrède Lepoint, Vadim Lyubashevsky, John M. Schanck, Gregor Seiler, Damien Stehlé.
 - **Authors' website**: https://pq-crystals.org/kyber/ and https://csrc.nist.gov/pubs/fips/203/ipd
 - **Specification version**: ML-KEM-ipd.
 - **Primary Source**<a name="primary-source"></a>:
-  - **Source**: https://github.com/pq-crystals/kyber/commit/11d00ff1f20cfca1f72d819e5a45165c1e0a2816 with copy_from_upstream patches
+  - **Source**: https://github.com/pq-crystals/kyber/commit/d1321ce5ac0b53f583eb47a040dc3625ee8e7e37 with copy_from_upstream patches
   - **Implementation license (SPDX-Identifier)**: CC0-1.0 or Apache-2.0
 
 
 ## Parameter set summary
 
diff --git a/docs/algorithms/kem/ml_kem.yml b/docs/algorithms/kem/ml_kem.yml
index 38b0a3ef..58d2ce19 100644
--- a/docs/algorithms/kem/ml_kem.yml
+++ b/docs/algorithms/kem/ml_kem.yml
@@ -15,11 +15,11 @@ auxiliary-submitters:
 crypto-assumption: Module LWE+R with base ring Z[x]/(3329, x^256+1)
 website: https://pq-crystals.org/kyber/ and https://csrc.nist.gov/pubs/fips/203/ipd
 nist-round: ipd
 spec-version: ML-KEM-ipd
 primary-upstream:
-  source: https://github.com/pq-crystals/kyber/commit/11d00ff1f20cfca1f72d819e5a45165c1e0a2816
+  source: https://github.com/pq-crystals/kyber/commit/d1321ce5ac0b53f583eb47a040dc3625ee8e7e37
     with copy_from_upstream patches
   spdx-license-identifier: CC0-1.0 or Apache-2.0
 parameter-sets:
 - name: ML-KEM-512-ipd
   alias: ML-KEM-512
diff --git a/scripts/copy_from_upstream/copy_from_upstream.yml b/scripts/copy_from_upstream/copy_from_upstream.yml
index d8a9a4d1..3417180c 100644
--- a/scripts/copy_from_upstream/copy_from_upstream.yml
+++ b/scripts/copy_from_upstream/copy_from_upstream.yml
@@ -23,19 +23,19 @@ upstreams:
     ignore: pqclean_sphincs-shake-256s-simple_aarch64, pqclean_sphincs-shake-256s-simple_aarch64, pqclean_sphincs-shake-256f-simple_aarch64, pqclean_sphincs-shake-192s-simple_aarch64, pqclean_sphincs-shake-192f-simple_aarch64, pqclean_sphincs-shake-128s-simple_aarch64, pqclean_sphincs-shake-128f-simple_aarch64, pqclean_kyber512_aarch64, pqclean_kyber1024_aarch64, pqclean_kyber768_aarch64, pqclean_dilithium2_aarch64, pqclean_dilithium3_aarch64, pqclean_dilithium5_aarch64
   -
     name: pqcrystals-kyber
     git_url: https://github.com/pq-crystals/kyber.git
     git_branch: master
-    git_commit: b628ba78711bc28327dc7d2d5c074a00f061884e
+    git_commit: 441c0519a07e8b86c8d079954a6b10bd31d29efc
     kem_meta_path: '{pretty_name_full}_META.yml'
     kem_scheme_path: '.'
     patches: [pqcrystals-kyber-yml.patch, pqcrystals-kyber-ref-shake-aes.patch, pqcrystals-kyber-avx2-shake-aes.patch]
   -
     name: pqcrystals-kyber-standard
     git_url: https://github.com/pq-crystals/kyber.git
     git_branch: standard
-    git_commit: 11d00ff1f20cfca1f72d819e5a45165c1e0a2816
+    git_commit: d1321ce5ac0b53f583eb47a040dc3625ee8e7e37
     kem_meta_path: '{pretty_name_full}_META.yml'
     kem_scheme_path: '.'
     patches: [pqcrystals-ml_kem_ipd.patch]
   -
     name: pqcrystals-dilithium
diff --git a/src/kem/kyber/pqcrystals-kyber_kyber1024_avx2/verify.h b/src/kem/kyber/pqcrystals-kyber_kyber1024_avx2/verify.h
index f95ac1b8..09f0ad50 100644
--- a/src/kem/kyber/pqcrystals-kyber_kyber1024_avx2/verify.h
+++ b/src/kem/kyber/pqcrystals-kyber_kyber1024_avx2/verify.h
@@ -9,6 +9,9 @@
 int verify(const uint8_t *a, const uint8_t *b, size_t len);
 
 #define cmov KYBER_NAMESPACE(cmov)
 void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b);
 
+#define cmov_int16 KYBER_NAMESPACE(cmov_int16)
+void cmov_int16(int16_t *r, int16_t v, uint16_t b);
+
 #endif
diff --git a/src/kem/kyber/pqcrystals-kyber_kyber1024_ref/poly.c b/src/kem/kyber/pqcrystals-kyber_kyber1024_ref/poly.c
index 3e73579e..9d3b1358 100644
--- a/src/kem/kyber/pqcrystals-kyber_kyber1024_ref/poly.c
+++ b/src/kem/kyber/pqcrystals-kyber_kyber1024_ref/poly.c
@@ -3,10 +3,11 @@
 #include "poly.h"
 #include "ntt.h"
 #include "reduce.h"
 #include "cbd.h"
 #include "symmetric.h"
+#include "verify.h"
 
 /*************************************************
 * Name:        poly_compress
 *
 * Description: Compression and subsequent serialization of a polynomial
@@ -164,20 +165,19 @@ void poly_frombytes(poly *r, const uint8_t a[KYBER_POLYBYTES])
 *              - const uint8_t *msg: pointer to input message
 **************************************************/
 void poly_frommsg(poly *r, const uint8_t msg[KYBER_INDCPA_MSGBYTES])
 {
   unsigned int i,j;
-  int16_t mask;
 
 #if (KYBER_INDCPA_MSGBYTES != KYBER_N/8)
 #error "KYBER_INDCPA_MSGBYTES must be equal to KYBER_N/8 bytes!"
 #endif
 
   for(i=0;i<KYBER_N/8;i++) {
     for(j=0;j<8;j++) {
-      mask = -(int16_t)((msg[i] >> j)&1);
-      r->coeffs[8*i+j] = mask & ((KYBER_Q+1)/2);
+      r->coeffs[8*i+j] = 0;
+      cmov_int16(r->coeffs+8*i+j, ((KYBER_Q+1)/2), (msg[i] >> j)&1);
     }
   }
 }
 
 /*************************************************
diff --git a/src/kem/kyber/pqcrystals-kyber_kyber1024_ref/verify.c b/src/kem/kyber/pqcrystals-kyber_kyber1024_ref/verify.c
index 1c43071c..4bcb1e5f 100644
--- a/src/kem/kyber/pqcrystals-kyber_kyber1024_ref/verify.c
+++ b/src/kem/kyber/pqcrystals-kyber_kyber1024_ref/verify.c
@@ -53,5 +53,22 @@ void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b)
 
   b = -b;
   for(i=0;i<len;i++)
     r[i] ^= b & (r[i] ^ x[i]);
 }
+
+/*************************************************
+* Name:        cmov_int16
+*
+* Description: Copy input v to *r if b is 1, don't modify *r if b is 0. 
+*              Requires b to be in {0,1};
+*              Runs in constant time.
+*
+* Arguments:   int16_t *r:       pointer to output int16_t
+*              int16_t v:        input int16_t 
+*              uint8_t b:        Condition bit; has to be in {0,1}
+**************************************************/
+void cmov_int16(int16_t *r, int16_t v, uint16_t b)
+{
+  b = -b;
+  *r ^= b & ((*r) ^ v);
+}
diff --git a/src/kem/kyber/pqcrystals-kyber_kyber1024_ref/verify.h b/src/kem/kyber/pqcrystals-kyber_kyber1024_ref/verify.h
index f95ac1b8..09f0ad50 100644
--- a/src/kem/kyber/pqcrystals-kyber_kyber1024_ref/verify.h
+++ b/src/kem/kyber/pqcrystals-kyber_kyber1024_ref/verify.h
@@ -9,6 +9,9 @@
 int verify(const uint8_t *a, const uint8_t *b, size_t len);
 
 #define cmov KYBER_NAMESPACE(cmov)
 void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b);
 
+#define cmov_int16 KYBER_NAMESPACE(cmov_int16)
+void cmov_int16(int16_t *r, int16_t v, uint16_t b);
+
 #endif
diff --git a/src/kem/kyber/pqcrystals-kyber_kyber512_avx2/verify.h b/src/kem/kyber/pqcrystals-kyber_kyber512_avx2/verify.h
index f95ac1b8..09f0ad50 100644
--- a/src/kem/kyber/pqcrystals-kyber_kyber512_avx2/verify.h
+++ b/src/kem/kyber/pqcrystals-kyber_kyber512_avx2/verify.h
@@ -9,6 +9,9 @@
 int verify(const uint8_t *a, const uint8_t *b, size_t len);
 
 #define cmov KYBER_NAMESPACE(cmov)
 void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b);
 
+#define cmov_int16 KYBER_NAMESPACE(cmov_int16)
+void cmov_int16(int16_t *r, int16_t v, uint16_t b);
+
 #endif
diff --git a/src/kem/kyber/pqcrystals-kyber_kyber512_ref/poly.c b/src/kem/kyber/pqcrystals-kyber_kyber512_ref/poly.c
index 3e73579e..9d3b1358 100644
--- a/src/kem/kyber/pqcrystals-kyber_kyber512_ref/poly.c
+++ b/src/kem/kyber/pqcrystals-kyber_kyber512_ref/poly.c
@@ -3,10 +3,11 @@
 #include "poly.h"
 #include "ntt.h"
 #include "reduce.h"
 #include "cbd.h"
 #include "symmetric.h"
+#include "verify.h"
 
 /*************************************************
 * Name:        poly_compress
 *
 * Description: Compression and subsequent serialization of a polynomial
@@ -164,20 +165,19 @@ void poly_frombytes(poly *r, const uint8_t a[KYBER_POLYBYTES])
 *              - const uint8_t *msg: pointer to input message
 **************************************************/
 void poly_frommsg(poly *r, const uint8_t msg[KYBER_INDCPA_MSGBYTES])
 {
   unsigned int i,j;
-  int16_t mask;
 
 #if (KYBER_INDCPA_MSGBYTES != KYBER_N/8)
 #error "KYBER_INDCPA_MSGBYTES must be equal to KYBER_N/8 bytes!"
 #endif
 
   for(i=0;i<KYBER_N/8;i++) {
     for(j=0;j<8;j++) {
-      mask = -(int16_t)((msg[i] >> j)&1);
-      r->coeffs[8*i+j] = mask & ((KYBER_Q+1)/2);
+      r->coeffs[8*i+j] = 0;
+      cmov_int16(r->coeffs+8*i+j, ((KYBER_Q+1)/2), (msg[i] >> j)&1);
     }
   }
 }
 
 /*************************************************
diff --git a/src/kem/kyber/pqcrystals-kyber_kyber512_ref/verify.c b/src/kem/kyber/pqcrystals-kyber_kyber512_ref/verify.c
index 1c43071c..4bcb1e5f 100644
--- a/src/kem/kyber/pqcrystals-kyber_kyber512_ref/verify.c
+++ b/src/kem/kyber/pqcrystals-kyber_kyber512_ref/verify.c
@@ -53,5 +53,22 @@ void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b)
 
   b = -b;
   for(i=0;i<len;i++)
     r[i] ^= b & (r[i] ^ x[i]);
 }
+
+/*************************************************
+* Name:        cmov_int16
+*
+* Description: Copy input v to *r if b is 1, don't modify *r if b is 0. 
+*              Requires b to be in {0,1};
+*              Runs in constant time.
+*
+* Arguments:   int16_t *r:       pointer to output int16_t
+*              int16_t v:        input int16_t 
+*              uint8_t b:        Condition bit; has to be in {0,1}
+**************************************************/
+void cmov_int16(int16_t *r, int16_t v, uint16_t b)
+{
+  b = -b;
+  *r ^= b & ((*r) ^ v);
+}
diff --git a/src/kem/kyber/pqcrystals-kyber_kyber512_ref/verify.h b/src/kem/kyber/pqcrystals-kyber_kyber512_ref/verify.h
index f95ac1b8..09f0ad50 100644
--- a/src/kem/kyber/pqcrystals-kyber_kyber512_ref/verify.h
+++ b/src/kem/kyber/pqcrystals-kyber_kyber512_ref/verify.h
@@ -9,6 +9,9 @@
 int verify(const uint8_t *a, const uint8_t *b, size_t len);
 
 #define cmov KYBER_NAMESPACE(cmov)
 void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b);
 
+#define cmov_int16 KYBER_NAMESPACE(cmov_int16)
+void cmov_int16(int16_t *r, int16_t v, uint16_t b);
+
 #endif
diff --git a/src/kem/kyber/pqcrystals-kyber_kyber768_avx2/verify.h b/src/kem/kyber/pqcrystals-kyber_kyber768_avx2/verify.h
index f95ac1b8..09f0ad50 100644
--- a/src/kem/kyber/pqcrystals-kyber_kyber768_avx2/verify.h
+++ b/src/kem/kyber/pqcrystals-kyber_kyber768_avx2/verify.h
@@ -9,6 +9,9 @@
 int verify(const uint8_t *a, const uint8_t *b, size_t len);
 
 #define cmov KYBER_NAMESPACE(cmov)
 void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b);
 
+#define cmov_int16 KYBER_NAMESPACE(cmov_int16)
+void cmov_int16(int16_t *r, int16_t v, uint16_t b);
+
 #endif
diff --git a/src/kem/kyber/pqcrystals-kyber_kyber768_ref/poly.c b/src/kem/kyber/pqcrystals-kyber_kyber768_ref/poly.c
index 3e73579e..9d3b1358 100644
--- a/src/kem/kyber/pqcrystals-kyber_kyber768_ref/poly.c
+++ b/src/kem/kyber/pqcrystals-kyber_kyber768_ref/poly.c
@@ -3,10 +3,11 @@
 #include "poly.h"
 #include "ntt.h"
 #include "reduce.h"
 #include "cbd.h"
 #include "symmetric.h"
+#include "verify.h"
 
 /*************************************************
 * Name:        poly_compress
 *
 * Description: Compression and subsequent serialization of a polynomial
@@ -164,20 +165,19 @@ void poly_frombytes(poly *r, const uint8_t a[KYBER_POLYBYTES])
 *              - const uint8_t *msg: pointer to input message
 **************************************************/
 void poly_frommsg(poly *r, const uint8_t msg[KYBER_INDCPA_MSGBYTES])
 {
   unsigned int i,j;
-  int16_t mask;
 
 #if (KYBER_INDCPA_MSGBYTES != KYBER_N/8)
 #error "KYBER_INDCPA_MSGBYTES must be equal to KYBER_N/8 bytes!"
 #endif
 
   for(i=0;i<KYBER_N/8;i++) {
     for(j=0;j<8;j++) {
-      mask = -(int16_t)((msg[i] >> j)&1);
-      r->coeffs[8*i+j] = mask & ((KYBER_Q+1)/2);
+      r->coeffs[8*i+j] = 0;
+      cmov_int16(r->coeffs+8*i+j, ((KYBER_Q+1)/2), (msg[i] >> j)&1);
     }
   }
 }
 
 /*************************************************
diff --git a/src/kem/kyber/pqcrystals-kyber_kyber768_ref/verify.c b/src/kem/kyber/pqcrystals-kyber_kyber768_ref/verify.c
index 1c43071c..4bcb1e5f 100644
--- a/src/kem/kyber/pqcrystals-kyber_kyber768_ref/verify.c
+++ b/src/kem/kyber/pqcrystals-kyber_kyber768_ref/verify.c
@@ -53,5 +53,22 @@ void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b)
 
   b = -b;
   for(i=0;i<len;i++)
     r[i] ^= b & (r[i] ^ x[i]);
 }
+
+/*************************************************
+* Name:        cmov_int16
+*
+* Description: Copy input v to *r if b is 1, don't modify *r if b is 0. 
+*              Requires b to be in {0,1};
+*              Runs in constant time.
+*
+* Arguments:   int16_t *r:       pointer to output int16_t
+*              int16_t v:        input int16_t 
+*              uint8_t b:        Condition bit; has to be in {0,1}
+**************************************************/
+void cmov_int16(int16_t *r, int16_t v, uint16_t b)
+{
+  b = -b;
+  *r ^= b & ((*r) ^ v);
+}
diff --git a/src/kem/kyber/pqcrystals-kyber_kyber768_ref/verify.h b/src/kem/kyber/pqcrystals-kyber_kyber768_ref/verify.h
index f95ac1b8..09f0ad50 100644
--- a/src/kem/kyber/pqcrystals-kyber_kyber768_ref/verify.h
+++ b/src/kem/kyber/pqcrystals-kyber_kyber768_ref/verify.h
@@ -9,6 +9,9 @@
 int verify(const uint8_t *a, const uint8_t *b, size_t len);
 
 #define cmov KYBER_NAMESPACE(cmov)
 void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b);
 
+#define cmov_int16 KYBER_NAMESPACE(cmov_int16)
+void cmov_int16(int16_t *r, int16_t v, uint16_t b);
+
 #endif
diff --git a/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-1024-ipd_avx2/verify.h b/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-1024-ipd_avx2/verify.h
index f95ac1b8..09f0ad50 100644
--- a/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-1024-ipd_avx2/verify.h
+++ b/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-1024-ipd_avx2/verify.h
@@ -9,6 +9,9 @@
 int verify(const uint8_t *a, const uint8_t *b, size_t len);
 
 #define cmov KYBER_NAMESPACE(cmov)
 void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b);
 
+#define cmov_int16 KYBER_NAMESPACE(cmov_int16)
+void cmov_int16(int16_t *r, int16_t v, uint16_t b);
+
 #endif
diff --git a/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-1024-ipd_ref/poly.c b/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-1024-ipd_ref/poly.c
index 0fe5a20f..7bf7ad53 100644
--- a/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-1024-ipd_ref/poly.c
+++ b/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-1024-ipd_ref/poly.c
@@ -3,10 +3,11 @@
 #include "poly.h"
 #include "ntt.h"
 #include "reduce.h"
 #include "cbd.h"
 #include "symmetric.h"
+#include "verify.h"
 
 /*************************************************
 * Name:        poly_compress
 *
 * Description: Compression and subsequent serialization of a polynomial
@@ -165,20 +166,19 @@ void poly_frombytes(poly *r, const uint8_t a[KYBER_POLYBYTES])
 *              - const uint8_t *msg: pointer to input message
 **************************************************/
 void poly_frommsg(poly *r, const uint8_t msg[KYBER_INDCPA_MSGBYTES])
 {
   unsigned int i,j;
-  int16_t mask;
 
 #if (KYBER_INDCPA_MSGBYTES != KYBER_N/8)
 #error "KYBER_INDCPA_MSGBYTES must be equal to KYBER_N/8 bytes!"
 #endif
 
   for(i=0;i<KYBER_N/8;i++) {
     for(j=0;j<8;j++) {
-      mask = -(int16_t)((msg[i] >> j)&1);
-      r->coeffs[8*i+j] = mask & ((KYBER_Q+1)/2);
+      r->coeffs[8*i+j] = 0;
+      cmov_int16(r->coeffs+8*i+j, ((KYBER_Q+1)/2), (msg[i] >> j)&1);
     }
   }
 }
 
 /*************************************************
diff --git a/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-1024-ipd_ref/verify.c b/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-1024-ipd_ref/verify.c
index ed4a6541..aad03b02 100644
--- a/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-1024-ipd_ref/verify.c
+++ b/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-1024-ipd_ref/verify.c
@@ -43,5 +43,23 @@ void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b)
 
   b = -b;
   for(i=0;i<len;i++)
     r[i] ^= b & (r[i] ^ x[i]);
 }
+
+
+/*************************************************
+* Name:        cmov_int16
+*
+* Description: Copy input v to *r if b is 1, don't modify *r if b is 0. 
+*              Requires b to be in {0,1};
+*              Runs in constant time.
+*
+* Arguments:   int16_t *r:       pointer to output int16_t
+*              int16_t v:        input int16_t 
+*              uint8_t b:        Condition bit; has to be in {0,1}
+**************************************************/
+void cmov_int16(int16_t *r, int16_t v, uint16_t b)
+{
+  b = -b;
+  *r ^= b & ((*r) ^ v);
+}
diff --git a/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-1024-ipd_ref/verify.h b/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-1024-ipd_ref/verify.h
index f95ac1b8..09f0ad50 100644
--- a/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-1024-ipd_ref/verify.h
+++ b/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-1024-ipd_ref/verify.h
@@ -9,6 +9,9 @@
 int verify(const uint8_t *a, const uint8_t *b, size_t len);
 
 #define cmov KYBER_NAMESPACE(cmov)
 void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b);
 
+#define cmov_int16 KYBER_NAMESPACE(cmov_int16)
+void cmov_int16(int16_t *r, int16_t v, uint16_t b);
+
 #endif
diff --git a/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-512-ipd_avx2/verify.h b/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-512-ipd_avx2/verify.h
index f95ac1b8..09f0ad50 100644
--- a/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-512-ipd_avx2/verify.h
+++ b/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-512-ipd_avx2/verify.h
@@ -9,6 +9,9 @@
 int verify(const uint8_t *a, const uint8_t *b, size_t len);
 
 #define cmov KYBER_NAMESPACE(cmov)
 void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b);
 
+#define cmov_int16 KYBER_NAMESPACE(cmov_int16)
+void cmov_int16(int16_t *r, int16_t v, uint16_t b);
+
 #endif
diff --git a/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-512-ipd_ref/poly.c b/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-512-ipd_ref/poly.c
index 0fe5a20f..7bf7ad53 100644
--- a/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-512-ipd_ref/poly.c
+++ b/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-512-ipd_ref/poly.c
@@ -3,10 +3,11 @@
 #include "poly.h"
 #include "ntt.h"
 #include "reduce.h"
 #include "cbd.h"
 #include "symmetric.h"
+#include "verify.h"
 
 /*************************************************
 * Name:        poly_compress
 *
 * Description: Compression and subsequent serialization of a polynomial
@@ -165,20 +166,19 @@ void poly_frombytes(poly *r, const uint8_t a[KYBER_POLYBYTES])
 *              - const uint8_t *msg: pointer to input message
 **************************************************/
 void poly_frommsg(poly *r, const uint8_t msg[KYBER_INDCPA_MSGBYTES])
 {
   unsigned int i,j;
-  int16_t mask;
 
 #if (KYBER_INDCPA_MSGBYTES != KYBER_N/8)
 #error "KYBER_INDCPA_MSGBYTES must be equal to KYBER_N/8 bytes!"
 #endif
 
   for(i=0;i<KYBER_N/8;i++) {
     for(j=0;j<8;j++) {
-      mask = -(int16_t)((msg[i] >> j)&1);
-      r->coeffs[8*i+j] = mask & ((KYBER_Q+1)/2);
+      r->coeffs[8*i+j] = 0;
+      cmov_int16(r->coeffs+8*i+j, ((KYBER_Q+1)/2), (msg[i] >> j)&1);
     }
   }
 }
 
 /*************************************************
diff --git a/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-512-ipd_ref/verify.c b/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-512-ipd_ref/verify.c
index ed4a6541..aad03b02 100644
--- a/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-512-ipd_ref/verify.c
+++ b/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-512-ipd_ref/verify.c
@@ -43,5 +43,23 @@ void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b)
 
   b = -b;
   for(i=0;i<len;i++)
     r[i] ^= b & (r[i] ^ x[i]);
 }
+
+
+/*************************************************
+* Name:        cmov_int16
+*
+* Description: Copy input v to *r if b is 1, don't modify *r if b is 0. 
+*              Requires b to be in {0,1};
+*              Runs in constant time.
+*
+* Arguments:   int16_t *r:       pointer to output int16_t
+*              int16_t v:        input int16_t 
+*              uint8_t b:        Condition bit; has to be in {0,1}
+**************************************************/
+void cmov_int16(int16_t *r, int16_t v, uint16_t b)
+{
+  b = -b;
+  *r ^= b & ((*r) ^ v);
+}
diff --git a/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-512-ipd_ref/verify.h b/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-512-ipd_ref/verify.h
index f95ac1b8..09f0ad50 100644
--- a/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-512-ipd_ref/verify.h
+++ b/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-512-ipd_ref/verify.h
@@ -9,6 +9,9 @@
 int verify(const uint8_t *a, const uint8_t *b, size_t len);
 
 #define cmov KYBER_NAMESPACE(cmov)
 void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b);
 
+#define cmov_int16 KYBER_NAMESPACE(cmov_int16)
+void cmov_int16(int16_t *r, int16_t v, uint16_t b);
+
 #endif
diff --git a/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-768-ipd_avx2/verify.h b/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-768-ipd_avx2/verify.h
index f95ac1b8..09f0ad50 100644
--- a/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-768-ipd_avx2/verify.h
+++ b/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-768-ipd_avx2/verify.h
@@ -9,6 +9,9 @@
 int verify(const uint8_t *a, const uint8_t *b, size_t len);
 
 #define cmov KYBER_NAMESPACE(cmov)
 void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b);
 
+#define cmov_int16 KYBER_NAMESPACE(cmov_int16)
+void cmov_int16(int16_t *r, int16_t v, uint16_t b);
+
 #endif
diff --git a/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-768-ipd_ref/poly.c b/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-768-ipd_ref/poly.c
index 0fe5a20f..7bf7ad53 100644
--- a/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-768-ipd_ref/poly.c
+++ b/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-768-ipd_ref/poly.c
@@ -3,10 +3,11 @@
 #include "poly.h"
 #include "ntt.h"
 #include "reduce.h"
 #include "cbd.h"
 #include "symmetric.h"
+#include "verify.h"
 
 /*************************************************
 * Name:        poly_compress
 *
 * Description: Compression and subsequent serialization of a polynomial
@@ -165,20 +166,19 @@ void poly_frombytes(poly *r, const uint8_t a[KYBER_POLYBYTES])
 *              - const uint8_t *msg: pointer to input message
 **************************************************/
 void poly_frommsg(poly *r, const uint8_t msg[KYBER_INDCPA_MSGBYTES])
 {
   unsigned int i,j;
-  int16_t mask;
 
 #if (KYBER_INDCPA_MSGBYTES != KYBER_N/8)
 #error "KYBER_INDCPA_MSGBYTES must be equal to KYBER_N/8 bytes!"
 #endif
 
   for(i=0;i<KYBER_N/8;i++) {
     for(j=0;j<8;j++) {
-      mask = -(int16_t)((msg[i] >> j)&1);
-      r->coeffs[8*i+j] = mask & ((KYBER_Q+1)/2);
+      r->coeffs[8*i+j] = 0;
+      cmov_int16(r->coeffs+8*i+j, ((KYBER_Q+1)/2), (msg[i] >> j)&1);
     }
   }
 }
 
 /*************************************************
diff --git a/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-768-ipd_ref/verify.c b/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-768-ipd_ref/verify.c
index ed4a6541..aad03b02 100644
--- a/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-768-ipd_ref/verify.c
+++ b/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-768-ipd_ref/verify.c
@@ -43,5 +43,23 @@ void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b)
 
   b = -b;
   for(i=0;i<len;i++)
     r[i] ^= b & (r[i] ^ x[i]);
 }
+
+
+/*************************************************
+* Name:        cmov_int16
+*
+* Description: Copy input v to *r if b is 1, don't modify *r if b is 0. 
+*              Requires b to be in {0,1};
+*              Runs in constant time.
+*
+* Arguments:   int16_t *r:       pointer to output int16_t
+*              int16_t v:        input int16_t 
+*              uint8_t b:        Condition bit; has to be in {0,1}
+**************************************************/
+void cmov_int16(int16_t *r, int16_t v, uint16_t b)
+{
+  b = -b;
+  *r ^= b & ((*r) ^ v);
+}
diff --git a/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-768-ipd_ref/verify.h b/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-768-ipd_ref/verify.h
index f95ac1b8..09f0ad50 100644
--- a/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-768-ipd_ref/verify.h
+++ b/src/kem/ml_kem/pqcrystals-kyber-standard_ml-kem-768-ipd_ref/verify.h
@@ -9,6 +9,9 @@
 int verify(const uint8_t *a, const uint8_t *b, size_t len);
 
 #define cmov KYBER_NAMESPACE(cmov)
 void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b);
 
+#define cmov_int16 KYBER_NAMESPACE(cmov_int16)
+void cmov_int16(int16_t *r, int16_t v, uint16_t b);
+
 #endif

Vulnerable code:   - **Source**: https://github.com/pq-crystals/kyber/commit/b628ba78711bc28327dc7d2d5c074a00f061884e with copy_from_upstream patches
- **Optimized Implementation sources**: https://github.com/pq-crystals/kyber/commit/b628ba78711bc28327dc7d2d5c074a00f061884e with copy_from_upstream patches
  source: https://github.com/pq-crystals/kyber/commit/b628ba78711bc28327dc7d2d5c074a00f061884e
  - **Source**: https://github.com/pq-crystals/kyber/commit/11d00ff1f20cfca1f72d819e5a45165c1e0a2816 with copy_from_upstream patches
  source: https://github.com/pq-crystals/kyber/commit/11d00ff1f20cfca1f72d819e5a45165c1e0a2816
    git_commit: b628ba78711bc28327dc7d2d5c074a00f061884e
    git_commit: 11d00ff1f20cfca1f72d819e5a45165c1e0a2816
  int16_t mask;
      mask = -(int16_t)((msg[i] >> j)&1);
      r->coeffs[8*i+j] = mask & ((KYBER_Q+1)/2);
  int16_t mask;
      mask = -(int16_t)((msg[i] >> j)&1);
      r->coeffs[8*i+j] = mask & ((KYBER_Q+1)/2);
  int16_t mask;
      mask = -(int16_t)((msg[i] >> j)&1);
      r->coeffs[8*i+j] = mask & ((KYBER_Q+1)/2);
  int16_t mask;
      mask = -(int16_t)((msg[i] >> j)&1);
      r->coeffs[8*i+j] = mask & ((KYBER_Q+1)/2);
  int16_t mask;
      mask = -(int16_t)((msg[i] >> j)&1);
      r->coeffs[8*i+j] = mask & ((KYBER_Q+1)/2);
  int16_t mask;
      mask = -(int16_t)((msg[i] >> j)&1);
      r->coeffs[8*i+j] = mask & ((KYBER_Q+1)/2);
Fixed code:   - **Source**: https://github.com/pq-crystals/kyber/commit/441c0519a07e8b86c8d079954a6b10bd31d29efc with copy_from_upstream patches
- **Optimized Implementation sources**: https://github.com/pq-crystals/kyber/commit/441c0519a07e8b86c8d079954a6b10bd31d29efc with copy_from_upstream patches
  source: https://github.com/pq-crystals/kyber/commit/441c0519a07e8b86c8d079954a6b10bd31d29efc
  - **Source**: https://github.com/pq-crystals/kyber/commit/d1321ce5ac0b53f583eb47a040dc3625ee8e7e37 with copy_from_upstream patches
  source: https://github.com/pq-crystals/kyber/commit/d1321ce5ac0b53f583eb47a040dc3625ee8e7e37
    git_commit: 441c0519a07e8b86c8d079954a6b10bd31d29efc
    git_commit: d1321ce5ac0b53f583eb47a040dc3625ee8e7e37
#define cmov_int16 KYBER_NAMESPACE(cmov_int16)
void cmov_int16(int16_t *r, int16_t v, uint16_t b);

#include "verify.h"
      r->coeffs[8*i+j] = 0;
      cmov_int16(r->coeffs+8*i+j, ((KYBER_Q+1)/2), (msg[i] >> j)&1);

/*************************************************
* Name:        cmov_int16
*
* Description: Copy input v to *r if b is 1, don't modify *r if b is 0. 
*              Requires b to be in {0,1};
*              Runs in constant time.
*
* Arguments:   int16_t *r:       pointer to output int16_t
*              int16_t v:        input int16_t 
*              uint8_t b:        Condition bit; has to be in {0,1}
**************************************************/
void cmov_int16(int16_t *r, int16_t v, uint16_t b)
{
  b = -b;
  *r ^= b & ((*r) ^ v);
}
#define cmov_int16 KYBER_NAMESPACE(cmov_int16)
void cmov_int16(int16_t *r, int16_t v, uint16_t b);

#define cmov_int16 KYBER_NAMESPACE(cmov_int16)
void cmov_int16(int16_t *r, int16_t v, uint16_t b);

#include "verify.h"
      r->coeffs[8*i+j] = 0;
      cmov_int16(r->coeffs+8*i+j, ((KYBER_Q+1)/2), (msg[i] >> j)&1);

/*************************************************
* Name:        cmov_int16
*
* Description: Copy input v to *r if b is 1, don't modify *r if b is 0. 
*              Requires b to be in {0,1};
*              Runs in constant time.
*
* Arguments:   int16_t *r:       pointer to output int16_t
*              int16_t v:        input int16_t 
*              uint8_t b:        Condition bit; has to be in {0,1}
**************************************************/
void cmov_int16(int16_t *r, int16_t v, uint16_t b)
{
  b = -b;
  *r ^= b & ((*r) ^ v);
}
#define cmov_int16 KYBER_NAMESPACE(cmov_int16)
void cmov_int16(int16_t *r, int16_t v, uint16_t b);

#define cmov_int16 KYBER_NAMESPACE(cmov_int16)
void cmov_int16(int16_t *r, int16_t v, uint16_t b);

#include "verify.h"
      r->coeffs[8*i+j] = 0;
      cmov_int16(r->coeffs+8*i+j, ((KYBER_Q+1)/2), (msg[i] >> j)&1);

/*************************************************
* Name:        cmov_int16
*
* Description: Copy input v to *r if b is 1, don't modify *r if b is 0. 
*              Requires b to be in {0,1};
*              Runs in constant time.
*
* Arguments:   int16_t *r:       pointer to output int16_t
*              int16_t v:        input int16_t 
*              uint8_t b:        Condition bit; has to be in {0,1}
**************************************************/
void cmov_int16(int16_t *r, int16_t v, uint16_t b)
{
  b = -b;
  *r ^= b & ((*r) ^ v);
}
#define cmov_int16 KYBER_NAMESPACE(cmov_int16)
void cmov_int16(int16_t *r, int16_t v, uint16_t b);

#define cmov_int16 KYBER_NAMESPACE(cmov_int16)
void cmov_int16(int16_t *r, int16_t v, uint16_t b);

#include "verify.h"
      r->coeffs[8*i+j] = 0;
      cmov_int16(r->coeffs+8*i+j, ((KYBER_Q+1)/2), (msg[i] >> j)&1);


/*************************************************
* Name:        cmov_int16
*
* Description: Copy input v to *r if b is 1, don't modify *r if b is 0. 
*              Requires b to be in {0,1};
*              Runs in constant time.
*
* Arguments:   int16_t *r:       pointer to output int16_t
*              int16_t v:        input int16_t 
*              uint8_t b:        Condition bit; has to be in {0,1}
**************************************************/
void cmov_int16(int16_t *r, int16_t v, uint16_t b)
{
  b = -b;
  *r ^= b & ((*r) ^ v);
}
#define cmov_int16 KYBER_NAMESPACE(cmov_int16)
void cmov_int16(int16_t *r, int16_t v, uint16_t b);

#define cmov_int16 KYBER_NAMESPACE(cmov_int16)
void cmov_int16(int16_t *r, int16_t v, uint16_t b);

#include "verify.h"
      r->coeffs[8*i+j] = 0;
      cmov_int16(r->coeffs+8*i+j, ((KYBER_Q+1)/2), (msg[i] >> j)&1);


/*************************************************
* Name:        cmov_int16
*
* Description: Copy input v to *r if b is 1, don't modify *r if b is 0. 
*              Requires b to be in {0,1};
*              Runs in constant time.
*
* Arguments:   int16_t *r:       pointer to output int16_t
*              int16_t v:        input int16_t 
*              uint8_t b:        Condition bit; has to be in {0,1}
**************************************************/
void cmov_int16(int16_t *r, int16_t v, uint16_t b)
{
  b = -b;
  *r ^= b & ((*r) ^ v);
}
#define cmov_int16 KYBER_NAMESPACE(cmov_int16)
void cmov_int16(int16_t *r, int16_t v, uint16_t b);

#define cmov_int16 KYBER_NAMESPACE(cmov_int16)
void cmov_int16(int16_t *r, int16_t v, uint16_t b);

#include "verify.h"
      r->coeffs[8*i+j] = 0;
      cmov_int16(r->coeffs+8*i+j, ((KYBER_Q+1)/2), (msg[i] >> j)&1);


/*************************************************
* Name:        cmov_int16
*
* Description: Copy input v to *r if b is 1, don't modify *r if b is 0. 
*              Requires b to be in {0,1};
*              Runs in constant time.
*
* Arguments:   int16_t *r:       pointer to output int16_t
*              int16_t v:        input int16_t 
*              uint8_t b:        Condition bit; has to be in {0,1}
**************************************************/
void cmov_int16(int16_t *r, int16_t v, uint16_t b)
{
  b = -b;
  *r ^= b & ((*r) ^ v);
}
#define cmov_int16 KYBER_NAMESPACE(cmov_int16)
void cmov_int16(int16_t *r, int16_t v, uint16_t b);