]>
Commit | Line | Data |
---|---|---|
552cc11b | 1 | /* |
7033a70f | 2 | * Copyright (C) 2005-2009 Martin Willi |
552cc11b MW |
3 | * Copyright (C) 2005 Jan Hutter |
4 | * Hochschule fuer Technik Rapperswil | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the | |
8 | * Free Software Foundation; either version 2 of the License, or (at your | |
9 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
13 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
14 | * for more details. | |
552cc11b | 15 | */ |
7daf5226 | 16 | |
552cc11b MW |
17 | #include <gmp.h> |
18 | #include <sys/stat.h> | |
19 | #include <unistd.h> | |
20 | #include <stdio.h> | |
21 | #include <string.h> | |
22 | ||
23 | #include "gmp_rsa_public_key.h" | |
24 | ||
25 | #include <debug.h> | |
d3d7e46b | 26 | #include <asn1/oid.h> |
552cc11b | 27 | #include <asn1/asn1.h> |
d3d7e46b | 28 | #include <asn1/asn1_parser.h> |
d3d7e46b | 29 | #include <crypto/hashers/hasher.h> |
552cc11b | 30 | |
3e35a6e7 MW |
31 | #ifdef HAVE_MPZ_POWM_SEC |
32 | # undef mpz_powm | |
33 | # define mpz_powm mpz_powm_sec | |
34 | #endif | |
35 | ||
552cc11b MW |
36 | typedef struct private_gmp_rsa_public_key_t private_gmp_rsa_public_key_t; |
37 | ||
38 | /** | |
39 | * Private data structure with signing context. | |
40 | */ | |
41 | struct private_gmp_rsa_public_key_t { | |
42 | /** | |
43 | * Public interface for this signer. | |
44 | */ | |
45 | gmp_rsa_public_key_t public; | |
7daf5226 | 46 | |
552cc11b MW |
47 | /** |
48 | * Public modulus. | |
49 | */ | |
50 | mpz_t n; | |
7daf5226 | 51 | |
552cc11b MW |
52 | /** |
53 | * Public exponent. | |
54 | */ | |
55 | mpz_t e; | |
7daf5226 | 56 | |
552cc11b MW |
57 | /** |
58 | * Keysize in bytes. | |
59 | */ | |
60 | size_t k; | |
7daf5226 | 61 | |
552cc11b MW |
62 | /** |
63 | * reference counter | |
64 | */ | |
65 | refcount_t ref; | |
66 | }; | |
67 | ||
8b799d55 AS |
68 | /** |
69 | * Shared functions defined in gmp_rsa_private_key.c | |
70 | */ | |
71 | extern chunk_t gmp_mpz_to_chunk(const mpz_t value); | |
8b799d55 | 72 | |
552cc11b MW |
73 | /** |
74 | * RSAEP algorithm specified in PKCS#1. | |
75 | */ | |
76 | static chunk_t rsaep(private_gmp_rsa_public_key_t *this, chunk_t data) | |
77 | { | |
78 | mpz_t m, c; | |
79 | chunk_t encrypted; | |
7daf5226 | 80 | |
552cc11b MW |
81 | mpz_init(c); |
82 | mpz_init(m); | |
7daf5226 | 83 | |
552cc11b | 84 | mpz_import(m, data.len, 1, 1, 1, 0, data.ptr); |
7daf5226 | 85 | |
552cc11b MW |
86 | mpz_powm(c, m, this->e, this->n); |
87 | ||
73f6886a MW |
88 | encrypted.len = this->k; |
89 | encrypted.ptr = mpz_export(NULL, NULL, 1, encrypted.len, 1, 0, c); | |
90 | if (encrypted.ptr == NULL) | |
91 | { | |
92 | encrypted.len = 0; | |
93 | } | |
7daf5226 | 94 | |
552cc11b | 95 | mpz_clear(c); |
73f6886a | 96 | mpz_clear(m); |
7daf5226 | 97 | |
552cc11b MW |
98 | return encrypted; |
99 | } | |
100 | ||
101 | /** | |
102 | * RSAVP1 algorithm specified in PKCS#1. | |
103 | */ | |
104 | static chunk_t rsavp1(private_gmp_rsa_public_key_t *this, chunk_t data) | |
105 | { | |
106 | return rsaep(this, data); | |
107 | } | |
108 | ||
c3628ebc AS |
109 | /** |
110 | * ASN.1 definition of digestInfo | |
111 | */ | |
112 | static const asn1Object_t digestInfoObjects[] = { | |
113 | { 0, "digestInfo", ASN1_SEQUENCE, ASN1_OBJ }, /* 0 */ | |
114 | { 1, "digestAlgorithm", ASN1_EOC, ASN1_RAW }, /* 1 */ | |
115 | { 1, "digest", ASN1_OCTET_STRING, ASN1_BODY }, /* 2 */ | |
460025e2 | 116 | { 0, "exit", ASN1_EOC, ASN1_EXIT } |
c3628ebc AS |
117 | }; |
118 | #define DIGEST_INFO 0 | |
119 | #define DIGEST_INFO_ALGORITHM 1 | |
120 | #define DIGEST_INFO_DIGEST 2 | |
c3628ebc | 121 | |
552cc11b MW |
122 | /** |
123 | * Verification of an EMPSA PKCS1 signature described in PKCS#1 | |
124 | */ | |
125 | static bool verify_emsa_pkcs1_signature(private_gmp_rsa_public_key_t *this, | |
126 | hash_algorithm_t algorithm, | |
127 | chunk_t data, chunk_t signature) | |
128 | { | |
129 | chunk_t em_ori, em; | |
d3d7e46b | 130 | bool success = FALSE; |
7daf5226 | 131 | |
552cc11b MW |
132 | /* remove any preceding 0-bytes from signature */ |
133 | while (signature.len && *(signature.ptr) == 0x00) | |
134 | { | |
e7227f0b | 135 | signature = chunk_skip(signature, 1); |
552cc11b | 136 | } |
7daf5226 | 137 | |
e7227f0b | 138 | if (signature.len == 0 || signature.len > this->k) |
552cc11b | 139 | { |
060b508e | 140 | return FALSE; |
552cc11b | 141 | } |
7daf5226 | 142 | |
552cc11b MW |
143 | /* unpack signature */ |
144 | em_ori = em = rsavp1(this, signature); | |
7daf5226 | 145 | |
552cc11b | 146 | /* result should look like this: |
7daf5226 | 147 | * EM = 0x00 || 0x01 || PS || 0x00 || T. |
552cc11b MW |
148 | * PS = 0xFF padding, with length to fill em |
149 | * T = oid || hash | |
150 | */ | |
7daf5226 | 151 | |
552cc11b MW |
152 | /* check magic bytes */ |
153 | if (*(em.ptr) != 0x00 || *(em.ptr+1) != 0x01) | |
154 | { | |
155 | goto end; | |
156 | } | |
e7227f0b | 157 | em = chunk_skip(em, 2); |
7daf5226 | 158 | |
552cc11b MW |
159 | /* find magic 0x00 */ |
160 | while (em.len > 0) | |
161 | { | |
162 | if (*em.ptr == 0x00) | |
163 | { | |
164 | /* found magic byte, stop */ | |
e7227f0b | 165 | em = chunk_skip(em, 1); |
552cc11b MW |
166 | break; |
167 | } | |
168 | else if (*em.ptr != 0xFF) | |
169 | { | |
170 | /* bad padding, decryption failed ?!*/ | |
171 | goto end; | |
172 | } | |
e7227f0b | 173 | em = chunk_skip(em, 1); |
552cc11b MW |
174 | } |
175 | ||
176 | if (em.len == 0) | |
177 | { | |
178 | /* no digestInfo found */ | |
179 | goto end; | |
180 | } | |
181 | ||
8b799d55 AS |
182 | if (algorithm == HASH_UNKNOWN) |
183 | { /* IKEv1 signatures without digestInfo */ | |
184 | if (em.len != data.len) | |
185 | { | |
8b0e0910 TB |
186 | DBG1(DBG_LIB, "hash size in signature is %u bytes instead of" |
187 | " %u bytes", em.len, data.len); | |
8b799d55 AS |
188 | goto end; |
189 | } | |
190 | success = memeq(em.ptr, data.ptr, data.len); | |
191 | } | |
192 | else | |
193 | { /* IKEv2 and X.509 certificate signatures */ | |
d3d7e46b | 194 | asn1_parser_t *parser; |
552cc11b | 195 | chunk_t object; |
d3d7e46b | 196 | int objectID; |
552cc11b MW |
197 | hash_algorithm_t hash_algorithm = HASH_UNKNOWN; |
198 | ||
8b0e0910 | 199 | DBG2(DBG_LIB, "signature verification:"); |
460025e2 | 200 | parser = asn1_parser_create(digestInfoObjects, em); |
552cc11b | 201 | |
d3d7e46b | 202 | while (parser->iterate(parser, &objectID, &object)) |
552cc11b | 203 | { |
552cc11b MW |
204 | switch (objectID) |
205 | { | |
206 | case DIGEST_INFO: | |
207 | { | |
208 | if (em.len > object.len) | |
209 | { | |
8b0e0910 TB |
210 | DBG1(DBG_LIB, "digestInfo field in signature is" |
211 | " followed by %u surplus bytes", | |
552cc11b | 212 | em.len - object.len); |
d3d7e46b | 213 | goto end_parser; |
552cc11b MW |
214 | } |
215 | break; | |
216 | } | |
217 | case DIGEST_INFO_ALGORITHM: | |
218 | { | |
d3d7e46b AS |
219 | int hash_oid = asn1_parse_algorithmIdentifier(object, |
220 | parser->get_level(parser)+1, NULL); | |
552cc11b MW |
221 | |
222 | hash_algorithm = hasher_algorithm_from_oid(hash_oid); | |
8b799d55 | 223 | if (hash_algorithm == HASH_UNKNOWN || hash_algorithm != algorithm) |
552cc11b | 224 | { |
8b0e0910 TB |
225 | DBG1(DBG_LIB, "expected hash algorithm %N, but found" |
226 | " %N (OID: %#B)", hash_algorithm_names, algorithm, | |
4f23ec78 | 227 | hash_algorithm_names, hash_algorithm, &object); |
d3d7e46b | 228 | goto end_parser; |
552cc11b MW |
229 | } |
230 | break; | |
231 | } | |
232 | case DIGEST_INFO_DIGEST: | |
233 | { | |
234 | chunk_t hash; | |
235 | hasher_t *hasher; | |
7daf5226 | 236 | |
552cc11b MW |
237 | hasher = lib->crypto->create_hasher(lib->crypto, hash_algorithm); |
238 | if (hasher == NULL) | |
239 | { | |
8b0e0910 | 240 | DBG1(DBG_LIB, "hash algorithm %N not supported", |
552cc11b | 241 | hash_algorithm_names, hash_algorithm); |
d3d7e46b | 242 | goto end_parser; |
552cc11b MW |
243 | } |
244 | ||
245 | if (object.len != hasher->get_hash_size(hasher)) | |
246 | { | |
8b0e0910 TB |
247 | DBG1(DBG_LIB, "hash size in signature is %u bytes" |
248 | " instead of %u bytes", object.len, | |
249 | hasher->get_hash_size(hasher)); | |
552cc11b | 250 | hasher->destroy(hasher); |
d3d7e46b | 251 | goto end_parser; |
552cc11b MW |
252 | } |
253 | ||
254 | /* build our own hash and compare */ | |
87dd205b MW |
255 | if (!hasher->allocate_hash(hasher, data, &hash)) |
256 | { | |
257 | hasher->destroy(hasher); | |
258 | goto end_parser; | |
259 | } | |
552cc11b | 260 | hasher->destroy(hasher); |
d3d7e46b | 261 | success = memeq(object.ptr, hash.ptr, hash.len); |
552cc11b MW |
262 | free(hash.ptr); |
263 | break; | |
264 | } | |
265 | default: | |
266 | break; | |
267 | } | |
552cc11b | 268 | } |
d3d7e46b AS |
269 | |
270 | end_parser: | |
271 | success &= parser->success(parser); | |
272 | parser->destroy(parser); | |
552cc11b MW |
273 | } |
274 | ||
275 | end: | |
276 | free(em_ori.ptr); | |
d3d7e46b | 277 | return success; |
552cc11b MW |
278 | } |
279 | ||
876b61e1 MW |
280 | METHOD(public_key_t, get_type, key_type_t, |
281 | private_gmp_rsa_public_key_t *this) | |
552cc11b MW |
282 | { |
283 | return KEY_RSA; | |
284 | } | |
285 | ||
876b61e1 MW |
286 | METHOD(public_key_t, verify, bool, |
287 | private_gmp_rsa_public_key_t *this, signature_scheme_t scheme, | |
288 | chunk_t data, chunk_t signature) | |
552cc11b MW |
289 | { |
290 | switch (scheme) | |
291 | { | |
8b799d55 | 292 | case SIGN_RSA_EMSA_PKCS1_NULL: |
552cc11b MW |
293 | return verify_emsa_pkcs1_signature(this, HASH_UNKNOWN, data, signature); |
294 | case SIGN_RSA_EMSA_PKCS1_MD5: | |
295 | return verify_emsa_pkcs1_signature(this, HASH_MD5, data, signature); | |
296 | case SIGN_RSA_EMSA_PKCS1_SHA1: | |
297 | return verify_emsa_pkcs1_signature(this, HASH_SHA1, data, signature); | |
b6f739c1 AS |
298 | case SIGN_RSA_EMSA_PKCS1_SHA224: |
299 | return verify_emsa_pkcs1_signature(this, HASH_SHA224, data, signature); | |
552cc11b MW |
300 | case SIGN_RSA_EMSA_PKCS1_SHA256: |
301 | return verify_emsa_pkcs1_signature(this, HASH_SHA256, data, signature); | |
302 | case SIGN_RSA_EMSA_PKCS1_SHA384: | |
303 | return verify_emsa_pkcs1_signature(this, HASH_SHA384, data, signature); | |
304 | case SIGN_RSA_EMSA_PKCS1_SHA512: | |
305 | return verify_emsa_pkcs1_signature(this, HASH_SHA512, data, signature); | |
306 | default: | |
8b0e0910 | 307 | DBG1(DBG_LIB, "signature scheme %N not supported in RSA", |
552cc11b MW |
308 | signature_scheme_names, scheme); |
309 | return FALSE; | |
310 | } | |
311 | } | |
312 | ||
741680d1 | 313 | #define MIN_PS_PADDING 8 |
c50ff68d | 314 | |
876b61e1 | 315 | METHOD(public_key_t, encrypt_, bool, |
33ddaaab MW |
316 | private_gmp_rsa_public_key_t *this, encryption_scheme_t scheme, |
317 | chunk_t plain, chunk_t *crypto) | |
552cc11b | 318 | { |
c50ff68d AS |
319 | chunk_t em; |
320 | u_char *pos; | |
5025135f | 321 | int padding; |
7b3814f7 | 322 | rng_t *rng; |
c50ff68d | 323 | |
33ddaaab | 324 | if (scheme != ENCRYPT_RSA_PKCS1) |
c50ff68d | 325 | { |
33ddaaab MW |
326 | DBG1(DBG_LIB, "encryption scheme %N not supported", |
327 | encryption_scheme_names, scheme); | |
c50ff68d AS |
328 | return FALSE; |
329 | } | |
c50ff68d AS |
330 | /* number of pseudo-random padding octets */ |
331 | padding = this->k - plain.len - 3; | |
7b3814f7 | 332 | if (padding < MIN_PS_PADDING) |
c50ff68d | 333 | { |
8b0e0910 TB |
334 | DBG1(DBG_LIB, "pseudo-random padding must be at least %d octets", |
335 | MIN_PS_PADDING); | |
c50ff68d AS |
336 | return FALSE; |
337 | } | |
33ddaaab MW |
338 | rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); |
339 | if (rng == NULL) | |
340 | { | |
341 | DBG1(DBG_LIB, "no random generator available"); | |
342 | return FALSE; | |
343 | } | |
c50ff68d AS |
344 | |
345 | /* padding according to PKCS#1 7.2.1 (RSAES-PKCS1-v1.5-ENCRYPT) */ | |
8b0e0910 TB |
346 | DBG2(DBG_LIB, "padding %u bytes of data to the rsa modulus size of" |
347 | " %u bytes", plain.len, this->k); | |
c50ff68d | 348 | em.len = this->k; |
7daf5226 | 349 | em.ptr = malloc(em.len); |
c50ff68d AS |
350 | pos = em.ptr; |
351 | *pos++ = 0x00; | |
352 | *pos++ = 0x02; | |
353 | ||
354 | /* fill with pseudo random octets */ | |
5025135f | 355 | if (!rng_get_bytes_not_zero(rng, padding, pos, TRUE)) |
c50ff68d | 356 | { |
5025135f TB |
357 | DBG1(DBG_LIB, "failed to allocate padding"); |
358 | chunk_clear(&em); | |
359 | rng->destroy(rng); | |
360 | return FALSE; | |
c50ff68d AS |
361 | } |
362 | rng->destroy(rng); | |
363 | ||
364 | /* append the padding terminator */ | |
365 | *pos++ = 0x00; | |
366 | ||
367 | /* now add the data */ | |
368 | memcpy(pos, plain.ptr, plain.len); | |
8b0e0910 | 369 | DBG3(DBG_LIB, "padded data before rsa encryption: %B", &em); |
7daf5226 | 370 | |
d615ffdc | 371 | /* rsa encryption using PKCS#1 RSAEP */ |
c50ff68d | 372 | *crypto = rsaep(this, em); |
8b0e0910 | 373 | DBG3(DBG_LIB, "rsa encrypted data: %B", crypto); |
c50ff68d AS |
374 | chunk_clear(&em); |
375 | return TRUE; | |
552cc11b MW |
376 | } |
377 | ||
a944d209 | 378 | METHOD(public_key_t, get_keysize, int, |
876b61e1 | 379 | private_gmp_rsa_public_key_t *this) |
552cc11b | 380 | { |
a944d209 | 381 | return mpz_sizeinbase(this->n, 2); |
552cc11b MW |
382 | } |
383 | ||
876b61e1 MW |
384 | METHOD(public_key_t, get_encoding, bool, |
385 | private_gmp_rsa_public_key_t *this, cred_encoding_type_t type, | |
386 | chunk_t *encoding) | |
8b799d55 | 387 | { |
741680d1 | 388 | chunk_t n, e; |
dd04a68f | 389 | bool success; |
7daf5226 | 390 | |
741680d1 MW |
391 | n = gmp_mpz_to_chunk(this->n); |
392 | e = gmp_mpz_to_chunk(this->e); | |
7daf5226 MW |
393 | |
394 | success = lib->encoding->encode(lib->encoding, type, NULL, encoding, | |
da9724e6 | 395 | CRED_PART_RSA_MODULUS, n, CRED_PART_RSA_PUB_EXP, e, CRED_PART_END); |
741680d1 MW |
396 | chunk_free(&n); |
397 | chunk_free(&e); | |
7daf5226 | 398 | |
741680d1 | 399 | return success; |
8b799d55 AS |
400 | } |
401 | ||
876b61e1 MW |
402 | METHOD(public_key_t, get_fingerprint, bool, |
403 | private_gmp_rsa_public_key_t *this, cred_encoding_type_t type, chunk_t *fp) | |
552cc11b | 404 | { |
741680d1 MW |
405 | chunk_t n, e; |
406 | bool success; | |
7daf5226 | 407 | |
741680d1 | 408 | if (lib->encoding->get_cache(lib->encoding, type, this, fp)) |
552cc11b | 409 | { |
741680d1 | 410 | return TRUE; |
552cc11b | 411 | } |
741680d1 MW |
412 | n = gmp_mpz_to_chunk(this->n); |
413 | e = gmp_mpz_to_chunk(this->e); | |
7daf5226 | 414 | |
741680d1 | 415 | success = lib->encoding->encode(lib->encoding, type, this, fp, |
da9724e6 | 416 | CRED_PART_RSA_MODULUS, n, CRED_PART_RSA_PUB_EXP, e, CRED_PART_END); |
741680d1 MW |
417 | chunk_free(&n); |
418 | chunk_free(&e); | |
7daf5226 | 419 | |
741680d1 | 420 | return success; |
552cc11b MW |
421 | } |
422 | ||
876b61e1 MW |
423 | METHOD(public_key_t, get_ref, public_key_t*, |
424 | private_gmp_rsa_public_key_t *this) | |
552cc11b MW |
425 | { |
426 | ref_get(&this->ref); | |
876b61e1 | 427 | return &this->public.key; |
552cc11b MW |
428 | } |
429 | ||
876b61e1 MW |
430 | METHOD(public_key_t, destroy, void, |
431 | private_gmp_rsa_public_key_t *this) | |
552cc11b MW |
432 | { |
433 | if (ref_put(&this->ref)) | |
434 | { | |
435 | mpz_clear(this->n); | |
436 | mpz_clear(this->e); | |
741680d1 | 437 | lib->encoding->clear_cache(lib->encoding, this); |
552cc11b MW |
438 | free(this); |
439 | } | |
440 | } | |
441 | ||
442 | /** | |
1086d00e | 443 | * See header. |
552cc11b | 444 | */ |
1086d00e | 445 | gmp_rsa_public_key_t *gmp_rsa_public_key_load(key_type_t type, va_list args) |
552cc11b | 446 | { |
1086d00e MW |
447 | private_gmp_rsa_public_key_t *this; |
448 | chunk_t n, e; | |
449 | ||
450 | n = e = chunk_empty; | |
451 | while (TRUE) | |
452 | { | |
453 | switch (va_arg(args, builder_part_t)) | |
454 | { | |
455 | case BUILD_RSA_MODULUS: | |
456 | n = va_arg(args, chunk_t); | |
457 | continue; | |
458 | case BUILD_RSA_PUB_EXP: | |
459 | e = va_arg(args, chunk_t); | |
460 | continue; | |
461 | case BUILD_END: | |
462 | break; | |
463 | default: | |
464 | return NULL; | |
465 | } | |
466 | break; | |
467 | } | |
468 | if (!e.ptr || !n.ptr) | |
469 | { | |
470 | return NULL; | |
471 | } | |
472 | ||
876b61e1 | 473 | INIT(this, |
ba31fe1f MW |
474 | .public = { |
475 | .key = { | |
476 | .get_type = _get_type, | |
477 | .verify = _verify, | |
478 | .encrypt = _encrypt_, | |
479 | .equals = public_key_equals, | |
480 | .get_keysize = _get_keysize, | |
481 | .get_fingerprint = _get_fingerprint, | |
482 | .has_fingerprint = public_key_has_fingerprint, | |
483 | .get_encoding = _get_encoding, | |
484 | .get_ref = _get_ref, | |
485 | .destroy = _destroy, | |
486 | }, | |
876b61e1 MW |
487 | }, |
488 | .ref = 1, | |
489 | ); | |
7daf5226 | 490 | |
8b799d55 AS |
491 | mpz_init(this->n); |
492 | mpz_init(this->e); | |
7daf5226 | 493 | |
7033a70f MW |
494 | mpz_import(this->n, n.len, 1, 1, 1, 0, n.ptr); |
495 | mpz_import(this->e, e.len, 1, 1, 1, 0, e.ptr); | |
7daf5226 | 496 | |
741680d1 | 497 | this->k = (mpz_sizeinbase(this->n, 2) + 7) / BITS_PER_BYTE; |
7daf5226 | 498 | |
552cc11b MW |
499 | return &this->public; |
500 | } | |
501 |