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