]>
Commit | Line | Data |
---|---|---|
ff8d3ba3 | 1 | /* |
183a9108 | 2 | * Copyright (C) 2017 Tobias Brunner |
ff8d3ba3 | 3 | * Copyright (C) 2005-2009 Martin Willi |
183a9108 | 4 | * HSR Hochschule fuer Technik Rapperswil |
ff8d3ba3 MW |
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. | |
15 | */ | |
16 | ||
17 | #include <gcrypt.h> | |
18 | ||
19 | #include "gcrypt_rsa_private_key.h" | |
20 | ||
f05b4272 | 21 | #include <utils/debug.h> |
ff8d3ba3 MW |
22 | #include <asn1/oid.h> |
23 | #include <asn1/asn1.h> | |
24 | #include <asn1/asn1_parser.h> | |
3ce8b055 | 25 | #include <credentials/keys/signature_params.h> |
ff8d3ba3 MW |
26 | |
27 | typedef struct private_gcrypt_rsa_private_key_t private_gcrypt_rsa_private_key_t; | |
28 | ||
29 | /** | |
30 | * Private data of a gcrypt_rsa_private_key_t object. | |
31 | */ | |
32 | struct private_gcrypt_rsa_private_key_t { | |
7daf5226 | 33 | |
ff8d3ba3 MW |
34 | /** |
35 | * Public interface | |
36 | */ | |
37 | gcrypt_rsa_private_key_t public; | |
7daf5226 | 38 | |
ff8d3ba3 MW |
39 | /** |
40 | * gcrypt S-expression representing an RSA key | |
41 | */ | |
42 | gcry_sexp_t key; | |
7daf5226 | 43 | |
ff8d3ba3 MW |
44 | /** |
45 | * reference count | |
46 | */ | |
47 | refcount_t ref; | |
48 | }; | |
49 | ||
50 | /** | |
a2f1bb23 MW |
51 | * find a token in a S-expression. If a key is given, its length is used to |
52 | * pad the output to a given length. | |
ff8d3ba3 | 53 | */ |
a2f1bb23 | 54 | chunk_t gcrypt_rsa_find_token(gcry_sexp_t sexp, char *name, gcry_sexp_t key) |
ff8d3ba3 MW |
55 | { |
56 | gcry_sexp_t token; | |
a2f1bb23 MW |
57 | chunk_t data = chunk_empty, tmp; |
58 | size_t len = 0; | |
7daf5226 | 59 | |
ff8d3ba3 MW |
60 | token = gcry_sexp_find_token(sexp, name, 1); |
61 | if (token) | |
62 | { | |
63 | data.ptr = (char*)gcry_sexp_nth_data(token, 1, &data.len); | |
64 | if (!data.ptr) | |
65 | { | |
66 | data.len = 0; | |
67 | } | |
a2f1bb23 MW |
68 | else |
69 | { | |
70 | if (key) | |
71 | { | |
72 | /* gcrypt might return more bytes than necessary. Truncate | |
f3bb1bd0 | 73 | * to key length if key given, or prepend zeros if needed */ |
a2f1bb23 MW |
74 | len = gcry_pk_get_nbits(key); |
75 | len = len / 8 + (len % 8 ? 1 : 0); | |
76 | if (len > data.len) | |
77 | { | |
78 | tmp = chunk_alloc(len); | |
79 | len -= data.len; | |
80 | memset(tmp.ptr, 0, tmp.len - len); | |
81 | memcpy(tmp.ptr + len, data.ptr, data.len); | |
82 | data = tmp; | |
83 | } | |
84 | else if (len < data.len) | |
85 | { | |
86 | data = chunk_clone(chunk_skip(data, data.len - len)); | |
87 | } | |
88 | else | |
89 | { | |
90 | data = chunk_clone(data); | |
91 | } | |
92 | } | |
93 | else | |
94 | { | |
95 | data = chunk_clone(data); | |
96 | } | |
97 | } | |
ff8d3ba3 MW |
98 | gcry_sexp_release(token); |
99 | } | |
100 | return data; | |
101 | } | |
102 | ||
103 | /** | |
04b348f6 MW |
104 | * Sign a chunk of data with direct PKCS#1 encoding, no hash OID |
105 | */ | |
106 | static bool sign_raw(private_gcrypt_rsa_private_key_t *this, | |
107 | chunk_t data, chunk_t *signature) | |
108 | { | |
109 | gcry_sexp_t in, out; | |
110 | gcry_error_t err; | |
111 | chunk_t em; | |
112 | size_t k; | |
7daf5226 | 113 | |
04b348f6 MW |
114 | /* EM = 0x00 || 0x01 || PS || 0x00 || T |
115 | * PS = 0xFF padding, with length to fill em | |
116 | * T = data | |
117 | */ | |
118 | k = gcry_pk_get_nbits(this->key) / 8; | |
119 | if (data.len > k - 3) | |
120 | { | |
121 | return FALSE; | |
122 | } | |
123 | em = chunk_alloc(k); | |
124 | memset(em.ptr, 0xFF, em.len); | |
125 | em.ptr[0] = 0x00; | |
126 | em.ptr[1] = 0x01; | |
127 | em.ptr[em.len - data.len - 1] = 0x00; | |
128 | memcpy(em.ptr + em.len - data.len, data.ptr, data.len); | |
7daf5226 | 129 | |
04b348f6 MW |
130 | err = gcry_sexp_build(&in, NULL, "(data(flags raw)(value %b))", |
131 | em.len, em.ptr); | |
132 | chunk_free(&em); | |
133 | if (err) | |
134 | { | |
8b0e0910 TB |
135 | DBG1(DBG_LIB, "building signature S-expression failed: %s", |
136 | gpg_strerror(err)); | |
04b348f6 MW |
137 | return FALSE; |
138 | } | |
139 | err = gcry_pk_sign(&out, in, this->key); | |
140 | gcry_sexp_release(in); | |
141 | if (err) | |
142 | { | |
8b0e0910 | 143 | DBG1(DBG_LIB, "creating pkcs1 signature failed: %s", gpg_strerror(err)); |
04b348f6 MW |
144 | return FALSE; |
145 | } | |
a2f1bb23 | 146 | *signature = gcrypt_rsa_find_token(out, "s", this->key); |
04b348f6 MW |
147 | gcry_sexp_release(out); |
148 | return !!signature->len; | |
149 | } | |
150 | ||
151 | /** | |
3ce8b055 | 152 | * Sign a chunk of data using hashing and PKCS#1v1.5/EMSA-PSS encoding |
ff8d3ba3 MW |
153 | */ |
154 | static bool sign_pkcs1(private_gcrypt_rsa_private_key_t *this, | |
3ce8b055 | 155 | hash_algorithm_t hash_algorithm, rsa_pss_params_t *pss, |
ff8d3ba3 MW |
156 | chunk_t data, chunk_t *signature) |
157 | { | |
158 | hasher_t *hasher; | |
159 | chunk_t hash; | |
160 | gcry_error_t err; | |
161 | gcry_sexp_t in, out; | |
3ce8b055 | 162 | char *hash_name = enum_to_name(hash_algorithm_short_names, hash_algorithm); |
7daf5226 | 163 | |
3ce8b055 TB |
164 | hasher = lib->crypto->create_hasher(lib->crypto, hash_algorithm); |
165 | if (!hasher) | |
ff8d3ba3 | 166 | { |
3ce8b055 TB |
167 | DBG1(DBG_LIB, "hash algorithm %N not supported", |
168 | hash_algorithm_names, hash_algorithm); | |
ff8d3ba3 MW |
169 | return FALSE; |
170 | } | |
3ce8b055 | 171 | if (!hasher->allocate_hash(hasher, data, &hash)) |
ff8d3ba3 | 172 | { |
3ce8b055 | 173 | hasher->destroy(hasher); |
ff8d3ba3 MW |
174 | return FALSE; |
175 | } | |
ff8d3ba3 | 176 | hasher->destroy(hasher); |
7daf5226 | 177 | |
3ce8b055 TB |
178 | if (pss) |
179 | { | |
37efb978 | 180 | if (pss->salt.len) |
3ce8b055 | 181 | { |
37efb978 TB |
182 | err = gcry_sexp_build(&in, NULL, |
183 | "(data(flags pss)(salt-length %u)" | |
184 | "(random-override %b)(hash %s %b))", | |
185 | pss->salt.len, pss->salt.len, pss->salt.ptr, | |
186 | hash_name, hash.len, hash.ptr); | |
187 | } | |
188 | else | |
189 | { | |
ecfe6755 | 190 | u_int slen = pss->salt_len; |
37efb978 TB |
191 | err = gcry_sexp_build(&in, NULL, |
192 | "(data(flags pss)(salt-length %u)(hash %s %b))", | |
193 | slen, hash_name, hash.len, hash.ptr); | |
3ce8b055 | 194 | } |
3ce8b055 TB |
195 | } |
196 | else | |
197 | { | |
198 | err = gcry_sexp_build(&in, NULL, "(data(flags pkcs1)(hash %s %b))", | |
199 | hash_name, hash.len, hash.ptr); | |
200 | } | |
ff8d3ba3 MW |
201 | chunk_free(&hash); |
202 | if (err) | |
203 | { | |
3ce8b055 TB |
204 | DBG1(DBG_LIB, "building signature S-expression failed: %s", |
205 | gpg_strerror(err)); | |
ff8d3ba3 MW |
206 | return FALSE; |
207 | } | |
208 | err = gcry_pk_sign(&out, in, this->key); | |
209 | gcry_sexp_release(in); | |
210 | if (err) | |
211 | { | |
3ce8b055 TB |
212 | DBG1(DBG_LIB, "creating pkcs1 signature failed: %s", |
213 | gpg_strerror(err)); | |
ff8d3ba3 MW |
214 | return FALSE; |
215 | } | |
3ce8b055 | 216 | |
a2f1bb23 | 217 | *signature = gcrypt_rsa_find_token(out, "s", this->key); |
ff8d3ba3 MW |
218 | gcry_sexp_release(out); |
219 | return !!signature->len; | |
220 | } | |
221 | ||
3ce8b055 TB |
222 | #if GCRYPT_VERSION_NUMBER >= 0x010700 |
223 | /** | |
224 | * Sign a chunk of data using hashing and EMSA-PSS encoding | |
225 | */ | |
226 | static bool sign_pss(private_gcrypt_rsa_private_key_t *this, | |
227 | rsa_pss_params_t *params, chunk_t data, chunk_t *signature) | |
228 | { | |
229 | if (!params) | |
230 | { | |
231 | return FALSE; | |
232 | } | |
233 | if (params->mgf1_hash != params->hash) | |
234 | { | |
235 | DBG1(DBG_LIB, "unable to use a different MGF1 hash for RSA-PSS"); | |
236 | return FALSE; | |
237 | } | |
238 | return sign_pkcs1(this, params->hash, params, data, signature); | |
239 | } | |
240 | #endif | |
241 | ||
646babd3 MW |
242 | METHOD(private_key_t, get_type, key_type_t, |
243 | private_gcrypt_rsa_private_key_t *this) | |
ff8d3ba3 MW |
244 | { |
245 | return KEY_RSA; | |
246 | } | |
247 | ||
646babd3 MW |
248 | METHOD(private_key_t, sign, bool, |
249 | private_gcrypt_rsa_private_key_t *this, signature_scheme_t scheme, | |
de280c2e | 250 | void *params, chunk_t data, chunk_t *sig) |
ff8d3ba3 MW |
251 | { |
252 | switch (scheme) | |
253 | { | |
04b348f6 MW |
254 | case SIGN_RSA_EMSA_PKCS1_NULL: |
255 | return sign_raw(this, data, sig); | |
40f2589a | 256 | case SIGN_RSA_EMSA_PKCS1_SHA2_224: |
3ce8b055 | 257 | return sign_pkcs1(this, HASH_SHA224, NULL, data, sig); |
40f2589a | 258 | case SIGN_RSA_EMSA_PKCS1_SHA2_256: |
3ce8b055 | 259 | return sign_pkcs1(this, HASH_SHA256, NULL, data, sig); |
40f2589a | 260 | case SIGN_RSA_EMSA_PKCS1_SHA2_384: |
3ce8b055 | 261 | return sign_pkcs1(this, HASH_SHA384, NULL, data, sig); |
40f2589a | 262 | case SIGN_RSA_EMSA_PKCS1_SHA2_512: |
3ce8b055 | 263 | return sign_pkcs1(this, HASH_SHA512, NULL, data, sig); |
40f2589a | 264 | case SIGN_RSA_EMSA_PKCS1_SHA1: |
3ce8b055 | 265 | return sign_pkcs1(this, HASH_SHA1, NULL, data, sig); |
ff8d3ba3 | 266 | case SIGN_RSA_EMSA_PKCS1_MD5: |
3ce8b055 TB |
267 | return sign_pkcs1(this, HASH_MD5, NULL, data, sig); |
268 | #if GCRYPT_VERSION_NUMBER >= 0x010700 | |
269 | case SIGN_RSA_EMSA_PSS: | |
270 | return sign_pss(this, params, data, sig); | |
271 | #endif | |
ff8d3ba3 | 272 | default: |
8b0e0910 | 273 | DBG1(DBG_LIB, "signature scheme %N not supported in RSA", |
ff8d3ba3 MW |
274 | signature_scheme_names, scheme); |
275 | return FALSE; | |
276 | } | |
277 | } | |
278 | ||
646babd3 | 279 | METHOD(private_key_t, decrypt, bool, |
33ddaaab | 280 | private_gcrypt_rsa_private_key_t *this, encryption_scheme_t scheme, |
4abb29f6 | 281 | void *params, chunk_t encrypted, chunk_t *plain) |
ff8d3ba3 | 282 | { |
d32b14db MW |
283 | gcry_error_t err; |
284 | gcry_sexp_t in, out; | |
285 | chunk_t padded; | |
286 | u_char *pos = NULL;; | |
7daf5226 | 287 | |
33ddaaab MW |
288 | if (scheme != ENCRYPT_RSA_PKCS1) |
289 | { | |
290 | DBG1(DBG_LIB, "encryption scheme %N not supported", | |
291 | encryption_scheme_names, scheme); | |
292 | return FALSE; | |
293 | } | |
d32b14db MW |
294 | err = gcry_sexp_build(&in, NULL, "(enc-val(flags)(rsa(a %b)))", |
295 | encrypted.len, encrypted.ptr); | |
296 | if (err) | |
297 | { | |
8b0e0910 TB |
298 | DBG1(DBG_LIB, "building decryption S-expression failed: %s", |
299 | gpg_strerror(err)); | |
d32b14db MW |
300 | return FALSE; |
301 | } | |
302 | err = gcry_pk_decrypt(&out, in, this->key); | |
303 | gcry_sexp_release(in); | |
304 | if (err) | |
305 | { | |
8b0e0910 | 306 | DBG1(DBG_LIB, "decrypting pkcs1 data failed: %s", gpg_strerror(err)); |
d32b14db MW |
307 | return FALSE; |
308 | } | |
309 | padded.ptr = (u_char*)gcry_sexp_nth_data(out, 1, &padded.len); | |
310 | /* result is padded, but gcrypt strips leading zero: | |
311 | * 00 | 02 | RANDOM | 00 | DATA */ | |
312 | if (padded.ptr && padded.len > 2 && padded.ptr[0] == 0x02) | |
313 | { | |
314 | pos = memchr(padded.ptr, 0x00, padded.len - 1); | |
315 | if (pos) | |
316 | { | |
317 | pos++; | |
318 | *plain = chunk_clone(chunk_create( | |
319 | pos, padded.len - (pos - padded.ptr))); | |
320 | } | |
321 | } | |
322 | gcry_sexp_release(out); | |
323 | if (!pos) | |
324 | { | |
8b0e0910 | 325 | DBG1(DBG_LIB, "decrypted data has invalid pkcs1 padding"); |
d32b14db MW |
326 | return FALSE; |
327 | } | |
328 | return TRUE; | |
ff8d3ba3 MW |
329 | } |
330 | ||
a944d209 | 331 | METHOD(private_key_t, get_keysize, int, |
646babd3 | 332 | private_gcrypt_rsa_private_key_t *this) |
ff8d3ba3 | 333 | { |
a944d209 | 334 | return gcry_pk_get_nbits(this->key); |
ff8d3ba3 MW |
335 | } |
336 | ||
646babd3 MW |
337 | METHOD(private_key_t, get_public_key, public_key_t*, |
338 | private_gcrypt_rsa_private_key_t *this) | |
ff8d3ba3 | 339 | { |
83805031 MW |
340 | chunk_t n, e; |
341 | public_key_t *public; | |
7daf5226 | 342 | |
83805031 MW |
343 | n = gcrypt_rsa_find_token(this->key, "n", NULL); |
344 | e = gcrypt_rsa_find_token(this->key, "e", NULL); | |
7daf5226 | 345 | |
83805031 MW |
346 | public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA, |
347 | BUILD_RSA_MODULUS, n, BUILD_RSA_PUB_EXP, e, BUILD_END); | |
348 | chunk_free(&n); | |
349 | chunk_free(&e); | |
7daf5226 | 350 | |
83805031 | 351 | return public; |
ff8d3ba3 MW |
352 | } |
353 | ||
646babd3 MW |
354 | METHOD(private_key_t, get_encoding, bool, |
355 | private_gcrypt_rsa_private_key_t *this, cred_encoding_type_t type, | |
356 | chunk_t *encoding) | |
ff8d3ba3 | 357 | { |
cbd51389 | 358 | chunk_t cn, ce, cp, cq, cd, cu, cexp1 = chunk_empty, cexp2 = chunk_empty; |
ff8d3ba3 MW |
359 | gcry_mpi_t p = NULL, q = NULL, d = NULL, exp1, exp2; |
360 | gcry_error_t err; | |
cbd51389 | 361 | bool success; |
7daf5226 | 362 | |
ff8d3ba3 | 363 | /* p and q are swapped, gcrypt expects p < q */ |
a2f1bb23 MW |
364 | cp = gcrypt_rsa_find_token(this->key, "q", NULL); |
365 | cq = gcrypt_rsa_find_token(this->key, "p", NULL); | |
366 | cd = gcrypt_rsa_find_token(this->key, "d", NULL); | |
7daf5226 | 367 | |
ff8d3ba3 MW |
368 | err = gcry_mpi_scan(&p, GCRYMPI_FMT_USG, cp.ptr, cp.len, NULL) |
369 | | gcry_mpi_scan(&q, GCRYMPI_FMT_USG, cq.ptr, cq.len, NULL) | |
370 | | gcry_mpi_scan(&d, GCRYMPI_FMT_USG, cd.ptr, cd.len, NULL); | |
371 | if (err) | |
372 | { | |
373 | gcry_mpi_release(p); | |
374 | gcry_mpi_release(q); | |
375 | gcry_mpi_release(d); | |
376 | chunk_clear(&cp); | |
377 | chunk_clear(&cq); | |
378 | chunk_clear(&cd); | |
8b0e0910 | 379 | DBG1(DBG_LIB, "scanning mpi for export failed: %s", gpg_strerror(err)); |
cbd51389 | 380 | return FALSE; |
ff8d3ba3 | 381 | } |
7daf5226 | 382 | |
ff8d3ba3 MW |
383 | gcry_mpi_sub_ui(p, p, 1); |
384 | exp1 = gcry_mpi_new(gcry_pk_get_nbits(this->key)); | |
385 | gcry_mpi_mod(exp1, d, p); | |
386 | gcry_mpi_release(p); | |
7daf5226 | 387 | |
ff8d3ba3 MW |
388 | gcry_mpi_sub_ui(q, q, 1); |
389 | exp2 = gcry_mpi_new(gcry_pk_get_nbits(this->key)); | |
eadbc3da | 390 | gcry_mpi_mod(exp2, d, q); |
ff8d3ba3 | 391 | gcry_mpi_release(q); |
7daf5226 | 392 | |
ff8d3ba3 MW |
393 | err = gcry_mpi_aprint(GCRYMPI_FMT_USG, &cexp1.ptr, &cexp1.len, exp1) |
394 | | gcry_mpi_aprint(GCRYMPI_FMT_USG, &cexp2.ptr, &cexp2.len, exp2); | |
7daf5226 | 395 | |
ff8d3ba3 MW |
396 | gcry_mpi_release(d); |
397 | gcry_mpi_release(exp1); | |
398 | gcry_mpi_release(exp2); | |
7daf5226 | 399 | |
ff8d3ba3 MW |
400 | if (err) |
401 | { | |
8b0e0910 | 402 | DBG1(DBG_LIB, "printing mpi for export failed: %s", gpg_strerror(err)); |
ff8d3ba3 MW |
403 | chunk_clear(&cp); |
404 | chunk_clear(&cq); | |
405 | chunk_clear(&cd); | |
406 | chunk_clear(&cexp1); | |
407 | chunk_clear(&cexp2); | |
cbd51389 | 408 | return FALSE; |
ff8d3ba3 | 409 | } |
7daf5226 | 410 | |
cbd51389 MW |
411 | cn = gcrypt_rsa_find_token(this->key, "n", NULL); |
412 | ce = gcrypt_rsa_find_token(this->key, "e", NULL); | |
413 | cu = gcrypt_rsa_find_token(this->key, "u", NULL); | |
7daf5226 | 414 | |
cbd51389 | 415 | success = lib->encoding->encode(lib->encoding, type, NULL, encoding, |
da9724e6 MW |
416 | CRED_PART_RSA_MODULUS, cn, |
417 | CRED_PART_RSA_PUB_EXP, ce, CRED_PART_RSA_PRIV_EXP, cd, | |
418 | CRED_PART_RSA_PRIME1, cp, CRED_PART_RSA_PRIME2, cq, | |
419 | CRED_PART_RSA_EXP1, cexp1, CRED_PART_RSA_EXP2, cexp2, | |
420 | CRED_PART_RSA_COEFF, cu, CRED_PART_END); | |
cbd51389 MW |
421 | chunk_free(&cn); |
422 | chunk_free(&ce); | |
423 | chunk_clear(&cd); | |
424 | chunk_clear(&cp); | |
425 | chunk_clear(&cq); | |
426 | chunk_clear(&cexp1); | |
427 | chunk_clear(&cexp2); | |
428 | chunk_clear(&cu); | |
7daf5226 | 429 | |
cbd51389 MW |
430 | return success; |
431 | } | |
432 | ||
646babd3 MW |
433 | METHOD(private_key_t, get_fingerprint, bool, |
434 | private_gcrypt_rsa_private_key_t *this, cred_encoding_type_t type, | |
435 | chunk_t *fp) | |
cbd51389 MW |
436 | { |
437 | chunk_t n, e; | |
438 | bool success; | |
7daf5226 | 439 | |
cbd51389 MW |
440 | if (lib->encoding->get_cache(lib->encoding, type, this, fp)) |
441 | { | |
442 | return TRUE; | |
443 | } | |
444 | n = gcrypt_rsa_find_token(this->key, "n", NULL); | |
445 | e = gcrypt_rsa_find_token(this->key, "e", NULL); | |
7daf5226 | 446 | |
cbd51389 | 447 | success = lib->encoding->encode(lib->encoding, |
da9724e6 MW |
448 | type, this, fp, CRED_PART_RSA_MODULUS, n, |
449 | CRED_PART_RSA_PUB_EXP, e, CRED_PART_END); | |
cbd51389 MW |
450 | chunk_free(&n); |
451 | chunk_free(&e); | |
452 | return success; | |
ff8d3ba3 MW |
453 | } |
454 | ||
646babd3 MW |
455 | METHOD(private_key_t, get_ref, private_key_t*, |
456 | private_gcrypt_rsa_private_key_t *this) | |
ff8d3ba3 MW |
457 | { |
458 | ref_get(&this->ref); | |
646babd3 | 459 | return &this->public.key; |
ff8d3ba3 MW |
460 | } |
461 | ||
646babd3 MW |
462 | METHOD(private_key_t, destroy, void, |
463 | private_gcrypt_rsa_private_key_t *this) | |
ff8d3ba3 MW |
464 | { |
465 | if (ref_put(&this->ref)) | |
466 | { | |
ff8d3ba3 | 467 | gcry_sexp_release(this->key); |
cbd51389 | 468 | lib->encoding->clear_cache(lib->encoding, this); |
ff8d3ba3 MW |
469 | free(this); |
470 | } | |
471 | } | |
472 | ||
473 | /** | |
474 | * Internal generic constructor | |
475 | */ | |
646babd3 | 476 | static private_gcrypt_rsa_private_key_t *create_empty() |
ff8d3ba3 | 477 | { |
646babd3 MW |
478 | private_gcrypt_rsa_private_key_t *this; |
479 | ||
480 | INIT(this, | |
ba31fe1f MW |
481 | .public = { |
482 | .key = { | |
483 | .get_type = _get_type, | |
484 | .sign = _sign, | |
485 | .decrypt = _decrypt, | |
486 | .get_keysize = _get_keysize, | |
487 | .get_public_key = _get_public_key, | |
488 | .equals = private_key_equals, | |
489 | .belongs_to = private_key_belongs_to, | |
490 | .get_fingerprint = _get_fingerprint, | |
491 | .has_fingerprint = private_key_has_fingerprint, | |
492 | .get_encoding = _get_encoding, | |
493 | .get_ref = _get_ref, | |
494 | .destroy = _destroy, | |
495 | }, | |
646babd3 MW |
496 | }, |
497 | .ref = 1, | |
498 | ); | |
7daf5226 | 499 | |
ff8d3ba3 MW |
500 | return this; |
501 | } | |
502 | ||
ff8d3ba3 | 503 | /** |
a94acb58 | 504 | * See header. |
ff8d3ba3 | 505 | */ |
a94acb58 MW |
506 | gcrypt_rsa_private_key_t *gcrypt_rsa_private_key_gen(key_type_t type, |
507 | va_list args) | |
ff8d3ba3 MW |
508 | { |
509 | private_gcrypt_rsa_private_key_t *this; | |
a94acb58 | 510 | gcry_sexp_t param; |
ff8d3ba3 | 511 | gcry_error_t err; |
a94acb58 MW |
512 | u_int key_size = 0; |
513 | ||
514 | while (TRUE) | |
515 | { | |
516 | switch (va_arg(args, builder_part_t)) | |
517 | { | |
518 | case BUILD_KEY_SIZE: | |
519 | key_size = va_arg(args, u_int); | |
520 | continue; | |
521 | case BUILD_END: | |
522 | break; | |
523 | default: | |
524 | return NULL; | |
525 | } | |
526 | break; | |
527 | } | |
528 | if (!key_size) | |
529 | { | |
530 | return NULL; | |
531 | } | |
7daf5226 | 532 | |
ff8d3ba3 MW |
533 | err = gcry_sexp_build(¶m, NULL, "(genkey(rsa(nbits %d)))", key_size); |
534 | if (err) | |
535 | { | |
8b0e0910 | 536 | DBG1(DBG_LIB, "building S-expression failed: %s", gpg_strerror(err)); |
ff8d3ba3 MW |
537 | return NULL; |
538 | } | |
646babd3 | 539 | this = create_empty(); |
a94acb58 | 540 | err = gcry_pk_genkey(&this->key, param); |
ff8d3ba3 MW |
541 | gcry_sexp_release(param); |
542 | if (err) | |
543 | { | |
a94acb58 | 544 | free(this); |
8b0e0910 | 545 | DBG1(DBG_LIB, "generating RSA key failed: %s", gpg_strerror(err)); |
ff8d3ba3 MW |
546 | return NULL; |
547 | } | |
ff8d3ba3 MW |
548 | return &this->public; |
549 | } | |
550 | ||
183a9108 TB |
551 | /** |
552 | * Recover the primes from n, e and d using the algorithm described in | |
553 | * Appendix C of NIST SP 800-56B. | |
554 | */ | |
555 | static bool calculate_pqu(chunk_t cn, chunk_t ce, chunk_t cd, chunk_t *cp, | |
556 | chunk_t *cq, chunk_t *cu) | |
557 | { | |
558 | gcry_mpi_t n, e, d, p, q, u, k, r, g, y, n1, x, two; | |
559 | int i, t, j; | |
560 | gcry_error_t err; | |
561 | bool success = FALSE; | |
562 | ||
563 | n = e = d = p = q = u = k = r = g = y = n1 = x = two = NULL; | |
564 | err = gcry_mpi_scan(&n, GCRYMPI_FMT_USG, cn.ptr, cn.len, NULL) | |
565 | | gcry_mpi_scan(&e, GCRYMPI_FMT_USG, ce.ptr, ce.len, NULL) | |
566 | | gcry_mpi_scan(&d, GCRYMPI_FMT_USG, cd.ptr, cd.len, NULL); | |
567 | if (err) | |
568 | { | |
569 | goto error; | |
570 | } | |
571 | /* k = (d * e) - 1 */ | |
572 | k = gcry_mpi_new(gcry_mpi_get_nbits(n)); | |
573 | gcry_mpi_mul(k, d, e); | |
574 | gcry_mpi_sub_ui(k, k, 1); | |
575 | if (gcry_mpi_test_bit(k, 0)) | |
576 | { | |
577 | goto error; | |
578 | } | |
579 | /* k = 2^t * r, where r is the largest odd integer dividing k, and t >= 1 */ | |
580 | r = gcry_mpi_copy(k); | |
581 | for (t = 0; !gcry_mpi_test_bit(r, 0); t++) | |
582 | { /* r = r/2 */ | |
583 | gcry_mpi_rshift(r, r, 1); | |
584 | } | |
585 | /* we need n-1 below */ | |
586 | n1 = gcry_mpi_new(gcry_mpi_get_nbits(n)); | |
587 | gcry_mpi_sub_ui(n1, n, 1); | |
588 | y = gcry_mpi_new(gcry_mpi_get_nbits(n)); | |
589 | g = gcry_mpi_new(gcry_mpi_get_nbits(n)); | |
590 | x = gcry_mpi_new(gcry_mpi_get_nbits(n)); | |
591 | two = gcry_mpi_set_ui(NULL, 2); | |
592 | for (i = 0; i < 100; i++) | |
593 | { /* generate random integer g in [0, n-1] */ | |
594 | do | |
595 | { | |
596 | gcry_mpi_randomize(g, gcry_mpi_get_nbits(n), GCRY_WEAK_RANDOM); | |
597 | } | |
598 | while (gcry_mpi_cmp(n, g) <= 0); | |
599 | /* y = g^r mod n */ | |
600 | gcry_mpi_powm(y, g, r, n); | |
601 | /* try again if y == 1 or y == n-1 */ | |
602 | if (gcry_mpi_cmp_ui(y, 1) == 0 || gcry_mpi_cmp(y, n1) == 0) | |
603 | { | |
604 | continue; | |
605 | } | |
606 | for (j = 0; j < t; j++) | |
607 | { /* x = y^2 mod n */ | |
608 | gcry_mpi_powm(x, y, two, n); | |
609 | /* stop if x == 1 */ | |
610 | if (gcry_mpi_cmp_ui(x, 1) == 0) | |
611 | { | |
612 | goto done; | |
613 | } | |
614 | /* retry with new g if x = n-1 */ | |
615 | if (gcry_mpi_cmp(x, n1) == 0) | |
616 | { | |
617 | break; | |
618 | } | |
619 | /* y = x */ | |
620 | gcry_mpi_set(y, x); | |
621 | } | |
622 | } | |
623 | goto error; | |
624 | ||
625 | done: | |
626 | /* p = gcd(y-1, n) */ | |
627 | gcry_mpi_sub_ui(y, y, 1); | |
628 | p = gcry_mpi_new(gcry_mpi_get_nbits(n)); | |
629 | gcry_mpi_gcd(p, y, n); | |
630 | /* q = n/p */ | |
631 | q = gcry_mpi_new(gcry_mpi_get_nbits(n)); | |
632 | gcry_mpi_div(q, NULL, n, p, 0); | |
633 | if (gcry_mpi_cmp(p, q) > 0) | |
634 | { /* gcrypt expects q < p */ | |
635 | gcry_mpi_swap(p, q); | |
636 | } | |
637 | /* u = q^-1 mod p */ | |
638 | u = gcry_mpi_new(gcry_mpi_get_nbits(n)); | |
639 | gcry_mpi_invm(u, p, q); | |
640 | err = gcry_mpi_aprint(GCRYMPI_FMT_USG, &cp->ptr, &cp->len, p) | |
641 | | gcry_mpi_aprint(GCRYMPI_FMT_USG, &cq->ptr, &cq->len, q) | |
642 | | gcry_mpi_aprint(GCRYMPI_FMT_USG, &cu->ptr, &cu->len, u); | |
643 | if (err) | |
644 | { | |
645 | goto error; | |
646 | } | |
647 | success = TRUE; | |
648 | ||
649 | error: | |
650 | gcry_mpi_release(n); | |
651 | gcry_mpi_release(e); | |
652 | gcry_mpi_release(d); | |
653 | gcry_mpi_release(p); | |
654 | gcry_mpi_release(q); | |
655 | gcry_mpi_release(u); | |
656 | gcry_mpi_release(k); | |
657 | gcry_mpi_release(r); | |
658 | gcry_mpi_release(g); | |
659 | gcry_mpi_release(y); | |
660 | gcry_mpi_release(n1); | |
661 | gcry_mpi_release(x); | |
662 | gcry_mpi_release(two); | |
663 | return success; | |
664 | } | |
665 | ||
ff8d3ba3 | 666 | /** |
a94acb58 | 667 | * See header. |
ff8d3ba3 | 668 | */ |
a94acb58 MW |
669 | gcrypt_rsa_private_key_t *gcrypt_rsa_private_key_load(key_type_t type, |
670 | va_list args) | |
ff8d3ba3 | 671 | { |
a94acb58 | 672 | private_gcrypt_rsa_private_key_t *this; |
183a9108 | 673 | chunk_t n, e, d, p, q, u, np, nq, nu; |
ff8d3ba3 | 674 | gcry_error_t err; |
7daf5226 | 675 | |
183a9108 | 676 | n = e = d = p = q = u = np = nq = nu = chunk_empty; |
a94acb58 MW |
677 | while (TRUE) |
678 | { | |
679 | switch (va_arg(args, builder_part_t)) | |
680 | { | |
681 | case BUILD_RSA_MODULUS: | |
682 | n = va_arg(args, chunk_t); | |
683 | continue; | |
684 | case BUILD_RSA_PUB_EXP: | |
685 | e = va_arg(args, chunk_t); | |
686 | continue; | |
687 | case BUILD_RSA_PRIV_EXP: | |
688 | d = va_arg(args, chunk_t); | |
689 | continue; | |
690 | case BUILD_RSA_PRIME1: | |
691 | /* swap p and q, gcrypt expects p < q */ | |
692 | q = va_arg(args, chunk_t); | |
693 | continue; | |
694 | case BUILD_RSA_PRIME2: | |
695 | p = va_arg(args, chunk_t); | |
696 | continue; | |
697 | case BUILD_RSA_EXP1: | |
698 | case BUILD_RSA_EXP2: | |
699 | /* not required for gcrypt */ | |
513701f4 | 700 | va_arg(args, chunk_t); |
a94acb58 MW |
701 | continue; |
702 | case BUILD_RSA_COEFF: | |
703 | u = va_arg(args, chunk_t); | |
704 | continue; | |
705 | case BUILD_END: | |
706 | break; | |
707 | default: | |
708 | return NULL; | |
709 | } | |
710 | break; | |
711 | } | |
183a9108 TB |
712 | if (!p.len || !q.len || !u.len) |
713 | { | |
714 | if (!calculate_pqu(n, e, d, &np, &nq, &nu)) | |
715 | { | |
716 | return NULL; | |
717 | } | |
718 | p = np; | |
719 | q = nq; | |
720 | u = nu; | |
721 | } | |
646babd3 | 722 | this = create_empty(); |
ff8d3ba3 MW |
723 | err = gcry_sexp_build(&this->key, NULL, |
724 | "(private-key(rsa(n %b)(e %b)(d %b)(p %b)(q %b)(u %b)))", | |
725 | n.len, n.ptr, e.len, e.ptr, d.len, d.ptr, | |
726 | p.len, p.ptr, q.len, q.ptr, u.len, u.ptr); | |
183a9108 TB |
727 | |
728 | chunk_clear(&np); | |
729 | chunk_clear(&nq); | |
730 | chunk_clear(&nu); | |
ff8d3ba3 MW |
731 | if (err) |
732 | { | |
8b0e0910 | 733 | DBG1(DBG_LIB, "loading private key failed: %s", gpg_strerror(err)); |
ff8d3ba3 MW |
734 | free(this); |
735 | return NULL; | |
736 | } | |
737 | err = gcry_pk_testkey(this->key); | |
738 | if (err) | |
739 | { | |
8b0e0910 | 740 | DBG1(DBG_LIB, "private key sanity check failed: %s", gpg_strerror(err)); |
ff8d3ba3 MW |
741 | destroy(this); |
742 | return NULL; | |
743 | } | |
744 | return &this->public; | |
745 | } | |
746 |