/src/hostap/src/tls/pkcs1.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * PKCS #1 (RSA Encryption) |
3 | | * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi> |
4 | | * |
5 | | * This software may be distributed under the terms of the BSD license. |
6 | | * See README for more details. |
7 | | */ |
8 | | |
9 | | #include "includes.h" |
10 | | |
11 | | #include "common.h" |
12 | | #include "crypto/crypto.h" |
13 | | #include "rsa.h" |
14 | | #include "asn1.h" |
15 | | #include "pkcs1.h" |
16 | | |
17 | | |
18 | | static int pkcs1_generate_encryption_block(u8 block_type, size_t modlen, |
19 | | const u8 *in, size_t inlen, |
20 | | u8 *out, size_t *outlen) |
21 | 0 | { |
22 | 0 | size_t ps_len; |
23 | 0 | u8 *pos; |
24 | | |
25 | | /* |
26 | | * PKCS #1 v1.5, 8.1: |
27 | | * |
28 | | * EB = 00 || BT || PS || 00 || D |
29 | | * BT = 00 or 01 for private-key operation; 02 for public-key operation |
30 | | * PS = k-3-||D||; at least eight octets |
31 | | * (BT=0: PS=0x00, BT=1: PS=0xff, BT=2: PS=pseudorandom non-zero) |
32 | | * k = length of modulus in octets (modlen) |
33 | | */ |
34 | |
|
35 | 0 | if (modlen < 12 || modlen > *outlen || inlen > modlen - 11) { |
36 | 0 | wpa_printf(MSG_DEBUG, "PKCS #1: %s - Invalid buffer " |
37 | 0 | "lengths (modlen=%lu outlen=%lu inlen=%lu)", |
38 | 0 | __func__, (unsigned long) modlen, |
39 | 0 | (unsigned long) *outlen, |
40 | 0 | (unsigned long) inlen); |
41 | 0 | return -1; |
42 | 0 | } |
43 | | |
44 | 0 | pos = out; |
45 | 0 | *pos++ = 0x00; |
46 | 0 | *pos++ = block_type; /* BT */ |
47 | 0 | ps_len = modlen - inlen - 3; |
48 | 0 | switch (block_type) { |
49 | 0 | case 0: |
50 | 0 | os_memset(pos, 0x00, ps_len); |
51 | 0 | pos += ps_len; |
52 | 0 | break; |
53 | 0 | case 1: |
54 | 0 | os_memset(pos, 0xff, ps_len); |
55 | 0 | pos += ps_len; |
56 | 0 | break; |
57 | 0 | case 2: |
58 | 0 | if (os_get_random(pos, ps_len) < 0) { |
59 | 0 | wpa_printf(MSG_DEBUG, "PKCS #1: %s - Failed to get " |
60 | 0 | "random data for PS", __func__); |
61 | 0 | return -1; |
62 | 0 | } |
63 | 0 | while (ps_len--) { |
64 | 0 | if (*pos == 0x00) |
65 | 0 | *pos = 0x01; |
66 | 0 | pos++; |
67 | 0 | } |
68 | 0 | break; |
69 | 0 | default: |
70 | 0 | wpa_printf(MSG_DEBUG, "PKCS #1: %s - Unsupported block type " |
71 | 0 | "%d", __func__, block_type); |
72 | 0 | return -1; |
73 | 0 | } |
74 | 0 | *pos++ = 0x00; |
75 | 0 | os_memcpy(pos, in, inlen); /* D */ |
76 | |
|
77 | 0 | return 0; |
78 | 0 | } |
79 | | |
80 | | |
81 | | int pkcs1_encrypt(int block_type, struct crypto_rsa_key *key, |
82 | | int use_private, const u8 *in, size_t inlen, |
83 | | u8 *out, size_t *outlen) |
84 | 0 | { |
85 | 0 | size_t modlen; |
86 | |
|
87 | 0 | modlen = crypto_rsa_get_modulus_len(key); |
88 | |
|
89 | 0 | if (pkcs1_generate_encryption_block(block_type, modlen, in, inlen, |
90 | 0 | out, outlen) < 0) |
91 | 0 | return -1; |
92 | | |
93 | 0 | return crypto_rsa_exptmod(out, modlen, out, outlen, key, use_private); |
94 | 0 | } |
95 | | |
96 | | |
97 | | int pkcs1_v15_private_key_decrypt(struct crypto_rsa_key *key, |
98 | | const u8 *in, size_t inlen, |
99 | | u8 *out, size_t *outlen) |
100 | 0 | { |
101 | 0 | int res; |
102 | 0 | u8 *pos, *end; |
103 | |
|
104 | 0 | res = crypto_rsa_exptmod(in, inlen, out, outlen, key, 1); |
105 | 0 | if (res) |
106 | 0 | return res; |
107 | | |
108 | 0 | if (*outlen < 2 || out[0] != 0 || out[1] != 2) |
109 | 0 | return -1; |
110 | | |
111 | | /* Skip PS (pseudorandom non-zero octets) */ |
112 | 0 | pos = out + 2; |
113 | 0 | end = out + *outlen; |
114 | 0 | while (*pos && pos < end) |
115 | 0 | pos++; |
116 | 0 | if (pos == end) |
117 | 0 | return -1; |
118 | 0 | if (pos - out - 2 < 8) { |
119 | | /* PKCS #1 v1.5, 8.1: At least eight octets long PS */ |
120 | 0 | wpa_printf(MSG_INFO, "LibTomCrypt: Too short padding"); |
121 | 0 | return -1; |
122 | 0 | } |
123 | 0 | pos++; |
124 | |
|
125 | 0 | *outlen -= pos - out; |
126 | | |
127 | | /* Strip PKCS #1 header */ |
128 | 0 | os_memmove(out, pos, *outlen); |
129 | |
|
130 | 0 | return 0; |
131 | 0 | } |
132 | | |
133 | | |
134 | | int pkcs1_decrypt_public_key(struct crypto_rsa_key *key, |
135 | | const u8 *crypt, size_t crypt_len, |
136 | | u8 *plain, size_t *plain_len) |
137 | 0 | { |
138 | 0 | size_t len; |
139 | 0 | u8 *pos; |
140 | |
|
141 | 0 | len = *plain_len; |
142 | 0 | if (crypto_rsa_exptmod(crypt, crypt_len, plain, &len, key, 0) < 0) |
143 | 0 | return -1; |
144 | | |
145 | | /* |
146 | | * PKCS #1 v1.5, 8.1: |
147 | | * |
148 | | * EB = 00 || BT || PS || 00 || D |
149 | | * BT = 00 or 01 |
150 | | * PS = k-3-||D|| times (00 if BT=00) or (FF if BT=01) |
151 | | * k = length of modulus in octets |
152 | | * |
153 | | * Based on 10.1.3, "The block type shall be 01" for a signature. |
154 | | */ |
155 | | |
156 | 0 | if (len < 3 + 8 + 16 /* min hash len */ || |
157 | 0 | plain[0] != 0x00 || plain[1] != 0x01) { |
158 | 0 | wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB " |
159 | 0 | "structure"); |
160 | 0 | wpa_hexdump_key(MSG_DEBUG, "Signature EB", plain, len); |
161 | 0 | return -1; |
162 | 0 | } |
163 | | |
164 | 0 | pos = plain + 3; |
165 | | /* BT = 01 */ |
166 | 0 | if (plain[2] != 0xff) { |
167 | 0 | wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature " |
168 | 0 | "PS (BT=01)"); |
169 | 0 | wpa_hexdump_key(MSG_DEBUG, "Signature EB", plain, len); |
170 | 0 | return -1; |
171 | 0 | } |
172 | 0 | while (pos < plain + len && *pos == 0xff) |
173 | 0 | pos++; |
174 | |
|
175 | 0 | if (pos - plain - 2 < 8) { |
176 | | /* PKCS #1 v1.5, 8.1: At least eight octets long PS */ |
177 | 0 | wpa_printf(MSG_INFO, "LibTomCrypt: Too short signature " |
178 | 0 | "padding"); |
179 | 0 | wpa_hexdump_key(MSG_DEBUG, "Signature EB", plain, len); |
180 | 0 | return -1; |
181 | 0 | } |
182 | | |
183 | 0 | if (pos + 16 /* min hash len */ >= plain + len || *pos != 0x00) { |
184 | 0 | wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB " |
185 | 0 | "structure (2)"); |
186 | 0 | wpa_hexdump_key(MSG_DEBUG, "Signature EB", plain, len); |
187 | 0 | return -1; |
188 | 0 | } |
189 | 0 | pos++; |
190 | 0 | len -= pos - plain; |
191 | | |
192 | | /* Strip PKCS #1 header */ |
193 | 0 | os_memmove(plain, pos, len); |
194 | 0 | *plain_len = len; |
195 | |
|
196 | 0 | return 0; |
197 | 0 | } |
198 | | |
199 | | |
200 | | int pkcs1_v15_sig_ver(struct crypto_public_key *pk, |
201 | | const u8 *s, size_t s_len, |
202 | | const struct asn1_oid *hash_alg, |
203 | | const u8 *hash, size_t hash_len) |
204 | 0 | { |
205 | 0 | int res; |
206 | 0 | u8 *decrypted; |
207 | 0 | size_t decrypted_len; |
208 | 0 | const u8 *pos, *end, *next, *da_end; |
209 | 0 | struct asn1_hdr hdr; |
210 | 0 | struct asn1_oid oid; |
211 | |
|
212 | 0 | decrypted = os_malloc(s_len); |
213 | 0 | if (decrypted == NULL) |
214 | 0 | return -1; |
215 | 0 | decrypted_len = s_len; |
216 | 0 | res = crypto_public_key_decrypt_pkcs1(pk, s, s_len, decrypted, |
217 | 0 | &decrypted_len); |
218 | 0 | if (res < 0) { |
219 | 0 | wpa_printf(MSG_INFO, "PKCS #1: RSA decrypt failed"); |
220 | 0 | os_free(decrypted); |
221 | 0 | return -1; |
222 | 0 | } |
223 | 0 | wpa_hexdump(MSG_DEBUG, "Decrypted(S)", decrypted, decrypted_len); |
224 | | |
225 | | /* |
226 | | * PKCS #1 v1.5, 10.1.2: |
227 | | * |
228 | | * DigestInfo ::= SEQUENCE { |
229 | | * digestAlgorithm DigestAlgorithmIdentifier, |
230 | | * digest Digest |
231 | | * } |
232 | | * |
233 | | * DigestAlgorithmIdentifier ::= AlgorithmIdentifier |
234 | | * |
235 | | * Digest ::= OCTET STRING |
236 | | * |
237 | | */ |
238 | 0 | if (asn1_get_next(decrypted, decrypted_len, &hdr) < 0 || |
239 | 0 | !asn1_is_sequence(&hdr)) { |
240 | 0 | asn1_unexpected(&hdr, |
241 | 0 | "PKCS #1: Expected SEQUENCE (DigestInfo)"); |
242 | 0 | os_free(decrypted); |
243 | 0 | return -1; |
244 | 0 | } |
245 | 0 | wpa_hexdump(MSG_MSGDUMP, "PKCS #1: DigestInfo", |
246 | 0 | hdr.payload, hdr.length); |
247 | |
|
248 | 0 | pos = hdr.payload; |
249 | 0 | end = pos + hdr.length; |
250 | | |
251 | | /* |
252 | | * X.509: |
253 | | * AlgorithmIdentifier ::= SEQUENCE { |
254 | | * algorithm OBJECT IDENTIFIER, |
255 | | * parameters ANY DEFINED BY algorithm OPTIONAL |
256 | | * } |
257 | | */ |
258 | |
|
259 | 0 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || |
260 | 0 | !asn1_is_sequence(&hdr)) { |
261 | 0 | asn1_unexpected(&hdr, |
262 | 0 | "PKCS #1: Expected SEQUENCE (AlgorithmIdentifier)"); |
263 | 0 | os_free(decrypted); |
264 | 0 | return -1; |
265 | 0 | } |
266 | 0 | wpa_hexdump(MSG_MSGDUMP, "PKCS #1: DigestAlgorithmIdentifier", |
267 | 0 | hdr.payload, hdr.length); |
268 | 0 | da_end = hdr.payload + hdr.length; |
269 | |
|
270 | 0 | if (asn1_get_oid(hdr.payload, hdr.length, &oid, &next)) { |
271 | 0 | wpa_printf(MSG_DEBUG, |
272 | 0 | "PKCS #1: Failed to parse digestAlgorithm"); |
273 | 0 | os_free(decrypted); |
274 | 0 | return -1; |
275 | 0 | } |
276 | 0 | wpa_hexdump(MSG_MSGDUMP, "PKCS #1: Digest algorithm parameters", |
277 | 0 | next, da_end - next); |
278 | | |
279 | | /* |
280 | | * RFC 5754: The correct encoding for the SHA2 algorithms would be to |
281 | | * omit the parameters, but there are implementation that encode these |
282 | | * as a NULL element. Allow these two cases and reject anything else. |
283 | | */ |
284 | 0 | if (da_end > next && |
285 | 0 | (asn1_get_next(next, da_end - next, &hdr) < 0 || |
286 | 0 | !asn1_is_null(&hdr) || |
287 | 0 | hdr.payload + hdr.length != da_end)) { |
288 | 0 | wpa_printf(MSG_DEBUG, |
289 | 0 | "PKCS #1: Unexpected digest algorithm parameters"); |
290 | 0 | os_free(decrypted); |
291 | 0 | return -1; |
292 | 0 | } |
293 | | |
294 | 0 | if (!asn1_oid_equal(&oid, hash_alg)) { |
295 | 0 | char txt[100], txt2[100]; |
296 | 0 | asn1_oid_to_str(&oid, txt, sizeof(txt)); |
297 | 0 | asn1_oid_to_str(hash_alg, txt2, sizeof(txt2)); |
298 | 0 | wpa_printf(MSG_DEBUG, |
299 | 0 | "PKCS #1: Hash alg OID mismatch: was %s, expected %s", |
300 | 0 | txt, txt2); |
301 | 0 | os_free(decrypted); |
302 | 0 | return -1; |
303 | 0 | } |
304 | | |
305 | | /* Digest ::= OCTET STRING */ |
306 | 0 | pos = da_end; |
307 | |
|
308 | 0 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || |
309 | 0 | !asn1_is_octetstring(&hdr)) { |
310 | 0 | asn1_unexpected(&hdr, |
311 | 0 | "PKCS #1: Expected OCTETSTRING (Digest)"); |
312 | 0 | os_free(decrypted); |
313 | 0 | return -1; |
314 | 0 | } |
315 | 0 | wpa_hexdump(MSG_MSGDUMP, "PKCS #1: Decrypted Digest", |
316 | 0 | hdr.payload, hdr.length); |
317 | |
|
318 | 0 | if (hdr.length != hash_len || |
319 | 0 | os_memcmp_const(hdr.payload, hash, hdr.length) != 0) { |
320 | 0 | wpa_printf(MSG_INFO, "PKCS #1: Digest value does not match calculated hash"); |
321 | 0 | os_free(decrypted); |
322 | 0 | return -1; |
323 | 0 | } |
324 | | |
325 | 0 | if (hdr.payload + hdr.length != decrypted + decrypted_len) { |
326 | 0 | wpa_printf(MSG_INFO, |
327 | 0 | "PKCS #1: Extra data after signature - reject"); |
328 | |
|
329 | 0 | wpa_hexdump(MSG_DEBUG, "PKCS #1: Extra data", |
330 | 0 | hdr.payload + hdr.length, |
331 | 0 | decrypted + decrypted_len - hdr.payload - |
332 | 0 | hdr.length); |
333 | |
|
334 | 0 | os_free(decrypted); |
335 | 0 | return -1; |
336 | 0 | } |
337 | | |
338 | 0 | os_free(decrypted); |
339 | |
|
340 | 0 | return 0; |
341 | 0 | } |