660e626a916e5f31bf7683643ac95cd91d5082d9
[openssl.git] / crypto / bn / bn_ctx.c
1 /* crypto/bn/bn_ctx.c */
2 /* Written by Ulf Moeller for the OpenSSL project. */
3 /* ====================================================================
4  * Copyright (c) 1998-2004 The OpenSSL Project.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  *
18  * 3. All advertising materials mentioning features or use of this
19  *    software must display the following acknowledgment:
20  *    "This product includes software developed by the OpenSSL Project
21  *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
22  *
23  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
24  *    endorse or promote products derived from this software without
25  *    prior written permission. For written permission, please contact
26  *    openssl-core@openssl.org.
27  *
28  * 5. Products derived from this software may not be called "OpenSSL"
29  *    nor may "OpenSSL" appear in their names without prior written
30  *    permission of the OpenSSL Project.
31  *
32  * 6. Redistributions of any form whatsoever must retain the following
33  *    acknowledgment:
34  *    "This product includes software developed by the OpenSSL Project
35  *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
36  *
37  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
38  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
39  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
40  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
41  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
43  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
44  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
45  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
46  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
47  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
48  * OF THE POSSIBILITY OF SUCH DAMAGE.
49  * ====================================================================
50  *
51  * This product includes cryptographic software written by Eric Young
52  * (eay@cryptsoft.com).  This product includes software written by Tim
53  * Hudson (tjh@cryptsoft.com).
54  *
55  */
56
57 #if !defined(BN_CTX_DEBUG) && !defined(BN_DEBUG)
58 # ifndef NDEBUG
59 #  define NDEBUG
60 # endif
61 #endif
62
63 #include <assert.h>
64
65 #include "internal/cryptlib.h"
66 #include "bn_lcl.h"
67
68 /*-
69  * TODO list
70  *
71  * 1. Check a bunch of "(words+1)" type hacks in various bignum functions and
72  * check they can be safely removed.
73  *  - Check +1 and other ugliness in BN_from_montgomery()
74  *
75  * 2. Consider allowing a BN_new_ex() that, at least, lets you specify an
76  * appropriate 'block' size that will be honoured by bn_expand_internal() to
77  * prevent piddly little reallocations. OTOH, profiling bignum expansions in
78  * BN_CTX doesn't show this to be a big issue.
79  */
80
81 /* How many bignums are in each "pool item"; */
82 #define BN_CTX_POOL_SIZE        16
83 /* The stack frame info is resizing, set a first-time expansion size; */
84 #define BN_CTX_START_FRAMES     32
85
86 /***********/
87 /* BN_POOL */
88 /***********/
89
90 /* A bundle of bignums that can be linked with other bundles */
91 typedef struct bignum_pool_item {
92     /* The bignum values */
93     BIGNUM vals[BN_CTX_POOL_SIZE];
94     /* Linked-list admin */
95     struct bignum_pool_item *prev, *next;
96 } BN_POOL_ITEM;
97 /* A linked-list of bignums grouped in bundles */
98 typedef struct bignum_pool {
99     /* Linked-list admin */
100     BN_POOL_ITEM *head, *current, *tail;
101     /* Stack depth and allocation size */
102     unsigned used, size;
103 } BN_POOL;
104 static void BN_POOL_init(BN_POOL *);
105 static void BN_POOL_finish(BN_POOL *);
106 static BIGNUM *BN_POOL_get(BN_POOL *, int);
107 static void BN_POOL_release(BN_POOL *, unsigned int);
108
109 /************/
110 /* BN_STACK */
111 /************/
112
113 /* A wrapper to manage the "stack frames" */
114 typedef struct bignum_ctx_stack {
115     /* Array of indexes into the bignum stack */
116     unsigned int *indexes;
117     /* Number of stack frames, and the size of the allocated array */
118     unsigned int depth, size;
119 } BN_STACK;
120 static void BN_STACK_init(BN_STACK *);
121 static void BN_STACK_finish(BN_STACK *);
122 static int BN_STACK_push(BN_STACK *, unsigned int);
123 static unsigned int BN_STACK_pop(BN_STACK *);
124
125 /**********/
126 /* BN_CTX */
127 /**********/
128
129 /* The opaque BN_CTX type */
130 struct bignum_ctx {
131     /* The bignum bundles */
132     BN_POOL pool;
133     /* The "stack frames", if you will */
134     BN_STACK stack;
135     /* The number of bignums currently assigned */
136     unsigned int used;
137     /* Depth of stack overflow */
138     int err_stack;
139     /* Block "gets" until an "end" (compatibility behaviour) */
140     int too_many;
141     /* Flags. */
142     int flags;
143 };
144
145 /* Enable this to find BN_CTX bugs */
146 #ifdef BN_CTX_DEBUG
147 static const char *ctxdbg_cur = NULL;
148 static void ctxdbg(BN_CTX *ctx)
149 {
150     unsigned int bnidx = 0, fpidx = 0;
151     BN_POOL_ITEM *item = ctx->pool.head;
152     BN_STACK *stack = &ctx->stack;
153     fprintf(stderr, "(%16p): ", ctx);
154     while (bnidx < ctx->used) {
155         fprintf(stderr, "%03x ", item->vals[bnidx++ % BN_CTX_POOL_SIZE].dmax);
156         if (!(bnidx % BN_CTX_POOL_SIZE))
157             item = item->next;
158     }
159     fprintf(stderr, "\n");
160     bnidx = 0;
161     fprintf(stderr, "          : ");
162     while (fpidx < stack->depth) {
163         while (bnidx++ < stack->indexes[fpidx])
164             fprintf(stderr, "    ");
165         fprintf(stderr, "^^^ ");
166         bnidx++;
167         fpidx++;
168     }
169     fprintf(stderr, "\n");
170 }
171
172 # define CTXDBG_ENTRY(str, ctx)  do { \
173                                 ctxdbg_cur = (str); \
174                                 fprintf(stderr,"Starting %s\n", ctxdbg_cur); \
175                                 ctxdbg(ctx); \
176                                 } while(0)
177 # define CTXDBG_EXIT(ctx)        do { \
178                                 fprintf(stderr,"Ending %s\n", ctxdbg_cur); \
179                                 ctxdbg(ctx); \
180                                 } while(0)
181 # define CTXDBG_RET(ctx,ret)
182 #else
183 # define CTXDBG_ENTRY(str, ctx)
184 # define CTXDBG_EXIT(ctx)
185 # define CTXDBG_RET(ctx,ret)
186 #endif
187
188
189 BN_CTX *BN_CTX_new(void)
190 {
191     BN_CTX *ret;
192
193     if ((ret = OPENSSL_malloc(sizeof(*ret))) == NULL) {
194         BNerr(BN_F_BN_CTX_NEW, ERR_R_MALLOC_FAILURE);
195         return NULL;
196     }
197     /* Initialise the structure */
198     BN_POOL_init(&ret->pool);
199     BN_STACK_init(&ret->stack);
200     ret->used = 0;
201     ret->err_stack = 0;
202     ret->too_many = 0;
203     ret->flags = 0;
204     return ret;
205 }
206
207 BN_CTX *BN_CTX_secure_new(void)
208 {
209     BN_CTX *ret = BN_CTX_new();
210
211     if (ret)
212         ret->flags = BN_FLG_SECURE;
213     return ret;
214 }
215
216 void BN_CTX_free(BN_CTX *ctx)
217 {
218     if (ctx == NULL)
219         return;
220 #ifdef BN_CTX_DEBUG
221     {
222         BN_POOL_ITEM *pool = ctx->pool.head;
223         fprintf(stderr, "BN_CTX_free, stack-size=%d, pool-bignums=%d\n",
224                 ctx->stack.size, ctx->pool.size);
225         fprintf(stderr, "dmaxs: ");
226         while (pool) {
227             unsigned loop = 0;
228             while (loop < BN_CTX_POOL_SIZE)
229                 fprintf(stderr, "%02x ", pool->vals[loop++].dmax);
230             pool = pool->next;
231         }
232         fprintf(stderr, "\n");
233     }
234 #endif
235     BN_STACK_finish(&ctx->stack);
236     BN_POOL_finish(&ctx->pool);
237     OPENSSL_free(ctx);
238 }
239
240 void BN_CTX_start(BN_CTX *ctx)
241 {
242     CTXDBG_ENTRY("BN_CTX_start", ctx);
243     /* If we're already overflowing ... */
244     if (ctx->err_stack || ctx->too_many)
245         ctx->err_stack++;
246     /* (Try to) get a new frame pointer */
247     else if (!BN_STACK_push(&ctx->stack, ctx->used)) {
248         BNerr(BN_F_BN_CTX_START, BN_R_TOO_MANY_TEMPORARY_VARIABLES);
249         ctx->err_stack++;
250     }
251     CTXDBG_EXIT(ctx);
252 }
253
254 void BN_CTX_end(BN_CTX *ctx)
255 {
256     CTXDBG_ENTRY("BN_CTX_end", ctx);
257     if (ctx->err_stack)
258         ctx->err_stack--;
259     else {
260         unsigned int fp = BN_STACK_pop(&ctx->stack);
261         /* Does this stack frame have anything to release? */
262         if (fp < ctx->used)
263             BN_POOL_release(&ctx->pool, ctx->used - fp);
264         ctx->used = fp;
265         /* Unjam "too_many" in case "get" had failed */
266         ctx->too_many = 0;
267     }
268     CTXDBG_EXIT(ctx);
269 }
270
271 BIGNUM *BN_CTX_get(BN_CTX *ctx)
272 {
273     BIGNUM *ret;
274
275     CTXDBG_ENTRY("BN_CTX_get", ctx);
276     if (ctx->err_stack || ctx->too_many)
277         return NULL;
278     if ((ret = BN_POOL_get(&ctx->pool, ctx->flags)) == NULL) {
279         /*
280          * Setting too_many prevents repeated "get" attempts from cluttering
281          * the error stack.
282          */
283         ctx->too_many = 1;
284         BNerr(BN_F_BN_CTX_GET, BN_R_TOO_MANY_TEMPORARY_VARIABLES);
285         return NULL;
286     }
287     /* OK, make sure the returned bignum is "zero" */
288     BN_zero(ret);
289     ctx->used++;
290     CTXDBG_RET(ctx, ret);
291     return ret;
292 }
293
294 /************/
295 /* BN_STACK */
296 /************/
297
298 static void BN_STACK_init(BN_STACK *st)
299 {
300     st->indexes = NULL;
301     st->depth = st->size = 0;
302 }
303
304 static void BN_STACK_finish(BN_STACK *st)
305 {
306     OPENSSL_free(st->indexes);
307     st->indexes = NULL;
308 }
309
310
311 static int BN_STACK_push(BN_STACK *st, unsigned int idx)
312 {
313     if (st->depth == st->size) {
314         /* Need to expand */
315         unsigned int newsize =
316             st->size ? (st->size * 3 / 2) : BN_CTX_START_FRAMES;
317         unsigned int *newitems = OPENSSL_malloc(sizeof(*newitems) * newsize);
318         if (newitems == NULL)
319             return 0;
320         if (st->depth)
321             memcpy(newitems, st->indexes, sizeof(*newitems) * st->depth);
322         OPENSSL_free(st->indexes);
323         st->indexes = newitems;
324         st->size = newsize;
325     }
326     st->indexes[(st->depth)++] = idx;
327     return 1;
328 }
329
330 static unsigned int BN_STACK_pop(BN_STACK *st)
331 {
332     return st->indexes[--(st->depth)];
333 }
334
335 /***********/
336 /* BN_POOL */
337 /***********/
338
339 static void BN_POOL_init(BN_POOL *p)
340 {
341     p->head = p->current = p->tail = NULL;
342     p->used = p->size = 0;
343 }
344
345 static void BN_POOL_finish(BN_POOL *p)
346 {
347     unsigned int loop;
348     BIGNUM *bn;
349
350     while (p->head) {
351         for (loop = 0, bn = p->head->vals; loop++ < BN_CTX_POOL_SIZE; bn++)
352             if (bn->d)
353                 BN_clear_free(bn);
354         p->current = p->head->next;
355         OPENSSL_free(p->head);
356         p->head = p->current;
357     }
358 }
359
360
361 static BIGNUM *BN_POOL_get(BN_POOL *p, int flag)
362 {
363     BIGNUM *bn;
364     unsigned int loop;
365
366     /* Full; allocate a new pool item and link it in. */
367     if (p->used == p->size) {
368         BN_POOL_ITEM *item = OPENSSL_malloc(sizeof(*item));
369         if (item == NULL)
370             return NULL;
371         for (loop = 0, bn = item->vals; loop++ < BN_CTX_POOL_SIZE; bn++) {
372             BN_init(bn);
373             if ((flag & BN_FLG_SECURE) != 0)
374                 BN_set_flags(bn, BN_FLG_SECURE);
375         }
376         item->prev = p->tail;
377         item->next = NULL;
378
379         if (p->head == NULL)
380             p->head = p->current = p->tail = item;
381         else {
382             p->tail->next = item;
383             p->tail = item;
384             p->current = item;
385         }
386         p->size += BN_CTX_POOL_SIZE;
387         p->used++;
388         /* Return the first bignum from the new pool */
389         return item->vals;
390     }
391
392     if (!p->used)
393         p->current = p->head;
394     else if ((p->used % BN_CTX_POOL_SIZE) == 0)
395         p->current = p->current->next;
396     return p->current->vals + ((p->used++) % BN_CTX_POOL_SIZE);
397 }
398
399 static void BN_POOL_release(BN_POOL *p, unsigned int num)
400 {
401     unsigned int offset = (p->used - 1) % BN_CTX_POOL_SIZE;
402
403     p->used -= num;
404     while (num--) {
405         bn_check_top(p->current->vals + offset);
406         if (offset == 0) {
407             offset = BN_CTX_POOL_SIZE - 1;
408             p->current = p->current->prev;
409         } else
410             offset--;
411     }
412 }