Allow EC_GROUP objects to share precomputation for improved memory
[openssl.git] / crypto / ec / ec_lib.c
index 5e3fb5c..c00875c 100644 (file)
@@ -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);
@@ -173,6 +170,8 @@ void EC_GROUP_clear_free(EC_GROUP *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,67 +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))
-               {
-#if 0 /* this was an error in 0.9.7, but that does not make a lot of sense */
-               ECerr(EC_F_EC_GROUP_GET_EXTRA_DATA, EC_R_NO_SUCH_EXTRA_DATA);
-#endif
+       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;
        }