This is a port of cyaSSL 2.7.0.

Dependents:   CyaSSL_DTLS_Cellular CyaSSL_DTLS_Ethernet

Revision:
0:714293de3836
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ctaocrypt/src/ecc.c	Thu Sep 05 10:33:04 2013 +0000
@@ -0,0 +1,1537 @@
+/* ecc.c
+ *
+ * Copyright (C) 2006-2013 wolfSSL Inc.
+ *
+ * This file is part of CyaSSL.
+ *
+ * CyaSSL is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * CyaSSL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+
+#ifdef HAVE_CONFIG_H
+    #include <config.h>
+#endif
+
+/* in case user set HAVE_ECC there */
+#include <cyassl/ctaocrypt/settings.h>
+
+#ifdef HAVE_ECC
+
+#include <cyassl/ctaocrypt/ecc.h>
+#include <cyassl/ctaocrypt/asn.h>
+#include <cyassl/ctaocrypt/ctaoerror2.h>
+
+
+/* map
+
+   ptmul -> mulmod
+
+*/
+
+#define ECC112
+#define ECC128
+#define ECC160
+#define ECC192
+#define ECC224
+#define ECC256
+#define ECC384
+#define ECC521
+
+
+
+/* This holds the key settings.  ***MUST*** be organized by size from
+   smallest to largest. */
+
+const ecc_set_type ecc_sets[] = {
+#ifdef ECC112
+{
+        14,
+        "SECP112R1",
+        "DB7C2ABF62E35E668076BEAD208B",
+        "659EF8BA043916EEDE8911702B22",
+        "DB7C2ABF62E35E7628DFAC6561C5",
+        "09487239995A5EE76B55F9C2F098",
+        "A89CE5AF8724C0A23E0E0FF77500"
+},
+#endif
+#ifdef ECC128
+{
+        16,
+        "SECP128R1",
+        "FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF",
+        "E87579C11079F43DD824993C2CEE5ED3",
+        "FFFFFFFE0000000075A30D1B9038A115",
+        "161FF7528B899B2D0C28607CA52C5B86",
+        "CF5AC8395BAFEB13C02DA292DDED7A83",
+},
+#endif
+#ifdef ECC160
+{
+        20,
+        "SECP160R1",
+        "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF",
+        "1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45",
+        "0100000000000000000001F4C8F927AED3CA752257",
+        "4A96B5688EF573284664698968C38BB913CBFC82",
+        "23A628553168947D59DCC912042351377AC5FB32",
+},
+#endif
+#ifdef ECC192
+{
+        24,
+        "ECC-192",
+        "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF",
+        "64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1",
+        "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831",
+        "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012",
+        "7192B95FFC8DA78631011ED6B24CDD573F977A11E794811",
+},
+#endif
+#ifdef ECC224
+{
+        28,
+        "ECC-224",
+        "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001",
+        "B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4",
+        "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D",
+        "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21",
+        "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34",
+},
+#endif
+#ifdef ECC256
+{
+        32,
+        "ECC-256",
+        "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF",
+        "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B",
+        "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551",
+        "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296",
+        "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5",
+},
+#endif
+#ifdef ECC384
+{
+        48,
+        "ECC-384",
+        "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF",
+        "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF",
+        "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973",
+        "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7",
+        "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F",
+},
+#endif
+#ifdef ECC521
+{
+        66,
+        "ECC-521",
+        "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
+        "51953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00",
+        "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409",
+        "C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66",
+        "11839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650",
+},
+#endif
+{
+   0,
+   NULL, NULL, NULL, NULL, NULL, NULL
+}
+};
+
+
+ecc_point* ecc_new_point(void);
+void ecc_del_point(ecc_point* p);
+int  ecc_map(ecc_point*, mp_int*, mp_digit*);
+int  ecc_projective_add_point(ecc_point* P, ecc_point* Q, ecc_point* R,
+                              mp_int* modulus, mp_digit* mp);
+int  ecc_projective_dbl_point(ecc_point* P, ecc_point* R, mp_int* modulus,
+                              mp_digit* mp);
+
+
+/* helper for either lib */
+static int get_digit_count(mp_int* a)
+{
+    if (a == NULL)
+        return 0;
+
+    return a->used;
+}
+
+/* helper for either lib */
+static unsigned long get_digit(mp_int* a, int n)
+{
+    if (a == NULL)
+        return 0;
+
+    return (n >= a->used || n < 0) ? 0 : a->dp[n];
+}
+
+
+/**
+   Add two ECC points
+   P        The point to add
+   Q        The point to add
+   R        [out] The destination of the double
+   modulus  The modulus of the field the ECC curve is in
+   mp       The "b" value from montgomery_setup()
+   return   MP_OKAY on success
+*/
+int ecc_projective_add_point(ecc_point* P, ecc_point* Q, ecc_point* R,
+                             mp_int* modulus, mp_digit* mp)
+{
+   mp_int t1;
+   mp_int t2;
+   mp_int x;
+   mp_int y;
+   mp_int z;
+   int    err;
+
+   if (P == NULL || Q == NULL || R == NULL || modulus == NULL || mp == NULL)
+       return ECC_BAD_ARG_E;
+
+   if ((err = mp_init_multi(&t1, &t2, &x, &y, &z, NULL)) != MP_OKAY) {
+      return err;
+   }
+   
+   /* should we dbl instead? */
+   err = mp_sub(modulus, &Q->y, &t1);
+
+   if (err == MP_OKAY) {
+       if ( (mp_cmp(&P->x, &Q->x) == MP_EQ) && 
+            (get_digit_count(&Q->z) && mp_cmp(&P->z, &Q->z) == MP_EQ) &&
+            (mp_cmp(&P->y, &Q->y) == MP_EQ || mp_cmp(&P->y, &t1) == MP_EQ)) {
+                mp_clear(&t1);
+                mp_clear(&t2);
+                mp_clear(&x);
+                mp_clear(&y);
+                mp_clear(&z);
+
+                return ecc_projective_dbl_point(P, R, modulus, mp);
+       }
+   }
+
+   if (err == MP_OKAY)
+       err = mp_copy(&P->x, &x);
+   if (err == MP_OKAY)
+       err = mp_copy(&P->y, &y);
+   if (err == MP_OKAY)
+       err = mp_copy(&P->z, &z);
+
+   /* if Z is one then these are no-operations */
+   if (err == MP_OKAY) {
+       if (get_digit_count(&Q->z)) {
+           /* T1 = Z' * Z' */
+           err = mp_sqr(&Q->z, &t1);
+           if (err == MP_OKAY)
+               err = mp_montgomery_reduce(&t1, modulus, *mp);
+
+           /* X = X * T1 */
+           if (err == MP_OKAY)
+               err = mp_mul(&t1, &x, &x);
+           if (err == MP_OKAY)
+               err = mp_montgomery_reduce(&x, modulus, *mp);
+
+           /* T1 = Z' * T1 */
+           if (err == MP_OKAY)
+               err = mp_mul(&Q->z, &t1, &t1);
+           if (err == MP_OKAY)
+               err = mp_montgomery_reduce(&t1, modulus, *mp);
+
+           /* Y = Y * T1 */
+           if (err == MP_OKAY)
+               err = mp_mul(&t1, &y, &y);
+           if (err == MP_OKAY)
+               err = mp_montgomery_reduce(&y, modulus, *mp);
+       }
+   }
+
+   /* T1 = Z*Z */
+   if (err == MP_OKAY)
+       err = mp_sqr(&z, &t1);
+   if (err == MP_OKAY)
+       err = mp_montgomery_reduce(&t1, modulus, *mp);
+
+   /* T2 = X' * T1 */
+   if (err == MP_OKAY)
+       err = mp_mul(&Q->x, &t1, &t2);
+   if (err == MP_OKAY)
+       err = mp_montgomery_reduce(&t2, modulus, *mp);
+
+   /* T1 = Z * T1 */
+   if (err == MP_OKAY)
+       err = mp_mul(&z, &t1, &t1);
+   if (err == MP_OKAY)
+       err = mp_montgomery_reduce(&t1, modulus, *mp);
+
+   /* T1 = Y' * T1 */
+   if (err == MP_OKAY)
+       err = mp_mul(&Q->y, &t1, &t1);
+   if (err == MP_OKAY)
+       err = mp_montgomery_reduce(&t1, modulus, *mp);
+
+   /* Y = Y - T1 */
+   if (err == MP_OKAY)
+       err = mp_sub(&y, &t1, &y);
+   if (err == MP_OKAY) {
+       if (mp_cmp_d(&y, 0) == MP_LT)
+           err = mp_add(&y, modulus, &y);
+   }
+   /* T1 = 2T1 */
+   if (err == MP_OKAY)
+       err = mp_add(&t1, &t1, &t1);
+   if (err == MP_OKAY) {
+       if (mp_cmp(&t1, modulus) != MP_LT)
+           err = mp_sub(&t1, modulus, &t1);
+   }
+   /* T1 = Y + T1 */
+   if (err == MP_OKAY)
+       err = mp_add(&t1, &y, &t1);
+   if (err == MP_OKAY) {
+       if (mp_cmp(&t1, modulus) != MP_LT)
+           err = mp_sub(&t1, modulus, &t1);
+   }
+   /* X = X - T2 */
+   if (err == MP_OKAY)
+       err = mp_sub(&x, &t2, &x);
+   if (err == MP_OKAY) {
+       if (mp_cmp_d(&x, 0) == MP_LT)
+           err = mp_add(&x, modulus, &x);
+   }
+   /* T2 = 2T2 */
+   if (err == MP_OKAY)
+       err = mp_add(&t2, &t2, &t2);
+   if (err == MP_OKAY) {
+       if (mp_cmp(&t2, modulus) != MP_LT)
+           err = mp_sub(&t2, modulus, &t2);
+   }
+   /* T2 = X + T2 */
+   if (err == MP_OKAY)
+       err = mp_add(&t2, &x, &t2);
+   if (err == MP_OKAY) {
+       if (mp_cmp(&t2, modulus) != MP_LT)
+           err = mp_sub(&t2, modulus, &t2);
+   }
+
+   if (err == MP_OKAY) {
+       if (get_digit_count(&Q->z)) {
+           /* Z = Z * Z' */
+           err = mp_mul(&z, &Q->z, &z);
+           if (err == MP_OKAY)
+               err = mp_montgomery_reduce(&z, modulus, *mp);
+       }
+   }
+
+   /* Z = Z * X */
+   if (err == MP_OKAY)
+       err = mp_mul(&z, &x, &z);
+   if (err == MP_OKAY)
+       err = mp_montgomery_reduce(&z, modulus, *mp);
+
+   /* T1 = T1 * X  */
+   if (err == MP_OKAY)
+       err = mp_mul(&t1, &x, &t1);
+   if (err == MP_OKAY)
+       err = mp_montgomery_reduce(&t1, modulus, *mp);
+
+   /* X = X * X */
+   if (err == MP_OKAY)
+       err = mp_sqr(&x, &x);
+   if (err == MP_OKAY)
+       err = mp_montgomery_reduce(&x, modulus, *mp);
+   
+   /* T2 = T2 * x */
+   if (err == MP_OKAY)
+       err = mp_mul(&t2, &x, &t2);
+   if (err == MP_OKAY)
+       err = mp_montgomery_reduce(&t2, modulus, *mp);
+
+   /* T1 = T1 * X  */
+   if (err == MP_OKAY)
+       err = mp_mul(&t1, &x, &t1);
+   if (err == MP_OKAY)
+       err = mp_montgomery_reduce(&t1, modulus, *mp);
+ 
+   /* X = Y*Y */
+   if (err == MP_OKAY)
+       err = mp_sqr(&y, &x);
+   if (err == MP_OKAY)
+       err = mp_montgomery_reduce(&x, modulus, *mp);
+
+   /* X = X - T2 */
+   if (err == MP_OKAY)
+       err = mp_sub(&x, &t2, &x);
+   if (err == MP_OKAY) {
+       if (mp_cmp_d(&x, 0) == MP_LT)
+           err = mp_add(&x, modulus, &x);
+   }
+   /* T2 = T2 - X */
+   if (err == MP_OKAY)
+       err = mp_sub(&t2, &x, &t2);
+   if (err == MP_OKAY) {
+       if (mp_cmp_d(&t2, 0) == MP_LT)
+           err = mp_add(&t2, modulus, &t2);
+   } 
+   /* T2 = T2 - X */
+   if (err == MP_OKAY)
+       err = mp_sub(&t2, &x, &t2);
+   if (err == MP_OKAY) {
+       if (mp_cmp_d(&t2, 0) == MP_LT)
+           err = mp_add(&t2, modulus, &t2);
+   }
+   /* T2 = T2 * Y */
+   if (err == MP_OKAY)
+       err = mp_mul(&t2, &y, &t2);
+   if (err == MP_OKAY)
+       err = mp_montgomery_reduce(&t2, modulus, *mp);
+
+   /* Y = T2 - T1 */
+   if (err == MP_OKAY)
+       err = mp_sub(&t2, &t1, &y);
+   if (err == MP_OKAY) {
+       if (mp_cmp_d(&y, 0) == MP_LT)
+           err = mp_add(&y, modulus, &y);
+   }
+   /* Y = Y/2 */
+   if (err == MP_OKAY) {
+       if (mp_isodd(&y))
+           err = mp_add(&y, modulus, &y);
+   }
+   if (err == MP_OKAY)
+       err = mp_div_2(&y, &y);
+
+   if (err == MP_OKAY)
+       err = mp_copy(&x, &R->x);
+   if (err == MP_OKAY)
+       err = mp_copy(&y, &R->y);
+   if (err == MP_OKAY)
+       err = mp_copy(&z, &R->z);
+
+   /* clean up */
+   mp_clear(&t1);
+   mp_clear(&t2);
+   mp_clear(&x);
+   mp_clear(&y);
+   mp_clear(&z);
+
+   return err;
+}
+
+
+/**
+   Double an ECC point
+   P   The point to double
+   R   [out] The destination of the double
+   modulus  The modulus of the field the ECC curve is in
+   mp       The "b" value from montgomery_setup()
+   return   MP_OKAY on success
+*/
+int ecc_projective_dbl_point(ecc_point *P, ecc_point *R, mp_int* modulus,
+                             mp_digit* mp)
+{
+   mp_int t1;
+   mp_int t2;
+   int    err;
+
+   if (P == NULL || R == NULL || modulus == NULL || mp == NULL)
+       return ECC_BAD_ARG_E;
+
+   if ((err = mp_init_multi(&t1, &t2, NULL, NULL, NULL, NULL)) != MP_OKAY) {
+      return err;
+   }
+
+   if (P != R) {
+      err = mp_copy(&P->x, &R->x);
+      if (err == MP_OKAY)
+          err = mp_copy(&P->y, &R->y);
+      if (err == MP_OKAY)
+          err = mp_copy(&P->z, &R->z);
+   }
+
+   /* t1 = Z * Z */
+   if (err == MP_OKAY)
+       err = mp_sqr(&R->z, &t1);
+   if (err == MP_OKAY)
+       err = mp_montgomery_reduce(&t1, modulus, *mp);
+
+   /* Z = Y * Z */
+   if (err == MP_OKAY)
+       err = mp_mul(&R->z, &R->y, &R->z);
+   if (err == MP_OKAY)
+       err = mp_montgomery_reduce(&R->z, modulus, *mp);
+
+   /* Z = 2Z */
+   if (err == MP_OKAY)
+       err = mp_add(&R->z, &R->z, &R->z);
+   if (err == MP_OKAY) {
+       if (mp_cmp(&R->z, modulus) != MP_LT)
+           err = mp_sub(&R->z, modulus, &R->z);
+   }
+
+   /* T2 = X - T1 */
+   if (err == MP_OKAY)
+       err = mp_sub(&R->x, &t1, &t2);
+   if (err == MP_OKAY) {
+       if (mp_cmp_d(&t2, 0) == MP_LT)
+           err = mp_add(&t2, modulus, &t2);
+   }
+   /* T1 = X + T1 */
+   if (err == MP_OKAY)
+       err = mp_add(&t1, &R->x, &t1);
+   if (err == MP_OKAY) {
+       if (mp_cmp(&t1, modulus) != MP_LT)
+           err = mp_sub(&t1, modulus, &t1);
+   }
+   /* T2 = T1 * T2 */
+   if (err == MP_OKAY)
+       err = mp_mul(&t1, &t2, &t2);
+   if (err == MP_OKAY)
+       err = mp_montgomery_reduce(&t2, modulus, *mp);
+
+   /* T1 = 2T2 */
+   if (err == MP_OKAY)
+       err = mp_add(&t2, &t2, &t1);
+   if (err == MP_OKAY) {
+       if (mp_cmp(&t1, modulus) != MP_LT)
+           err = mp_sub(&t1, modulus, &t1);
+   }
+   /* T1 = T1 + T2 */
+   if (err == MP_OKAY)
+       err = mp_add(&t1, &t2, &t1);
+   if (err == MP_OKAY) {
+       if (mp_cmp(&t1, modulus) != MP_LT)
+           err = mp_sub(&t1, modulus, &t1);
+   }
+   /* Y = 2Y */
+   if (err == MP_OKAY)
+       err = mp_add(&R->y, &R->y, &R->y);
+   if (err == MP_OKAY) {
+       if (mp_cmp(&R->y, modulus) != MP_LT)
+           err = mp_sub(&R->y, modulus, &R->y);
+   }
+   /* Y = Y * Y */
+   if (err == MP_OKAY)
+       err = mp_sqr(&R->y, &R->y);
+   if (err == MP_OKAY)
+       err = mp_montgomery_reduce(&R->y, modulus, *mp);
+   
+   /* T2 = Y * Y */
+   if (err == MP_OKAY)
+       err = mp_sqr(&R->y, &t2);
+   if (err == MP_OKAY)
+       err = mp_montgomery_reduce(&t2, modulus, *mp);
+
+   /* T2 = T2/2 */
+   if (err == MP_OKAY) {
+       if (mp_isodd(&t2))
+           err = mp_add(&t2, modulus, &t2);
+   }
+   if (err == MP_OKAY)
+       err = mp_div_2(&t2, &t2);
+   
+   /* Y = Y * X */
+   if (err == MP_OKAY)
+       err = mp_mul(&R->y, &R->x, &R->y);
+   if (err == MP_OKAY)
+       err = mp_montgomery_reduce(&R->y, modulus, *mp);
+
+   /* X  = T1 * T1 */
+   if (err == MP_OKAY)
+       err = mp_sqr(&t1, &R->x);
+   if (err == MP_OKAY)
+       err = mp_montgomery_reduce(&R->x, modulus, *mp);
+
+   /* X = X - Y */
+   if (err == MP_OKAY)
+       err = mp_sub(&R->x, &R->y, &R->x);
+   if (err == MP_OKAY) {
+       if (mp_cmp_d(&R->x, 0) == MP_LT)
+           err = mp_add(&R->x, modulus, &R->x);
+   }
+   /* X = X - Y */
+   if (err == MP_OKAY)
+       err = mp_sub(&R->x, &R->y, &R->x);
+   if (err == MP_OKAY) {
+       if (mp_cmp_d(&R->x, 0) == MP_LT)
+           err = mp_add(&R->x, modulus, &R->x);
+   }
+   /* Y = Y - X */     
+   if (err == MP_OKAY)
+       err = mp_sub(&R->y, &R->x, &R->y);
+   if (err == MP_OKAY) {
+       if (mp_cmp_d(&R->y, 0) == MP_LT)
+           err = mp_add(&R->y, modulus, &R->y);
+   }
+   /* Y = Y * T1 */
+   if (err == MP_OKAY)
+       err = mp_mul(&R->y, &t1, &R->y);
+   if (err == MP_OKAY)
+       err = mp_montgomery_reduce(&R->y, modulus, *mp);
+
+   /* Y = Y - T2 */
+   if (err == MP_OKAY)
+       err = mp_sub(&R->y, &t2, &R->y);
+   if (err == MP_OKAY) {
+       if (mp_cmp_d(&R->y, 0) == MP_LT)
+           err = mp_add(&R->y, modulus, &R->y);
+   }
+
+   /* clean up */ 
+   mp_clear(&t1);
+   mp_clear(&t2);
+
+   return err;
+}
+
+
+/**
+  Map a projective jacbobian point back to affine space
+  P        [in/out] The point to map
+  modulus  The modulus of the field the ECC curve is in
+  mp       The "b" value from montgomery_setup()
+  return   MP_OKAY on success
+*/
+int ecc_map(ecc_point* P, mp_int* modulus, mp_digit* mp)
+{
+   mp_int t1;
+   mp_int t2;
+   int    err;
+
+   if (P == NULL || mp == NULL || modulus == NULL)
+       return ECC_BAD_ARG_E;
+
+   if ((err = mp_init_multi(&t1, &t2, NULL, NULL, NULL, NULL)) != MP_OKAY) {
+      return MEMORY_E;
+   }
+
+   /* first map z back to normal */
+   err = mp_montgomery_reduce(&P->z, modulus, *mp);
+
+   /* get 1/z */
+   if (err == MP_OKAY)
+       err = mp_invmod(&P->z, modulus, &t1);
+ 
+   /* get 1/z^2 and 1/z^3 */
+   if (err == MP_OKAY)
+       err = mp_sqr(&t1, &t2);
+   if (err == MP_OKAY)
+       err = mp_mod(&t2, modulus, &t2);
+   if (err == MP_OKAY)
+       err = mp_mul(&t1, &t2, &t1);
+   if (err == MP_OKAY)
+       err = mp_mod(&t1, modulus, &t1);
+
+   /* multiply against x/y */
+   if (err == MP_OKAY)
+       err = mp_mul(&P->x, &t2, &P->x);
+   if (err == MP_OKAY)
+       err = mp_montgomery_reduce(&P->x, modulus, *mp);
+   if (err == MP_OKAY)
+       err = mp_mul(&P->y, &t1, &P->y);
+   if (err == MP_OKAY)
+       err = mp_montgomery_reduce(&P->y, modulus, *mp);
+   
+   if (err == MP_OKAY)
+       mp_set(&P->z, 1);
+
+   /* clean up */
+   mp_clear(&t1);
+   mp_clear(&t2);
+
+   return err;
+}
+
+
+#ifndef ECC_TIMING_RESISTANT
+
+/* size of sliding window, don't change this! */
+#define WINSIZE 4
+
+/**
+   Perform a point multiplication 
+   k    The scalar to multiply by
+   G    The base point
+   R    [out] Destination for kG
+   modulus  The modulus of the field the ECC curve is in
+   map      Boolean whether to map back to affine or not
+                (1==map, 0 == leave in projective)
+   return MP_OKAY on success
+*/
+static int ecc_mulmod(mp_int* k, ecc_point *G, ecc_point *R, mp_int* modulus,
+                      int map)
+{
+   ecc_point *tG, *M[8];
+   int           i, j, err;
+   mp_int        mu;
+   mp_digit      mp;
+   unsigned long buf;
+   int           first, bitbuf, bitcpy, bitcnt, mode, digidx;
+
+   if (k == NULL || G == NULL || R == NULL || modulus == NULL)
+       return ECC_BAD_ARG_E;
+
+   /* init montgomery reduction */
+   if ((err = mp_montgomery_setup(modulus, &mp)) != MP_OKAY) {
+      return err;
+   }
+   if ((err = mp_init(&mu)) != MP_OKAY) {
+      return err;
+   }
+   if ((err = mp_montgomery_calc_normalization(&mu, modulus)) != MP_OKAY) {
+      mp_clear(&mu);
+      return err;
+   }
+  
+  /* alloc ram for window temps */
+  for (i = 0; i < 8; i++) {
+      M[i] = ecc_new_point();
+      if (M[i] == NULL) {
+         for (j = 0; j < i; j++) {
+             ecc_del_point(M[j]);
+         }
+         mp_clear(&mu);
+         return MEMORY_E;
+      }
+  }
+
+   /* make a copy of G incase R==G */
+   tG = ecc_new_point();
+   if (tG == NULL)
+       err = MEMORY_E;
+
+   /* tG = G  and convert to montgomery */
+   if (err == MP_OKAY) {
+       if (mp_cmp_d(&mu, 1) == MP_EQ) {
+           err = mp_copy(&G->x, &tG->x);
+           if (err == MP_OKAY)
+               err = mp_copy(&G->y, &tG->y);
+           if (err == MP_OKAY)
+               err = mp_copy(&G->z, &tG->z);
+       } else {
+           err = mp_mulmod(&G->x, &mu, modulus, &tG->x);
+           if (err == MP_OKAY)
+               err = mp_mulmod(&G->y, &mu, modulus, &tG->y);
+           if (err == MP_OKAY)
+               err = mp_mulmod(&G->z, &mu, modulus, &tG->z);
+       }
+   }
+   mp_clear(&mu);
+   
+   /* calc the M tab, which holds kG for k==8..15 */
+   /* M[0] == 8G */
+   if (err == MP_OKAY)
+       err = ecc_projective_dbl_point(tG, M[0], modulus, &mp);
+   if (err == MP_OKAY)
+       err = ecc_projective_dbl_point(M[0], M[0], modulus, &mp);
+   if (err == MP_OKAY)
+       err = ecc_projective_dbl_point(M[0], M[0], modulus, &mp);
+
+   /* now find (8+k)G for k=1..7 */
+   if (err == MP_OKAY)
+       for (j = 9; j < 16; j++) {
+           err = ecc_projective_add_point(M[j-9], tG, M[j-8], modulus, &mp);
+           if (err != MP_OKAY) break;
+       }
+
+   /* setup sliding window */
+   if (err == MP_OKAY) {
+       mode   = 0;
+       bitcnt = 1;
+       buf    = 0;
+       digidx = get_digit_count(k) - 1;
+       bitcpy = bitbuf = 0;
+       first  = 1;
+
+       /* perform ops */
+       for (;;) {
+           /* grab next digit as required */
+           if (--bitcnt == 0) {
+               if (digidx == -1) {
+                   break;
+               }
+               buf    = get_digit(k, digidx);
+               bitcnt = (int) DIGIT_BIT; 
+               --digidx;
+           }
+
+           /* grab the next msb from the ltiplicand */
+           i = (int)(buf >> (DIGIT_BIT - 1)) & 1;
+           buf <<= 1;
+
+           /* skip leading zero bits */
+           if (mode == 0 && i == 0)
+               continue;
+
+           /* if the bit is zero and mode == 1 then we double */
+           if (mode == 1 && i == 0) {
+               err = ecc_projective_dbl_point(R, R, modulus, &mp);
+               if (err != MP_OKAY) break;
+               continue;
+           }
+
+           /* else we add it to the window */
+           bitbuf |= (i << (WINSIZE - ++bitcpy));
+           mode = 2;
+
+           if (bitcpy == WINSIZE) {
+               /* if this is the first window we do a simple copy */
+               if (first == 1) {
+                   /* R = kG [k = first window] */
+                   err = mp_copy(&M[bitbuf-8]->x, &R->x);
+                   if (err != MP_OKAY) break;
+
+                   err = mp_copy(&M[bitbuf-8]->y, &R->y);
+                   if (err != MP_OKAY) break;
+
+                   err = mp_copy(&M[bitbuf-8]->z, &R->z);
+                   first = 0;
+               } else {
+                   /* normal window */
+                   /* ok window is filled so double as required and add  */
+                   /* double first */
+                   for (j = 0; j < WINSIZE; j++) {
+                       err = ecc_projective_dbl_point(R, R, modulus, &mp);
+                       if (err != MP_OKAY) break;
+                   }
+                   if (err != MP_OKAY) break;  /* out of first for(;;) */
+
+                   /* then add, bitbuf will be 8..15 [8..2^WINSIZE] guaranted */
+                   err = ecc_projective_add_point(R,M[bitbuf-8],R,modulus,&mp);
+               }
+               if (err != MP_OKAY) break;
+               /* empty window and reset */
+               bitcpy = bitbuf = 0;
+               mode = 1;
+           }
+       }
+   }
+
+   /* if bits remain then double/add */
+   if (err == MP_OKAY) {
+       if (mode == 2 && bitcpy > 0) {
+           /* double then add */
+           for (j = 0; j < bitcpy; j++) {
+               /* only double if we have had at least one add first */
+               if (first == 0) {
+                   err = ecc_projective_dbl_point(R, R, modulus, &mp);
+                   if (err != MP_OKAY) break;
+               }
+
+               bitbuf <<= 1;
+               if ((bitbuf & (1 << WINSIZE)) != 0) {
+                   if (first == 1) {
+                       /* first add, so copy */
+                       err = mp_copy(&tG->x, &R->x);
+                       if (err != MP_OKAY) break;
+
+                       err = mp_copy(&tG->y, &R->y);
+                       if (err != MP_OKAY) break;
+
+                       err = mp_copy(&tG->z, &R->z);
+                       if (err != MP_OKAY) break;
+                       first = 0;
+                   } else {
+                       /* then add */
+                       err = ecc_projective_add_point(R, tG, R, modulus, &mp);
+                       if (err != MP_OKAY) break;
+                   }
+               }
+           }
+       }
+   }
+
+   /* map R back from projective space */
+   if (err == MP_OKAY && map)
+       err = ecc_map(R, modulus, &mp);
+
+   mp_clear(&mu);
+   ecc_del_point(tG);
+   for (i = 0; i < 8; i++) {
+       ecc_del_point(M[i]);
+   }
+   return err;
+}
+
+#undef WINSIZE
+#endif /* ECC_TIMING_RESISTANT */
+
+
+/**
+   Allocate a new ECC point
+   return A newly allocated point or NULL on error 
+*/
+ecc_point* ecc_new_point(void)
+{
+   ecc_point* p;
+   p = (ecc_point*)XMALLOC(sizeof(ecc_point), 0, DYNAMIC_TYPE_BIGINT);
+   if (p == NULL) {
+      return NULL;
+   }
+   XMEMSET(p, 0, sizeof(ecc_point));
+   if (mp_init_multi(&p->x, &p->y, &p->z, NULL, NULL, NULL) != MP_OKAY) {
+      XFREE(p, 0, DYNAMIC_TYPE_BIGINT);
+      return NULL;
+   }
+   return p;
+}
+
+/** Free an ECC point from memory
+  p   The point to free
+*/
+void ecc_del_point(ecc_point* p)
+{
+   /* prevents free'ing null arguments */
+   if (p != NULL) {
+      mp_clear(&p->x);
+      mp_clear(&p->y);
+      mp_clear(&p->z);
+      XFREE(p, 0, DYNAMIC_TYPE_BIGINT);
+   }
+}
+
+
+/** Returns whether an ECC idx is valid or not
+  n      The idx number to check
+  return 1 if valid, 0 if not
+*/  
+static int ecc_is_valid_idx(int n)
+{
+   int x;
+
+   for (x = 0; ecc_sets[x].size != 0; x++)
+       ;
+   /* -1 is a valid index --- indicating that the domain params
+      were supplied by the user */
+   if ((n >= -1) && (n < x)) {
+      return 1;
+   }
+   return 0;
+}
+
+
+/**
+  Create an ECC shared secret between two keys
+  private_key      The private ECC key
+  public_key       The public key
+  out              [out] Destination of the shared secret
+                   Conforms to EC-DH from ANSI X9.63
+  outlen           [in/out] The max size and resulting size of the shared secret
+  return           MP_OKAY if successful
+*/
+int ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, byte* out,
+                      word32* outlen)
+{
+   word32         x = 0;
+   ecc_point*     result;
+   mp_int         prime;
+   int            err;
+
+   if (private_key == NULL || public_key == NULL || out == NULL ||
+                                                    outlen == NULL)
+       return BAD_FUNC_ARG;
+
+   /* type valid? */
+   if (private_key->type != ECC_PRIVATEKEY) {
+      return ECC_BAD_ARG_E;
+   }
+
+   if (ecc_is_valid_idx(private_key->idx) == 0 ||
+       ecc_is_valid_idx(public_key->idx)  == 0)
+      return ECC_BAD_ARG_E;
+
+   if (XSTRNCMP(private_key->dp->name, public_key->dp->name, ECC_MAXNAME) != 0)
+      return ECC_BAD_ARG_E;
+
+   /* make new point */
+   result = ecc_new_point();
+   if (result == NULL) {
+      return MEMORY_E;
+   }
+
+   if ((err = mp_init(&prime)) != MP_OKAY) {
+      ecc_del_point(result);
+      return err;
+   }
+
+   err = mp_read_radix(&prime, (char *)private_key->dp->prime, 16);
+
+   if (err == MP_OKAY)
+       err = ecc_mulmod(&private_key->k, &public_key->pubkey, result, &prime,1);
+
+   if (err == MP_OKAY) {
+       x = mp_unsigned_bin_size(&prime);
+       if (*outlen < x)
+          err = BUFFER_E;
+   }
+
+   if (err == MP_OKAY) {
+       XMEMSET(out, 0, x);
+       err = mp_to_unsigned_bin(&result->x,out + (x -
+                                            mp_unsigned_bin_size(&result->x)));
+       *outlen = x;
+   }
+
+   mp_clear(&prime);
+   ecc_del_point(result);
+
+   return err;
+}
+
+
+int ecc_make_key_ex(RNG* rng, ecc_key* key, const ecc_set_type* dp);
+
+/**
+  Make a new ECC key 
+  rng          An active RNG state
+  keysize      The keysize for the new key (in octets from 20 to 65 bytes)
+  key          [out] Destination of the newly created key
+  return       MP_OKAY if successful,
+                       upon error all allocated memory will be freed
+*/
+int ecc_make_key(RNG* rng, int keysize, ecc_key* key)
+{
+   int x, err;
+
+   /* find key size */
+   for (x = 0; (keysize > ecc_sets[x].size) && (ecc_sets[x].size != 0); x++)
+       ;
+   keysize = ecc_sets[x].size;
+
+   if (keysize > ECC_MAXSIZE || ecc_sets[x].size == 0) {
+      return BAD_FUNC_ARG;
+   }
+   err = ecc_make_key_ex(rng, key, &ecc_sets[x]);
+   key->idx = x;
+
+   return err;
+}
+
+int ecc_make_key_ex(RNG* rng, ecc_key* key, const ecc_set_type* dp)
+{
+   int            err;
+   ecc_point*     base;
+   mp_int         prime;
+   mp_int         order;
+   byte           buf[ECC_MAXSIZE];
+   int            keysize;
+
+   if (key == NULL || rng == NULL || dp == NULL)
+       return ECC_BAD_ARG_E;
+
+   key->idx = -1;
+   key->dp  = dp;
+   keysize  = dp->size;
+
+   /* allocate ram */
+   base = NULL;
+
+   /* make up random string */
+   RNG_GenerateBlock(rng, buf, keysize);
+   buf[0] |= 0x0c;
+
+   /* setup the key variables */
+   if ((err = mp_init_multi(&key->pubkey.x, &key->pubkey.y, &key->pubkey.z,
+                            &key->k, &prime, &order)) != MP_OKAY)
+       return MEMORY_E;
+
+   base = ecc_new_point();
+   if (base == NULL)
+      err = MEMORY_E;
+
+   /* read in the specs for this key */
+   if (err == MP_OKAY) 
+       err = mp_read_radix(&prime,   (char *)key->dp->prime, 16);
+   if (err == MP_OKAY) 
+       err = mp_read_radix(&order,   (char *)key->dp->order, 16);
+   if (err == MP_OKAY) 
+       err = mp_read_radix(&base->x, (char *)key->dp->Gx, 16);
+   if (err == MP_OKAY) 
+       err = mp_read_radix(&base->y, (char *)key->dp->Gy, 16);
+   
+   if (err == MP_OKAY) 
+       mp_set(&base->z, 1);
+   if (err == MP_OKAY) 
+       err = mp_read_unsigned_bin(&key->k, (byte*)buf, keysize);
+
+   /* the key should be smaller than the order of base point */
+   if (err == MP_OKAY) { 
+       if (mp_cmp(&key->k, &order) != MP_LT)
+           err = mp_mod(&key->k, &order, &key->k);
+   }
+   /* make the public key */
+   if (err == MP_OKAY)
+       err = ecc_mulmod(&key->k, base, &key->pubkey, &prime, 1);
+   if (err == MP_OKAY)
+       key->type = ECC_PRIVATEKEY;
+
+   if (err != MP_OKAY) {
+       /* clean up */
+       mp_clear(&key->pubkey.x);
+       mp_clear(&key->pubkey.y);
+       mp_clear(&key->pubkey.z);
+       mp_clear(&key->k);
+   }
+   ecc_del_point(base);
+   mp_clear(&prime);
+   mp_clear(&order);
+#ifdef ECC_CLEAN_STACK
+   XMEMSET(buff, 0, ECC_MAXSIZE);
+#endif
+   return err;
+}
+
+
+/* Setup dynamic pointers is using normal math for proper freeing */
+void ecc_init(ecc_key* key)
+{
+    (void)key;
+#ifndef USE_FAST_MATH
+    key->pubkey.x.dp = NULL;
+    key->pubkey.y.dp = NULL;
+    key->pubkey.z.dp = NULL;
+
+    key->k.dp = NULL;
+#endif
+}
+
+
+/**
+  Sign a message digest
+  in        The message digest to sign
+  inlen     The length of the digest
+  out       [out] The destination for the signature
+  outlen    [in/out] The max size and resulting size of the signature
+  key       A private ECC key
+  return    MP_OKAY if successful
+*/
+int ecc_sign_hash(const byte* in, word32 inlen, byte* out, word32 *outlen, 
+                  RNG* rng, ecc_key* key)
+{
+   mp_int        r;
+   mp_int        s;
+   mp_int        e;
+   mp_int        p;
+   int           err;
+
+   if (in == NULL || out == NULL || outlen == NULL || key == NULL || rng ==NULL)
+       return ECC_BAD_ARG_E;
+
+   /* is this a private key? */
+   if (key->type != ECC_PRIVATEKEY) {
+      return ECC_BAD_ARG_E;
+   }
+   
+   /* is the IDX valid ?  */
+   if (ecc_is_valid_idx(key->idx) != 1) {
+      return ECC_BAD_ARG_E;
+   }
+
+   /* get the hash and load it as a bignum into 'e' */
+   /* init the bignums */
+   if ((err = mp_init_multi(&r, &s, &p, &e, NULL, NULL)) != MP_OKAY) { 
+      return err;
+   }
+   err = mp_read_radix(&p, (char *)key->dp->order, 16);
+
+   if (err == MP_OKAY) {
+       int truncLen = (int)inlen;
+       if (truncLen > ecc_size(key))
+           truncLen = ecc_size(key);
+       err = mp_read_unsigned_bin(&e, (byte*)in, truncLen);
+   }
+
+   /* make up a key and export the public copy */
+   if (err == MP_OKAY) {
+       ecc_key pubkey;
+       ecc_init(&pubkey);
+       for (;;) {
+           err = ecc_make_key_ex(rng, &pubkey, key->dp);
+           if (err != MP_OKAY) break;
+
+           /* find r = x1 mod n */
+           err = mp_mod(&pubkey.pubkey.x, &p, &r);
+           if (err != MP_OKAY) break;
+
+           if (mp_iszero(&r) == MP_YES)
+               ecc_free(&pubkey);
+           else { 
+               /* find s = (e + xr)/k */
+               err = mp_invmod(&pubkey.k, &p, &pubkey.k);
+               if (err != MP_OKAY) break;
+
+               err = mp_mulmod(&key->k, &r, &p, &s);   /* s = xr */
+               if (err != MP_OKAY) break;
+           
+               err = mp_add(&e, &s, &s);               /* s = e +  xr */
+               if (err != MP_OKAY) break;
+
+               err = mp_mod(&s, &p, &s);               /* s = e +  xr */
+               if (err != MP_OKAY) break;
+
+               err = mp_mulmod(&s, &pubkey.k, &p, &s); /* s = (e + xr)/k */
+               if (err != MP_OKAY) break;
+
+               ecc_free(&pubkey);
+               if (mp_iszero(&s) == MP_NO)
+                   break;
+            }
+       }
+       ecc_free(&pubkey);
+   }
+
+   /* store as SEQUENCE { r, s -- integer } */
+   if (err == MP_OKAY)
+       err = StoreECC_DSA_Sig(out, outlen, &r, &s);
+
+   mp_clear(&r);
+   mp_clear(&s);
+   mp_clear(&p);
+   mp_clear(&e);
+
+   return err;
+}
+
+
+/**
+  Free an ECC key from memory
+  key   The key you wish to free
+*/
+void ecc_free(ecc_key* key)
+{
+   if (key == NULL)
+       return;
+
+   mp_clear(&key->pubkey.x);
+   mp_clear(&key->pubkey.y);
+   mp_clear(&key->pubkey.z);
+   mp_clear(&key->k);
+}
+
+
+/* verify 
+ *
+ * w  = s^-1 mod n
+ * u1 = xw 
+ * u2 = rw
+ * X = u1*G + u2*Q
+ * v = X_x1 mod n
+ * accept if v == r
+ */
+
+/**
+   Verify an ECC signature
+   sig         The signature to verify
+   siglen      The length of the signature (octets)
+   hash        The hash (message digest) that was signed
+   hashlen     The length of the hash (octets)
+   stat        Result of signature, 1==valid, 0==invalid
+   key         The corresponding public ECC key
+   return      MP_OKAY if successful (even if the signature is not valid)
+*/
+int ecc_verify_hash(const byte* sig, word32 siglen, byte* hash, word32 hashlen, 
+                    int* stat, ecc_key* key)
+{
+   ecc_point    *mG, *mQ;
+   mp_int        r;
+   mp_int        s;
+   mp_int        v;
+   mp_int        w;
+   mp_int        u1;
+   mp_int        u2;
+   mp_int        e;
+   mp_int        p;
+   mp_int        m;
+   mp_digit      mp;
+   int           err;
+
+   if (sig == NULL || hash == NULL || stat == NULL || key == NULL)
+       return ECC_BAD_ARG_E; 
+
+   /* default to invalid signature */
+   *stat = 0;
+
+   /* is the IDX valid ?  */
+   if (ecc_is_valid_idx(key->idx) != 1) {
+      return ECC_BAD_ARG_E;
+   }
+
+   /* allocate ints */
+   if ((err = mp_init_multi(&v, &w, &u1, &u2, &p, &e)) != MP_OKAY) {
+      return MEMORY_E;
+   }
+
+   if ((err = mp_init(&m)) != MP_OKAY) {
+      mp_clear(&v);
+      mp_clear(&w);
+      mp_clear(&u1);
+      mp_clear(&u2);
+      mp_clear(&p);
+      mp_clear(&e);
+      return MEMORY_E;
+   }
+
+   /* allocate points */
+   mG = ecc_new_point();
+   mQ = ecc_new_point();
+   if (mQ  == NULL || mG == NULL)
+      err = MEMORY_E;
+
+   /* Note, DecodeECC_DSA_Sig() calls mp_init() on r and s.
+    * If either of those don't allocate correctly, none of
+    * the rest of this function will execute, and everything
+    * gets cleaned up at the end. */
+   XMEMSET(&r, 0, sizeof(r));
+   XMEMSET(&s, 0, sizeof(s));
+   if (err == MP_OKAY) 
+       err = DecodeECC_DSA_Sig(sig, siglen, &r, &s);
+
+   /* get the order */
+   if (err == MP_OKAY)
+       err = mp_read_radix(&p, (char *)key->dp->order, 16);
+
+   /* get the modulus */
+   if (err == MP_OKAY)
+       err = mp_read_radix(&m, (char *)key->dp->prime, 16);
+
+   /* check for zero */
+   if (err == MP_OKAY) {
+       if (mp_iszero(&r) || mp_iszero(&s) || mp_cmp(&r, &p) != MP_LT ||
+                                             mp_cmp(&s, &p) != MP_LT)
+           err = MP_ZERO_E; 
+   }
+   /* read hash */
+   if (err == MP_OKAY) {
+       int truncLen = (int)hashlen;
+       if (truncLen > ecc_size(key))
+           truncLen = ecc_size(key);
+       err = mp_read_unsigned_bin(&e, (byte*)hash, truncLen);
+   }
+
+   /*  w  = s^-1 mod n */
+   if (err == MP_OKAY)
+       err = mp_invmod(&s, &p, &w);
+
+   /* u1 = ew */
+   if (err == MP_OKAY)
+       err = mp_mulmod(&e, &w, &p, &u1);
+
+   /* u2 = rw */
+   if (err == MP_OKAY)
+       err = mp_mulmod(&r, &w, &p, &u2);
+
+   /* find mG and mQ */
+   if (err == MP_OKAY)
+       err = mp_read_radix(&mG->x, (char *)key->dp->Gx, 16);
+
+   if (err == MP_OKAY)
+       err = mp_read_radix(&mG->y, (char *)key->dp->Gy, 16);
+   if (err == MP_OKAY)
+       mp_set(&mG->z, 1);
+
+   if (err == MP_OKAY)
+       err = mp_copy(&key->pubkey.x, &mQ->x);
+   if (err == MP_OKAY)
+       err = mp_copy(&key->pubkey.y, &mQ->y);
+   if (err == MP_OKAY)
+       err = mp_copy(&key->pubkey.z, &mQ->z);
+
+#ifndef ECC_SHAMIR
+       /* compute u1*mG + u2*mQ = mG */
+       if (err == MP_OKAY)
+           err = ecc_mulmod(&u1, mG, mG, &m, 0);
+       if (err == MP_OKAY)
+           err = ecc_mulmod(&u2, mQ, mQ, &m, 0);
+  
+       /* find the montgomery mp */
+       if (err == MP_OKAY)
+           err = mp_montgomery_setup(&m, &mp);
+
+       /* add them */
+       if (err == MP_OKAY)
+           err = ecc_projective_add_point(mQ, mG, mG, &m, &mp);
+   
+       /* reduce */
+       if (err == MP_OKAY)
+           err = ecc_map(mG, &m, &mp);
+#else
+       /* use Shamir's trick to compute u1*mG + u2*mQ using half the doubles */
+       if (err == MP_OKAY)
+           err = ecc_mul2add(mG, &u1, mQ, &u2, mG, &m);
+#endif /* ECC_SHAMIR */ 
+
+   /* v = X_x1 mod n */
+   if (err == MP_OKAY)
+       err = mp_mod(&mG->x, &p, &v);
+
+   /* does v == r */
+   if (err == MP_OKAY) {
+       if (mp_cmp(&v, &r) == MP_EQ)
+           *stat = 1;
+   }
+
+   ecc_del_point(mG);
+   ecc_del_point(mQ);
+
+   mp_clear(&r);
+   mp_clear(&s);
+   mp_clear(&v);
+   mp_clear(&w);
+   mp_clear(&u1);
+   mp_clear(&u2);
+   mp_clear(&p);
+   mp_clear(&e);
+   mp_clear(&m);
+
+   return err;
+}
+
+
+/* export public ECC key in ANSI X9.63 format */
+int ecc_export_x963(ecc_key* key, byte* out, word32* outLen)
+{
+   byte   buf[ECC_BUFSIZE];
+   word32 numlen;
+
+   if (key == NULL || out == NULL || outLen == NULL)
+       return ECC_BAD_ARG_E;
+
+   if (ecc_is_valid_idx(key->idx) == 0) {
+      return ECC_BAD_ARG_E;
+   }
+   numlen = key->dp->size;
+
+   if (*outLen < (1 + 2*numlen)) {
+      *outLen = 1 + 2*numlen;
+      return BUFFER_E;
+   }
+
+   /* store byte 0x04 */
+   out[0] = 0x04;
+
+   /* pad and store x */
+   XMEMSET(buf, 0, sizeof(buf));
+   mp_to_unsigned_bin(&key->pubkey.x,
+                      buf + (numlen - mp_unsigned_bin_size(&key->pubkey.x)));
+   XMEMCPY(out+1, buf, numlen);
+
+   /* pad and store y */
+   XMEMSET(buf, 0, sizeof(buf));
+   mp_to_unsigned_bin(&key->pubkey.y,
+                      buf + (numlen - mp_unsigned_bin_size(&key->pubkey.y)));
+   XMEMCPY(out+1+numlen, buf, numlen);
+
+   *outLen = 1 + 2*numlen;
+
+   return 0;
+}
+
+
+/* import public ECC key in ANSI X9.63 format */
+int ecc_import_x963(const byte* in, word32 inLen, ecc_key* key)
+{
+   int x, err;
+
+   
+   if (in == NULL || key == NULL)
+       return ECC_BAD_ARG_E;
+
+   /* must be odd */
+   if ((inLen & 1) == 0) {
+      return ECC_BAD_ARG_E;
+   }
+
+   /* init key */
+   if (mp_init_multi(&key->pubkey.x, &key->pubkey.y, &key->pubkey.z, &key->k,
+                     NULL, NULL) != MP_OKAY) {
+      return MEMORY_E;
+   }
+   err = MP_OKAY;
+
+   /* check for 4, 6 or 7 */
+   if (in[0] != 4 && in[0] != 6 && in[0] != 7) {
+      err = ASN_PARSE_E;
+   }
+
+   /* read data */
+   if (err == MP_OKAY) 
+       err = mp_read_unsigned_bin(&key->pubkey.x, (byte*)in+1, (inLen-1)>>1);
+
+   if (err == MP_OKAY) 
+       err = mp_read_unsigned_bin(&key->pubkey.y, (byte*)in+1+((inLen-1)>>1),
+                                  (inLen-1)>>1);
+   
+   if (err == MP_OKAY) 
+       mp_set(&key->pubkey.z, 1);
+
+   if (err == MP_OKAY) {
+     /* determine the idx */
+      for (x = 0; ecc_sets[x].size != 0; x++) {
+         if ((unsigned)ecc_sets[x].size >= ((inLen-1)>>1)) {
+            break;
+         }
+      }
+      if (ecc_sets[x].size == 0) {
+         err = ASN_PARSE_E;
+      } else {
+          /* set the idx */
+          key->idx  = x;
+          key->dp = &ecc_sets[x];
+          key->type = ECC_PUBLICKEY;
+      }
+   }
+
+   if (err != MP_OKAY) {
+       mp_clear(&key->pubkey.x);
+       mp_clear(&key->pubkey.y);
+       mp_clear(&key->pubkey.z);
+       mp_clear(&key->k);
+   }
+
+   return err;
+}
+
+
+/* ecc private key import, public key in ANSI X9.63 format, private raw */
+int ecc_import_private_key(const byte* priv, word32 privSz, const byte* pub,
+                           word32 pubSz, ecc_key* key)
+{
+    int ret = ecc_import_x963(pub, pubSz, key);
+    if (ret != 0)
+        return ret;
+
+    key->type = ECC_PRIVATEKEY;
+
+    return mp_read_unsigned_bin(&key->k, priv, privSz);
+}
+
+
+/* key size in octets */
+int ecc_size(ecc_key* key)
+{
+    if (key == NULL) return 0;
+
+    return key->dp->size;
+}
+
+
+/* signature size in octets */
+int ecc_sig_size(ecc_key* key)
+{
+    int sz = ecc_size(key);
+    if (sz < 0)
+        return sz;
+
+    return sz * 2 + SIG_HEADER_SZ;
+}
+
+#endif /* HAVE_ECC */