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 "-DOPENSSL_USE_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 * "-DOPENSSL_USE_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 89#ifndef OPENSSL_NO_HW 90#if defined(OPENSSL_USE_GMP) && !defined(OPENSSL_NO_HW_GMP) 91 92#include <gmp.h> 93 94#define E_GMP_LIB_NAME "gmp engine" 95#include "e_gmp_err.c" 96 97static int e_gmp_destroy(ENGINE *e); 98static int e_gmp_init(ENGINE *e); 99static int e_gmp_finish(ENGINE *e); 100static int e_gmp_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f)(void)); 101 102#ifndef OPENSSL_NO_RSA 103/* RSA stuff */ 104static int e_gmp_rsa_mod_exp(BIGNUM *r, const BIGNUM *I, RSA *rsa, BN_CTX *ctx); 105static int e_gmp_rsa_finish(RSA *r); 106#endif 107 108/* The definitions for control commands specific to this engine */ 109/* #define E_GMP_CMD_SO_PATH ENGINE_CMD_BASE */ 110static const ENGINE_CMD_DEFN e_gmp_cmd_defns[] = { 111#if 0 112 {E_GMP_CMD_SO_PATH, 113 "SO_PATH", 114 "Specifies the path to the 'e_gmp' shared library", 115 ENGINE_CMD_FLAG_STRING}, 116#endif 117 {0, NULL, NULL, 0} 118 }; 119 120#ifndef OPENSSL_NO_RSA 121/* Our internal RSA_METHOD that we provide pointers to */ 122static RSA_METHOD e_gmp_rsa = 123 { 124 "GMP RSA method", 125 NULL, 126 NULL, 127 NULL, 128 NULL, 129 e_gmp_rsa_mod_exp, 130 NULL, 131 NULL, 132 e_gmp_rsa_finish, 133 /* These flags initialise montgomery crud that GMP ignores, however it 134 * makes sure the public key ops (which are done in openssl) don't seem 135 * *slower* than usual :-) */ 136 RSA_FLAG_CACHE_PUBLIC|RSA_FLAG_CACHE_PRIVATE, 137 NULL, 138 NULL, 139 NULL 140 }; 141#endif 142 143/* Constants used when creating the ENGINE */ 144static const char *engine_e_gmp_id = "gmp"; 145static const char *engine_e_gmp_name = "GMP engine support"; 146 147/* This internal function is used by ENGINE_gmp() and possibly by the 148 * "dynamic" ENGINE support too */ 149static int bind_helper(ENGINE *e) 150 { 151#ifndef OPENSSL_NO_RSA 152 const RSA_METHOD *meth1; 153#endif 154 if(!ENGINE_set_id(e, engine_e_gmp_id) || 155 !ENGINE_set_name(e, engine_e_gmp_name) || 156#ifndef OPENSSL_NO_RSA 157 !ENGINE_set_RSA(e, &e_gmp_rsa) || 158#endif 159 !ENGINE_set_destroy_function(e, e_gmp_destroy) || 160 !ENGINE_set_init_function(e, e_gmp_init) || 161 !ENGINE_set_finish_function(e, e_gmp_finish) || 162 !ENGINE_set_ctrl_function(e, e_gmp_ctrl) || 163 !ENGINE_set_cmd_defns(e, e_gmp_cmd_defns)) 164 return 0; 165 166#ifndef OPENSSL_NO_RSA 167 meth1 = RSA_PKCS1_SSLeay(); 168 e_gmp_rsa.rsa_pub_enc = meth1->rsa_pub_enc; 169 e_gmp_rsa.rsa_pub_dec = meth1->rsa_pub_dec; 170 e_gmp_rsa.rsa_priv_enc = meth1->rsa_priv_enc; 171 e_gmp_rsa.rsa_priv_dec = meth1->rsa_priv_dec; 172 e_gmp_rsa.bn_mod_exp = meth1->bn_mod_exp; 173#endif 174 175 /* Ensure the e_gmp error handling is set up */ 176 ERR_load_GMP_strings(); 177 return 1; 178 } 179 180static ENGINE *engine_gmp(void) 181 { 182 ENGINE *ret = ENGINE_new(); 183 if(!ret) 184 return NULL; 185 if(!bind_helper(ret)) 186 { 187 ENGINE_free(ret); 188 return NULL; 189 } 190 return ret; 191 } 192 193void ENGINE_load_gmp(void) 194 { 195 /* Copied from eng_[openssl|dyn].c */ 196 ENGINE *toadd = engine_gmp(); 197 if(!toadd) return; 198 ENGINE_add(toadd); 199 ENGINE_free(toadd); 200 ERR_clear_error(); 201 } 202 203#ifndef OPENSSL_NO_RSA 204/* Used to attach our own key-data to an RSA structure */ 205static int hndidx_rsa = -1; 206#endif 207 208static int e_gmp_destroy(ENGINE *e) 209 { 210 ERR_unload_GMP_strings(); 211 return 1; 212 } 213 214/* (de)initialisation functions. */ 215static int e_gmp_init(ENGINE *e) 216 { 217#ifndef OPENSSL_NO_RSA 218 if (hndidx_rsa == -1) 219 hndidx_rsa = RSA_get_ex_new_index(0, 220 "GMP-based RSA key handle", 221 NULL, NULL, NULL); 222#endif 223 if (hndidx_rsa == -1) 224 return 0; 225 return 1; 226 } 227 228static int e_gmp_finish(ENGINE *e) 229 { 230 return 1; 231 } 232 233static int e_gmp_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f)(void)) 234 { 235 int to_return = 1; 236 237 switch(cmd) 238 { 239#if 0 240 case E_GMP_CMD_SO_PATH: 241 /* ... */ 242#endif 243 /* The command isn't understood by this engine */ 244 default: 245 GMPerr(GMP_F_E_GMP_CTRL, 246 GMP_R_CTRL_COMMAND_NOT_IMPLEMENTED); 247 to_return = 0; 248 break; 249 } 250 251 return to_return; 252 } 253 254/* HACK - use text I/O functions in openssl and GMP to handle conversions. This 255 * is vile. */ 256static int bn2gmp(const BIGNUM *bn, mpz_t g) 257 { 258 int toret; 259 char *tmpchar = BN_bn2hex(bn); 260 if(!tmpchar) return 0; 261 toret = (mpz_set_str(g, tmpchar, 16) == 0 ? 1 : 0); 262 OPENSSL_free(tmpchar); 263 return toret; 264 } 265 266static int gmp2bn(mpz_t g, BIGNUM *bn) 267 { 268 int toret; 269 char *tmpchar = OPENSSL_malloc(mpz_sizeinbase(g, 16) + 10); 270 if(!tmpchar) return 0; 271 mpz_get_str(tmpchar, 16, g); 272 toret = BN_hex2bn(&bn, tmpchar); 273 OPENSSL_free(tmpchar); 274 return toret; 275 } 276 277#ifndef OPENSSL_NO_RSA 278typedef struct st_e_gmp_rsa_ctx 279 { 280 int public_only; 281 mpz_t n; 282 mpz_t d; 283 mpz_t e; 284 mpz_t p; 285 mpz_t q; 286 mpz_t dmp1; 287 mpz_t dmq1; 288 mpz_t iqmp; 289 mpz_t r0, r1, I0, m1; 290 } E_GMP_RSA_CTX; 291 292static E_GMP_RSA_CTX *e_gmp_get_rsa(RSA *rsa) 293 { 294 E_GMP_RSA_CTX *hptr = RSA_get_ex_data(rsa, hndidx_rsa); 295 if(hptr) return hptr; 296 hptr = OPENSSL_malloc(sizeof(E_GMP_RSA_CTX)); 297 if(!hptr) return NULL; 298 /* These inits could probably be replaced by more intelligent 299 * mpz_init2() versions, to reduce malloc-thrashing. */ 300 mpz_init(hptr->n); 301 mpz_init(hptr->d); 302 mpz_init(hptr->e); 303 mpz_init(hptr->p); 304 mpz_init(hptr->q); 305 mpz_init(hptr->dmp1); 306 mpz_init(hptr->dmq1); 307 mpz_init(hptr->iqmp); 308 mpz_init(hptr->r0); 309 mpz_init(hptr->r1); 310 mpz_init(hptr->I0); 311 mpz_init(hptr->m1); 312 if(!bn2gmp(rsa->n, hptr->n) || !bn2gmp(rsa->e, hptr->e)) 313 goto err; 314 if(!rsa->p || !rsa->q || !rsa->d || !rsa->dmp1 || !rsa->dmq1 || !rsa->iqmp) 315 { 316 hptr->public_only = 1; 317 return hptr; 318 } 319 if(!bn2gmp(rsa->d, hptr->d) || !bn2gmp(rsa->p, hptr->p) || 320 !bn2gmp(rsa->q, hptr->q) || !bn2gmp(rsa->dmp1, hptr->dmp1) || 321 !bn2gmp(rsa->dmq1, hptr->dmq1) || !bn2gmp(rsa->iqmp, hptr->iqmp)) 322 goto err; 323 hptr->public_only = 0; 324 RSA_set_ex_data(rsa, hndidx_rsa, hptr); 325 return hptr; 326err: 327 mpz_clear(hptr->n); 328 mpz_clear(hptr->d); 329 mpz_clear(hptr->e); 330 mpz_clear(hptr->p); 331 mpz_clear(hptr->q); 332 mpz_clear(hptr->dmp1); 333 mpz_clear(hptr->dmq1); 334 mpz_clear(hptr->iqmp); 335 mpz_clear(hptr->r0); 336 mpz_clear(hptr->r1); 337 mpz_clear(hptr->I0); 338 mpz_clear(hptr->m1); 339 OPENSSL_free(hptr); 340 return NULL; 341 } 342 343static int e_gmp_rsa_finish(RSA *rsa) 344 { 345 E_GMP_RSA_CTX *hptr = RSA_get_ex_data(rsa, hndidx_rsa); 346 if(!hptr) return 0; 347 mpz_clear(hptr->n); 348 mpz_clear(hptr->d); 349 mpz_clear(hptr->e); 350 mpz_clear(hptr->p); 351 mpz_clear(hptr->q); 352 mpz_clear(hptr->dmp1); 353 mpz_clear(hptr->dmq1); 354 mpz_clear(hptr->iqmp); 355 mpz_clear(hptr->r0); 356 mpz_clear(hptr->r1); 357 mpz_clear(hptr->I0); 358 mpz_clear(hptr->m1); 359 OPENSSL_free(hptr); 360 RSA_set_ex_data(rsa, hndidx_rsa, NULL); 361 return 1; 362 } 363 364static int e_gmp_rsa_mod_exp(BIGNUM *r, const BIGNUM *I, RSA *rsa, BN_CTX *ctx) 365 { 366 E_GMP_RSA_CTX *hptr; 367 int to_return = 0; 368 369 hptr = e_gmp_get_rsa(rsa); 370 if(!hptr) 371 { 372 GMPerr(GMP_F_E_GMP_RSA_MOD_EXP, 373 GMP_R_KEY_CONTEXT_ERROR); 374 return 0; 375 } 376 if(hptr->public_only) 377 { 378 GMPerr(GMP_F_E_GMP_RSA_MOD_EXP, 379 GMP_R_MISSING_KEY_COMPONENTS); 380 return 0; 381 } 382 383 /* ugh!!! */ 384 if(!bn2gmp(I, hptr->I0)) 385 return 0; 386 387 /* This is basically the CRT logic in crypto/rsa/rsa_eay.c reworded into 388 * GMP-speak. It may be that GMP's API facilitates cleaner formulations 389 * of this stuff, eg. better handling of negatives, or functions that 390 * combine operations. */ 391 392 mpz_mod(hptr->r1, hptr->I0, hptr->q); 393 mpz_powm(hptr->m1, hptr->r1, hptr->dmq1, hptr->q); 394 395 mpz_mod(hptr->r1, hptr->I0, hptr->p); 396 mpz_powm(hptr->r0, hptr->r1, hptr->dmp1, hptr->p); 397 398 mpz_sub(hptr->r0, hptr->r0, hptr->m1); 399 400 if(mpz_sgn(hptr->r0) < 0) 401 mpz_add(hptr->r0, hptr->r0, hptr->p); 402 mpz_mul(hptr->r1, hptr->r0, hptr->iqmp); 403 mpz_mod(hptr->r0, hptr->r1, hptr->p); 404 405 if(mpz_sgn(hptr->r0) < 0) 406 mpz_add(hptr->r0, hptr->r0, hptr->p); 407 mpz_mul(hptr->r1, hptr->r0, hptr->q); 408 mpz_add(hptr->r0, hptr->r1, hptr->m1); 409 410 /* ugh!!! */ 411 if(gmp2bn(hptr->r0, r)) 412 to_return = 1; 413 414 return 1; 415 } 416#endif 417 418/* This stuff is needed if this ENGINE is being compiled into a self-contained 419 * shared-library. */ 420#ifdef ENGINE_DYNAMIC_SUPPORT 421static int bind_fn(ENGINE *e, const char *id) 422 { 423 if(id && (strcmp(id, engine_e_gmp_id) != 0)) 424 return 0; 425 if(!bind_helper(e)) 426 return 0; 427 return 1; 428 } 429IMPLEMENT_DYNAMIC_CHECK_FN() 430IMPLEMENT_DYNAMIC_BIND_FN(bind_fn) 431#endif /* ENGINE_DYNAMIC_SUPPORT */ 432 433#endif /* !OPENSSL_NO_HW_GMP */ 434#endif /* !OPENSSL_NO_HW */ 435 436