Line | Count | Source (jump to first uncovered line) |
1 | | /* oaep.c |
2 | | |
3 | | PKCS#1 RSA-OAEP (RFC-8017). |
4 | | |
5 | | Copyright (C) 2021-2024 Nicolas Mora |
6 | | Copyright (C) 2024 Daiki Ueno |
7 | | |
8 | | This file is part of GNU Nettle. |
9 | | |
10 | | GNU Nettle is free software: you can redistribute it and/or |
11 | | modify it under the terms of either: |
12 | | |
13 | | * the GNU Lesser General Public License as published by the Free |
14 | | Software Foundation; either version 3 of the License, or (at your |
15 | | option) any later version. |
16 | | |
17 | | or |
18 | | |
19 | | * the GNU General Public License as published by the Free |
20 | | Software Foundation; either version 2 of the License, or (at your |
21 | | option) any later version. |
22 | | |
23 | | or both in parallel, as here. |
24 | | |
25 | | GNU Nettle is distributed in the hope that it will be useful, |
26 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
27 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
28 | | General Public License for more details. |
29 | | |
30 | | You should have received copies of the GNU General Public License and |
31 | | the GNU Lesser General Public License along with this program. If |
32 | | not, see http://www.gnu.org/licenses/. |
33 | | */ |
34 | | |
35 | | #if HAVE_CONFIG_H |
36 | | #include "config.h" |
37 | | #endif |
38 | | |
39 | | #include "oaep.h" |
40 | | |
41 | | #include "gmp-glue.h" |
42 | | #include "memops.h" |
43 | | #include "memxor.h" |
44 | | #include "nettle-internal.h" |
45 | | #include "pss-mgf1.h" |
46 | | #include <stdlib.h> |
47 | | #include <string.h> |
48 | | |
49 | | /* Inputs are always cast to uint32_t values. But all values used in this |
50 | | * function should never exceed the maximum value of a uint32_t anyway. |
51 | | * these macros returns 1 on success, 0 on failure */ |
52 | | #define NOT_EQUAL(a, b) \ |
53 | 0 | ((0U - ((uint32_t)(a) ^ (uint32_t)(b))) >> 31) |
54 | 0 | #define EQUAL(a, b) (IS_ZERO_SMALL ((a) ^ (b))) |
55 | | #define GREATER_OR_EQUAL(a, b) \ |
56 | 0 | (1U - (((uint32_t)(a) - (uint32_t)(b)) >> 31)) |
57 | | |
58 | | /* This is a copy of _pkcs1_sec_decrypt_variable with a slight |
59 | | * modification for the padding format. |
60 | | */ |
61 | | static int |
62 | | _oaep_sec_decrypt_variable(size_t *length, uint8_t *message, |
63 | | size_t padded_message_length, |
64 | | const volatile uint8_t *padded_message, |
65 | | volatile size_t offset) |
66 | 0 | { |
67 | 0 | volatile int not_found = 1; |
68 | 0 | volatile int ok = 1; |
69 | 0 | size_t buflen, msglen; |
70 | 0 | size_t shift, i; |
71 | | |
72 | | /* length is discovered in a side-channel silent way. |
73 | | * not_found goes to 0 when the terminator is found. */ |
74 | 0 | for (i = offset; i < padded_message_length; i++) |
75 | 0 | { |
76 | 0 | not_found &= NOT_EQUAL(padded_message[i], 1); |
77 | 0 | offset += not_found; |
78 | 0 | } |
79 | | /* check if we ran out of buffer */ |
80 | 0 | ok &= NOT_EQUAL(not_found, 1); |
81 | | |
82 | | /* skip terminator */ |
83 | 0 | offset++; |
84 | | |
85 | | /* offset can be up to padded_message_length, due to the loop above, |
86 | | * therefore msglen can't underflow */ |
87 | 0 | msglen = padded_message_length - offset; |
88 | | |
89 | | /* we always fill the whole buffer but only up to |
90 | | * padded_message_length length */ |
91 | 0 | buflen = *length; |
92 | 0 | if (buflen > padded_message_length) { /* input independent branch */ |
93 | 0 | buflen = padded_message_length; |
94 | 0 | } |
95 | | |
96 | | /* if the message length is larger than the buffer we must fail */ |
97 | 0 | ok &= GREATER_OR_EQUAL(buflen, msglen); |
98 | | |
99 | | /* fill destination buffer fully regardless of outcome. Copies the message |
100 | | * in a memory access independent way. The destination message buffer will |
101 | | * be clobbered past the message length. */ |
102 | 0 | shift = padded_message_length - buflen; |
103 | 0 | cnd_memcpy(ok, message, padded_message + shift, buflen); |
104 | 0 | offset -= shift; |
105 | | /* In this loop, the bits of the 'offset' variable are used as shifting |
106 | | * conditions, starting from the least significant bit. The end result is |
107 | | * that the buffer is shifted left exactly 'offset' bytes. */ |
108 | 0 | for (shift = 1; shift < buflen; shift <<= 1, offset >>= 1) |
109 | 0 | { |
110 | | /* 'ok' is both a least significant bit mask and a condition */ |
111 | 0 | cnd_memcpy(offset & ok, message, message + shift, buflen - shift); |
112 | 0 | } |
113 | | |
114 | | /* update length only if we succeeded, otherwise leave unchanged */ |
115 | 0 | *length = (msglen & (-(size_t) ok)) + (*length & ((size_t) ok - 1)); |
116 | |
|
117 | 0 | return ok; |
118 | 0 | } |
119 | | |
120 | | int |
121 | | _oaep_decode_mgf1 (const uint8_t *em, |
122 | | size_t key_size, |
123 | | void *hash_ctx, const struct nettle_hash *hash, |
124 | | size_t label_length, const uint8_t *label, |
125 | | size_t *length, uint8_t *message) |
126 | 0 | { |
127 | 0 | const uint8_t *db; |
128 | 0 | size_t db_length; |
129 | 0 | const uint8_t *seed; |
130 | 0 | TMP_GMP_DECL(db_mask, uint8_t); |
131 | 0 | uint8_t seed_mask[NETTLE_MAX_HASH_DIGEST_SIZE]; |
132 | 0 | uint8_t lhash[NETTLE_MAX_HASH_DIGEST_SIZE]; |
133 | 0 | int ok = 1; |
134 | |
|
135 | 0 | assert (key_size >= 2 * hash->digest_size - 2); |
136 | | |
137 | | /* EM = 0x00 || maskedSeed || maskedDB */ |
138 | 0 | ok &= EQUAL(*em, 0); |
139 | 0 | seed = em + 1; |
140 | 0 | db = seed + hash->digest_size; |
141 | 0 | db_length = key_size - hash->digest_size - 1; |
142 | |
|
143 | 0 | TMP_GMP_ALLOC(db_mask, db_length); |
144 | | |
145 | | /* seedMask = MGF(maskedDB, hLen) */ |
146 | 0 | hash->init (hash_ctx); |
147 | 0 | hash->update (hash_ctx, db_length, db); |
148 | 0 | pss_mgf1 (hash_ctx, hash, hash->digest_size, seed_mask); |
149 | | |
150 | | /* seed = maskedSeed \xor seedMask */ |
151 | 0 | memxor (seed_mask, seed, hash->digest_size); |
152 | | |
153 | | /* dbMask = MGF(seed, seed - hLen - 1) */ |
154 | 0 | hash->init (hash_ctx); |
155 | 0 | hash->update (hash_ctx, hash->digest_size, seed_mask); |
156 | 0 | pss_mgf1 (hash_ctx, hash, db_length, db_mask); |
157 | | |
158 | | /* DB = maskedDB \xor dbMask */ |
159 | 0 | memxor (db_mask, db, db_length); |
160 | |
|
161 | 0 | hash->init (hash_ctx); |
162 | 0 | hash->update (hash_ctx, label_length, label); |
163 | 0 | hash->digest (hash_ctx, hash->digest_size, lhash); |
164 | |
|
165 | 0 | ok &= memeql_sec (db_mask, lhash, hash->digest_size); |
166 | |
|
167 | 0 | ok &= _oaep_sec_decrypt_variable (length, message, |
168 | 0 | db_length, db_mask, |
169 | 0 | hash->digest_size); |
170 | |
|
171 | 0 | TMP_GMP_FREE (db_mask); |
172 | |
|
173 | 0 | return ok; |
174 | 0 | } |
175 | | |
176 | | int |
177 | | _oaep_encode_mgf1 (mpz_t m, size_t key_size, |
178 | | void *random_ctx, nettle_random_func *random, |
179 | | void *hash_ctx, const struct nettle_hash *hash, |
180 | | size_t label_length, const uint8_t *label, |
181 | | size_t message_length, const uint8_t *message) |
182 | 0 | { |
183 | 0 | TMP_GMP_DECL(em, uint8_t); |
184 | 0 | TMP_GMP_DECL(db_mask, uint8_t); |
185 | 0 | uint8_t *db; |
186 | 0 | size_t db_length; |
187 | 0 | uint8_t *seed; |
188 | 0 | uint8_t seed_mask[NETTLE_MAX_HASH_DIGEST_SIZE]; |
189 | |
|
190 | 0 | if (message_length > key_size |
191 | 0 | || message_length + 2 + 2 * hash->digest_size > key_size) |
192 | 0 | return 0; |
193 | | |
194 | 0 | TMP_GMP_ALLOC(em, key_size); |
195 | 0 | TMP_GMP_ALLOC(db_mask, key_size); |
196 | | |
197 | | /* EM = 0x00 || maskedSeed || maskedDB */ |
198 | 0 | *em = 0; |
199 | 0 | seed = em + 1; |
200 | 0 | db = seed + hash->digest_size; |
201 | 0 | db_length = key_size - hash->digest_size - 1; |
202 | | |
203 | | /* DB = Hash(L) || PS || 0x01 || M */ |
204 | 0 | memset (db, 0, db_length); |
205 | 0 | hash->init (hash_ctx); |
206 | 0 | hash->update (hash_ctx, label_length, label); |
207 | 0 | hash->digest (hash_ctx, hash->digest_size, db); |
208 | 0 | memcpy (&db[db_length - message_length], message, message_length); |
209 | 0 | db[db_length - message_length - 1] = 0x01; |
210 | | |
211 | | /* Generate seed */ |
212 | 0 | random (random_ctx, hash->digest_size, seed); |
213 | | |
214 | | /* dbMask = MGF(seed, k - hLen - 1) */ |
215 | 0 | hash->init (hash_ctx); |
216 | 0 | hash->update (hash_ctx, hash->digest_size, seed); |
217 | 0 | pss_mgf1 (hash_ctx, hash, db_length, db_mask); |
218 | | |
219 | | /* maskedDB = DB \xor dbMask */ |
220 | 0 | memxor (db, db_mask, db_length); |
221 | | |
222 | | /* seedMask = MGF(maskedDB, hLen) */ |
223 | 0 | hash->init (hash_ctx); |
224 | 0 | hash->update (hash_ctx, db_length, db); |
225 | 0 | pss_mgf1 (hash_ctx, hash, hash->digest_size, seed_mask); |
226 | | |
227 | | /* maskedSeed = seed \xor seedMask */ |
228 | 0 | memxor (seed, seed_mask, hash->digest_size); |
229 | |
|
230 | 0 | nettle_mpz_set_str_256_u (m, key_size, em); |
231 | |
|
232 | 0 | TMP_GMP_FREE (em); |
233 | 0 | TMP_GMP_FREE (db_mask); |
234 | |
|
235 | 0 | return 1; |
236 | 0 | } |