summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNarpat Mali <narpat.mali@windriver.com>2024-02-09 15:43:55 +0000
committerSteve Sakoman <steve@sakoman.com>2024-02-15 03:51:57 -1000
commite17cf6a549c53c48629d9454ec744fd2c824a83f (patch)
treef6323003aea6761ec845b25b014f91bbbba34894
parentd83cb2d0e584f92762bf3ad6d32fd8d96d5c8471 (diff)
downloadpoky-e17cf6a549c53c48629d9454ec744fd2c824a83f.tar.gz
python3-pycryptodome: Fix CVE-2023-52323
PyCryptodome and pycryptodomex before 3.19.1 allow side-channel leakage for OAEP decryption, exploitable for a Manger attack. References: https://security-tracker.debian.org/tracker/CVE-2023-52323 https://github.com/Legrandin/pycryptodome/blob/master/Changelog.rst (From OE-Core rev: 04c9b6b081914005209bac8eeb9f417e7b989cca) Signed-off-by: Narpat Mali <narpat.mali@windriver.com> Signed-off-by: Steve Sakoman <steve@sakoman.com>
-rw-r--r--meta/recipes-devtools/python/python3-pycryptodome/CVE-2023-52323.patch436
-rw-r--r--meta/recipes-devtools/python/python3-pycryptodome_3.14.1.bb1
-rw-r--r--meta/recipes-devtools/python/python3-pycryptodomex/CVE-2023-52323.patch436
-rw-r--r--meta/recipes-devtools/python/python3-pycryptodomex_3.14.1.bb2
4 files changed, 875 insertions, 0 deletions
diff --git a/meta/recipes-devtools/python/python3-pycryptodome/CVE-2023-52323.patch b/meta/recipes-devtools/python/python3-pycryptodome/CVE-2023-52323.patch
new file mode 100644
index 0000000000..be3090eb8d
--- /dev/null
+++ b/meta/recipes-devtools/python/python3-pycryptodome/CVE-2023-52323.patch
@@ -0,0 +1,436 @@
1From 73bbed822fadddf3c0ab4a945ee6ab16bbca6961 Mon Sep 17 00:00:00 2001
2From: Helder Eijs <helderijs@gmail.com>
3Date: Thu, 1 Feb 2024 13:43:44 +0000
4Subject: [PATCH] Use constant-time (faster) padding decoding also for OAEP
5
6CVE: CVE-2023-52323
7
8Upstream-Status: Backport [https://github.com/Legrandin/pycryptodome/commit/0deea1bfe1489e8c80d2053bbb06a1aa0b181ebd]
9
10Signed-off-by: Narpat Mali <narpat.mali@windriver.com>
11---
12 lib/Crypto/Cipher/PKCS1_OAEP.py | 38 +++++-------
13 lib/Crypto/Cipher/PKCS1_v1_5.py | 31 +---------
14 lib/Crypto/Cipher/_pkcs1_oaep_decode.py | 41 +++++++++++++
15 src/pkcs1_decode.c | 79 +++++++++++++++++++++++--
16 src/test/test_pkcs1.c | 22 +++----
17 5 files changed, 145 insertions(+), 66 deletions(-)
18 create mode 100644 lib/Crypto/Cipher/_pkcs1_oaep_decode.py
19
20diff --git a/lib/Crypto/Cipher/PKCS1_OAEP.py b/lib/Crypto/Cipher/PKCS1_OAEP.py
21index 57a982b..6974584 100644
22--- a/lib/Crypto/Cipher/PKCS1_OAEP.py
23+++ b/lib/Crypto/Cipher/PKCS1_OAEP.py
24@@ -23,11 +23,13 @@
25 from Crypto.Signature.pss import MGF1
26 import Crypto.Hash.SHA1
27
28-from Crypto.Util.py3compat import bord, _copy_bytes
29+from Crypto.Util.py3compat import _copy_bytes
30 import Crypto.Util.number
31-from Crypto.Util.number import ceil_div, bytes_to_long, long_to_bytes
32-from Crypto.Util.strxor import strxor
33+from Crypto.Util.number import ceil_div, bytes_to_long, long_to_bytes
34+from Crypto.Util.strxor import strxor
35 from Crypto import Random
36+from ._pkcs1_oaep_decode import oaep_decode
37+
38
39 class PKCS1OAEP_Cipher:
40 """Cipher object for PKCS#1 v1.5 OAEP.
41@@ -68,7 +70,7 @@ class PKCS1OAEP_Cipher:
42 if mgfunc:
43 self._mgf = mgfunc
44 else:
45- self._mgf = lambda x,y: MGF1(x,y,self._hashObj)
46+ self._mgf = lambda x, y: MGF1(x, y, self._hashObj)
47
48 self._label = _copy_bytes(None, None, label)
49 self._randfunc = randfunc
50@@ -105,7 +107,7 @@ class PKCS1OAEP_Cipher:
51
52 # See 7.1.1 in RFC3447
53 modBits = Crypto.Util.number.size(self._key.n)
54- k = ceil_div(modBits, 8) # Convert from bits to bytes
55+ k = ceil_div(modBits, 8) # Convert from bits to bytes
56 hLen = self._hashObj.digest_size
57 mLen = len(message)
58
59@@ -159,11 +161,11 @@ class PKCS1OAEP_Cipher:
60
61 # See 7.1.2 in RFC3447
62 modBits = Crypto.Util.number.size(self._key.n)
63- k = ceil_div(modBits,8) # Convert from bits to bytes
64+ k = ceil_div(modBits, 8) # Convert from bits to bytes
65 hLen = self._hashObj.digest_size
66
67 # Step 1b and 1c
68- if len(ciphertext) != k or k<hLen+2:
69+ if len(ciphertext) != k or k < hLen+2:
70 raise ValueError("Ciphertext with incorrect length.")
71 # Step 2a (O2SIP)
72 ct_int = bytes_to_long(ciphertext)
73@@ -173,8 +175,6 @@ class PKCS1OAEP_Cipher:
74 em = long_to_bytes(m_int, k)
75 # Step 3a
76 lHash = self._hashObj.new(self._label).digest()
77- # Step 3b
78- y = em[0]
79 # y must be 0, but we MUST NOT check it here in order not to
80 # allow attacks like Manger's (http://dl.acm.org/citation.cfm?id=704143)
81 maskedSeed = em[1:hLen+1]
82@@ -187,22 +187,17 @@ class PKCS1OAEP_Cipher:
83 dbMask = self._mgf(seed, k-hLen-1)
84 # Step 3f
85 db = strxor(maskedDB, dbMask)
86- # Step 3g
87- one_pos = hLen + db[hLen:].find(b'\x01')
88- lHash1 = db[:hLen]
89- invalid = bord(y) | int(one_pos < hLen)
90- hash_compare = strxor(lHash1, lHash)
91- for x in hash_compare:
92- invalid |= bord(x)
93- for x in db[hLen:one_pos]:
94- invalid |= bord(x)
95- if invalid != 0:
96+ # Step 3b + 3g
97+ res = oaep_decode(em, lHash, db)
98+ if res <= 0:
99 raise ValueError("Incorrect decryption.")
100 # Step 4
101- return db[one_pos + 1:]
102+ return db[res:]
103+
104
105 def new(key, hashAlgo=None, mgfunc=None, label=b'', randfunc=None):
106- """Return a cipher object :class:`PKCS1OAEP_Cipher` that can be used to perform PKCS#1 OAEP encryption or decryption.
107+ """Return a cipher object :class:`PKCS1OAEP_Cipher`
108+ that can be used to perform PKCS#1 OAEP encryption or decryption.
109
110 :param key:
111 The key object to use to encrypt or decrypt the message.
112@@ -236,4 +231,3 @@ def new(key, hashAlgo=None, mgfunc=None, label=b'', randfunc=None):
113 if randfunc is None:
114 randfunc = Random.get_random_bytes
115 return PKCS1OAEP_Cipher(key, hashAlgo, mgfunc, label, randfunc)
116-
117diff --git a/lib/Crypto/Cipher/PKCS1_v1_5.py b/lib/Crypto/Cipher/PKCS1_v1_5.py
118index d0d474a..94e99cf 100644
119--- a/lib/Crypto/Cipher/PKCS1_v1_5.py
120+++ b/lib/Crypto/Cipher/PKCS1_v1_5.py
121@@ -25,31 +25,7 @@ __all__ = ['new', 'PKCS115_Cipher']
122 from Crypto import Random
123 from Crypto.Util.number import bytes_to_long, long_to_bytes
124 from Crypto.Util.py3compat import bord, is_bytes, _copy_bytes
125-
126-from Crypto.Util._raw_api import (load_pycryptodome_raw_lib, c_size_t,
127- c_uint8_ptr)
128-
129-
130-_raw_pkcs1_decode = load_pycryptodome_raw_lib("Crypto.Cipher._pkcs1_decode",
131- """
132- int pkcs1_decode(const uint8_t *em, size_t len_em,
133- const uint8_t *sentinel, size_t len_sentinel,
134- size_t expected_pt_len,
135- uint8_t *output);
136- """)
137-
138-
139-def _pkcs1_decode(em, sentinel, expected_pt_len, output):
140- if len(em) != len(output):
141- raise ValueError("Incorrect output length")
142-
143- ret = _raw_pkcs1_decode.pkcs1_decode(c_uint8_ptr(em),
144- c_size_t(len(em)),
145- c_uint8_ptr(sentinel),
146- c_size_t(len(sentinel)),
147- c_size_t(expected_pt_len),
148- c_uint8_ptr(output))
149- return ret
150+from ._pkcs1_oaep_decode import pkcs1_decode
151
152
153 class PKCS115_Cipher:
154@@ -113,7 +89,6 @@ class PKCS115_Cipher:
155 continue
156 ps.append(new_byte)
157 ps = b"".join(ps)
158- assert(len(ps) == k - mLen - 3)
159 # Step 2b
160 em = b'\x00\x02' + ps + b'\x00' + _copy_bytes(None, None, message)
161 # Step 3a (OS2IP)
162@@ -185,14 +160,14 @@ class PKCS115_Cipher:
163 # Step 3 (not constant time when the sentinel is not a byte string)
164 output = bytes(bytearray(k))
165 if not is_bytes(sentinel) or len(sentinel) > k:
166- size = _pkcs1_decode(em, b'', expected_pt_len, output)
167+ size = pkcs1_decode(em, b'', expected_pt_len, output)
168 if size < 0:
169 return sentinel
170 else:
171 return output[size:]
172
173 # Step 3 (somewhat constant time)
174- size = _pkcs1_decode(em, sentinel, expected_pt_len, output)
175+ size = pkcs1_decode(em, sentinel, expected_pt_len, output)
176 return output[size:]
177
178
179diff --git a/lib/Crypto/Cipher/_pkcs1_oaep_decode.py b/lib/Crypto/Cipher/_pkcs1_oaep_decode.py
180new file mode 100644
181index 0000000..fc07528
182--- /dev/null
183+++ b/lib/Crypto/Cipher/_pkcs1_oaep_decode.py
184@@ -0,0 +1,41 @@
185+from Crypto.Util._raw_api import (load_pycryptodome_raw_lib, c_size_t,
186+ c_uint8_ptr)
187+
188+
189+_raw_pkcs1_decode = load_pycryptodome_raw_lib("Crypto.Cipher._pkcs1_decode",
190+ """
191+ int pkcs1_decode(const uint8_t *em, size_t len_em,
192+ const uint8_t *sentinel, size_t len_sentinel,
193+ size_t expected_pt_len,
194+ uint8_t *output);
195+
196+ int oaep_decode(const uint8_t *em,
197+ size_t em_len,
198+ const uint8_t *lHash,
199+ size_t hLen,
200+ const uint8_t *db,
201+ size_t db_len);
202+ """)
203+
204+
205+def pkcs1_decode(em, sentinel, expected_pt_len, output):
206+ if len(em) != len(output):
207+ raise ValueError("Incorrect output length")
208+
209+ ret = _raw_pkcs1_decode.pkcs1_decode(c_uint8_ptr(em),
210+ c_size_t(len(em)),
211+ c_uint8_ptr(sentinel),
212+ c_size_t(len(sentinel)),
213+ c_size_t(expected_pt_len),
214+ c_uint8_ptr(output))
215+ return ret
216+
217+
218+def oaep_decode(em, lHash, db):
219+ ret = _raw_pkcs1_decode.oaep_decode(c_uint8_ptr(em),
220+ c_size_t(len(em)),
221+ c_uint8_ptr(lHash),
222+ c_size_t(len(lHash)),
223+ c_uint8_ptr(db),
224+ c_size_t(len(db)))
225+ return ret
226diff --git a/src/pkcs1_decode.c b/src/pkcs1_decode.c
227index 207b198..74cb4a2 100644
228--- a/src/pkcs1_decode.c
229+++ b/src/pkcs1_decode.c
230@@ -130,7 +130,7 @@ STATIC size_t safe_select_idx(size_t in1, size_t in2, uint8_t choice)
231 * - in1[] is NOT equal to in2[] where neq_mask[] is 0xFF.
232 * Return non-zero otherwise.
233 */
234-STATIC uint8_t safe_cmp(const uint8_t *in1, const uint8_t *in2,
235+STATIC uint8_t safe_cmp_masks(const uint8_t *in1, const uint8_t *in2,
236 const uint8_t *eq_mask, const uint8_t *neq_mask,
237 size_t len)
238 {
239@@ -187,7 +187,7 @@ STATIC size_t safe_search(const uint8_t *in1, uint8_t c, size_t len)
240 return result;
241 }
242
243-#define EM_PREFIX_LEN 10
244+#define PKCS1_PREFIX_LEN 10
245
246 /*
247 * Decode and verify the PKCS#1 padding, then put either the plaintext
248@@ -222,13 +222,13 @@ EXPORT_SYM int pkcs1_decode(const uint8_t *em, size_t len_em_output,
249 if (NULL == em || NULL == output || NULL == sentinel) {
250 return -1;
251 }
252- if (len_em_output < (EM_PREFIX_LEN + 2)) {
253+ if (len_em_output < (PKCS1_PREFIX_LEN + 2)) {
254 return -1;
255 }
256 if (len_sentinel > len_em_output) {
257 return -1;
258 }
259- if (expected_pt_len > 0 && expected_pt_len > (len_em_output - EM_PREFIX_LEN - 1)) {
260+ if (expected_pt_len > 0 && expected_pt_len > (len_em_output - PKCS1_PREFIX_LEN - 1)) {
261 return -1;
262 }
263
264@@ -240,7 +240,7 @@ EXPORT_SYM int pkcs1_decode(const uint8_t *em, size_t len_em_output,
265 memcpy(padded_sentinel + (len_em_output - len_sentinel), sentinel, len_sentinel);
266
267 /** The first 10 bytes must follow the pattern **/
268- match = safe_cmp(em,
269+ match = safe_cmp_masks(em,
270 (const uint8_t*)"\x00\x02" "\x00\x00\x00\x00\x00\x00\x00\x00",
271 (const uint8_t*)"\xFF\xFF" "\x00\x00\x00\x00\x00\x00\x00\x00",
272 (const uint8_t*)"\x00\x00" "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
273@@ -283,3 +283,72 @@ end:
274 free(padded_sentinel);
275 return result;
276 }
277+
278+/*
279+ * Decode and verify the OAEP padding in constant time.
280+ *
281+ * The function returns the number of bytes to ignore at the beginning
282+ * of db (the rest is the plaintext), or -1 in case of problems.
283+ */
284+
285+EXPORT_SYM int oaep_decode(const uint8_t *em,
286+ size_t em_len,
287+ const uint8_t *lHash,
288+ size_t hLen,
289+ const uint8_t *db,
290+ size_t db_len) /* em_len - 1 - hLen */
291+{
292+ int result;
293+ size_t one_pos, search_len, i;
294+ uint8_t wrong_padding;
295+ uint8_t *eq_mask = NULL;
296+ uint8_t *neq_mask = NULL;
297+ uint8_t *target_db = NULL;
298+
299+ if (NULL == em || NULL == lHash || NULL == db) {
300+ return -1;
301+ }
302+
303+ if (em_len < 2*hLen+2 || db_len != em_len-1-hLen) {
304+ return -1;
305+ }
306+
307+ /* Allocate */
308+ eq_mask = (uint8_t*) calloc(1, db_len);
309+ neq_mask = (uint8_t*) calloc(1, db_len);
310+ target_db = (uint8_t*) calloc(1, db_len);
311+ if (NULL == eq_mask || NULL == neq_mask || NULL == target_db) {
312+ result = -1;
313+ goto cleanup;
314+ }
315+
316+ /* Step 3g */
317+ search_len = db_len - hLen;
318+
319+ one_pos = safe_search(db + hLen, 0x01, search_len);
320+ if (SIZE_T_MAX == one_pos) {
321+ result = -1;
322+ goto cleanup;
323+ }
324+
325+ memset(eq_mask, 0xAA, db_len);
326+ memcpy(target_db, lHash, hLen);
327+ memset(eq_mask, 0xFF, hLen);
328+
329+ for (i=0; i<search_len; i++) {
330+ eq_mask[hLen + i] = propagate_ones(i < one_pos);
331+ }
332+
333+ wrong_padding = em[0];
334+ wrong_padding |= safe_cmp_masks(db, target_db, eq_mask, neq_mask, db_len);
335+ set_if_match(&wrong_padding, one_pos, search_len);
336+
337+ result = wrong_padding ? -1 : (int)(hLen + 1 + one_pos);
338+
339+cleanup:
340+ free(eq_mask);
341+ free(neq_mask);
342+ free(target_db);
343+
344+ return result;
345+}
346diff --git a/src/test/test_pkcs1.c b/src/test/test_pkcs1.c
347index 6ef63cb..69aaac5 100644
348--- a/src/test/test_pkcs1.c
349+++ b/src/test/test_pkcs1.c
350@@ -5,7 +5,7 @@ void set_if_match(uint8_t *flag, size_t term1, size_t term2);
351 void set_if_no_match(uint8_t *flag, size_t term1, size_t term2);
352 void safe_select(const uint8_t *in1, const uint8_t *in2, uint8_t *out, uint8_t choice, size_t len);
353 size_t safe_select_idx(size_t in1, size_t in2, uint8_t choice);
354-uint8_t safe_cmp(const uint8_t *in1, const uint8_t *in2,
355+uint8_t safe_cmp_masks(const uint8_t *in1, const uint8_t *in2,
356 const uint8_t *eq_mask, const uint8_t *neq_mask,
357 size_t len);
358 size_t safe_search(const uint8_t *in1, uint8_t c, size_t len);
359@@ -80,29 +80,29 @@ void test_safe_select_idx()
360 assert(safe_select_idx(0x100004, 0x223344, 1) == 0x223344);
361 }
362
363-void test_safe_cmp()
364+void test_safe_cmp_masks(void)
365 {
366 uint8_t res;
367
368- res = safe_cmp(onezero, onezero,
369+ res = safe_cmp_masks(onezero, onezero,
370 (uint8_t*)"\xFF\xFF",
371 (uint8_t*)"\x00\x00",
372 2);
373 assert(res == 0);
374
375- res = safe_cmp(onezero, zerozero,
376+ res = safe_cmp_masks(onezero, zerozero,
377 (uint8_t*)"\xFF\xFF",
378 (uint8_t*)"\x00\x00",
379 2);
380 assert(res != 0);
381
382- res = safe_cmp(onezero, oneone,
383+ res = safe_cmp_masks(onezero, oneone,
384 (uint8_t*)"\xFF\xFF",
385 (uint8_t*)"\x00\x00",
386 2);
387 assert(res != 0);
388
389- res = safe_cmp(onezero, oneone,
390+ res = safe_cmp_masks(onezero, oneone,
391 (uint8_t*)"\xFF\x00",
392 (uint8_t*)"\x00\x00",
393 2);
394@@ -110,19 +110,19 @@ void test_safe_cmp()
395
396 /** -- **/
397
398- res = safe_cmp(onezero, onezero,
399+ res = safe_cmp_masks(onezero, onezero,
400 (uint8_t*)"\x00\x00",
401 (uint8_t*)"\xFF\xFF",
402 2);
403 assert(res != 0);
404
405- res = safe_cmp(oneone, zerozero,
406+ res = safe_cmp_masks(oneone, zerozero,
407 (uint8_t*)"\x00\x00",
408 (uint8_t*)"\xFF\xFF",
409 2);
410 assert(res == 0);
411
412- res = safe_cmp(onezero, oneone,
413+ res = safe_cmp_masks(onezero, oneone,
414 (uint8_t*)"\x00\x00",
415 (uint8_t*)"\x00\xFF",
416 2);
417@@ -130,7 +130,7 @@ void test_safe_cmp()
418
419 /** -- **/
420
421- res = safe_cmp(onezero, oneone,
422+ res = safe_cmp_masks(onezero, oneone,
423 (uint8_t*)"\xFF\x00",
424 (uint8_t*)"\x00\xFF",
425 2);
426@@ -158,7 +158,7 @@ int main(void)
427 test_set_if_no_match();
428 test_safe_select();
429 test_safe_select_idx();
430- test_safe_cmp();
431+ test_safe_cmp_masks();
432 test_safe_search();
433 return 0;
434 }
435--
4362.40.0
diff --git a/meta/recipes-devtools/python/python3-pycryptodome_3.14.1.bb b/meta/recipes-devtools/python/python3-pycryptodome_3.14.1.bb
index c0324590c2..1e6c514224 100644
--- a/meta/recipes-devtools/python/python3-pycryptodome_3.14.1.bb
+++ b/meta/recipes-devtools/python/python3-pycryptodome_3.14.1.bb
@@ -3,3 +3,4 @@ inherit setuptools3
3 3
4SRC_URI[sha256sum] = "e04e40a7f8c1669195536a37979dd87da2c32dbdc73d6fe35f0077b0c17c803b" 4SRC_URI[sha256sum] = "e04e40a7f8c1669195536a37979dd87da2c32dbdc73d6fe35f0077b0c17c803b"
5 5
6SRC_URI += "file://CVE-2023-52323.patch"
diff --git a/meta/recipes-devtools/python/python3-pycryptodomex/CVE-2023-52323.patch b/meta/recipes-devtools/python/python3-pycryptodomex/CVE-2023-52323.patch
new file mode 100644
index 0000000000..56000b996e
--- /dev/null
+++ b/meta/recipes-devtools/python/python3-pycryptodomex/CVE-2023-52323.patch
@@ -0,0 +1,436 @@
1From 8ed5cf533be298d40ec9f75a188738ad4c3a8417 Mon Sep 17 00:00:00 2001
2From: Narpat Mali <narpat.mali@windriver.com>
3Date: Thu, 8 Feb 2024 09:09:35 +0000
4Subject: [PATCH] Use constant-time (faster) padding decoding also for OAEP
5
6CVE: CVE-2023-52323
7
8Upstream-Status: Backport [https://github.com/Legrandin/pycryptodome/commit/0deea1bfe1489e8c80d2053bbb06a1aa0b181ebd]
9
10Signed-off-by: Narpat Mali <narpat.mali@windriver.com>
11---
12 lib/Cryptodome/Cipher/PKCS1_OAEP.py | 38 +++++-----
13 lib/Cryptodome/Cipher/PKCS1_v1_5.py | 31 +-------
14 lib/Cryptodome/Cipher/_pkcs1_oaep_decode.py | 41 +++++++++++
15 src/pkcs1_decode.c | 79 +++++++++++++++++++--
16 src/test/test_pkcs1.c | 22 +++---
17 5 files changed, 145 insertions(+), 66 deletions(-)
18 create mode 100644 lib/Cryptodome/Cipher/_pkcs1_oaep_decode.py
19
20diff --git a/lib/Cryptodome/Cipher/PKCS1_OAEP.py b/lib/Cryptodome/Cipher/PKCS1_OAEP.py
21index 7525c5d..653df04 100644
22--- a/lib/Cryptodome/Cipher/PKCS1_OAEP.py
23+++ b/lib/Cryptodome/Cipher/PKCS1_OAEP.py
24@@ -23,11 +23,13 @@
25 from Cryptodome.Signature.pss import MGF1
26 import Cryptodome.Hash.SHA1
27
28-from Cryptodome.Util.py3compat import bord, _copy_bytes
29+from Crypto.Util.py3compat import _copy_bytes
30 import Cryptodome.Util.number
31-from Cryptodome.Util.number import ceil_div, bytes_to_long, long_to_bytes
32-from Cryptodome.Util.strxor import strxor
33+from Crypto.Util.number import ceil_div, bytes_to_long, long_to_bytes
34+from Crypto.Util.strxor import strxor
35 from Cryptodome import Random
36+from ._pkcs1_oaep_decode import oaep_decode
37+
38
39 class PKCS1OAEP_Cipher:
40 """Cipher object for PKCS#1 v1.5 OAEP.
41@@ -68,7 +70,7 @@ class PKCS1OAEP_Cipher:
42 if mgfunc:
43 self._mgf = mgfunc
44 else:
45- self._mgf = lambda x,y: MGF1(x,y,self._hashObj)
46+ self._mgf = lambda x, y: MGF1(x, y, self._hashObj)
47
48 self._label = _copy_bytes(None, None, label)
49 self._randfunc = randfunc
50@@ -105,7 +107,7 @@ class PKCS1OAEP_Cipher:
51
52 # See 7.1.1 in RFC3447
53 modBits = Cryptodome.Util.number.size(self._key.n)
54- k = ceil_div(modBits, 8) # Convert from bits to bytes
55+ k = ceil_div(modBits, 8) # Convert from bits to bytes
56 hLen = self._hashObj.digest_size
57 mLen = len(message)
58
59@@ -159,11 +161,11 @@ class PKCS1OAEP_Cipher:
60
61 # See 7.1.2 in RFC3447
62 modBits = Cryptodome.Util.number.size(self._key.n)
63- k = ceil_div(modBits,8) # Convert from bits to bytes
64+ k = ceil_div(modBits, 8) # Convert from bits to bytes
65 hLen = self._hashObj.digest_size
66
67 # Step 1b and 1c
68- if len(ciphertext) != k or k<hLen+2:
69+ if len(ciphertext) != k or k < hLen+2:
70 raise ValueError("Ciphertext with incorrect length.")
71 # Step 2a (O2SIP)
72 ct_int = bytes_to_long(ciphertext)
73@@ -173,8 +175,6 @@ class PKCS1OAEP_Cipher:
74 em = long_to_bytes(m_int, k)
75 # Step 3a
76 lHash = self._hashObj.new(self._label).digest()
77- # Step 3b
78- y = em[0]
79 # y must be 0, but we MUST NOT check it here in order not to
80 # allow attacks like Manger's (http://dl.acm.org/citation.cfm?id=704143)
81 maskedSeed = em[1:hLen+1]
82@@ -187,22 +187,17 @@ class PKCS1OAEP_Cipher:
83 dbMask = self._mgf(seed, k-hLen-1)
84 # Step 3f
85 db = strxor(maskedDB, dbMask)
86- # Step 3g
87- one_pos = hLen + db[hLen:].find(b'\x01')
88- lHash1 = db[:hLen]
89- invalid = bord(y) | int(one_pos < hLen)
90- hash_compare = strxor(lHash1, lHash)
91- for x in hash_compare:
92- invalid |= bord(x)
93- for x in db[hLen:one_pos]:
94- invalid |= bord(x)
95- if invalid != 0:
96+ # Step 3b + 3g
97+ res = oaep_decode(em, lHash, db)
98+ if res <= 0:
99 raise ValueError("Incorrect decryption.")
100 # Step 4
101- return db[one_pos + 1:]
102+ return db[res:]
103+
104
105 def new(key, hashAlgo=None, mgfunc=None, label=b'', randfunc=None):
106- """Return a cipher object :class:`PKCS1OAEP_Cipher` that can be used to perform PKCS#1 OAEP encryption or decryption.
107+ """Return a cipher object :class:`PKCS1OAEP_Cipher`
108+ that can be used to perform PKCS#1 OAEP encryption or decryption.
109
110 :param key:
111 The key object to use to encrypt or decrypt the message.
112@@ -236,4 +231,3 @@ def new(key, hashAlgo=None, mgfunc=None, label=b'', randfunc=None):
113 if randfunc is None:
114 randfunc = Random.get_random_bytes
115 return PKCS1OAEP_Cipher(key, hashAlgo, mgfunc, label, randfunc)
116-
117diff --git a/lib/Cryptodome/Cipher/PKCS1_v1_5.py b/lib/Cryptodome/Cipher/PKCS1_v1_5.py
118index 17ef9eb..f20a7ce 100644
119--- a/lib/Cryptodome/Cipher/PKCS1_v1_5.py
120+++ b/lib/Cryptodome/Cipher/PKCS1_v1_5.py
121@@ -25,31 +25,7 @@ __all__ = ['new', 'PKCS115_Cipher']
122 from Cryptodome import Random
123 from Cryptodome.Util.number import bytes_to_long, long_to_bytes
124 from Cryptodome.Util.py3compat import bord, is_bytes, _copy_bytes
125-
126-from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, c_size_t,
127- c_uint8_ptr)
128-
129-
130-_raw_pkcs1_decode = load_pycryptodome_raw_lib("Cryptodome.Cipher._pkcs1_decode",
131- """
132- int pkcs1_decode(const uint8_t *em, size_t len_em,
133- const uint8_t *sentinel, size_t len_sentinel,
134- size_t expected_pt_len,
135- uint8_t *output);
136- """)
137-
138-
139-def _pkcs1_decode(em, sentinel, expected_pt_len, output):
140- if len(em) != len(output):
141- raise ValueError("Incorrect output length")
142-
143- ret = _raw_pkcs1_decode.pkcs1_decode(c_uint8_ptr(em),
144- c_size_t(len(em)),
145- c_uint8_ptr(sentinel),
146- c_size_t(len(sentinel)),
147- c_size_t(expected_pt_len),
148- c_uint8_ptr(output))
149- return ret
150+from ._pkcs1_oaep_decode import pkcs1_decode
151
152
153 class PKCS115_Cipher:
154@@ -113,7 +89,6 @@ class PKCS115_Cipher:
155 continue
156 ps.append(new_byte)
157 ps = b"".join(ps)
158- assert(len(ps) == k - mLen - 3)
159 # Step 2b
160 em = b'\x00\x02' + ps + b'\x00' + _copy_bytes(None, None, message)
161 # Step 3a (OS2IP)
162@@ -185,14 +160,14 @@ class PKCS115_Cipher:
163 # Step 3 (not constant time when the sentinel is not a byte string)
164 output = bytes(bytearray(k))
165 if not is_bytes(sentinel) or len(sentinel) > k:
166- size = _pkcs1_decode(em, b'', expected_pt_len, output)
167+ size = pkcs1_decode(em, b'', expected_pt_len, output)
168 if size < 0:
169 return sentinel
170 else:
171 return output[size:]
172
173 # Step 3 (somewhat constant time)
174- size = _pkcs1_decode(em, sentinel, expected_pt_len, output)
175+ size = pkcs1_decode(em, sentinel, expected_pt_len, output)
176 return output[size:]
177
178
179diff --git a/lib/Cryptodome/Cipher/_pkcs1_oaep_decode.py b/lib/Cryptodome/Cipher/_pkcs1_oaep_decode.py
180new file mode 100644
181index 0000000..fc07528
182--- /dev/null
183+++ b/lib/Cryptodome/Cipher/_pkcs1_oaep_decode.py
184@@ -0,0 +1,41 @@
185+from Crypto.Util._raw_api import (load_pycryptodome_raw_lib, c_size_t,
186+ c_uint8_ptr)
187+
188+
189+_raw_pkcs1_decode = load_pycryptodome_raw_lib("Crypto.Cipher._pkcs1_decode",
190+ """
191+ int pkcs1_decode(const uint8_t *em, size_t len_em,
192+ const uint8_t *sentinel, size_t len_sentinel,
193+ size_t expected_pt_len,
194+ uint8_t *output);
195+
196+ int oaep_decode(const uint8_t *em,
197+ size_t em_len,
198+ const uint8_t *lHash,
199+ size_t hLen,
200+ const uint8_t *db,
201+ size_t db_len);
202+ """)
203+
204+
205+def pkcs1_decode(em, sentinel, expected_pt_len, output):
206+ if len(em) != len(output):
207+ raise ValueError("Incorrect output length")
208+
209+ ret = _raw_pkcs1_decode.pkcs1_decode(c_uint8_ptr(em),
210+ c_size_t(len(em)),
211+ c_uint8_ptr(sentinel),
212+ c_size_t(len(sentinel)),
213+ c_size_t(expected_pt_len),
214+ c_uint8_ptr(output))
215+ return ret
216+
217+
218+def oaep_decode(em, lHash, db):
219+ ret = _raw_pkcs1_decode.oaep_decode(c_uint8_ptr(em),
220+ c_size_t(len(em)),
221+ c_uint8_ptr(lHash),
222+ c_size_t(len(lHash)),
223+ c_uint8_ptr(db),
224+ c_size_t(len(db)))
225+ return ret
226diff --git a/src/pkcs1_decode.c b/src/pkcs1_decode.c
227index 207b198..74cb4a2 100644
228--- a/src/pkcs1_decode.c
229+++ b/src/pkcs1_decode.c
230@@ -130,7 +130,7 @@ STATIC size_t safe_select_idx(size_t in1, size_t in2, uint8_t choice)
231 * - in1[] is NOT equal to in2[] where neq_mask[] is 0xFF.
232 * Return non-zero otherwise.
233 */
234-STATIC uint8_t safe_cmp(const uint8_t *in1, const uint8_t *in2,
235+STATIC uint8_t safe_cmp_masks(const uint8_t *in1, const uint8_t *in2,
236 const uint8_t *eq_mask, const uint8_t *neq_mask,
237 size_t len)
238 {
239@@ -187,7 +187,7 @@ STATIC size_t safe_search(const uint8_t *in1, uint8_t c, size_t len)
240 return result;
241 }
242
243-#define EM_PREFIX_LEN 10
244+#define PKCS1_PREFIX_LEN 10
245
246 /*
247 * Decode and verify the PKCS#1 padding, then put either the plaintext
248@@ -222,13 +222,13 @@ EXPORT_SYM int pkcs1_decode(const uint8_t *em, size_t len_em_output,
249 if (NULL == em || NULL == output || NULL == sentinel) {
250 return -1;
251 }
252- if (len_em_output < (EM_PREFIX_LEN + 2)) {
253+ if (len_em_output < (PKCS1_PREFIX_LEN + 2)) {
254 return -1;
255 }
256 if (len_sentinel > len_em_output) {
257 return -1;
258 }
259- if (expected_pt_len > 0 && expected_pt_len > (len_em_output - EM_PREFIX_LEN - 1)) {
260+ if (expected_pt_len > 0 && expected_pt_len > (len_em_output - PKCS1_PREFIX_LEN - 1)) {
261 return -1;
262 }
263
264@@ -240,7 +240,7 @@ EXPORT_SYM int pkcs1_decode(const uint8_t *em, size_t len_em_output,
265 memcpy(padded_sentinel + (len_em_output - len_sentinel), sentinel, len_sentinel);
266
267 /** The first 10 bytes must follow the pattern **/
268- match = safe_cmp(em,
269+ match = safe_cmp_masks(em,
270 (const uint8_t*)"\x00\x02" "\x00\x00\x00\x00\x00\x00\x00\x00",
271 (const uint8_t*)"\xFF\xFF" "\x00\x00\x00\x00\x00\x00\x00\x00",
272 (const uint8_t*)"\x00\x00" "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
273@@ -283,3 +283,72 @@ end:
274 free(padded_sentinel);
275 return result;
276 }
277+
278+/*
279+ * Decode and verify the OAEP padding in constant time.
280+ *
281+ * The function returns the number of bytes to ignore at the beginning
282+ * of db (the rest is the plaintext), or -1 in case of problems.
283+ */
284+
285+EXPORT_SYM int oaep_decode(const uint8_t *em,
286+ size_t em_len,
287+ const uint8_t *lHash,
288+ size_t hLen,
289+ const uint8_t *db,
290+ size_t db_len) /* em_len - 1 - hLen */
291+{
292+ int result;
293+ size_t one_pos, search_len, i;
294+ uint8_t wrong_padding;
295+ uint8_t *eq_mask = NULL;
296+ uint8_t *neq_mask = NULL;
297+ uint8_t *target_db = NULL;
298+
299+ if (NULL == em || NULL == lHash || NULL == db) {
300+ return -1;
301+ }
302+
303+ if (em_len < 2*hLen+2 || db_len != em_len-1-hLen) {
304+ return -1;
305+ }
306+
307+ /* Allocate */
308+ eq_mask = (uint8_t*) calloc(1, db_len);
309+ neq_mask = (uint8_t*) calloc(1, db_len);
310+ target_db = (uint8_t*) calloc(1, db_len);
311+ if (NULL == eq_mask || NULL == neq_mask || NULL == target_db) {
312+ result = -1;
313+ goto cleanup;
314+ }
315+
316+ /* Step 3g */
317+ search_len = db_len - hLen;
318+
319+ one_pos = safe_search(db + hLen, 0x01, search_len);
320+ if (SIZE_T_MAX == one_pos) {
321+ result = -1;
322+ goto cleanup;
323+ }
324+
325+ memset(eq_mask, 0xAA, db_len);
326+ memcpy(target_db, lHash, hLen);
327+ memset(eq_mask, 0xFF, hLen);
328+
329+ for (i=0; i<search_len; i++) {
330+ eq_mask[hLen + i] = propagate_ones(i < one_pos);
331+ }
332+
333+ wrong_padding = em[0];
334+ wrong_padding |= safe_cmp_masks(db, target_db, eq_mask, neq_mask, db_len);
335+ set_if_match(&wrong_padding, one_pos, search_len);
336+
337+ result = wrong_padding ? -1 : (int)(hLen + 1 + one_pos);
338+
339+cleanup:
340+ free(eq_mask);
341+ free(neq_mask);
342+ free(target_db);
343+
344+ return result;
345+}
346diff --git a/src/test/test_pkcs1.c b/src/test/test_pkcs1.c
347index 6ef63cb..69aaac5 100644
348--- a/src/test/test_pkcs1.c
349+++ b/src/test/test_pkcs1.c
350@@ -5,7 +5,7 @@ void set_if_match(uint8_t *flag, size_t term1, size_t term2);
351 void set_if_no_match(uint8_t *flag, size_t term1, size_t term2);
352 void safe_select(const uint8_t *in1, const uint8_t *in2, uint8_t *out, uint8_t choice, size_t len);
353 size_t safe_select_idx(size_t in1, size_t in2, uint8_t choice);
354-uint8_t safe_cmp(const uint8_t *in1, const uint8_t *in2,
355+uint8_t safe_cmp_masks(const uint8_t *in1, const uint8_t *in2,
356 const uint8_t *eq_mask, const uint8_t *neq_mask,
357 size_t len);
358 size_t safe_search(const uint8_t *in1, uint8_t c, size_t len);
359@@ -80,29 +80,29 @@ void test_safe_select_idx()
360 assert(safe_select_idx(0x100004, 0x223344, 1) == 0x223344);
361 }
362
363-void test_safe_cmp()
364+void test_safe_cmp_masks(void)
365 {
366 uint8_t res;
367
368- res = safe_cmp(onezero, onezero,
369+ res = safe_cmp_masks(onezero, onezero,
370 (uint8_t*)"\xFF\xFF",
371 (uint8_t*)"\x00\x00",
372 2);
373 assert(res == 0);
374
375- res = safe_cmp(onezero, zerozero,
376+ res = safe_cmp_masks(onezero, zerozero,
377 (uint8_t*)"\xFF\xFF",
378 (uint8_t*)"\x00\x00",
379 2);
380 assert(res != 0);
381
382- res = safe_cmp(onezero, oneone,
383+ res = safe_cmp_masks(onezero, oneone,
384 (uint8_t*)"\xFF\xFF",
385 (uint8_t*)"\x00\x00",
386 2);
387 assert(res != 0);
388
389- res = safe_cmp(onezero, oneone,
390+ res = safe_cmp_masks(onezero, oneone,
391 (uint8_t*)"\xFF\x00",
392 (uint8_t*)"\x00\x00",
393 2);
394@@ -110,19 +110,19 @@ void test_safe_cmp()
395
396 /** -- **/
397
398- res = safe_cmp(onezero, onezero,
399+ res = safe_cmp_masks(onezero, onezero,
400 (uint8_t*)"\x00\x00",
401 (uint8_t*)"\xFF\xFF",
402 2);
403 assert(res != 0);
404
405- res = safe_cmp(oneone, zerozero,
406+ res = safe_cmp_masks(oneone, zerozero,
407 (uint8_t*)"\x00\x00",
408 (uint8_t*)"\xFF\xFF",
409 2);
410 assert(res == 0);
411
412- res = safe_cmp(onezero, oneone,
413+ res = safe_cmp_masks(onezero, oneone,
414 (uint8_t*)"\x00\x00",
415 (uint8_t*)"\x00\xFF",
416 2);
417@@ -130,7 +130,7 @@ void test_safe_cmp()
418
419 /** -- **/
420
421- res = safe_cmp(onezero, oneone,
422+ res = safe_cmp_masks(onezero, oneone,
423 (uint8_t*)"\xFF\x00",
424 (uint8_t*)"\x00\xFF",
425 2);
426@@ -158,7 +158,7 @@ int main(void)
427 test_set_if_no_match();
428 test_safe_select();
429 test_safe_select_idx();
430- test_safe_cmp();
431+ test_safe_cmp_masks();
432 test_safe_search();
433 return 0;
434 }
435--
4362.40.0
diff --git a/meta/recipes-devtools/python/python3-pycryptodomex_3.14.1.bb b/meta/recipes-devtools/python/python3-pycryptodomex_3.14.1.bb
index 79a3fee19c..31ad3fda5e 100644
--- a/meta/recipes-devtools/python/python3-pycryptodomex_3.14.1.bb
+++ b/meta/recipes-devtools/python/python3-pycryptodomex_3.14.1.bb
@@ -3,6 +3,8 @@ inherit setuptools3
3 3
4SRC_URI[sha256sum] = "2ce76ed0081fd6ac8c74edc75b9d14eca2064173af79843c24fa62573263c1f2" 4SRC_URI[sha256sum] = "2ce76ed0081fd6ac8c74edc75b9d14eca2064173af79843c24fa62573263c1f2"
5 5
6SRC_URI += "file://CVE-2023-52323.patch"
7
6FILES:${PN}-tests = " \ 8FILES:${PN}-tests = " \
7 ${PYTHON_SITEPACKAGES_DIR}/Cryptodome/SelfTest/ \ 9 ${PYTHON_SITEPACKAGES_DIR}/Cryptodome/SelfTest/ \
8 ${PYTHON_SITEPACKAGES_DIR}/Cryptodome/SelfTest/__pycache__/ \ 10 ${PYTHON_SITEPACKAGES_DIR}/Cryptodome/SelfTest/__pycache__/ \