Allow EC_GROUP objects to share precomputation for improved memory
[openssl.git] / crypto / ec / ec_lib.c
index 9669f42aab94ebe10791c773236a1b209fc05d3f..c00875cd73e9483ea3b0fc9cd2b827fdbdb502b0 100644 (file)
@@ -3,7 +3,7 @@
  * Originally written by Bodo Moeller for the OpenSSL project.
  */
 /* ====================================================================
- * Copyright (c) 1998-2002 The OpenSSL Project.  All rights reserved.
+ * Copyright (c) 1998-2003 The OpenSSL Project.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -98,9 +98,6 @@ EC_GROUP *EC_GROUP_new(const EC_METHOD *meth)
        ret->meth = meth;
 
        ret->extra_data = NULL;
-       ret->extra_data_dup_func = 0;
-       ret->extra_data_free_func = 0;
-       ret->extra_data_clear_free_func = 0;
 
        ret->generator = NULL;
        BN_init(&ret->order);
@@ -130,7 +127,7 @@ void EC_GROUP_free(EC_GROUP *group)
        if (group->meth->group_finish != 0)
                group->meth->group_finish(group);
 
-       EC_GROUP_free_extra_data(group);
+       EC_GROUP_free_all_extra_data(group);
 
        if (group->generator != NULL)
                EC_POINT_free(group->generator);
@@ -153,7 +150,7 @@ void EC_GROUP_clear_free(EC_GROUP *group)
        else if (group->meth != NULL && group->meth->group_finish != 0)
                group->meth->group_finish(group);
 
-       EC_GROUP_clear_free_extra_data(group);
+       EC_GROUP_clear_free_all_extra_data(group);
 
        if (group->generator != NULL)
                EC_POINT_clear_free(group->generator);
@@ -162,17 +159,19 @@ void EC_GROUP_clear_free(EC_GROUP *group)
 
        if (group->seed)
                {
-               memset(group->seed, 0, group->seed_len);
+               OPENSSL_cleanse(group->seed, group->seed_len);
                OPENSSL_free(group->seed);
                }
 
-       memset(group, 0, sizeof *group);
+       OPENSSL_cleanse(group, sizeof *group);
        OPENSSL_free(group);
        }
 
 
 int EC_GROUP_copy(EC_GROUP *dest, const EC_GROUP *src)
        {
+       EC_EXTRA_DATA *d;
+
        if (dest->meth->group_copy == 0)
                {
                ECerr(EC_F_EC_GROUP_COPY, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
@@ -186,19 +185,16 @@ int EC_GROUP_copy(EC_GROUP *dest, const EC_GROUP *src)
        if (dest == src)
                return 1;
        
-       EC_GROUP_clear_free_extra_data(dest);
-       if (src->extra_data_dup_func)
-               {
-               if (src->extra_data != NULL)
-                       {
-                       dest->extra_data = src->extra_data_dup_func(src->extra_data);
-                       if (dest->extra_data == NULL)
-                               return 0;
-                       }
+       EC_GROUP_free_all_extra_data(dest);
 
-               dest->extra_data_dup_func = src->extra_data_dup_func;
-               dest->extra_data_free_func = src->extra_data_free_func;
-               dest->extra_data_clear_free_func = src->extra_data_clear_free_func;
+       for (d = src->extra_data; d != NULL; d = d->next)
+               {
+               void *t = d->dup_func(d->data);
+               
+               if (t == NULL)
+                       return 0;
+               if (!EC_GROUP_set_extra_data(dest, t, d->dup_func, d->free_func, d->clear_free_func))
+                       return 0;
                }
 
        if (src->generator != NULL)
@@ -475,65 +471,148 @@ int EC_GROUP_check_discriminant(const EC_GROUP *group, BN_CTX *ctx)
 
 
 /* this has 'package' visibility */
-int EC_GROUP_set_extra_data(EC_GROUP *group, void *extra_data, void *(*extra_data_dup_func)(void *),
-       void (*extra_data_free_func)(void *), void (*extra_data_clear_free_func)(void *))
+int EC_GROUP_set_extra_data(EC_GROUP *group, void *data,
+       void *(*dup_func)(void *), void (*free_func)(void *), void (*clear_free_func)(void *))
        {
-       if ((group->extra_data != NULL)
-               || (group->extra_data_dup_func != 0)
-               || (group->extra_data_free_func != 0)
-               || (group->extra_data_clear_free_func != 0))
-               {
-               ECerr(EC_F_EC_GROUP_SET_EXTRA_DATA, EC_R_SLOT_FULL);
+       EC_EXTRA_DATA *d;
+
+       if (group == NULL)
                return 0;
+
+       for (d = group->extra_data; d != NULL; d = d->next)
+               {
+               if (d->dup_func == dup_func && d->free_func == free_func && d->clear_free_func == clear_free_func)
+                       {
+                       ECerr(EC_F_EC_GROUP_SET_EXTRA_DATA, EC_R_SLOT_FULL);
+                       return 0;
+                       }
                }
 
-       group->extra_data = extra_data;
-       group->extra_data_dup_func = extra_data_dup_func;
-       group->extra_data_free_func = extra_data_free_func;
-       group->extra_data_clear_free_func = extra_data_clear_free_func;
+       if (data == NULL)
+               /* no explicit entry needed */
+               return 1;
+
+       d = OPENSSL_malloc(sizeof *d);
+       if (d == NULL)
+               return 0;
+
+       d->data = data;
+       d->dup_func = dup_func;
+       d->free_func = free_func;
+       d->clear_free_func = clear_free_func;
+
+       d->next = group->extra_data;
+       group->extra_data = d;
+
        return 1;
        }
 
-
 /* this has 'package' visibility */
-void *EC_GROUP_get_extra_data(const EC_GROUP *group, void *(*extra_data_dup_func)(void *),
-       void (*extra_data_free_func)(void *), void (*extra_data_clear_free_func)(void *))
+void *EC_GROUP_get_extra_data(const EC_GROUP *group,
+       void *(*dup_func)(void *), void (*free_func)(void *), void (*clear_free_func)(void *))
        {
-       if ((group->extra_data_dup_func != extra_data_dup_func)
-               || (group->extra_data_free_func != extra_data_free_func)
-               || (group->extra_data_clear_free_func != extra_data_clear_free_func))
-               {
-               ECerr(EC_F_EC_GROUP_GET_EXTRA_DATA, EC_R_NO_SUCH_EXTRA_DATA);
+       EC_EXTRA_DATA *d;
+
+       if (group == NULL)
                return NULL;
+
+       for (d = group->extra_data; d != NULL; d = d->next)
+               {
+               if (d->dup_func == dup_func && d->free_func == free_func && d->clear_free_func == clear_free_func)
+                       return d->data;
                }
+       
+       return NULL;
+       }
 
-       return group->extra_data;
+/* this has 'package' visibility */
+void EC_GROUP_free_extra_data(EC_GROUP *group,
+       void *(*dup_func)(void *), void (*free_func)(void *), void (*clear_free_func)(void *))
+       {
+       EC_EXTRA_DATA **p;
+
+       if (group == NULL)
+               return;
+
+       for (p = &group->extra_data; *p != NULL; p = &((*p)->next))
+               {
+               if ((*p)->dup_func == dup_func && (*p)->free_func == free_func && (*p)->clear_free_func == clear_free_func)
+                       {
+                       EC_EXTRA_DATA *next = (*p)->next;
+
+                       (*p)->free_func((*p)->data);
+                       OPENSSL_free(*p);
+                       
+                       *p = next;
+                       return;
+                       }
+               }
        }
 
+/* this has 'package' visibility */
+void EC_GROUP_clear_free_extra_data(EC_GROUP *group,
+       void *(*dup_func)(void *), void (*free_func)(void *), void (*clear_free_func)(void *))
+       {
+       EC_EXTRA_DATA **p;
+
+       if (group == NULL)
+               return;
+
+       for (p = &group->extra_data; *p != NULL; p = &((*p)->next))
+               {
+               if ((*p)->dup_func == dup_func && (*p)->free_func == free_func && (*p)->clear_free_func == clear_free_func)
+                       {
+                       EC_EXTRA_DATA *next = (*p)->next;
+
+                       (*p)->clear_free_func((*p)->data);
+                       OPENSSL_free(*p);
+                       
+                       *p = next;
+                       return;
+                       }
+               }
+       }
 
 /* this has 'package' visibility */
-void EC_GROUP_free_extra_data(EC_GROUP *group)
+void EC_GROUP_free_all_extra_data(EC_GROUP *group)
        {
-       if (group->extra_data_free_func)
-               group->extra_data_free_func(group->extra_data);
+       EC_EXTRA_DATA *d;
+
+       if (group == NULL)
+               return;
+
+       d = group->extra_data;
+       while (d)
+               {
+               EC_EXTRA_DATA *next = d->next;
+               
+               d->free_func(d->data);
+               OPENSSL_free(d);
+               
+               d = next;
+               }
        group->extra_data = NULL;
-       group->extra_data_dup_func = 0;
-       group->extra_data_free_func = 0;
-       group->extra_data_clear_free_func = 0;
        }
 
-
 /* this has 'package' visibility */
-void EC_GROUP_clear_free_extra_data(EC_GROUP *group)
+void EC_GROUP_clear_free_all_extra_data(EC_GROUP *group)
        {
-       if (group->extra_data_clear_free_func)
-               group->extra_data_clear_free_func(group->extra_data);
-       else if (group->extra_data_free_func)
-               group->extra_data_free_func(group->extra_data);
+       EC_EXTRA_DATA *d;
+
+       if (group == NULL)
+               return;
+
+       d = group->extra_data;
+       while (d)
+               {
+               EC_EXTRA_DATA *next = d->next;
+               
+               d->clear_free_func(d->data);
+               OPENSSL_free(d);
+               
+               d = next;
+               }
        group->extra_data = NULL;
-       group->extra_data_dup_func = 0;
-       group->extra_data_free_func = 0;
-       group->extra_data_clear_free_func = 0;
        }
 
 
@@ -591,7 +670,7 @@ void EC_POINT_clear_free(EC_POINT *point)
                point->meth->point_clear_finish(point);
        else if (point->meth != NULL && point->meth->point_finish != 0)
                point->meth->point_finish(point);
-       memset(point, 0, sizeof *point);
+       OPENSSL_cleanse(point, sizeof *point);
        OPENSSL_free(point);
        }
 
@@ -956,3 +1035,58 @@ int EC_POINTs_make_affine(const EC_GROUP *group, size_t num, EC_POINT *points[],
                }
        return group->meth->points_make_affine(group, num, points, ctx);
        }
+
+
+/* Functions for point multiplication.
+ *
+ * If group->meth->mul is 0, we use the wNAF-based implementations in ec_mult.c;
+ * otherwise we dispatch through methods.
+ */
+
+int EC_POINTs_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *scalar,
+       size_t num, const EC_POINT *points[], const BIGNUM *scalars[], BN_CTX *ctx)
+       {
+       if (group->meth->mul == 0)
+               /* use default */
+               return ec_wNAF_mul(group, r, scalar, num, points, scalars, ctx);
+
+       return group->meth->mul(group, r, scalar, num, points, scalars, ctx);
+       }
+
+int EC_POINT_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *g_scalar,
+       const EC_POINT *point, const BIGNUM *p_scalar, BN_CTX *ctx)
+       {
+       /* just a convenient interface to EC_POINTs_mul() */
+
+       const EC_POINT *points[1];
+       const BIGNUM *scalars[1];
+
+       points[0] = point;
+       scalars[0] = p_scalar;
+
+       return EC_POINTs_mul(group, r, g_scalar, (point != NULL && p_scalar != NULL), points, scalars, ctx);
+       }
+
+int EC_GROUP_precompute_mult(EC_GROUP *group, BN_CTX *ctx)
+       {
+       if (group->meth->mul == 0)
+               /* use default */
+               return ec_wNAF_precompute_mult(group, ctx);
+
+       if (group->meth->precompute_mult != 0)
+               return group->meth->precompute_mult(group, ctx);
+       else
+               return 1; /* nothing to do, so report success */
+       }
+
+int EC_GROUP_have_precompute_mult(const EC_GROUP *group)
+       {
+       if (group->meth->mul == 0)
+               /* use default */
+               return ec_wNAF_have_precompute_mult(group);
+
+       if (group->meth->have_precompute_mult != 0)
+               return group->meth->have_precompute_mult(group);
+       else
+               return 0; /* cannot tell whether precomputation has been performed */
+       }