e_gmp.c revision 296465
1/* crypto/engine/e_gmp.c */ 2/* 3 * Written by Geoff Thorpe (geoff@geoffthorpe.net) for the OpenSSL project 4 * 2003. 5 */ 6/* ==================================================================== 7 * Copyright (c) 1999-2001 The OpenSSL Project. All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * 21 * 3. All advertising materials mentioning features or use of this 22 * software must display the following acknowledgment: 23 * "This product includes software developed by the OpenSSL Project 24 * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" 25 * 26 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to 27 * endorse or promote products derived from this software without 28 * prior written permission. For written permission, please contact 29 * licensing@OpenSSL.org. 30 * 31 * 5. Products derived from this software may not be called "OpenSSL" 32 * nor may "OpenSSL" appear in their names without prior written 33 * permission of the OpenSSL Project. 34 * 35 * 6. Redistributions of any form whatsoever must retain the following 36 * acknowledgment: 37 * "This product includes software developed by the OpenSSL Project 38 * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" 39 * 40 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY 41 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 42 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 43 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR 44 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 45 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 46 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 47 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 49 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 50 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 51 * OF THE POSSIBILITY OF SUCH DAMAGE. 52 * ==================================================================== 53 * 54 * This product includes cryptographic software written by Eric Young 55 * (eay@cryptsoft.com). This product includes software written by Tim 56 * Hudson (tjh@cryptsoft.com). 57 * 58 */ 59 60/* 61 * This engine is not (currently) compiled in by default. Do enable it, 62 * reconfigure OpenSSL with "enable-gmp -lgmp". The GMP libraries and headers 63 * must reside in one of the paths searched by the compiler/linker, otherwise 64 * paths must be specified - eg. try configuring with "enable-gmp 65 * -I<includepath> -L<libpath> -lgmp". YMMV. 66 */ 67 68/*- 69 * As for what this does - it's a largely unoptimised implementation of an 70 * ENGINE that uses the GMP library to perform RSA private key operations. To 71 * obtain more information about what "unoptimised" means, see my original mail 72 * on the subject (though ignore the build instructions which have since 73 * changed); 74 * 75 * http://www.mail-archive.com/openssl-dev@openssl.org/msg12227.html 76 * 77 * On my athlon system at least, it appears the builtin OpenSSL code is now 78 * slightly faster, which is to say that the RSA-related MPI performance 79 * between OpenSSL's BIGNUM and GMP's mpz implementations is probably pretty 80 * balanced for this chip, and so the performance degradation in this ENGINE by 81 * having to convert to/from GMP formats (and not being able to cache 82 * montgomery forms) is probably the difference. However, if some unconfirmed 83 * reports from users is anything to go by, the situation on some other 84 * chipsets might be a good deal more favourable to the GMP version (eg. PPC). 85 * Feedback welcome. */ 86 87#include <stdio.h> 88#include <string.h> 89#include <openssl/crypto.h> 90#include <openssl/buffer.h> 91#include <openssl/engine.h> 92#include <openssl/rsa.h> 93#include <openssl/bn.h> 94 95#ifndef OPENSSL_NO_HW 96# ifndef OPENSSL_NO_GMP 97 98# include <gmp.h> 99 100# define E_GMP_LIB_NAME "gmp engine" 101# include "e_gmp_err.c" 102 103static int e_gmp_destroy(ENGINE *e); 104static int e_gmp_init(ENGINE *e); 105static int e_gmp_finish(ENGINE *e); 106static int e_gmp_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f) (void)); 107 108# ifndef OPENSSL_NO_RSA 109/* RSA stuff */ 110static int e_gmp_rsa_mod_exp(BIGNUM *r, const BIGNUM *I, RSA *rsa, 111 BN_CTX *ctx); 112static int e_gmp_rsa_finish(RSA *r); 113# endif 114 115/* The definitions for control commands specific to this engine */ 116/* #define E_GMP_CMD_SO_PATH ENGINE_CMD_BASE */ 117static const ENGINE_CMD_DEFN e_gmp_cmd_defns[] = { 118# if 0 119 {E_GMP_CMD_SO_PATH, 120 "SO_PATH", 121 "Specifies the path to the 'e_gmp' shared library", 122 ENGINE_CMD_FLAG_STRING}, 123# endif 124 {0, NULL, NULL, 0} 125}; 126 127# ifndef OPENSSL_NO_RSA 128/* Our internal RSA_METHOD that we provide pointers to */ 129static RSA_METHOD e_gmp_rsa = { 130 "GMP RSA method", 131 NULL, 132 NULL, 133 NULL, 134 NULL, 135 e_gmp_rsa_mod_exp, 136 NULL, 137 NULL, 138 e_gmp_rsa_finish, 139 /* 140 * These flags initialise montgomery crud that GMP ignores, however it 141 * makes sure the public key ops (which are done in openssl) don't seem 142 * *slower* than usual :-) 143 */ 144 RSA_FLAG_CACHE_PUBLIC | RSA_FLAG_CACHE_PRIVATE, 145 NULL, 146 NULL, 147 NULL 148}; 149# endif 150 151/* Constants used when creating the ENGINE */ 152static const char *engine_e_gmp_id = "gmp"; 153static const char *engine_e_gmp_name = "GMP engine support"; 154 155/* 156 * This internal function is used by ENGINE_gmp() and possibly by the 157 * "dynamic" ENGINE support too 158 */ 159static int bind_helper(ENGINE *e) 160{ 161# ifndef OPENSSL_NO_RSA 162 const RSA_METHOD *meth1; 163# endif 164 if (!ENGINE_set_id(e, engine_e_gmp_id) || 165 !ENGINE_set_name(e, engine_e_gmp_name) || 166# ifndef OPENSSL_NO_RSA 167 !ENGINE_set_RSA(e, &e_gmp_rsa) || 168# endif 169 !ENGINE_set_destroy_function(e, e_gmp_destroy) || 170 !ENGINE_set_init_function(e, e_gmp_init) || 171 !ENGINE_set_finish_function(e, e_gmp_finish) || 172 !ENGINE_set_ctrl_function(e, e_gmp_ctrl) || 173 !ENGINE_set_cmd_defns(e, e_gmp_cmd_defns)) 174 return 0; 175 176# ifndef OPENSSL_NO_RSA 177 meth1 = RSA_PKCS1_SSLeay(); 178 e_gmp_rsa.rsa_pub_enc = meth1->rsa_pub_enc; 179 e_gmp_rsa.rsa_pub_dec = meth1->rsa_pub_dec; 180 e_gmp_rsa.rsa_priv_enc = meth1->rsa_priv_enc; 181 e_gmp_rsa.rsa_priv_dec = meth1->rsa_priv_dec; 182 e_gmp_rsa.bn_mod_exp = meth1->bn_mod_exp; 183# endif 184 185 /* Ensure the e_gmp error handling is set up */ 186 ERR_load_GMP_strings(); 187 return 1; 188} 189 190static ENGINE *engine_gmp(void) 191{ 192 ENGINE *ret = ENGINE_new(); 193 if (!ret) 194 return NULL; 195 if (!bind_helper(ret)) { 196 ENGINE_free(ret); 197 return NULL; 198 } 199 return ret; 200} 201 202void ENGINE_load_gmp(void) 203{ 204 /* Copied from eng_[openssl|dyn].c */ 205 ENGINE *toadd = engine_gmp(); 206 if (!toadd) 207 return; 208 ENGINE_add(toadd); 209 ENGINE_free(toadd); 210 ERR_clear_error(); 211} 212 213# ifndef OPENSSL_NO_RSA 214/* Used to attach our own key-data to an RSA structure */ 215static int hndidx_rsa = -1; 216# endif 217 218static int e_gmp_destroy(ENGINE *e) 219{ 220 ERR_unload_GMP_strings(); 221 return 1; 222} 223 224/* (de)initialisation functions. */ 225static int e_gmp_init(ENGINE *e) 226{ 227# ifndef OPENSSL_NO_RSA 228 if (hndidx_rsa == -1) 229 hndidx_rsa = RSA_get_ex_new_index(0, 230 "GMP-based RSA key handle", 231 NULL, NULL, NULL); 232# endif 233 if (hndidx_rsa == -1) 234 return 0; 235 return 1; 236} 237 238static int e_gmp_finish(ENGINE *e) 239{ 240 return 1; 241} 242 243static int e_gmp_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f) (void)) 244{ 245 int to_return = 1; 246 247 switch (cmd) { 248# if 0 249 case E_GMP_CMD_SO_PATH: 250 /* ... */ 251# endif 252 /* The command isn't understood by this engine */ 253 default: 254 GMPerr(GMP_F_E_GMP_CTRL, GMP_R_CTRL_COMMAND_NOT_IMPLEMENTED); 255 to_return = 0; 256 break; 257 } 258 259 return to_return; 260} 261 262/* 263 * Most often limb sizes will be the same. If not, we use hex conversion 264 * which is neat, but extremely inefficient. 265 */ 266static int bn2gmp(const BIGNUM *bn, mpz_t g) 267{ 268 bn_check_top(bn); 269 if (((sizeof(bn->d[0]) * 8) == GMP_NUMB_BITS) && 270 (BN_BITS2 == GMP_NUMB_BITS)) { 271 /* The common case */ 272 if (!_mpz_realloc(g, bn->top)) 273 return 0; 274 memcpy(&g->_mp_d[0], &bn->d[0], bn->top * sizeof(bn->d[0])); 275 g->_mp_size = bn->top; 276 if (bn->neg) 277 g->_mp_size = -g->_mp_size; 278 return 1; 279 } else { 280 int toret; 281 char *tmpchar = BN_bn2hex(bn); 282 if (!tmpchar) 283 return 0; 284 toret = (mpz_set_str(g, tmpchar, 16) == 0 ? 1 : 0); 285 OPENSSL_free(tmpchar); 286 return toret; 287 } 288} 289 290static int gmp2bn(mpz_t g, BIGNUM *bn) 291{ 292 if (((sizeof(bn->d[0]) * 8) == GMP_NUMB_BITS) && 293 (BN_BITS2 == GMP_NUMB_BITS)) { 294 /* The common case */ 295 int s = (g->_mp_size >= 0) ? g->_mp_size : -g->_mp_size; 296 BN_zero(bn); 297 if (bn_expand2(bn, s) == NULL) 298 return 0; 299 bn->top = s; 300 memcpy(&bn->d[0], &g->_mp_d[0], s * sizeof(bn->d[0])); 301 bn_correct_top(bn); 302 bn->neg = g->_mp_size >= 0 ? 0 : 1; 303 return 1; 304 } else { 305 int toret; 306 char *tmpchar = OPENSSL_malloc(mpz_sizeinbase(g, 16) + 10); 307 if (!tmpchar) 308 return 0; 309 mpz_get_str(tmpchar, 16, g); 310 toret = BN_hex2bn(&bn, tmpchar); 311 OPENSSL_free(tmpchar); 312 return toret; 313 } 314} 315 316# ifndef OPENSSL_NO_RSA 317typedef struct st_e_gmp_rsa_ctx { 318 int public_only; 319 mpz_t n; 320 mpz_t d; 321 mpz_t e; 322 mpz_t p; 323 mpz_t q; 324 mpz_t dmp1; 325 mpz_t dmq1; 326 mpz_t iqmp; 327 mpz_t r0, r1, I0, m1; 328} E_GMP_RSA_CTX; 329 330static E_GMP_RSA_CTX *e_gmp_get_rsa(RSA *rsa) 331{ 332 E_GMP_RSA_CTX *hptr = RSA_get_ex_data(rsa, hndidx_rsa); 333 if (hptr) 334 return hptr; 335 hptr = OPENSSL_malloc(sizeof(E_GMP_RSA_CTX)); 336 if (!hptr) 337 return NULL; 338 /* 339 * These inits could probably be replaced by more intelligent mpz_init2() 340 * versions, to reduce malloc-thrashing. 341 */ 342 mpz_init(hptr->n); 343 mpz_init(hptr->d); 344 mpz_init(hptr->e); 345 mpz_init(hptr->p); 346 mpz_init(hptr->q); 347 mpz_init(hptr->dmp1); 348 mpz_init(hptr->dmq1); 349 mpz_init(hptr->iqmp); 350 mpz_init(hptr->r0); 351 mpz_init(hptr->r1); 352 mpz_init(hptr->I0); 353 mpz_init(hptr->m1); 354 if (!bn2gmp(rsa->n, hptr->n) || !bn2gmp(rsa->e, hptr->e)) 355 goto err; 356 if (!rsa->p || !rsa->q || !rsa->d || !rsa->dmp1 || !rsa->dmq1 357 || !rsa->iqmp) { 358 hptr->public_only = 1; 359 return hptr; 360 } 361 if (!bn2gmp(rsa->d, hptr->d) || !bn2gmp(rsa->p, hptr->p) || 362 !bn2gmp(rsa->q, hptr->q) || !bn2gmp(rsa->dmp1, hptr->dmp1) || 363 !bn2gmp(rsa->dmq1, hptr->dmq1) || !bn2gmp(rsa->iqmp, hptr->iqmp)) 364 goto err; 365 hptr->public_only = 0; 366 RSA_set_ex_data(rsa, hndidx_rsa, hptr); 367 return hptr; 368 err: 369 mpz_clear(hptr->n); 370 mpz_clear(hptr->d); 371 mpz_clear(hptr->e); 372 mpz_clear(hptr->p); 373 mpz_clear(hptr->q); 374 mpz_clear(hptr->dmp1); 375 mpz_clear(hptr->dmq1); 376 mpz_clear(hptr->iqmp); 377 mpz_clear(hptr->r0); 378 mpz_clear(hptr->r1); 379 mpz_clear(hptr->I0); 380 mpz_clear(hptr->m1); 381 OPENSSL_free(hptr); 382 return NULL; 383} 384 385static int e_gmp_rsa_finish(RSA *rsa) 386{ 387 E_GMP_RSA_CTX *hptr = RSA_get_ex_data(rsa, hndidx_rsa); 388 if (!hptr) 389 return 0; 390 mpz_clear(hptr->n); 391 mpz_clear(hptr->d); 392 mpz_clear(hptr->e); 393 mpz_clear(hptr->p); 394 mpz_clear(hptr->q); 395 mpz_clear(hptr->dmp1); 396 mpz_clear(hptr->dmq1); 397 mpz_clear(hptr->iqmp); 398 mpz_clear(hptr->r0); 399 mpz_clear(hptr->r1); 400 mpz_clear(hptr->I0); 401 mpz_clear(hptr->m1); 402 OPENSSL_free(hptr); 403 RSA_set_ex_data(rsa, hndidx_rsa, NULL); 404 return 1; 405} 406 407static int e_gmp_rsa_mod_exp(BIGNUM *r, const BIGNUM *I, RSA *rsa, 408 BN_CTX *ctx) 409{ 410 E_GMP_RSA_CTX *hptr; 411 int to_return = 0; 412 413 hptr = e_gmp_get_rsa(rsa); 414 if (!hptr) { 415 GMPerr(GMP_F_E_GMP_RSA_MOD_EXP, GMP_R_KEY_CONTEXT_ERROR); 416 return 0; 417 } 418 if (hptr->public_only) { 419 GMPerr(GMP_F_E_GMP_RSA_MOD_EXP, GMP_R_MISSING_KEY_COMPONENTS); 420 return 0; 421 } 422 423 /* ugh!!! */ 424 if (!bn2gmp(I, hptr->I0)) 425 return 0; 426 427 /* 428 * This is basically the CRT logic in crypto/rsa/rsa_eay.c reworded into 429 * GMP-speak. It may be that GMP's API facilitates cleaner formulations 430 * of this stuff, eg. better handling of negatives, or functions that 431 * combine operations. 432 */ 433 434 mpz_mod(hptr->r1, hptr->I0, hptr->q); 435 mpz_powm(hptr->m1, hptr->r1, hptr->dmq1, hptr->q); 436 437 mpz_mod(hptr->r1, hptr->I0, hptr->p); 438 mpz_powm(hptr->r0, hptr->r1, hptr->dmp1, hptr->p); 439 440 mpz_sub(hptr->r0, hptr->r0, hptr->m1); 441 442 if (mpz_sgn(hptr->r0) < 0) 443 mpz_add(hptr->r0, hptr->r0, hptr->p); 444 mpz_mul(hptr->r1, hptr->r0, hptr->iqmp); 445 mpz_mod(hptr->r0, hptr->r1, hptr->p); 446 447 if (mpz_sgn(hptr->r0) < 0) 448 mpz_add(hptr->r0, hptr->r0, hptr->p); 449 mpz_mul(hptr->r1, hptr->r0, hptr->q); 450 mpz_add(hptr->r0, hptr->r1, hptr->m1); 451 452 /* ugh!!! */ 453 if (gmp2bn(hptr->r0, r)) 454 to_return = 1; 455 456 return 1; 457} 458# endif 459 460# endif /* !OPENSSL_NO_GMP */ 461 462/* 463 * This stuff is needed if this ENGINE is being compiled into a 464 * self-contained shared-library. 465 */ 466# ifndef OPENSSL_NO_DYNAMIC_ENGINE 467IMPLEMENT_DYNAMIC_CHECK_FN() 468# ifndef OPENSSL_NO_GMP 469static int bind_fn(ENGINE *e, const char *id) 470{ 471 if (id && (strcmp(id, engine_e_gmp_id) != 0)) 472 return 0; 473 if (!bind_helper(e)) 474 return 0; 475 return 1; 476} 477 478IMPLEMENT_DYNAMIC_BIND_FN(bind_fn) 479# else 480OPENSSL_EXPORT 481 int bind_engine(ENGINE *e, const char *id, const dynamic_fns *fns) 482{ 483 return 0; 484} 485# endif 486# endif /* OPENSSL_NO_DYNAMIC_ENGINE */ 487 488#endif /* !OPENSSL_NO_HW */ 489