sl@0: /* crypto/bn/bn_ctx.c */ sl@0: /* Written by Ulf Moeller for the OpenSSL project. */ sl@0: /* ==================================================================== sl@0: * Copyright (c) 1998-2004 The OpenSSL Project. All rights reserved. sl@0: * sl@0: * Redistribution and use in source and binary forms, with or without sl@0: * modification, are permitted provided that the following conditions sl@0: * are met: sl@0: * sl@0: * 1. Redistributions of source code must retain the above copyright sl@0: * notice, this list of conditions and the following disclaimer. sl@0: * sl@0: * 2. Redistributions in binary form must reproduce the above copyright sl@0: * notice, this list of conditions and the following disclaimer in sl@0: * the documentation and/or other materials provided with the sl@0: * distribution. sl@0: * sl@0: * 3. All advertising materials mentioning features or use of this sl@0: * software must display the following acknowledgment: sl@0: * "This product includes software developed by the OpenSSL Project sl@0: * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" sl@0: * sl@0: * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to sl@0: * endorse or promote products derived from this software without sl@0: * prior written permission. For written permission, please contact sl@0: * openssl-core@openssl.org. sl@0: * sl@0: * 5. Products derived from this software may not be called "OpenSSL" sl@0: * nor may "OpenSSL" appear in their names without prior written sl@0: * permission of the OpenSSL Project. sl@0: * sl@0: * 6. Redistributions of any form whatsoever must retain the following sl@0: * acknowledgment: sl@0: * "This product includes software developed by the OpenSSL Project sl@0: * for use in the OpenSSL Toolkit (http://www.openssl.org/)" sl@0: * sl@0: * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY sl@0: * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE sl@0: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR sl@0: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR sl@0: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, sl@0: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT sl@0: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; sl@0: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) sl@0: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, sl@0: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) sl@0: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED sl@0: * OF THE POSSIBILITY OF SUCH DAMAGE. sl@0: * ==================================================================== sl@0: * sl@0: * This product includes cryptographic software written by Eric Young sl@0: * (eay@cryptsoft.com). This product includes software written by Tim sl@0: * Hudson (tjh@cryptsoft.com). sl@0: * sl@0: */ sl@0: sl@0: #if !defined(BN_CTX_DEBUG) && !defined(BN_DEBUG) sl@0: #ifndef NDEBUG sl@0: #define NDEBUG sl@0: #endif sl@0: #endif sl@0: sl@0: #include sl@0: #include sl@0: sl@0: #include "cryptlib.h" sl@0: #include "bn_lcl.h" sl@0: sl@0: /* TODO list sl@0: * sl@0: * 1. Check a bunch of "(words+1)" type hacks in various bignum functions and sl@0: * check they can be safely removed. sl@0: * - Check +1 and other ugliness in BN_from_montgomery() sl@0: * sl@0: * 2. Consider allowing a BN_new_ex() that, at least, lets you specify an sl@0: * appropriate 'block' size that will be honoured by bn_expand_internal() to sl@0: * prevent piddly little reallocations. OTOH, profiling bignum expansions in sl@0: * BN_CTX doesn't show this to be a big issue. sl@0: */ sl@0: sl@0: /* How many bignums are in each "pool item"; */ sl@0: #define BN_CTX_POOL_SIZE 16 sl@0: /* The stack frame info is resizing, set a first-time expansion size; */ sl@0: #define BN_CTX_START_FRAMES 32 sl@0: sl@0: /***********/ sl@0: /* BN_POOL */ sl@0: /***********/ sl@0: sl@0: /* A bundle of bignums that can be linked with other bundles */ sl@0: typedef struct bignum_pool_item sl@0: { sl@0: /* The bignum values */ sl@0: BIGNUM vals[BN_CTX_POOL_SIZE]; sl@0: /* Linked-list admin */ sl@0: struct bignum_pool_item *prev, *next; sl@0: } BN_POOL_ITEM; sl@0: /* A linked-list of bignums grouped in bundles */ sl@0: typedef struct bignum_pool sl@0: { sl@0: /* Linked-list admin */ sl@0: BN_POOL_ITEM *head, *current, *tail; sl@0: /* Stack depth and allocation size */ sl@0: unsigned used, size; sl@0: } BN_POOL; sl@0: static void BN_POOL_init(BN_POOL *); sl@0: static void BN_POOL_finish(BN_POOL *); sl@0: #ifndef OPENSSL_NO_DEPRECATED sl@0: static void BN_POOL_reset(BN_POOL *); sl@0: #endif sl@0: static BIGNUM * BN_POOL_get(BN_POOL *); sl@0: static void BN_POOL_release(BN_POOL *, unsigned int); sl@0: sl@0: /************/ sl@0: /* BN_STACK */ sl@0: /************/ sl@0: sl@0: /* A wrapper to manage the "stack frames" */ sl@0: typedef struct bignum_ctx_stack sl@0: { sl@0: /* Array of indexes into the bignum stack */ sl@0: unsigned int *indexes; sl@0: /* Number of stack frames, and the size of the allocated array */ sl@0: unsigned int depth, size; sl@0: } BN_STACK; sl@0: static void BN_STACK_init(BN_STACK *); sl@0: static void BN_STACK_finish(BN_STACK *); sl@0: #ifndef OPENSSL_NO_DEPRECATED sl@0: static void BN_STACK_reset(BN_STACK *); sl@0: #endif sl@0: static int BN_STACK_push(BN_STACK *, unsigned int); sl@0: static unsigned int BN_STACK_pop(BN_STACK *); sl@0: sl@0: /**********/ sl@0: /* BN_CTX */ sl@0: /**********/ sl@0: sl@0: /* The opaque BN_CTX type */ sl@0: struct bignum_ctx sl@0: { sl@0: /* The bignum bundles */ sl@0: BN_POOL pool; sl@0: /* The "stack frames", if you will */ sl@0: BN_STACK stack; sl@0: /* The number of bignums currently assigned */ sl@0: unsigned int used; sl@0: /* Depth of stack overflow */ sl@0: int err_stack; sl@0: /* Block "gets" until an "end" (compatibility behaviour) */ sl@0: int too_many; sl@0: }; sl@0: sl@0: /* Enable this to find BN_CTX bugs */ sl@0: #ifdef BN_CTX_DEBUG sl@0: static const char *ctxdbg_cur = NULL; sl@0: static void ctxdbg(BN_CTX *ctx) sl@0: { sl@0: unsigned int bnidx = 0, fpidx = 0; sl@0: BN_POOL_ITEM *item = ctx->pool.head; sl@0: BN_STACK *stack = &ctx->stack; sl@0: fprintf(stderr,"(%08x): ", (unsigned int)ctx); sl@0: while(bnidx < ctx->used) sl@0: { sl@0: fprintf(stderr,"%02x ", item->vals[bnidx++ % BN_CTX_POOL_SIZE].dmax); sl@0: if(!(bnidx % BN_CTX_POOL_SIZE)) sl@0: item = item->next; sl@0: } sl@0: fprintf(stderr,"\n"); sl@0: bnidx = 0; sl@0: fprintf(stderr," : "); sl@0: while(fpidx < stack->depth) sl@0: { sl@0: while(bnidx++ < stack->indexes[fpidx]) sl@0: fprintf(stderr," "); sl@0: fprintf(stderr,"^^ "); sl@0: bnidx++; sl@0: fpidx++; sl@0: } sl@0: fprintf(stderr,"\n"); sl@0: } sl@0: #define CTXDBG_ENTRY(str, ctx) do { \ sl@0: ctxdbg_cur = (str); \ sl@0: fprintf(stderr,"Starting %s\n", ctxdbg_cur); \ sl@0: ctxdbg(ctx); \ sl@0: } while(0) sl@0: #define CTXDBG_EXIT(ctx) do { \ sl@0: fprintf(stderr,"Ending %s\n", ctxdbg_cur); \ sl@0: ctxdbg(ctx); \ sl@0: } while(0) sl@0: #define CTXDBG_RET(ctx,ret) sl@0: #else sl@0: #define CTXDBG_ENTRY(str, ctx) sl@0: #define CTXDBG_EXIT(ctx) sl@0: #define CTXDBG_RET(ctx,ret) sl@0: #endif sl@0: sl@0: /* This function is an evil legacy and should not be used. This implementation sl@0: * is WYSIWYG, though I've done my best. */ sl@0: #ifndef OPENSSL_NO_DEPRECATED sl@0: EXPORT_C void BN_CTX_init(BN_CTX *ctx) sl@0: { sl@0: /* Assume the caller obtained the context via BN_CTX_new() and so is sl@0: * trying to reset it for use. Nothing else makes sense, least of all sl@0: * binary compatibility from a time when they could declare a static sl@0: * variable. */ sl@0: BN_POOL_reset(&ctx->pool); sl@0: BN_STACK_reset(&ctx->stack); sl@0: ctx->used = 0; sl@0: ctx->err_stack = 0; sl@0: ctx->too_many = 0; sl@0: } sl@0: #endif sl@0: sl@0: EXPORT_C BN_CTX *BN_CTX_new(void) sl@0: { sl@0: BN_CTX *ret = OPENSSL_malloc(sizeof(BN_CTX)); sl@0: if(!ret) sl@0: { sl@0: BNerr(BN_F_BN_CTX_NEW,ERR_R_MALLOC_FAILURE); sl@0: return NULL; sl@0: } sl@0: /* Initialise the structure */ sl@0: BN_POOL_init(&ret->pool); sl@0: BN_STACK_init(&ret->stack); sl@0: ret->used = 0; sl@0: ret->err_stack = 0; sl@0: ret->too_many = 0; sl@0: return ret; sl@0: } sl@0: sl@0: EXPORT_C void BN_CTX_free(BN_CTX *ctx) sl@0: { sl@0: if (ctx == NULL) sl@0: return; sl@0: #ifdef BN_CTX_DEBUG sl@0: { sl@0: BN_POOL_ITEM *pool = ctx->pool.head; sl@0: fprintf(stderr,"BN_CTX_free, stack-size=%d, pool-bignums=%d\n", sl@0: ctx->stack.size, ctx->pool.size); sl@0: fprintf(stderr,"dmaxs: "); sl@0: while(pool) { sl@0: unsigned loop = 0; sl@0: while(loop < BN_CTX_POOL_SIZE) sl@0: fprintf(stderr,"%02x ", pool->vals[loop++].dmax); sl@0: pool = pool->next; sl@0: } sl@0: fprintf(stderr,"\n"); sl@0: } sl@0: #endif sl@0: BN_STACK_finish(&ctx->stack); sl@0: BN_POOL_finish(&ctx->pool); sl@0: OPENSSL_free(ctx); sl@0: } sl@0: sl@0: EXPORT_C void BN_CTX_start(BN_CTX *ctx) sl@0: { sl@0: CTXDBG_ENTRY("BN_CTX_start", ctx); sl@0: /* If we're already overflowing ... */ sl@0: if(ctx->err_stack || ctx->too_many) sl@0: ctx->err_stack++; sl@0: /* (Try to) get a new frame pointer */ sl@0: else if(!BN_STACK_push(&ctx->stack, ctx->used)) sl@0: { sl@0: BNerr(BN_F_BN_CTX_START,BN_R_TOO_MANY_TEMPORARY_VARIABLES); sl@0: ctx->err_stack++; sl@0: } sl@0: CTXDBG_EXIT(ctx); sl@0: } sl@0: sl@0: EXPORT_C void BN_CTX_end(BN_CTX *ctx) sl@0: { sl@0: CTXDBG_ENTRY("BN_CTX_end", ctx); sl@0: if(ctx->err_stack) sl@0: ctx->err_stack--; sl@0: else sl@0: { sl@0: unsigned int fp = BN_STACK_pop(&ctx->stack); sl@0: /* Does this stack frame have anything to release? */ sl@0: if(fp < ctx->used) sl@0: BN_POOL_release(&ctx->pool, ctx->used - fp); sl@0: ctx->used = fp; sl@0: /* Unjam "too_many" in case "get" had failed */ sl@0: ctx->too_many = 0; sl@0: } sl@0: CTXDBG_EXIT(ctx); sl@0: } sl@0: sl@0: EXPORT_C BIGNUM *BN_CTX_get(BN_CTX *ctx) sl@0: { sl@0: BIGNUM *ret; sl@0: CTXDBG_ENTRY("BN_CTX_get", ctx); sl@0: if(ctx->err_stack || ctx->too_many) return NULL; sl@0: if((ret = BN_POOL_get(&ctx->pool)) == NULL) sl@0: { sl@0: /* Setting too_many prevents repeated "get" attempts from sl@0: * cluttering the error stack. */ sl@0: ctx->too_many = 1; sl@0: BNerr(BN_F_BN_CTX_GET,BN_R_TOO_MANY_TEMPORARY_VARIABLES); sl@0: return NULL; sl@0: } sl@0: /* OK, make sure the returned bignum is "zero" */ sl@0: BN_zero(ret); sl@0: ctx->used++; sl@0: CTXDBG_RET(ctx, ret); sl@0: return ret; sl@0: } sl@0: sl@0: /************/ sl@0: /* BN_STACK */ sl@0: /************/ sl@0: sl@0: static void BN_STACK_init(BN_STACK *st) sl@0: { sl@0: st->indexes = NULL; sl@0: st->depth = st->size = 0; sl@0: } sl@0: sl@0: static void BN_STACK_finish(BN_STACK *st) sl@0: { sl@0: if(st->size) OPENSSL_free(st->indexes); sl@0: } sl@0: sl@0: #ifndef OPENSSL_NO_DEPRECATED sl@0: static void BN_STACK_reset(BN_STACK *st) sl@0: { sl@0: st->depth = 0; sl@0: } sl@0: #endif sl@0: sl@0: static int BN_STACK_push(BN_STACK *st, unsigned int idx) sl@0: { sl@0: if(st->depth == st->size) sl@0: /* Need to expand */ sl@0: { sl@0: unsigned int newsize = (st->size ? sl@0: (st->size * 3 / 2) : BN_CTX_START_FRAMES); sl@0: unsigned int *newitems = OPENSSL_malloc(newsize * sl@0: sizeof(unsigned int)); sl@0: if(!newitems) return 0; sl@0: if(st->depth) sl@0: memcpy(newitems, st->indexes, st->depth * sl@0: sizeof(unsigned int)); sl@0: if(st->size) OPENSSL_free(st->indexes); sl@0: st->indexes = newitems; sl@0: st->size = newsize; sl@0: } sl@0: st->indexes[(st->depth)++] = idx; sl@0: return 1; sl@0: } sl@0: sl@0: static unsigned int BN_STACK_pop(BN_STACK *st) sl@0: { sl@0: return st->indexes[--(st->depth)]; sl@0: } sl@0: sl@0: /***********/ sl@0: /* BN_POOL */ sl@0: /***********/ sl@0: sl@0: static void BN_POOL_init(BN_POOL *p) sl@0: { sl@0: p->head = p->current = p->tail = NULL; sl@0: p->used = p->size = 0; sl@0: } sl@0: sl@0: static void BN_POOL_finish(BN_POOL *p) sl@0: { sl@0: while(p->head) sl@0: { sl@0: unsigned int loop = 0; sl@0: BIGNUM *bn = p->head->vals; sl@0: while(loop++ < BN_CTX_POOL_SIZE) sl@0: { sl@0: if(bn->d) BN_clear_free(bn); sl@0: bn++; sl@0: } sl@0: p->current = p->head->next; sl@0: OPENSSL_free(p->head); sl@0: p->head = p->current; sl@0: } sl@0: } sl@0: sl@0: #ifndef OPENSSL_NO_DEPRECATED sl@0: static void BN_POOL_reset(BN_POOL *p) sl@0: { sl@0: BN_POOL_ITEM *item = p->head; sl@0: while(item) sl@0: { sl@0: unsigned int loop = 0; sl@0: BIGNUM *bn = item->vals; sl@0: while(loop++ < BN_CTX_POOL_SIZE) sl@0: { sl@0: if(bn->d) BN_clear(bn); sl@0: bn++; sl@0: } sl@0: item = item->next; sl@0: } sl@0: p->current = p->head; sl@0: p->used = 0; sl@0: } sl@0: #endif sl@0: sl@0: static BIGNUM *BN_POOL_get(BN_POOL *p) sl@0: { sl@0: if(p->used == p->size) sl@0: { sl@0: BIGNUM *bn; sl@0: unsigned int loop = 0; sl@0: BN_POOL_ITEM *item = OPENSSL_malloc(sizeof(BN_POOL_ITEM)); sl@0: if(!item) return NULL; sl@0: /* Initialise the structure */ sl@0: bn = item->vals; sl@0: while(loop++ < BN_CTX_POOL_SIZE) sl@0: BN_init(bn++); sl@0: item->prev = p->tail; sl@0: item->next = NULL; sl@0: /* Link it in */ sl@0: if(!p->head) sl@0: p->head = p->current = p->tail = item; sl@0: else sl@0: { sl@0: p->tail->next = item; sl@0: p->tail = item; sl@0: p->current = item; sl@0: } sl@0: p->size += BN_CTX_POOL_SIZE; sl@0: p->used++; sl@0: /* Return the first bignum from the new pool */ sl@0: return item->vals; sl@0: } sl@0: if(!p->used) sl@0: p->current = p->head; sl@0: else if((p->used % BN_CTX_POOL_SIZE) == 0) sl@0: p->current = p->current->next; sl@0: return p->current->vals + ((p->used++) % BN_CTX_POOL_SIZE); sl@0: } sl@0: sl@0: static void BN_POOL_release(BN_POOL *p, unsigned int num) sl@0: { sl@0: unsigned int offset = (p->used - 1) % BN_CTX_POOL_SIZE; sl@0: p->used -= num; sl@0: while(num--) sl@0: { sl@0: bn_check_top(p->current->vals + offset); sl@0: if(!offset) sl@0: { sl@0: offset = BN_CTX_POOL_SIZE - 1; sl@0: p->current = p->current->prev; sl@0: } sl@0: else sl@0: offset--; sl@0: } sl@0: } sl@0: